mirror of https://github.com/k3s-io/k3s
vendor buildozer
parent
d49953e8d0
commit
e070d9fd67
|
@ -472,16 +472,51 @@
|
||||||
"Comment": "0.15.0",
|
"Comment": "0.15.0",
|
||||||
"Rev": "c728ce9f663e2bff26361ba5978ec5c9e6816a3c"
|
"Rev": "c728ce9f663e2bff26361ba5978ec5c9e6816a3c"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bazelbuild/buildtools/api_proto",
|
||||||
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/bazelbuild/buildtools/build",
|
"ImportPath": "github.com/bazelbuild/buildtools/build",
|
||||||
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bazelbuild/buildtools/build_proto",
|
||||||
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bazelbuild/buildtools/buildozer",
|
||||||
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bazelbuild/buildtools/edit",
|
||||||
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bazelbuild/buildtools/file",
|
||||||
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bazelbuild/buildtools/lang",
|
||||||
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/bazelbuild/buildtools/tables",
|
"ImportPath": "github.com/bazelbuild/buildtools/tables",
|
||||||
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/bazelbuild/buildtools/wspace",
|
||||||
|
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||||
|
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||||
"Rev": "3ac7bf7a47d159a033b107610db8a1b6575507a4"
|
"Rev": "3ac7bf7a47d159a033b107610db8a1b6575507a4"
|
||||||
|
@ -4240,4 +4275,4 @@
|
||||||
"Rev": "db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394"
|
"Rev": "db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -14961,6 +14961,27 @@ THE SOFTWARE.
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/api_proto licensed under: =
|
||||||
|
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/LICENSE adb52eb384caedba181cd51fbcdf4b99
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
= vendor/github.com/bazelbuild/buildtools/build licensed under: =
|
= vendor/github.com/bazelbuild/buildtools/build licensed under: =
|
||||||
|
|
||||||
|
@ -14982,6 +15003,111 @@ limitations under the License.
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/buildozer licensed under: =
|
||||||
|
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/LICENSE adb52eb384caedba181cd51fbcdf4b99
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/build_proto licensed under: =
|
||||||
|
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/LICENSE adb52eb384caedba181cd51fbcdf4b99
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/edit licensed under: =
|
||||||
|
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/LICENSE adb52eb384caedba181cd51fbcdf4b99
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/file licensed under: =
|
||||||
|
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/LICENSE adb52eb384caedba181cd51fbcdf4b99
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/lang licensed under: =
|
||||||
|
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/LICENSE adb52eb384caedba181cd51fbcdf4b99
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
= vendor/github.com/bazelbuild/buildtools/tables licensed under: =
|
= vendor/github.com/bazelbuild/buildtools/tables licensed under: =
|
||||||
|
|
||||||
|
@ -15003,6 +15129,27 @@ limitations under the License.
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/wspace licensed under: =
|
||||||
|
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
= vendor/github.com/bazelbuild/buildtools/LICENSE adb52eb384caedba181cd51fbcdf4b99
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
= vendor/github.com/beorn7/perks/quantile licensed under: =
|
= vendor/github.com/beorn7/perks/quantile licensed under: =
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,15 @@ filegroup(
|
||||||
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/version:all-srcs",
|
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/version:all-srcs",
|
||||||
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/walk:all-srcs",
|
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/walk:all-srcs",
|
||||||
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/wspace:all-srcs",
|
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/wspace:all-srcs",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/api_proto:all-srcs",
|
||||||
"//vendor/github.com/bazelbuild/buildtools/build:all-srcs",
|
"//vendor/github.com/bazelbuild/buildtools/build:all-srcs",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/build_proto:all-srcs",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/buildozer:all-srcs",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/edit:all-srcs",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/file:all-srcs",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/lang:all-srcs",
|
||||||
"//vendor/github.com/bazelbuild/buildtools/tables:all-srcs",
|
"//vendor/github.com/bazelbuild/buildtools/tables:all-srcs",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/wspace:all-srcs",
|
||||||
"//vendor/github.com/beorn7/perks/quantile:all-srcs",
|
"//vendor/github.com/beorn7/perks/quantile:all-srcs",
|
||||||
"//vendor/github.com/blang/semver:all-srcs",
|
"//vendor/github.com/blang/semver:all-srcs",
|
||||||
"//vendor/github.com/cespare/prettybench:all-srcs",
|
"//vendor/github.com/cespare/prettybench:all-srcs",
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["api.gen.pb.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/github.com/bazelbuild/buildtools/api_proto",
|
||||||
|
importpath = "github.com/bazelbuild/buildtools/api_proto",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = ["//vendor/github.com/golang/protobuf/proto:go_default_library"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
315
vendor/github.com/bazelbuild/buildtools/api_proto/api.gen.pb.go
generated
vendored
Executable file
315
vendor/github.com/bazelbuild/buildtools/api_proto/api.gen.pb.go
generated
vendored
Executable file
|
@ -0,0 +1,315 @@
|
||||||
|
// Code generated by protoc-gen-go.
|
||||||
|
// source: api_proto/api.proto
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package devtools_buildozer is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
api_proto/api.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
Output
|
||||||
|
RepeatedString
|
||||||
|
*/
|
||||||
|
package devtools_buildozer
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Output_Record_Field_ERROR int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Output_Record_Field_UNKNOWN Output_Record_Field_ERROR = 0
|
||||||
|
Output_Record_Field_MISSING Output_Record_Field_ERROR = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var Output_Record_Field_ERROR_name = map[int32]string{
|
||||||
|
0: "UNKNOWN",
|
||||||
|
1: "MISSING",
|
||||||
|
}
|
||||||
|
var Output_Record_Field_ERROR_value = map[string]int32{
|
||||||
|
"UNKNOWN": 0,
|
||||||
|
"MISSING": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Output_Record_Field_ERROR) String() string {
|
||||||
|
return proto.EnumName(Output_Record_Field_ERROR_name, int32(x))
|
||||||
|
}
|
||||||
|
func (Output_Record_Field_ERROR) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor0, []int{0, 0, 0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output struct {
|
||||||
|
Records []*Output_Record `protobuf:"bytes,1,rep,name=records" json:"records,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output) Reset() { *m = Output{} }
|
||||||
|
func (m *Output) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Output) ProtoMessage() {}
|
||||||
|
func (*Output) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
func (m *Output) GetRecords() []*Output_Record {
|
||||||
|
if m != nil {
|
||||||
|
return m.Records
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output_Record struct {
|
||||||
|
Fields []*Output_Record_Field `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output_Record) Reset() { *m = Output_Record{} }
|
||||||
|
func (m *Output_Record) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Output_Record) ProtoMessage() {}
|
||||||
|
func (*Output_Record) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
|
||||||
|
|
||||||
|
func (m *Output_Record) GetFields() []*Output_Record_Field {
|
||||||
|
if m != nil {
|
||||||
|
return m.Fields
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output_Record_Field struct {
|
||||||
|
// Types that are valid to be assigned to Value:
|
||||||
|
// *Output_Record_Field_Text
|
||||||
|
// *Output_Record_Field_Number
|
||||||
|
// *Output_Record_Field_Error
|
||||||
|
// *Output_Record_Field_List
|
||||||
|
Value isOutput_Record_Field_Value `protobuf_oneof:"value"`
|
||||||
|
// Used internally by Buildozer to decide whether a field should be quoted
|
||||||
|
// when printing. This does not affect the contents of 'value'.
|
||||||
|
QuoteWhenPrinting bool `protobuf:"varint,7,opt,name=quote_when_printing,json=quoteWhenPrinting" json:"quote_when_printing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output_Record_Field) Reset() { *m = Output_Record_Field{} }
|
||||||
|
func (m *Output_Record_Field) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Output_Record_Field) ProtoMessage() {}
|
||||||
|
func (*Output_Record_Field) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0, 0} }
|
||||||
|
|
||||||
|
type isOutput_Record_Field_Value interface {
|
||||||
|
isOutput_Record_Field_Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output_Record_Field_Text struct {
|
||||||
|
Text string `protobuf:"bytes,1,opt,name=text,oneof"`
|
||||||
|
}
|
||||||
|
type Output_Record_Field_Number struct {
|
||||||
|
Number int32 `protobuf:"varint,2,opt,name=number,oneof"`
|
||||||
|
}
|
||||||
|
type Output_Record_Field_Error struct {
|
||||||
|
Error Output_Record_Field_ERROR `protobuf:"varint,3,opt,name=error,enum=devtools.buildozer.Output_Record_Field_ERROR,oneof"`
|
||||||
|
}
|
||||||
|
type Output_Record_Field_List struct {
|
||||||
|
List *RepeatedString `protobuf:"bytes,5,opt,name=list,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Output_Record_Field_Text) isOutput_Record_Field_Value() {}
|
||||||
|
func (*Output_Record_Field_Number) isOutput_Record_Field_Value() {}
|
||||||
|
func (*Output_Record_Field_Error) isOutput_Record_Field_Value() {}
|
||||||
|
func (*Output_Record_Field_List) isOutput_Record_Field_Value() {}
|
||||||
|
|
||||||
|
func (m *Output_Record_Field) GetValue() isOutput_Record_Field_Value {
|
||||||
|
if m != nil {
|
||||||
|
return m.Value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output_Record_Field) GetText() string {
|
||||||
|
if x, ok := m.GetValue().(*Output_Record_Field_Text); ok {
|
||||||
|
return x.Text
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output_Record_Field) GetNumber() int32 {
|
||||||
|
if x, ok := m.GetValue().(*Output_Record_Field_Number); ok {
|
||||||
|
return x.Number
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output_Record_Field) GetError() Output_Record_Field_ERROR {
|
||||||
|
if x, ok := m.GetValue().(*Output_Record_Field_Error); ok {
|
||||||
|
return x.Error
|
||||||
|
}
|
||||||
|
return Output_Record_Field_UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output_Record_Field) GetList() *RepeatedString {
|
||||||
|
if x, ok := m.GetValue().(*Output_Record_Field_List); ok {
|
||||||
|
return x.List
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Output_Record_Field) GetQuoteWhenPrinting() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.QuoteWhenPrinting
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||||
|
func (*Output_Record_Field) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||||
|
return _Output_Record_Field_OneofMarshaler, _Output_Record_Field_OneofUnmarshaler, _Output_Record_Field_OneofSizer, []interface{}{
|
||||||
|
(*Output_Record_Field_Text)(nil),
|
||||||
|
(*Output_Record_Field_Number)(nil),
|
||||||
|
(*Output_Record_Field_Error)(nil),
|
||||||
|
(*Output_Record_Field_List)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Output_Record_Field_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||||
|
m := msg.(*Output_Record_Field)
|
||||||
|
// value
|
||||||
|
switch x := m.Value.(type) {
|
||||||
|
case *Output_Record_Field_Text:
|
||||||
|
b.EncodeVarint(1<<3 | proto.WireBytes)
|
||||||
|
b.EncodeStringBytes(x.Text)
|
||||||
|
case *Output_Record_Field_Number:
|
||||||
|
b.EncodeVarint(2<<3 | proto.WireVarint)
|
||||||
|
b.EncodeVarint(uint64(x.Number))
|
||||||
|
case *Output_Record_Field_Error:
|
||||||
|
b.EncodeVarint(3<<3 | proto.WireVarint)
|
||||||
|
b.EncodeVarint(uint64(x.Error))
|
||||||
|
case *Output_Record_Field_List:
|
||||||
|
b.EncodeVarint(5<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.List); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Output_Record_Field.Value has unexpected type %T", x)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Output_Record_Field_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||||
|
m := msg.(*Output_Record_Field)
|
||||||
|
switch tag {
|
||||||
|
case 1: // value.text
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
x, err := b.DecodeStringBytes()
|
||||||
|
m.Value = &Output_Record_Field_Text{x}
|
||||||
|
return true, err
|
||||||
|
case 2: // value.number
|
||||||
|
if wire != proto.WireVarint {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
x, err := b.DecodeVarint()
|
||||||
|
m.Value = &Output_Record_Field_Number{int32(x)}
|
||||||
|
return true, err
|
||||||
|
case 3: // value.error
|
||||||
|
if wire != proto.WireVarint {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
x, err := b.DecodeVarint()
|
||||||
|
m.Value = &Output_Record_Field_Error{Output_Record_Field_ERROR(x)}
|
||||||
|
return true, err
|
||||||
|
case 5: // value.list
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(RepeatedString)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.Value = &Output_Record_Field_List{msg}
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Output_Record_Field_OneofSizer(msg proto.Message) (n int) {
|
||||||
|
m := msg.(*Output_Record_Field)
|
||||||
|
// value
|
||||||
|
switch x := m.Value.(type) {
|
||||||
|
case *Output_Record_Field_Text:
|
||||||
|
n += proto.SizeVarint(1<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(len(x.Text)))
|
||||||
|
n += len(x.Text)
|
||||||
|
case *Output_Record_Field_Number:
|
||||||
|
n += proto.SizeVarint(2<<3 | proto.WireVarint)
|
||||||
|
n += proto.SizeVarint(uint64(x.Number))
|
||||||
|
case *Output_Record_Field_Error:
|
||||||
|
n += proto.SizeVarint(3<<3 | proto.WireVarint)
|
||||||
|
n += proto.SizeVarint(uint64(x.Error))
|
||||||
|
case *Output_Record_Field_List:
|
||||||
|
s := proto.Size(x.List)
|
||||||
|
n += proto.SizeVarint(5<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepeatedString struct {
|
||||||
|
Strings []string `protobuf:"bytes,1,rep,name=strings" json:"strings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RepeatedString) Reset() { *m = RepeatedString{} }
|
||||||
|
func (m *RepeatedString) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*RepeatedString) ProtoMessage() {}
|
||||||
|
func (*RepeatedString) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||||
|
|
||||||
|
func (m *RepeatedString) GetStrings() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Strings
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Output)(nil), "devtools.buildozer.Output")
|
||||||
|
proto.RegisterType((*Output_Record)(nil), "devtools.buildozer.Output.Record")
|
||||||
|
proto.RegisterType((*Output_Record_Field)(nil), "devtools.buildozer.Output.Record.Field")
|
||||||
|
proto.RegisterType((*RepeatedString)(nil), "devtools.buildozer.RepeatedString")
|
||||||
|
proto.RegisterEnum("devtools.buildozer.Output_Record_Field_ERROR", Output_Record_Field_ERROR_name, Output_Record_Field_ERROR_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("api_proto/api.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 336 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x4a, 0xf3, 0x40,
|
||||||
|
0x14, 0x85, 0x33, 0x6d, 0x93, 0xfc, 0xbd, 0x85, 0xd2, 0x7f, 0xea, 0x62, 0xe8, 0x2a, 0xcd, 0xc6,
|
||||||
|
0x20, 0x38, 0x42, 0xdd, 0x08, 0x2e, 0x04, 0xa1, 0xda, 0x22, 0xa6, 0x32, 0x45, 0xba, 0x2c, 0xa9,
|
||||||
|
0x19, 0xdb, 0x81, 0x98, 0x89, 0x93, 0x49, 0x15, 0x9f, 0xc3, 0x87, 0xf0, 0xb1, 0x7c, 0x14, 0xc9,
|
||||||
|
0x24, 0x15, 0x44, 0x41, 0x77, 0xf7, 0xdc, 0x7b, 0xbe, 0x73, 0x42, 0x06, 0xfa, 0x51, 0x26, 0x96,
|
||||||
|
0x99, 0x92, 0x5a, 0x1e, 0x45, 0x99, 0xa0, 0x66, 0xc2, 0x38, 0xe6, 0x5b, 0x2d, 0x65, 0x92, 0xd3,
|
||||||
|
0x55, 0x21, 0x92, 0x58, 0xbe, 0x70, 0xe5, 0xbf, 0x35, 0xc1, 0x99, 0x15, 0x3a, 0x2b, 0x34, 0x3e,
|
||||||
|
0x05, 0x57, 0xf1, 0x3b, 0xa9, 0xe2, 0x9c, 0x20, 0xaf, 0x19, 0x74, 0x46, 0x43, 0xfa, 0x1d, 0xa0,
|
||||||
|
0x95, 0x99, 0x32, 0xe3, 0x64, 0x3b, 0x62, 0xf0, 0xde, 0x00, 0xa7, 0xda, 0xe1, 0x33, 0x70, 0xee,
|
||||||
|
0x05, 0x4f, 0x3e, 0x63, 0xf6, 0x7f, 0x8d, 0xa1, 0x17, 0xa5, 0x9f, 0xd5, 0xd8, 0xe0, 0xb5, 0x01,
|
||||||
|
0xb6, 0xd9, 0xe0, 0x3d, 0x68, 0x69, 0xfe, 0xac, 0x09, 0xf2, 0x50, 0xd0, 0x9e, 0x58, 0xcc, 0x28,
|
||||||
|
0x4c, 0xc0, 0x49, 0x8b, 0x87, 0x15, 0x57, 0xa4, 0xe1, 0xa1, 0xc0, 0x9e, 0x58, 0xac, 0xd6, 0x78,
|
||||||
|
0x0c, 0x36, 0x57, 0x4a, 0x2a, 0xd2, 0xf4, 0x50, 0xd0, 0x1d, 0x1d, 0xfe, 0xb1, 0x99, 0x8e, 0x19,
|
||||||
|
0x9b, 0xb1, 0x89, 0xc5, 0x2a, 0x1a, 0x9f, 0x40, 0x2b, 0x11, 0xb9, 0x26, 0xb6, 0x87, 0x82, 0xce,
|
||||||
|
0xc8, 0xff, 0x29, 0x85, 0xf1, 0x8c, 0x47, 0x9a, 0xc7, 0x73, 0xad, 0x44, 0xba, 0x2e, 0x3f, 0xad,
|
||||||
|
0x24, 0x30, 0x85, 0xfe, 0x63, 0x21, 0x35, 0x5f, 0x3e, 0x6d, 0x78, 0xba, 0xcc, 0x94, 0x48, 0xb5,
|
||||||
|
0x48, 0xd7, 0xc4, 0xf5, 0x50, 0xf0, 0x8f, 0xfd, 0x37, 0xa7, 0xc5, 0x86, 0xa7, 0x37, 0xf5, 0xc1,
|
||||||
|
0x1f, 0x82, 0x6d, 0xba, 0x71, 0x07, 0xdc, 0xdb, 0xf0, 0x2a, 0x9c, 0x2d, 0xc2, 0x9e, 0x55, 0x8a,
|
||||||
|
0xeb, 0xe9, 0x7c, 0x3e, 0x0d, 0x2f, 0x7b, 0xe8, 0xdc, 0x05, 0x7b, 0x1b, 0x25, 0x05, 0xf7, 0x0f,
|
||||||
|
0xa0, 0xfb, 0xb5, 0x15, 0x13, 0x70, 0x73, 0x33, 0x55, 0xbf, 0xba, 0xcd, 0x76, 0x72, 0xe5, 0x98,
|
||||||
|
0x17, 0x3f, 0xfe, 0x08, 0x00, 0x00, 0xff, 0xff, 0x8d, 0x62, 0x58, 0xc4, 0x08, 0x02, 0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package devtools.buildozer;
|
||||||
|
|
||||||
|
message Output {
|
||||||
|
repeated Record records = 1;
|
||||||
|
message Record {
|
||||||
|
repeated Field fields = 1;
|
||||||
|
message Field {
|
||||||
|
oneof value {
|
||||||
|
string text = 1;
|
||||||
|
int32 number = 2;
|
||||||
|
ERROR error = 3;
|
||||||
|
RepeatedString list = 5;
|
||||||
|
}
|
||||||
|
// Used internally by Buildozer to decide whether a field should be quoted
|
||||||
|
// when printing. This does not affect the contents of 'value'.
|
||||||
|
bool quote_when_printing = 7;
|
||||||
|
|
||||||
|
enum ERROR {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
MISSING = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message RepeatedString {
|
||||||
|
repeated string strings = 1;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["build.gen.pb.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/github.com/bazelbuild/buildtools/build_proto",
|
||||||
|
importpath = "github.com/bazelbuild/buildtools/build_proto",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = ["//vendor/github.com/golang/protobuf/proto:go_default_library"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
2092
vendor/github.com/bazelbuild/buildtools/build_proto/build.gen.pb.go
generated
vendored
Executable file
2092
vendor/github.com/bazelbuild/buildtools/build_proto/build.gen.pb.go
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["main.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/github.com/bazelbuild/buildtools/buildozer",
|
||||||
|
importpath = "github.com/bazelbuild/buildtools/buildozer",
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/edit:go_default_library",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/tables:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_binary(
|
||||||
|
name = "buildozer",
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,266 @@
|
||||||
|
# Buildozer
|
||||||
|
|
||||||
|
Buildozer is a command line tool to rewrite multiple
|
||||||
|
[Bazel](https://github.com/bazelbuild/bazel) BUILD files using
|
||||||
|
standard commands.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
1. Protobuf go runtime: to download
|
||||||
|
`go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Change directory to the buildifier/buildozer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gopath=$(go env GOPATH)
|
||||||
|
cd $gopath/src/github.com/bazelbuild/buildtools/buildozer
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```shell
|
||||||
|
buildozer [OPTIONS] ['command args' | -f FILE ] label-list
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, `label-list` is a comma-separated list of Bazel labels, for example
|
||||||
|
`//path/to/pkg1:rule1, //path/to/pkg2:rule2`. Buildozer reads commands from
|
||||||
|
`FILE` (`-` for stdin (format: `|`-separated command line arguments to buildozer,
|
||||||
|
excluding flags))
|
||||||
|
|
||||||
|
You should specify at least one command and one target. Buildozer will execute
|
||||||
|
all commands on all targets. Commands are executed in order, files are processed
|
||||||
|
in parallel.
|
||||||
|
|
||||||
|
### Targets
|
||||||
|
|
||||||
|
Targets look like Bazel labels, but there can be some differences in presence of
|
||||||
|
macros.
|
||||||
|
|
||||||
|
* Use the label notation to refer to a rule: `//buildtools/buildozer:edit`
|
||||||
|
* Use the `__pkg__` suffix to refer to the package declaration:
|
||||||
|
`//buildtools/buildozer:__pkg__`
|
||||||
|
* Use an asterisk to refer to all rules in a file: `//pkg:*`
|
||||||
|
* Use `...` to refer to all descendant BUILD files in a directory: `//pkg/...:*`
|
||||||
|
* Use percent to refer to all rules of a certain kind: `//pkg:%java_library`
|
||||||
|
* Use percent-and-number to refer to a rule that begins at a certain line:
|
||||||
|
`//pkg:%123`.
|
||||||
|
* Use `-` for the package name if you want to process standard input stream
|
||||||
|
instead of a file: `-:all_tests`.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
OPTIONS include the following options:
|
||||||
|
|
||||||
|
* `-stdout` : write changed BUILD file to stdout
|
||||||
|
* `-buildifier` : format output using a specific buildifier binary. If empty, use built-in formatter.
|
||||||
|
* `-k` : apply all commands, even if there are failures
|
||||||
|
* `-quiet` : suppress informational messages
|
||||||
|
* `-shorten_labels` : convert added labels to short form, e.g. //foo:bar => :bar
|
||||||
|
* `-types`: Filter the targets, keeping only those of the given types, e.g.
|
||||||
|
`buildozer -types go_library,go_binary 'print rule' '//buildtools/buildozer:*'`
|
||||||
|
* `-eol-comments=false`: When adding new comments, put them on a separate line.
|
||||||
|
|
||||||
|
See `buildozer -help` for the full list.
|
||||||
|
|
||||||
|
### Edit commands
|
||||||
|
|
||||||
|
Buildozer supports the following commands(`'command args'`):
|
||||||
|
|
||||||
|
* `add <attr> <value(s)>`: Adds value(s) to a list attribute of a rule. If a
|
||||||
|
value is already present in the list, it is not added.
|
||||||
|
* `new_load <path> <symbol(s)>`: Add a load statement for the given path,
|
||||||
|
importing the symbols. Before using this, make sure to run
|
||||||
|
`buildozer 'fix movePackageToTop'`. Afterwards, consider running
|
||||||
|
`buildozer 'fix unusedLoads'`.
|
||||||
|
* `comment <attr>? <value>? <comment>`: Add a comment to a rule, an attribute,
|
||||||
|
or a specific value in a list. Spaces in the comment should be escaped with
|
||||||
|
backslashes.
|
||||||
|
* `print_comment <attr>? <value>?`
|
||||||
|
* `delete`: Delete a rule.
|
||||||
|
* `fix <fix(es)>?`: Apply a fix.
|
||||||
|
* `move <old_attr> <new_attr> <value(s)>`: Moves `value(s)` from the list `old_attr`
|
||||||
|
to the list `new_attr`. The wildcard `*` matches all values.
|
||||||
|
* `new <rule_kind> <rule_name> [(before|after) <relative_rule_name>]`: Add a
|
||||||
|
new rule at the end of the BUILD file (before/after `<relative_rule>`).
|
||||||
|
* `print <attr(s)>`
|
||||||
|
* `remove <attr> <value(s)>`: Removes `value(s)` from the list `attr`. The
|
||||||
|
wildcard `*` matches all attributes. Lists containing none of the `value(s)` are
|
||||||
|
not modified.
|
||||||
|
* `rename <old_attr> <new_attr>`: Rename the `old_attr` to `new_attr` which must
|
||||||
|
not yet exist.
|
||||||
|
* `replace <attr> <old_value> <new_value>`: Replaces `old_value` with `new_value`
|
||||||
|
in the list `attr`. Wildcard `*` matches all attributes. Lists not containing
|
||||||
|
`old_value` are not modified.
|
||||||
|
* `set <attr> <value(s)>`: Sets the value of an attribute. If the attribute
|
||||||
|
was already present, its old value is replaced.
|
||||||
|
* `set_if_absent <attr> <value(s)>`: Sets the value of an attribute. If the
|
||||||
|
attribute was already present, no action is taken.
|
||||||
|
* `set kind <value>`: Set the target type to value.
|
||||||
|
* `copy <attr> <from_rule>`: Copies the value of `attr` between rules. If it
|
||||||
|
exists in the `to_rule`, it will be overwritten.
|
||||||
|
* `copy_no_overwrite <attr> <from_rule>`: Copies the value of `attr` between
|
||||||
|
rules. If it exists in the `to_rule`, no action is taken.
|
||||||
|
|
||||||
|
Here, `<attr>` represents an attribute (being `add`ed/`rename`d/`delete`d etc.),
|
||||||
|
e.g.: `srcs`, `<value(s)>` represents values of the attribute and so on.
|
||||||
|
A '?' indicates that the preceding argument is optional.
|
||||||
|
|
||||||
|
The fix command without a fix specified applied to all eligible fixes.
|
||||||
|
Use `//path/to/pkg:__pkg__` as label for file level changes like `new_load` and
|
||||||
|
`new_rule`.
|
||||||
|
A transformation can be applied to all rules of a particular kind by using
|
||||||
|
`%rule_kind` at the end of the label(see examples below).
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit //pkg:rule and //pkg:rule2, and add a dependency on //base
|
||||||
|
buildozer 'add deps //base' //pkg:rule //pkg:rule2
|
||||||
|
|
||||||
|
# A load for a skylark file in //pkg
|
||||||
|
buildozer 'new_load /tools/build_rules/build_test build_test' //pkg:__pkg__
|
||||||
|
|
||||||
|
# Change the default_visibility to public for the package //pkg
|
||||||
|
buildozer 'set default_visibility //visibility:public' //pkg:__pkg__
|
||||||
|
|
||||||
|
# Change all gwt_module targets to java_library in the package //pkg
|
||||||
|
buildozer 'set kind java_library' //pkg:%gwt_module
|
||||||
|
|
||||||
|
# Replace the dependency on pkg_v1 with a dependency on pkg_v2
|
||||||
|
buildozer 'replace deps //pkg_v1 //pkg_v2' //pkg:rule
|
||||||
|
|
||||||
|
# Delete the dependency on foo in every cc_library in the package
|
||||||
|
buildozer 'remove deps foo' //pkg:%cc_library
|
||||||
|
|
||||||
|
# Delete the testonly attribute in every rule in the package
|
||||||
|
buildozer 'remove testonly' '//pkg:*'
|
||||||
|
|
||||||
|
# Add a comment to the timeout attribute of //pkg:rule_test
|
||||||
|
buildozer 'comment timeout Delete\ this\ after\ 2015-12-31.' //pkg:rule_test
|
||||||
|
|
||||||
|
# Add a new rule at the end of the file
|
||||||
|
buildozer 'new java_library foo' //pkg:__pkg__
|
||||||
|
|
||||||
|
# Add a cc_binary rule named new_bin before the rule named tests
|
||||||
|
buildozer 'new cc_binary new_bin before tests' //:__pkg__
|
||||||
|
|
||||||
|
# Copy an attribute from `protolib` to `py_protolib`.
|
||||||
|
buildozer 'copy testonly protolib' //pkg:py_protolib
|
||||||
|
|
||||||
|
# Set two attributes in the same rule
|
||||||
|
buildozer 'set compile 1' 'set srcmap 1' //pkg:rule
|
||||||
|
|
||||||
|
# Make a default explicit in all soy_js rules in a package
|
||||||
|
buildozer 'set_if_absent allowv1syntax 1' //pkg:%soy_js
|
||||||
|
|
||||||
|
# Add an attribute new_attr with value "def_val" to all cc_binary rules
|
||||||
|
# Note that special characters will automatically be escaped in the string
|
||||||
|
buildozer 'add new_attr def_val' //:%cc_binary
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print commands
|
||||||
|
|
||||||
|
They work just like the edit commands. Expect a return code of 3 as they are not
|
||||||
|
modifying any file.
|
||||||
|
|
||||||
|
* `print <attribute(s)>`: For each target, prints the value of the attributes
|
||||||
|
(see below).
|
||||||
|
* `print_comment <attr>? <value>?`: Prints a comment associated with a rule,
|
||||||
|
an attribute or a specific value in a list.
|
||||||
|
|
||||||
|
The print command prints the value of the attributes. If a target doesn't have
|
||||||
|
the attribute, a warning is printed on stderr.
|
||||||
|
|
||||||
|
There are some special attributes in the `print` command:
|
||||||
|
|
||||||
|
* `kind`: displays the name of the function
|
||||||
|
* `label`: the fully qualified label
|
||||||
|
* `rule`: the entire rule definition
|
||||||
|
* `startline`: the line number on which the rule begins in the BUILD file
|
||||||
|
* `endline`: the line number on which the rule ends in the BUILD file
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Print the kind of a target
|
||||||
|
buildozer 'print kind' base # output: cc_library
|
||||||
|
|
||||||
|
# Print the name of all cc_library in //base
|
||||||
|
buildozer 'print name' base:%cc_library
|
||||||
|
|
||||||
|
# Get the default visibility of the //base package
|
||||||
|
buildozer 'print default_visibility' base:%package
|
||||||
|
|
||||||
|
# Print labels of cc_library targets in //base that have a deps attribute
|
||||||
|
buildozer 'print label deps' base:%cc_library 2>/dev/null | cut -d' ' -f1
|
||||||
|
|
||||||
|
# Print the list of labels in //base that explicitly set the testonly attribute:
|
||||||
|
buildozer 'print label testonly' 'base:*' 2>/dev/null
|
||||||
|
|
||||||
|
# Print the entire definition (including comments) of the //base:heapcheck rule:
|
||||||
|
buildozer 'print rule' //base:heapcheck
|
||||||
|
```
|
||||||
|
|
||||||
|
## Converting labels
|
||||||
|
|
||||||
|
Buildozer works at the syntax-level. It doesn't evaluate the BUILD files. If you
|
||||||
|
need to query the information Bazel has, please use `bazel query`. If you have a
|
||||||
|
list of Bazel labels, chances are that some of them are generated by BUILD
|
||||||
|
extensions. Labels in Buildozer are slightly different from labels in Bazel.
|
||||||
|
Bazel cares about the generated code, while Buildozer looks at the BUILD file
|
||||||
|
before macro expansion.
|
||||||
|
|
||||||
|
To see the expanded BUILD files, try:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
bazel query --output=build //path/to/BUILD
|
||||||
|
```
|
||||||
|
|
||||||
|
## Do multiple changes at once
|
||||||
|
|
||||||
|
Use `buildozer -f <file>` to load a list of commands from a file. The usage is
|
||||||
|
just like arguments on the command-line, except that arguments are separated by
|
||||||
|
`|`.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ cat /tmp/cmds
|
||||||
|
new cc_library foo|//buildtools/buildozer/BUILD
|
||||||
|
add deps //base //strings|add srcs foo.cc|//buildtools/buildozer:foo
|
||||||
|
add deps :foo|//buildtools/buildozer
|
||||||
|
|
||||||
|
$ buildozer -f /tmp/cmds
|
||||||
|
fixed //buildtools/buildozer/BUILD
|
||||||
|
```
|
||||||
|
|
||||||
|
The list of commands will typically be generated and can be large. This is
|
||||||
|
efficient: Commands are grouped so that each file is modified once. Files are
|
||||||
|
processed in parallel.
|
||||||
|
|
||||||
|
## Error code
|
||||||
|
|
||||||
|
The return code is:
|
||||||
|
|
||||||
|
* `0` on success, if changes were made
|
||||||
|
* `1` when there is a usage error
|
||||||
|
* `2` when at least one command has failed
|
||||||
|
* `3` on success, when no changes were made
|
||||||
|
|
||||||
|
## Source Structure
|
||||||
|
|
||||||
|
* `buildozer/main.go` : Entry point for the buildozer binary
|
||||||
|
* `edit/buildozer.go` : Implementation of functions for the buildozer commands
|
||||||
|
* `edit/edit.go`: Library functions to perform various operations on ASTs. These
|
||||||
|
* functions are called by the impl functions in buildozer.go
|
||||||
|
* `edit/fix.go`: Functions for various fixes for the `buildozer 'fix <fix(es)>'`
|
||||||
|
command, like cleaning unused loads, changing labels to canonical notation, etc.
|
||||||
|
* `edit/types.go`: Type information for attributes
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
// Entry-point for Buildozer binary.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bazelbuild/buildtools/edit"
|
||||||
|
"github.com/bazelbuild/buildtools/tables"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
stdout = flag.Bool("stdout", false, "write changed BUILD file to stdout")
|
||||||
|
buildifier = flag.String("buildifier", "", "format output using a specific buildifier binary. If empty, use built-in formatter")
|
||||||
|
parallelism = flag.Int("P", 0, "number of cores to use for concurrent actions")
|
||||||
|
numio = flag.Int("numio", 200, "number of concurrent actions")
|
||||||
|
commandsFile = flag.String("f", "", "file name to read commands from, use '-' for stdin (format:|-separated command line arguments to buildozer, excluding flags)")
|
||||||
|
keepGoing = flag.Bool("k", false, "apply all commands, even if there are failures")
|
||||||
|
filterRuleTypes = stringList("types", "comma-separated list of rule types to change, the default empty list means all rules")
|
||||||
|
preferEOLComments = flag.Bool("eol-comments", true, "when adding a new comment, put it on the same line if possible")
|
||||||
|
rootDir = flag.String("root_dir", "", "If present, use this folder rather than $PWD to find the root directory.")
|
||||||
|
quiet = flag.Bool("quiet", false, "suppress informational messages")
|
||||||
|
editVariables = flag.Bool("edit-variables", false, "For attributes that simply assign a variable (e.g. hdrs = LIB_HDRS), edit the build variable instead of appending to the attribute.")
|
||||||
|
isPrintingProto = flag.Bool("output_proto", false, "output serialized devtools.buildozer.Output protos instead of human-readable strings.")
|
||||||
|
tablesPath = flag.String("tables", "", "path to JSON file with custom table definitions which will replace the built-in tables")
|
||||||
|
|
||||||
|
shortenLabelsFlag = flag.Bool("shorten_labels", true, "convert added labels to short form, e.g. //foo:bar => :bar")
|
||||||
|
deleteWithComments = flag.Bool("delete_with_comments", true, "If a list attribute should be deleted even if there is a comment attached to it")
|
||||||
|
)
|
||||||
|
|
||||||
|
func stringList(name, help string) func() []string {
|
||||||
|
f := flag.String(name, "", help)
|
||||||
|
return func() []string {
|
||||||
|
if *f == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res := strings.Split(*f, ",")
|
||||||
|
for i := range res {
|
||||||
|
res[i] = strings.TrimSpace(res[i])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *tablesPath != "" {
|
||||||
|
if err := tables.ParseAndUpdateJSONDefinitions(*tablesPath, false); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "buildifier: failed to parse %s for -tables: %s\n", *tablesPath, err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edit.ShortenLabelsFlag = *shortenLabelsFlag
|
||||||
|
edit.DeleteWithComments = *deleteWithComments
|
||||||
|
edit.Opts = edit.Options{
|
||||||
|
Stdout: *stdout,
|
||||||
|
Buildifier: *buildifier,
|
||||||
|
Parallelism: *parallelism,
|
||||||
|
NumIO: *numio,
|
||||||
|
CommandsFile: *commandsFile,
|
||||||
|
KeepGoing: *keepGoing,
|
||||||
|
FilterRuleTypes: filterRuleTypes(),
|
||||||
|
PreferEOLComments: *preferEOLComments,
|
||||||
|
RootDir: *rootDir,
|
||||||
|
Quiet: *quiet,
|
||||||
|
EditVariables: *editVariables,
|
||||||
|
IsPrintingProto: *isPrintingProto,
|
||||||
|
}
|
||||||
|
os.Exit(edit.Buildozer(flag.Args()))
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"buildozer.go",
|
||||||
|
"edit.go",
|
||||||
|
"fix.go",
|
||||||
|
"types.go",
|
||||||
|
],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/github.com/bazelbuild/buildtools/edit",
|
||||||
|
importpath = "github.com/bazelbuild/buildtools/edit",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/api_proto:go_default_library",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/build:go_default_library",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/build_proto:go_default_library",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/file:go_default_library",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/lang:go_default_library",
|
||||||
|
"//vendor/github.com/bazelbuild/buildtools/wspace:go_default_library",
|
||||||
|
"//vendor/github.com/golang/protobuf/proto:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,976 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
// Buildozer is a tool for programatically editing BUILD files.
|
||||||
|
|
||||||
|
package edit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
apipb "github.com/bazelbuild/buildtools/api_proto"
|
||||||
|
"github.com/bazelbuild/buildtools/build"
|
||||||
|
"github.com/bazelbuild/buildtools/file"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options represents choices about how buildozer should behave.
|
||||||
|
type Options struct {
|
||||||
|
Stdout bool // write changed BUILD file to stdout
|
||||||
|
Buildifier string // path to buildifier binary
|
||||||
|
Parallelism int // number of cores to use for concurrent actions
|
||||||
|
NumIO int // number of concurrent actions
|
||||||
|
CommandsFile string // file name to read commands from, use '-' for stdin (format:|-separated command line arguments to buildozer, excluding flags
|
||||||
|
KeepGoing bool // apply all commands, even if there are failures
|
||||||
|
FilterRuleTypes []string // list of rule types to change, empty means all
|
||||||
|
PreferEOLComments bool // when adding a new comment, put it on the same line if possible
|
||||||
|
RootDir string // If present, use this folder rather than $PWD to find the root dir
|
||||||
|
Quiet bool // suppress informational messages.
|
||||||
|
EditVariables bool // for attributes that simply assign a variable (e.g. hdrs = LIB_HDRS), edit the build variable instead of appending to the attribute.
|
||||||
|
IsPrintingProto bool // output serialized devtools.buildozer.Output protos instead of human-readable strings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opts represents the options to be used by buildozer, and can be overriden before calling Buildozer.
|
||||||
|
var Opts = Options{NumIO: 200, PreferEOLComments: true}
|
||||||
|
|
||||||
|
// Usage is a user-overriden func to print the program usage.
|
||||||
|
var Usage = func() {}
|
||||||
|
|
||||||
|
var fileModified = false // set to true when a file has been fixed
|
||||||
|
|
||||||
|
const stdinPackageName = "-" // the special package name to represent stdin
|
||||||
|
|
||||||
|
// CmdEnvironment stores the information the commands below have access to.
|
||||||
|
type CmdEnvironment struct {
|
||||||
|
File *build.File // the AST
|
||||||
|
Rule *build.Rule // the rule to modify
|
||||||
|
Vars map[string]*build.BinaryExpr // global variables set in the build file
|
||||||
|
Pkg string // the full package name
|
||||||
|
Args []string // the command-line arguments
|
||||||
|
output *apipb.Output_Record // output proto, stores whatever a command wants to print
|
||||||
|
}
|
||||||
|
|
||||||
|
// The cmdXXX functions implement the various commands.
|
||||||
|
|
||||||
|
func cmdAdd(env CmdEnvironment) (*build.File, error) {
|
||||||
|
attr := env.Args[0]
|
||||||
|
for _, val := range env.Args[1:] {
|
||||||
|
if IsIntList(attr) {
|
||||||
|
AddValueToListAttribute(env.Rule, attr, env.Pkg, &build.LiteralExpr{Token: val}, &env.Vars)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
strVal := &build.StringExpr{Value: ShortenLabel(val, env.Pkg)}
|
||||||
|
AddValueToListAttribute(env.Rule, attr, env.Pkg, strVal, &env.Vars)
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdComment(env CmdEnvironment) (*build.File, error) {
|
||||||
|
// The comment string is always the last argument in the list.
|
||||||
|
str := env.Args[len(env.Args)-1]
|
||||||
|
str = strings.Replace(str, "\\n", "\n", -1)
|
||||||
|
// Multiline comments should go on a separate line.
|
||||||
|
fullLine := !Opts.PreferEOLComments || strings.Contains(str, "\n")
|
||||||
|
str = strings.Replace("# "+str, "\n", "\n# ", -1)
|
||||||
|
comment := []build.Comment{build.Comment{Token: str}}
|
||||||
|
|
||||||
|
// The comment might be attached to a rule, an attribute, or a value in a list,
|
||||||
|
// depending on how many arguments are passed.
|
||||||
|
switch len(env.Args) {
|
||||||
|
case 1: // Attach to a rule
|
||||||
|
env.Rule.Call.Comments.Before = comment
|
||||||
|
case 2: // Attach to an attribute
|
||||||
|
if attr := env.Rule.AttrDefn(env.Args[0]); attr != nil {
|
||||||
|
if fullLine {
|
||||||
|
attr.X.Comment().Before = comment
|
||||||
|
} else {
|
||||||
|
attr.Y.Comment().Suffix = comment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 3: // Attach to a specific value in a list
|
||||||
|
if attr := env.Rule.Attr(env.Args[0]); attr != nil {
|
||||||
|
if expr := ListFind(attr, env.Args[1], env.Pkg); expr != nil {
|
||||||
|
if fullLine {
|
||||||
|
expr.Comments.Before = comment
|
||||||
|
} else {
|
||||||
|
expr.Comments.Suffix = comment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("cmdComment")
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// commentsText concatenates comments into a single line.
|
||||||
|
func commentsText(comments []build.Comment) string {
|
||||||
|
var segments []string
|
||||||
|
for _, comment := range comments {
|
||||||
|
token := comment.Token
|
||||||
|
if strings.HasPrefix(token, "#") {
|
||||||
|
token = token[1:]
|
||||||
|
}
|
||||||
|
segments = append(segments, strings.TrimSpace(token))
|
||||||
|
}
|
||||||
|
return strings.Replace(strings.Join(segments, " "), "\n", " ", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdPrintComment(env CmdEnvironment) (*build.File, error) {
|
||||||
|
attrError := func() error {
|
||||||
|
return fmt.Errorf("rule \"//%s:%s\" has no attribute \"%s\"", env.Pkg, env.Rule.Name(), env.Args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(env.Args) {
|
||||||
|
case 0: // Print rule comment.
|
||||||
|
env.output.Fields = []*apipb.Output_Record_Field{
|
||||||
|
&apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{commentsText(env.Rule.Call.Comments.Before)}},
|
||||||
|
}
|
||||||
|
case 1: // Print attribute comment.
|
||||||
|
attr := env.Rule.AttrDefn(env.Args[0])
|
||||||
|
if attr == nil {
|
||||||
|
return nil, attrError()
|
||||||
|
}
|
||||||
|
comments := append(attr.Before, attr.Suffix...)
|
||||||
|
env.output.Fields = []*apipb.Output_Record_Field{
|
||||||
|
&apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{commentsText(comments)}},
|
||||||
|
}
|
||||||
|
case 2: // Print comment of a specific value in a list.
|
||||||
|
attr := env.Rule.Attr(env.Args[0])
|
||||||
|
if attr == nil {
|
||||||
|
return nil, attrError()
|
||||||
|
}
|
||||||
|
value := env.Args[1]
|
||||||
|
expr := ListFind(attr, value, env.Pkg)
|
||||||
|
if expr == nil {
|
||||||
|
return nil, fmt.Errorf("attribute \"%s\" has no value \"%s\"", env.Args[0], value)
|
||||||
|
}
|
||||||
|
comments := append(expr.Comments.Before, expr.Comments.Suffix...)
|
||||||
|
env.output.Fields = []*apipb.Output_Record_Field{
|
||||||
|
&apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{commentsText(comments)}},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("cmdPrintComment")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdDelete(env CmdEnvironment) (*build.File, error) {
|
||||||
|
return DeleteRule(env.File, env.Rule), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdMove(env CmdEnvironment) (*build.File, error) {
|
||||||
|
oldAttr := env.Args[0]
|
||||||
|
newAttr := env.Args[1]
|
||||||
|
if len(env.Args) == 3 && env.Args[2] == "*" {
|
||||||
|
if err := MoveAllListAttributeValues(env.Rule, oldAttr, newAttr, env.Pkg, &env.Vars); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
fixed := false
|
||||||
|
for _, val := range env.Args[2:] {
|
||||||
|
if deleted := ListAttributeDelete(env.Rule, oldAttr, val, env.Pkg); deleted != nil {
|
||||||
|
AddValueToListAttribute(env.Rule, newAttr, env.Pkg, deleted, &env.Vars)
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fixed {
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdNew(env CmdEnvironment) (*build.File, error) {
|
||||||
|
kind := env.Args[0]
|
||||||
|
name := env.Args[1]
|
||||||
|
addAtEOF, insertionIndex, err := findInsertionIndex(env)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if FindRuleByName(env.File, name) != nil {
|
||||||
|
return nil, fmt.Errorf("rule '%s' already exists", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
call := &build.CallExpr{X: &build.LiteralExpr{Token: kind}}
|
||||||
|
rule := &build.Rule{Call: call}
|
||||||
|
rule.SetAttr("name", &build.StringExpr{Value: name})
|
||||||
|
|
||||||
|
if addAtEOF {
|
||||||
|
env.File.Stmt = InsertAfterLastOfSameKind(env.File.Stmt, rule.Call)
|
||||||
|
} else {
|
||||||
|
env.File.Stmt = InsertAfter(insertionIndex, env.File.Stmt, call)
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findInsertionIndex is used by cmdNew to find the place at which to insert the new rule.
|
||||||
|
func findInsertionIndex(env CmdEnvironment) (bool, int, error) {
|
||||||
|
if len(env.Args) < 4 {
|
||||||
|
return true, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relativeToRuleName := env.Args[3]
|
||||||
|
ruleIdx := IndexOfRuleByName(env.File, relativeToRuleName)
|
||||||
|
if ruleIdx == -1 {
|
||||||
|
return true, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch env.Args[2] {
|
||||||
|
case "before":
|
||||||
|
return false, ruleIdx - 1, nil
|
||||||
|
case "after":
|
||||||
|
return false, ruleIdx, nil
|
||||||
|
default:
|
||||||
|
return true, 0, fmt.Errorf("Unknown relative operator '%s'; allowed: 'before', 'after'", env.Args[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdNewLoad(env CmdEnvironment) (*build.File, error) {
|
||||||
|
env.File.Stmt = InsertLoad(env.File.Stmt, env.Args)
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdPrint(env CmdEnvironment) (*build.File, error) {
|
||||||
|
format := env.Args
|
||||||
|
if len(format) == 0 {
|
||||||
|
format = []string{"name", "kind"}
|
||||||
|
}
|
||||||
|
fields := make([]*apipb.Output_Record_Field, len(format))
|
||||||
|
|
||||||
|
for i, str := range format {
|
||||||
|
value := env.Rule.Attr(str)
|
||||||
|
if str == "kind" {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{env.Rule.Kind()}}
|
||||||
|
} else if str == "label" {
|
||||||
|
if env.Rule.Attr("name") != nil {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{fmt.Sprintf("//%s:%s", env.Pkg, env.Rule.Name())}}
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
} else if str == "rule" {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{
|
||||||
|
Value: &apipb.Output_Record_Field_Text{build.FormatString(env.Rule.Call)},
|
||||||
|
}
|
||||||
|
} else if str == "startline" {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Number{int32(env.Rule.Call.ListStart.Line)}}
|
||||||
|
} else if str == "endline" {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Number{int32(env.Rule.Call.End.Pos.Line)}}
|
||||||
|
} else if value == nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "rule \"//%s:%s\" has no attribute \"%s\"\n",
|
||||||
|
env.Pkg, env.Rule.Name(), str)
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Error{Error: apipb.Output_Record_Field_MISSING}}
|
||||||
|
} else if lit, ok := value.(*build.LiteralExpr); ok {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{lit.Token}}
|
||||||
|
} else if string, ok := value.(*build.StringExpr); ok {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{
|
||||||
|
Value: &apipb.Output_Record_Field_Text{string.Value},
|
||||||
|
QuoteWhenPrinting: true,
|
||||||
|
}
|
||||||
|
} else if strList := env.Rule.AttrStrings(str); strList != nil {
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_List{List: &apipb.RepeatedString{Strings: strList}}}
|
||||||
|
} else {
|
||||||
|
// Some other Expr we haven't listed above. Just print it.
|
||||||
|
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{build.FormatString(value)}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.output.Fields = fields
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func attrKeysForPattern(rule *build.Rule, pattern string) []string {
|
||||||
|
if pattern == "*" {
|
||||||
|
return rule.AttrKeys()
|
||||||
|
}
|
||||||
|
return []string{pattern}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdRemove(env CmdEnvironment) (*build.File, error) {
|
||||||
|
if len(env.Args) == 1 { // Remove the attribute
|
||||||
|
if env.Rule.DelAttr(env.Args[0]) != nil {
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
} else { // Remove values in the attribute.
|
||||||
|
fixed := false
|
||||||
|
for _, key := range attrKeysForPattern(env.Rule, env.Args[0]) {
|
||||||
|
for _, val := range env.Args[1:] {
|
||||||
|
ListAttributeDelete(env.Rule, key, val, env.Pkg)
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fixed {
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdRename(env CmdEnvironment) (*build.File, error) {
|
||||||
|
oldAttr := env.Args[0]
|
||||||
|
newAttr := env.Args[1]
|
||||||
|
if err := RenameAttribute(env.Rule, oldAttr, newAttr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdReplace(env CmdEnvironment) (*build.File, error) {
|
||||||
|
oldV := env.Args[1]
|
||||||
|
newV := env.Args[2]
|
||||||
|
for _, key := range attrKeysForPattern(env.Rule, env.Args[0]) {
|
||||||
|
attr := env.Rule.Attr(key)
|
||||||
|
if e, ok := attr.(*build.StringExpr); ok {
|
||||||
|
if LabelsEqual(e.Value, oldV, env.Pkg) {
|
||||||
|
env.Rule.SetAttr(key, getAttrValueExpr(key, []string{newV}))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ListReplace(attr, oldV, newV, env.Pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdSet(env CmdEnvironment) (*build.File, error) {
|
||||||
|
attr := env.Args[0]
|
||||||
|
args := env.Args[1:]
|
||||||
|
if attr == "kind" {
|
||||||
|
env.Rule.SetKind(args[0])
|
||||||
|
} else {
|
||||||
|
env.Rule.SetAttr(attr, getAttrValueExpr(attr, args))
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdSetIfAbsent(env CmdEnvironment) (*build.File, error) {
|
||||||
|
attr := env.Args[0]
|
||||||
|
args := env.Args[1:]
|
||||||
|
if attr == "kind" {
|
||||||
|
return nil, fmt.Errorf("setting 'kind' is not allowed for set_if_absent. Got %s", env.Args)
|
||||||
|
}
|
||||||
|
if env.Rule.Attr(attr) == nil {
|
||||||
|
env.Rule.SetAttr(attr, getAttrValueExpr(attr, args))
|
||||||
|
}
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAttrValueExpr(attr string, args []string) build.Expr {
|
||||||
|
switch {
|
||||||
|
case attr == "kind":
|
||||||
|
return nil
|
||||||
|
case IsIntList(attr):
|
||||||
|
var list []build.Expr
|
||||||
|
for _, i := range args {
|
||||||
|
list = append(list, &build.LiteralExpr{Token: i})
|
||||||
|
}
|
||||||
|
return &build.ListExpr{List: list}
|
||||||
|
case IsList(attr) && !(len(args) == 1 && strings.HasPrefix(args[0], "glob(")):
|
||||||
|
var list []build.Expr
|
||||||
|
for _, i := range args {
|
||||||
|
list = append(list, &build.StringExpr{Value: i})
|
||||||
|
}
|
||||||
|
return &build.ListExpr{List: list}
|
||||||
|
case IsString(attr):
|
||||||
|
return &build.StringExpr{Value: args[0]}
|
||||||
|
default:
|
||||||
|
return &build.LiteralExpr{Token: args[0]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdCopy(env CmdEnvironment) (*build.File, error) {
|
||||||
|
attrName := env.Args[0]
|
||||||
|
from := env.Args[1]
|
||||||
|
|
||||||
|
return copyAttributeBetweenRules(env, attrName, from)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdCopyNoOverwrite(env CmdEnvironment) (*build.File, error) {
|
||||||
|
attrName := env.Args[0]
|
||||||
|
from := env.Args[1]
|
||||||
|
|
||||||
|
if env.Rule.Attr(attrName) != nil {
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyAttributeBetweenRules(env, attrName, from)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyAttributeBetweenRules(env CmdEnvironment, attrName string, from string) (*build.File, error) {
|
||||||
|
fromRule := FindRuleByName(env.File, from)
|
||||||
|
if fromRule == nil {
|
||||||
|
return nil, fmt.Errorf("could not find rule '%s'", from)
|
||||||
|
}
|
||||||
|
attr := fromRule.Attr(attrName)
|
||||||
|
if attr == nil {
|
||||||
|
return nil, fmt.Errorf("rule '%s' does not have attribute '%s'", from, attrName)
|
||||||
|
}
|
||||||
|
|
||||||
|
ast, err := build.Parse("" /* filename */, []byte(build.FormatString(attr)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse attribute value %v", build.FormatString(attr))
|
||||||
|
}
|
||||||
|
|
||||||
|
env.Rule.SetAttr(attrName, ast.Stmt[0])
|
||||||
|
return env.File, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdFix(env CmdEnvironment) (*build.File, error) {
|
||||||
|
// Fix the whole file
|
||||||
|
if env.Rule.Kind() == "package" {
|
||||||
|
return FixFile(env.File, env.Pkg, env.Args), nil
|
||||||
|
}
|
||||||
|
// Fix a specific rule
|
||||||
|
return FixRule(env.File, env.Pkg, env.Rule, env.Args), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandInfo provides a command function and info on incoming arguments.
|
||||||
|
type CommandInfo struct {
|
||||||
|
Fn func(CmdEnvironment) (*build.File, error)
|
||||||
|
MinArg int
|
||||||
|
MaxArg int
|
||||||
|
Template string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllCommands associates the command names with their function and number
|
||||||
|
// of arguments.
|
||||||
|
var AllCommands = map[string]CommandInfo{
|
||||||
|
"add": {cmdAdd, 2, -1, "<attr> <value(s)>"},
|
||||||
|
"new_load": {cmdNewLoad, 1, -1, "<path> <symbol(s)>"},
|
||||||
|
"comment": {cmdComment, 1, 3, "<attr>? <value>? <comment>"},
|
||||||
|
"print_comment": {cmdPrintComment, 0, 2, "<attr>? <value>?"},
|
||||||
|
"delete": {cmdDelete, 0, 0, ""},
|
||||||
|
"fix": {cmdFix, 0, -1, "<fix(es)>?"},
|
||||||
|
"move": {cmdMove, 3, -1, "<old_attr> <new_attr> <value(s)>"},
|
||||||
|
"new": {cmdNew, 2, 4, "<rule_kind> <rule_name> [(before|after) <relative_rule_name>]"},
|
||||||
|
"print": {cmdPrint, 0, -1, "<attribute(s)>"},
|
||||||
|
"remove": {cmdRemove, 1, -1, "<attr> <value(s)>"},
|
||||||
|
"rename": {cmdRename, 2, 2, "<old_attr> <new_attr>"},
|
||||||
|
"replace": {cmdReplace, 3, 3, "<attr> <old_value> <new_value>"},
|
||||||
|
"set": {cmdSet, 2, -1, "<attr> <value(s)>"},
|
||||||
|
"set_if_absent": {cmdSetIfAbsent, 2, -1, "<attr> <value(s)>"},
|
||||||
|
"copy": {cmdCopy, 2, 2, "<attr> <from_rule>"},
|
||||||
|
"copy_no_overwrite": {cmdCopyNoOverwrite, 2, 2, "<attr> <from_rule>"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandTargets(f *build.File, rule string) ([]*build.Rule, error) {
|
||||||
|
if r := FindRuleByName(f, rule); r != nil {
|
||||||
|
return []*build.Rule{r}, nil
|
||||||
|
} else if r := FindExportedFile(f, rule); r != nil {
|
||||||
|
return []*build.Rule{r}, nil
|
||||||
|
} else if rule == "all" || rule == "*" {
|
||||||
|
// "all" is a valid name, it is a wildcard only if no such rule is found.
|
||||||
|
return f.Rules(""), nil
|
||||||
|
} else if strings.HasPrefix(rule, "%") {
|
||||||
|
// "%java_library" will match all java_library functions in the package
|
||||||
|
// "%<LINENUM>" will match the rule which begins at LINENUM.
|
||||||
|
// This is for convenience, "%" is not a valid character in bazel targets.
|
||||||
|
kind := rule[1:]
|
||||||
|
if linenum, err := strconv.Atoi(kind); err == nil {
|
||||||
|
if r := f.RuleAt(linenum); r != nil {
|
||||||
|
return []*build.Rule{r}, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return f.Rules(kind), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("rule '%s' not found", rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterRules(rules []*build.Rule) (result []*build.Rule) {
|
||||||
|
if len(Opts.FilterRuleTypes) == 0 {
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
for _, rule := range rules {
|
||||||
|
acceptableType := false
|
||||||
|
for _, filterType := range Opts.FilterRuleTypes {
|
||||||
|
if rule.Kind() == filterType {
|
||||||
|
acceptableType = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if acceptableType || rule.Kind() == "package" {
|
||||||
|
result = append(result, rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// command contains a list of tokens that describe a buildozer command.
|
||||||
|
type command struct {
|
||||||
|
tokens []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkCommandUsage checks the number of argument of a command.
|
||||||
|
// It prints an error and usage when it is not valid.
|
||||||
|
func checkCommandUsage(name string, cmd CommandInfo, count int) {
|
||||||
|
if count >= cmd.MinArg && (cmd.MaxArg == -1 || count <= cmd.MaxArg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if count < cmd.MinArg {
|
||||||
|
fmt.Fprintf(os.Stderr, "Too few arguments for command '%s', expected at least %d.\n",
|
||||||
|
name, cmd.MinArg)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "Too many arguments for command '%s', expected at most %d.\n",
|
||||||
|
name, cmd.MaxArg)
|
||||||
|
}
|
||||||
|
Usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match text that only contains spaces if they're escaped with '\'.
|
||||||
|
var spaceRegex = regexp.MustCompile(`(\\ |[^ ])+`)
|
||||||
|
|
||||||
|
// SplitOnSpaces behaves like strings.Fields, except that spaces can be escaped.
|
||||||
|
// " some dummy\\ string" -> ["some", "dummy string"]
|
||||||
|
func SplitOnSpaces(input string) []string {
|
||||||
|
result := spaceRegex.FindAllString(input, -1)
|
||||||
|
for i, s := range result {
|
||||||
|
result[i] = strings.Replace(s, `\ `, " ", -1)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCommands parses commands and targets they should be applied on from
|
||||||
|
// a list of arguments.
|
||||||
|
// Each argument can be either:
|
||||||
|
// - a command (as defined by AllCommands) and its parameters, separated by
|
||||||
|
// whitespace
|
||||||
|
// - a target all commands that are parsed during one call to parseCommands
|
||||||
|
// should be applied on
|
||||||
|
func parseCommands(args []string) (commands []command, targets []string) {
|
||||||
|
for _, arg := range args {
|
||||||
|
commandTokens := SplitOnSpaces(arg)
|
||||||
|
cmd, found := AllCommands[commandTokens[0]]
|
||||||
|
if found {
|
||||||
|
checkCommandUsage(commandTokens[0], cmd, len(commandTokens)-1)
|
||||||
|
commands = append(commands, command{commandTokens})
|
||||||
|
} else {
|
||||||
|
targets = append(targets, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandsForTarget contains commands to be executed on the given target.
|
||||||
|
type commandsForTarget struct {
|
||||||
|
target string
|
||||||
|
commands []command
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandsForFile contains the file name and all commands that should be
|
||||||
|
// applied on that file, indexed by their target.
|
||||||
|
type commandsForFile struct {
|
||||||
|
file string
|
||||||
|
commands []commandsForTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandError returns an error that formats 'err' in the context of the
|
||||||
|
// commands to be executed on the given target.
|
||||||
|
func commandError(commands []command, target string, err error) error {
|
||||||
|
return fmt.Errorf("error while executing commands %s on target %s: %s", commands, target, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewriteResult contains the outcome of applying fixes to a single file.
|
||||||
|
type rewriteResult struct {
|
||||||
|
file string
|
||||||
|
errs []error
|
||||||
|
modified bool
|
||||||
|
records []*apipb.Output_Record
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGlobalVariables returns the global variable assignments in the provided list of expressions.
|
||||||
|
// That is, for each variable assignment of the form
|
||||||
|
// a = v
|
||||||
|
// vars["a"] will contain the BinaryExpr whose Y value is the assignment "a = v".
|
||||||
|
func getGlobalVariables(exprs []build.Expr) (vars map[string]*build.BinaryExpr) {
|
||||||
|
vars = make(map[string]*build.BinaryExpr)
|
||||||
|
for _, expr := range exprs {
|
||||||
|
if binExpr, ok := expr.(*build.BinaryExpr); ok {
|
||||||
|
if binExpr.Op != "=" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if lhs, ok := binExpr.X.(*build.LiteralExpr); ok {
|
||||||
|
vars[lhs.Token] = binExpr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vars
|
||||||
|
}
|
||||||
|
|
||||||
|
// When checking the filesystem, we need to look for any of the
|
||||||
|
// possible buildFileNames. For historical reasons, the
|
||||||
|
// parts of the tool that generate paths that we may want to examine
|
||||||
|
// continue to assume that build files are all named "BUILD".
|
||||||
|
var buildFileNames = [...]string{"BUILD.bazel", "BUILD", "BUCK"}
|
||||||
|
var buildFileNamesSet = map[string]bool{
|
||||||
|
"BUILD.bazel": true,
|
||||||
|
"BUILD": true,
|
||||||
|
"BUCK": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewrite parses the BUILD file for the given file, transforms the AST,
|
||||||
|
// and write the changes back in the file (or on stdout).
|
||||||
|
func rewrite(commandsForFile commandsForFile) *rewriteResult {
|
||||||
|
name := commandsForFile.file
|
||||||
|
var data []byte
|
||||||
|
var err error
|
||||||
|
var fi os.FileInfo
|
||||||
|
records := []*apipb.Output_Record{}
|
||||||
|
if name == stdinPackageName { // read on stdin
|
||||||
|
data, err = ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return &rewriteResult{file: name, errs: []error{err}}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
origName := name
|
||||||
|
for _, suffix := range buildFileNames {
|
||||||
|
if strings.HasSuffix(name, "/"+suffix) {
|
||||||
|
name = strings.TrimSuffix(name, suffix)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, suffix := range buildFileNames {
|
||||||
|
name = name + suffix
|
||||||
|
data, fi, err = file.ReadFile(name)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
name = strings.TrimSuffix(name, suffix)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
data, fi, err = file.ReadFile(name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New("file not found or not readable")
|
||||||
|
return &rewriteResult{file: origName, errs: []error{err}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := build.Parse(name, data)
|
||||||
|
if err != nil {
|
||||||
|
return &rewriteResult{file: name, errs: []error{err}}
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := map[string]*build.BinaryExpr{}
|
||||||
|
if Opts.EditVariables {
|
||||||
|
vars = getGlobalVariables(f.Stmt)
|
||||||
|
}
|
||||||
|
var errs []error
|
||||||
|
changed := false
|
||||||
|
for _, commands := range commandsForFile.commands {
|
||||||
|
target := commands.target
|
||||||
|
commands := commands.commands
|
||||||
|
_, absPkg, rule := InterpretLabelForWorkspaceLocation(Opts.RootDir, target)
|
||||||
|
_, pkg, _ := ParseLabel(target)
|
||||||
|
if pkg == stdinPackageName { // Special-case: This is already absolute
|
||||||
|
absPkg = stdinPackageName
|
||||||
|
}
|
||||||
|
|
||||||
|
targets, err := expandTargets(f, rule)
|
||||||
|
if err != nil {
|
||||||
|
cerr := commandError(commands, target, err)
|
||||||
|
errs = append(errs, cerr)
|
||||||
|
if !Opts.KeepGoing {
|
||||||
|
return &rewriteResult{file: name, errs: errs, records: records}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targets = filterRules(targets)
|
||||||
|
for _, cmd := range commands {
|
||||||
|
for _, r := range targets {
|
||||||
|
cmdInfo := AllCommands[cmd.tokens[0]]
|
||||||
|
record := &apipb.Output_Record{}
|
||||||
|
newf, err := cmdInfo.Fn(CmdEnvironment{f, r, vars, absPkg, cmd.tokens[1:], record})
|
||||||
|
if len(record.Fields) != 0 {
|
||||||
|
records = append(records, record)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
cerr := commandError([]command{cmd}, target, err)
|
||||||
|
if Opts.KeepGoing {
|
||||||
|
errs = append(errs, cerr)
|
||||||
|
} else {
|
||||||
|
return &rewriteResult{file: name, errs: []error{cerr}, records: records}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newf != nil {
|
||||||
|
changed = true
|
||||||
|
f = newf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !changed {
|
||||||
|
return &rewriteResult{file: name, errs: errs, records: records}
|
||||||
|
}
|
||||||
|
f = RemoveEmptyPackage(f)
|
||||||
|
ndata, err := runBuildifier(f)
|
||||||
|
if err != nil {
|
||||||
|
return &rewriteResult{file: name, errs: []error{fmt.Errorf("running buildifier: %v", err)}, records: records}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Opts.Stdout || name == stdinPackageName {
|
||||||
|
os.Stdout.Write(ndata)
|
||||||
|
return &rewriteResult{file: name, errs: errs, records: records}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(data, ndata) {
|
||||||
|
return &rewriteResult{file: name, errs: errs, records: records}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := EditFile(fi, name); err != nil {
|
||||||
|
return &rewriteResult{file: name, errs: []error{err}, records: records}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := file.WriteFile(name, ndata); err != nil {
|
||||||
|
return &rewriteResult{file: name, errs: []error{err}, records: records}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileModified = true
|
||||||
|
return &rewriteResult{file: name, errs: errs, modified: true, records: records}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditFile is a function that does any prework needed before editing a file.
|
||||||
|
// e.g. "checking out for write" from a locking source control repo.
|
||||||
|
var EditFile = func(fi os.FileInfo, name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runBuildifier formats the build file f.
|
||||||
|
// Runs Opts.Buildifier if it's non-empty, otherwise uses built-in formatter.
|
||||||
|
// Opts.Buildifier is useful to force consistency with other tools that call Buildifier.
|
||||||
|
func runBuildifier(f *build.File) ([]byte, error) {
|
||||||
|
if Opts.Buildifier == "" {
|
||||||
|
build.Rewrite(f, nil)
|
||||||
|
return build.Format(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(Opts.Buildifier)
|
||||||
|
data := build.Format(f)
|
||||||
|
cmd.Stdin = bytes.NewBuffer(data)
|
||||||
|
stdout := bytes.NewBuffer(nil)
|
||||||
|
stderr := bytes.NewBuffer(nil)
|
||||||
|
cmd.Stdout = stdout
|
||||||
|
cmd.Stderr = stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if stderr.Len() > 0 {
|
||||||
|
return nil, fmt.Errorf("%s", stderr.Bytes())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stdout.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a target, whose package may contain a trailing "/...", returns all
|
||||||
|
// extisting BUILD file paths which match the package.
|
||||||
|
func targetExpressionToBuildFiles(target string) []string {
|
||||||
|
file, _, _ := InterpretLabelForWorkspaceLocation(Opts.RootDir, target)
|
||||||
|
if Opts.RootDir == "" {
|
||||||
|
var err error
|
||||||
|
if file, err = filepath.Abs(file); err != nil {
|
||||||
|
fmt.Printf("Cannot make path absolute: %s\n", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(file, "/.../BUILD") {
|
||||||
|
return []string{file}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildFiles []string
|
||||||
|
searchDirs := []string{strings.TrimSuffix(file, "/.../BUILD")}
|
||||||
|
for len(searchDirs) != 0 {
|
||||||
|
lastIndex := len(searchDirs) - 1
|
||||||
|
dir := searchDirs[lastIndex]
|
||||||
|
searchDirs = searchDirs[:lastIndex]
|
||||||
|
|
||||||
|
dirFiles, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dirFile := range dirFiles {
|
||||||
|
if dirFile.IsDir() {
|
||||||
|
searchDirs = append(searchDirs, path.Join(dir, dirFile.Name()))
|
||||||
|
} else if _, ok := buildFileNamesSet[dirFile.Name()]; ok {
|
||||||
|
buildFiles = append(buildFiles, path.Join(dir, dirFile.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendCommands adds the given commands to be applied to each of the given targets
|
||||||
|
// via the commandMap.
|
||||||
|
func appendCommands(commandMap map[string][]commandsForTarget, args []string) {
|
||||||
|
commands, targets := parseCommands(args)
|
||||||
|
for _, target := range targets {
|
||||||
|
if strings.HasSuffix(target, "/BUILD") {
|
||||||
|
target = strings.TrimSuffix(target, "/BUILD") + ":__pkg__"
|
||||||
|
}
|
||||||
|
var buildFiles []string
|
||||||
|
_, pkg, _ := ParseLabel(target)
|
||||||
|
if pkg == stdinPackageName {
|
||||||
|
buildFiles = []string{stdinPackageName}
|
||||||
|
} else {
|
||||||
|
buildFiles = targetExpressionToBuildFiles(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range buildFiles {
|
||||||
|
commandMap[file] = append(commandMap[file], commandsForTarget{target, commands})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendCommandsFromFile(commandsByFile map[string][]commandsForTarget, fileName string) {
|
||||||
|
var reader io.Reader
|
||||||
|
if Opts.CommandsFile == stdinPackageName {
|
||||||
|
reader = os.Stdin
|
||||||
|
} else {
|
||||||
|
rc := file.OpenReadFile(Opts.CommandsFile)
|
||||||
|
reader = rc
|
||||||
|
defer rc.Close()
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
args := strings.Split(line, "|")
|
||||||
|
appendCommands(commandsByFile, args)
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error while reading commands file: %v", scanner.Err())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printRecord(writer io.Writer, record *apipb.Output_Record) {
|
||||||
|
fields := record.Fields
|
||||||
|
line := make([]string, len(fields))
|
||||||
|
for i, field := range fields {
|
||||||
|
switch value := field.Value.(type) {
|
||||||
|
case *apipb.Output_Record_Field_Text:
|
||||||
|
if field.QuoteWhenPrinting && strings.ContainsRune(value.Text, ' ') {
|
||||||
|
line[i] = fmt.Sprintf("%q", value.Text)
|
||||||
|
} else {
|
||||||
|
line[i] = value.Text
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case *apipb.Output_Record_Field_Number:
|
||||||
|
line[i] = strconv.Itoa(int(value.Number))
|
||||||
|
break
|
||||||
|
case *apipb.Output_Record_Field_Error:
|
||||||
|
switch value.Error {
|
||||||
|
case apipb.Output_Record_Field_UNKNOWN:
|
||||||
|
line[i] = "(unknown)"
|
||||||
|
break
|
||||||
|
case apipb.Output_Record_Field_MISSING:
|
||||||
|
line[i] = "(missing)"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case *apipb.Output_Record_Field_List:
|
||||||
|
line[i] = fmt.Sprintf("[%s]", strings.Join(value.List.Strings, " "))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(writer, strings.Join(line, " ")+"\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buildozer loops over all arguments on the command line fixing BUILD files.
|
||||||
|
func Buildozer(args []string) int {
|
||||||
|
commandsByFile := make(map[string][]commandsForTarget)
|
||||||
|
if Opts.CommandsFile != "" {
|
||||||
|
appendCommandsFromFile(commandsByFile, Opts.CommandsFile)
|
||||||
|
} else {
|
||||||
|
if len(args) == 0 {
|
||||||
|
Usage()
|
||||||
|
}
|
||||||
|
appendCommands(commandsByFile, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
numFiles := len(commandsByFile)
|
||||||
|
if Opts.Parallelism > 0 {
|
||||||
|
runtime.GOMAXPROCS(Opts.Parallelism)
|
||||||
|
}
|
||||||
|
results := make(chan *rewriteResult, numFiles)
|
||||||
|
data := make(chan commandsForFile)
|
||||||
|
|
||||||
|
for i := 0; i < Opts.NumIO; i++ {
|
||||||
|
go func(results chan *rewriteResult, data chan commandsForFile) {
|
||||||
|
for commandsForFile := range data {
|
||||||
|
results <- rewrite(commandsForFile)
|
||||||
|
}
|
||||||
|
}(results, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
for file, commands := range commandsByFile {
|
||||||
|
data <- commandsForFile{file, commands}
|
||||||
|
}
|
||||||
|
close(data)
|
||||||
|
records := []*apipb.Output_Record{}
|
||||||
|
hasErrors := false
|
||||||
|
for i := 0; i < numFiles; i++ {
|
||||||
|
fileResults := <-results
|
||||||
|
if fileResults == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hasErrors = hasErrors || len(fileResults.errs) > 0
|
||||||
|
for _, err := range fileResults.errs {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: %s\n", fileResults.file, err)
|
||||||
|
}
|
||||||
|
if fileResults.modified && !Opts.Quiet {
|
||||||
|
fmt.Fprintf(os.Stderr, "fixed %s\n", fileResults.file)
|
||||||
|
}
|
||||||
|
if fileResults.records != nil {
|
||||||
|
records = append(records, fileResults.records...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Opts.IsPrintingProto {
|
||||||
|
data, err := proto.Marshal(&apipb.Output{Records: records})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("marshaling error: ", err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stdout, "%s", data)
|
||||||
|
} else {
|
||||||
|
for _, record := range records {
|
||||||
|
printRecord(os.Stdout, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasErrors {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if !fileModified && !Opts.Stdout {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,823 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package edit provides high-level auxiliary functions for AST manipulation
|
||||||
|
// on BUILD files.
|
||||||
|
package edit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bazelbuild/buildtools/build"
|
||||||
|
"github.com/bazelbuild/buildtools/wspace"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ShortenLabelsFlag if true converts added labels to short form , e.g. //foo:bar => :bar
|
||||||
|
ShortenLabelsFlag = true
|
||||||
|
// DeleteWithComments if true a list attribute will be be deleted in ListDelete, even if there is a comment attached to it
|
||||||
|
DeleteWithComments = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseLabel parses a Blaze label (eg. //devtools/buildozer:rule), and returns
|
||||||
|
// the repo name ("" for the main repo), package (with leading slashes trimmed)
|
||||||
|
// and rule name (e.g. ["", "devtools/buildozer", "rule"]).
|
||||||
|
func ParseLabel(target string) (string, string, string) {
|
||||||
|
repo := ""
|
||||||
|
if strings.HasPrefix(target, "@") {
|
||||||
|
target = strings.TrimLeft(target, "@")
|
||||||
|
parts := strings.SplitN(target, "/", 2)
|
||||||
|
if len(parts) == 1 {
|
||||||
|
// "@foo" -> "foo", "", "foo" (ie @foo//:foo)
|
||||||
|
return target, "", target
|
||||||
|
}
|
||||||
|
repo = parts[0]
|
||||||
|
target = "/" + parts[1]
|
||||||
|
}
|
||||||
|
// TODO(bazel-team): check if the next line can now be deleted
|
||||||
|
target = strings.TrimRight(target, ":") // labels can end with ':'
|
||||||
|
parts := strings.SplitN(target, ":", 2)
|
||||||
|
parts[0] = strings.TrimPrefix(parts[0], "//")
|
||||||
|
if len(parts) == 1 {
|
||||||
|
if strings.HasPrefix(target, "//") {
|
||||||
|
// "//absolute/pkg" -> "absolute/pkg", "pkg"
|
||||||
|
return repo, parts[0], path.Base(parts[0])
|
||||||
|
}
|
||||||
|
// "relative/label" -> "", "relative/label"
|
||||||
|
return repo, "", parts[0]
|
||||||
|
}
|
||||||
|
return repo, parts[0], parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShortenLabel rewrites labels to use the canonical form (the form
|
||||||
|
// recommended by build-style). This behavior can be disabled using the
|
||||||
|
// --noshorten_labels flag for projects that consistently use long-form labels.
|
||||||
|
// "//foo/bar:bar" => "//foo/bar", or ":bar" when possible.
|
||||||
|
func ShortenLabel(label string, pkg string) string {
|
||||||
|
if !ShortenLabelsFlag {
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(label, "//") {
|
||||||
|
// It doesn't look like a long label, so we preserve it.
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
repo, labelPkg, rule := ParseLabel(label)
|
||||||
|
if repo == "" && labelPkg == pkg { // local label
|
||||||
|
return ":" + rule
|
||||||
|
}
|
||||||
|
slash := strings.LastIndex(labelPkg, "/")
|
||||||
|
if (slash >= 0 && labelPkg[slash+1:] == rule) || labelPkg == rule {
|
||||||
|
return "//" + labelPkg
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelsEqual returns true if label1 and label2 are equal. The function
|
||||||
|
// takes care of the optional ":" prefix and differences between long-form
|
||||||
|
// labels and local labels.
|
||||||
|
func LabelsEqual(label1, label2, pkg string) bool {
|
||||||
|
str1 := strings.TrimPrefix(ShortenLabel(label1, pkg), ":")
|
||||||
|
str2 := strings.TrimPrefix(ShortenLabel(label2, pkg), ":")
|
||||||
|
return str1 == str2
|
||||||
|
}
|
||||||
|
|
||||||
|
// isFile returns true if the path refers to a regular file after following
|
||||||
|
// symlinks.
|
||||||
|
func isFile(path string) bool {
|
||||||
|
path, err := filepath.EvalSymlinks(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.Mode().IsRegular()
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterpretLabelForWorkspaceLocation returns the name of the BUILD file to
|
||||||
|
// edit, the full package name, and the rule. It takes a workspace-rooted
|
||||||
|
// directory to use.
|
||||||
|
func InterpretLabelForWorkspaceLocation(root string, target string) (buildFile string, pkg string, rule string) {
|
||||||
|
repo, pkg, rule := ParseLabel(target)
|
||||||
|
rootDir, relativePath := wspace.FindWorkspaceRoot(root)
|
||||||
|
if repo != "" {
|
||||||
|
files, err := wspace.FindRepoBuildFiles(rootDir)
|
||||||
|
if err == nil {
|
||||||
|
if buildFile, ok := files[repo]; ok {
|
||||||
|
return buildFile, pkg, rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(rodrigoq): report error for other repos
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(target, "//") {
|
||||||
|
buildFile = path.Join(rootDir, pkg, "BUILD")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isFile(pkg) {
|
||||||
|
// allow operation on other files like WORKSPACE
|
||||||
|
buildFile = pkg
|
||||||
|
pkg = path.Join(relativePath, filepath.Dir(pkg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pkg != "" {
|
||||||
|
buildFile = pkg + "/BUILD"
|
||||||
|
} else {
|
||||||
|
buildFile = "BUILD"
|
||||||
|
}
|
||||||
|
pkg = path.Join(relativePath, pkg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterpretLabel returns the name of the BUILD file to edit, the full
|
||||||
|
// package name, and the rule. It uses the pwd for resolving workspace file paths.
|
||||||
|
func InterpretLabel(target string) (buildFile string, pkg string, rule string) {
|
||||||
|
return InterpretLabelForWorkspaceLocation("", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExprToRule returns a Rule from an Expr.
|
||||||
|
// The boolean is false iff the Expr is not a function call, or does not have
|
||||||
|
// the expected kind.
|
||||||
|
func ExprToRule(expr build.Expr, kind string) (*build.Rule, bool) {
|
||||||
|
call, ok := expr.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
k, ok := call.X.(*build.LiteralExpr)
|
||||||
|
if !ok || k.Token != kind {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return &build.Rule{Call: call}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingPackageDeclaration returns the package declaration, or nil if there is none.
|
||||||
|
func ExistingPackageDeclaration(f *build.File) *build.Rule {
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
if rule, ok := ExprToRule(stmt, "package"); ok {
|
||||||
|
return rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageDeclaration returns the package declaration. If it doesn't
|
||||||
|
// exist, it is created at the top of the BUILD file, after leading
|
||||||
|
// comments.
|
||||||
|
func PackageDeclaration(f *build.File) *build.Rule {
|
||||||
|
if pkg := ExistingPackageDeclaration(f); pkg != nil {
|
||||||
|
return pkg
|
||||||
|
}
|
||||||
|
all := []build.Expr{}
|
||||||
|
added := false
|
||||||
|
call := &build.CallExpr{X: &build.LiteralExpr{Token: "package"}}
|
||||||
|
// Skip CommentBlocks and find a place to insert the package declaration.
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
_, ok := stmt.(*build.CommentBlock)
|
||||||
|
if !ok && !added {
|
||||||
|
all = append(all, call)
|
||||||
|
added = true
|
||||||
|
}
|
||||||
|
all = append(all, stmt)
|
||||||
|
}
|
||||||
|
if !added { // In case the file is empty.
|
||||||
|
all = append(all, call)
|
||||||
|
}
|
||||||
|
f.Stmt = all
|
||||||
|
return &build.Rule{Call: call}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEmptyPackage removes empty package declarations from the file, i.e.:
|
||||||
|
// package()
|
||||||
|
// This might appear because of a buildozer transformation (e.g. when removing a package
|
||||||
|
// attribute). Removing it is required for the file to be valid.
|
||||||
|
func RemoveEmptyPackage(f *build.File) *build.File {
|
||||||
|
var all []build.Expr
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
if call, ok := stmt.(*build.CallExpr); ok {
|
||||||
|
functionName, ok := call.X.(*build.LiteralExpr)
|
||||||
|
if ok && functionName.Token == "package" && len(call.List) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all = append(all, stmt)
|
||||||
|
}
|
||||||
|
return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertAfter inserts an expression after index i.
|
||||||
|
func InsertAfter(i int, stmt []build.Expr, expr build.Expr) []build.Expr {
|
||||||
|
i = i + 1 // index after the element at i
|
||||||
|
result := make([]build.Expr, len(stmt)+1)
|
||||||
|
copy(result[0:i], stmt[0:i])
|
||||||
|
result[i] = expr
|
||||||
|
copy(result[i+1:], stmt[i:])
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOfLast finds the index of the last expression of a specific kind.
|
||||||
|
func IndexOfLast(stmt []build.Expr, Kind string) int {
|
||||||
|
lastIndex := -1
|
||||||
|
for i, s := range stmt {
|
||||||
|
sAsCallExpr, ok := s.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
literal, ok := sAsCallExpr.X.(*build.LiteralExpr)
|
||||||
|
if ok && literal.Token == Kind {
|
||||||
|
lastIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertAfterLastOfSameKind inserts an expression after the last expression of the same kind.
|
||||||
|
func InsertAfterLastOfSameKind(stmt []build.Expr, expr *build.CallExpr) []build.Expr {
|
||||||
|
index := IndexOfLast(stmt, expr.X.(*build.LiteralExpr).Token)
|
||||||
|
if index == -1 {
|
||||||
|
return InsertAtEnd(stmt, expr)
|
||||||
|
}
|
||||||
|
return InsertAfter(index, stmt, expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertAtEnd inserts an expression at the end of a list, before trailing comments.
|
||||||
|
func InsertAtEnd(stmt []build.Expr, expr build.Expr) []build.Expr {
|
||||||
|
var i int
|
||||||
|
for i = len(stmt) - 1; i >= 0; i-- {
|
||||||
|
_, ok := stmt[i].(*build.CommentBlock)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InsertAfter(i, stmt, expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRuleByName returns the rule in the file that has the given name.
|
||||||
|
// If the name is "__pkg__", it returns the global package declaration.
|
||||||
|
func FindRuleByName(f *build.File, name string) *build.Rule {
|
||||||
|
if name == "__pkg__" {
|
||||||
|
return PackageDeclaration(f)
|
||||||
|
}
|
||||||
|
i := IndexOfRuleByName(f, name)
|
||||||
|
if i != -1 {
|
||||||
|
return &build.Rule{Call: f.Stmt[i].(*build.CallExpr)}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseImplicitName returns the rule in the file if it meets these conditions:
|
||||||
|
// - It is the only unnamed rule in the file.
|
||||||
|
// - The file path's ending directory name and the passed rule name match.
|
||||||
|
// In the Pants Build System, by pantsbuild, the use of an implicit name makes
|
||||||
|
// creating targets easier. This function implements such names.
|
||||||
|
func UseImplicitName(f *build.File, rule string) *build.Rule {
|
||||||
|
// We disallow empty names
|
||||||
|
if f.Path == "BUILD" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ruleCount := 0
|
||||||
|
var temp, found *build.Rule
|
||||||
|
pkg := filepath.Base(filepath.Dir(f.Path))
|
||||||
|
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
call, ok := stmt.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
temp = &build.Rule{Call: call}
|
||||||
|
if temp.Kind() != "" && temp.Name() == "" {
|
||||||
|
ruleCount++
|
||||||
|
found = temp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ruleCount == 1 {
|
||||||
|
if rule == pkg {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOfRuleByName returns the index (in f.Stmt) of the CallExpr which defines a rule named `name`, or -1 if it doesn't exist.
|
||||||
|
func IndexOfRuleByName(f *build.File, name string) int {
|
||||||
|
linenum := -1
|
||||||
|
if strings.HasPrefix(name, "%") {
|
||||||
|
// "%<LINENUM>" will match the rule which begins at LINENUM.
|
||||||
|
// This is for convenience, "%" is not a valid character in bazel targets.
|
||||||
|
if result, err := strconv.Atoi(name[1:]); err == nil {
|
||||||
|
linenum = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, stmt := range f.Stmt {
|
||||||
|
call, ok := stmt.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := &build.Rule{Call: call}
|
||||||
|
start, _ := call.X.Span()
|
||||||
|
if r.Name() == name || start.Line == linenum {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindExportedFile returns the first exports_files call which contains the
|
||||||
|
// file 'name', or nil if not found
|
||||||
|
func FindExportedFile(f *build.File, name string) *build.Rule {
|
||||||
|
for _, r := range f.Rules("exports_files") {
|
||||||
|
if len(r.Call.List) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkg := "" // Files are not affected by the package name
|
||||||
|
if ListFind(r.Call.List[0], name, pkg) != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRule returns the AST without the specified rule
|
||||||
|
func DeleteRule(f *build.File, rule *build.Rule) *build.File {
|
||||||
|
var all []build.Expr
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
if stmt == rule.Call {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
all = append(all, stmt)
|
||||||
|
}
|
||||||
|
return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRuleByName returns the AST without the rules that have the
|
||||||
|
// given name.
|
||||||
|
func DeleteRuleByName(f *build.File, name string) *build.File {
|
||||||
|
var all []build.Expr
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
call, ok := stmt.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
all = append(all, stmt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := &build.Rule{Call: call}
|
||||||
|
if r.Name() != name {
|
||||||
|
all = append(all, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRuleByKind removes the rules of the specified kind from the AST.
|
||||||
|
// Returns an updated copy of f.
|
||||||
|
func DeleteRuleByKind(f *build.File, kind string) *build.File {
|
||||||
|
var all []build.Expr
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
call, ok := stmt.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
all = append(all, stmt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k, ok := call.X.(*build.LiteralExpr)
|
||||||
|
if !ok || k.Token != kind {
|
||||||
|
all = append(all, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllLists returns all the lists concatenated in an expression.
|
||||||
|
// For example, in: glob(["*.go"]) + [":rule"]
|
||||||
|
// the function will return [[":rule"]].
|
||||||
|
func AllLists(e build.Expr) []*build.ListExpr {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *build.ListExpr:
|
||||||
|
return []*build.ListExpr{e}
|
||||||
|
case *build.BinaryExpr:
|
||||||
|
if e.Op == "+" {
|
||||||
|
return append(AllLists(e.X), AllLists(e.Y)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstList works in the same way as AllLists, except that it
|
||||||
|
// returns only one list, or nil.
|
||||||
|
func FirstList(e build.Expr) *build.ListExpr {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *build.ListExpr:
|
||||||
|
return e
|
||||||
|
case *build.BinaryExpr:
|
||||||
|
if e.Op == "+" {
|
||||||
|
li := FirstList(e.X)
|
||||||
|
if li == nil {
|
||||||
|
return FirstList(e.Y)
|
||||||
|
}
|
||||||
|
return li
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllStrings returns all the string literals concatenated in an expression.
|
||||||
|
// For example, in: "foo" + x + "bar"
|
||||||
|
// the function will return ["foo", "bar"].
|
||||||
|
func AllStrings(e build.Expr) []*build.StringExpr {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *build.StringExpr:
|
||||||
|
return []*build.StringExpr{e}
|
||||||
|
case *build.BinaryExpr:
|
||||||
|
if e.Op == "+" {
|
||||||
|
return append(AllStrings(e.X), AllStrings(e.Y)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFind looks for a string in the list expression (which may be a
|
||||||
|
// concatenation of lists). It returns the element if it is found. nil
|
||||||
|
// otherwise.
|
||||||
|
func ListFind(e build.Expr, item string, pkg string) *build.StringExpr {
|
||||||
|
item = ShortenLabel(item, pkg)
|
||||||
|
for _, li := range AllLists(e) {
|
||||||
|
for _, elem := range li.List {
|
||||||
|
str, ok := elem.(*build.StringExpr)
|
||||||
|
if ok && LabelsEqual(str.Value, item, pkg) {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasComments returns whether the StringExpr literal has a comment attached to it.
|
||||||
|
func hasComments(literal *build.StringExpr) bool {
|
||||||
|
return len(literal.Before) > 0 || len(literal.Suffix) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsComments returns whether the expr has a comment that includes str.
|
||||||
|
func ContainsComments(expr build.Expr, str string) bool {
|
||||||
|
str = strings.ToLower(str)
|
||||||
|
com := expr.Comment()
|
||||||
|
comments := append(com.Before, com.Suffix...)
|
||||||
|
comments = append(comments, com.After...)
|
||||||
|
for _, c := range comments {
|
||||||
|
if strings.Contains(strings.ToLower(c.Token), str) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDelete deletes the item from a list expression in e and returns
|
||||||
|
// the StringExpr deleted, or nil otherwise.
|
||||||
|
func ListDelete(e build.Expr, item, pkg string) (deleted *build.StringExpr) {
|
||||||
|
deleted = nil
|
||||||
|
item = ShortenLabel(item, pkg)
|
||||||
|
for _, li := range AllLists(e) {
|
||||||
|
var all []build.Expr
|
||||||
|
for _, elem := range li.List {
|
||||||
|
if str, ok := elem.(*build.StringExpr); ok {
|
||||||
|
if LabelsEqual(str.Value, item, pkg) && (DeleteWithComments || !hasComments(str)) {
|
||||||
|
deleted = str
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all = append(all, elem)
|
||||||
|
}
|
||||||
|
li.List = all
|
||||||
|
}
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAttributeDelete deletes string item from list attribute attr, deletes attr if empty,
|
||||||
|
// and returns the StringExpr deleted, or nil otherwise.
|
||||||
|
func ListAttributeDelete(rule *build.Rule, attr, item, pkg string) *build.StringExpr {
|
||||||
|
deleted := ListDelete(rule.Attr(attr), item, pkg)
|
||||||
|
if deleted != nil {
|
||||||
|
if listExpr, ok := rule.Attr(attr).(*build.ListExpr); ok && len(listExpr.List) == 0 {
|
||||||
|
rule.DelAttr(attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListReplace replaces old with value in all lists in e and returns a Boolean
|
||||||
|
// to indicate whether the replacement was successful.
|
||||||
|
func ListReplace(e build.Expr, old, value, pkg string) bool {
|
||||||
|
replaced := false
|
||||||
|
old = ShortenLabel(old, pkg)
|
||||||
|
for _, li := range AllLists(e) {
|
||||||
|
for k, elem := range li.List {
|
||||||
|
str, ok := elem.(*build.StringExpr)
|
||||||
|
if !ok || !LabelsEqual(str.Value, old, pkg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
li.List[k] = &build.StringExpr{Value: ShortenLabel(value, pkg), Comments: *elem.Comment()}
|
||||||
|
replaced = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return replaced
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExprLessThan compares two Expr statements. Currently, only labels are supported.
|
||||||
|
func isExprLessThan(x1, x2 build.Expr) bool {
|
||||||
|
str1, ok1 := x1.(*build.StringExpr)
|
||||||
|
str2, ok2 := x2.(*build.StringExpr)
|
||||||
|
if ok1 != ok2 {
|
||||||
|
return ok2
|
||||||
|
}
|
||||||
|
if ok1 && ok2 {
|
||||||
|
// Labels starting with // are put at the end.
|
||||||
|
pre1 := strings.HasPrefix(str1.Value, "//")
|
||||||
|
pre2 := strings.HasPrefix(str2.Value, "//")
|
||||||
|
if pre1 != pre2 {
|
||||||
|
return pre2
|
||||||
|
}
|
||||||
|
return str1.Value < str2.Value
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortedInsert(list []build.Expr, item build.Expr) []build.Expr {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(list); i++ {
|
||||||
|
if isExprLessThan(item, list[i]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res := make([]build.Expr, 0, len(list)+1)
|
||||||
|
res = append(res, list[:i]...)
|
||||||
|
res = append(res, item)
|
||||||
|
res = append(res, list[i:]...)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributeMustNotBeSorted returns true if the list in the attribute cannot be
|
||||||
|
// sorted. For some attributes, it makes sense to try to do a sorted insert
|
||||||
|
// (e.g. deps), even when buildifier will not sort it for conservative reasons.
|
||||||
|
// For a few attributes, sorting will never make sense.
|
||||||
|
func attributeMustNotBeSorted(rule, attr string) bool {
|
||||||
|
// TODO(bazel-team): Come up with a more complete list.
|
||||||
|
return attr == "args"
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVariable returns the binary expression that assignes a variable to expr, if expr is
|
||||||
|
// an identifier of a variable that vars contains a mapping for.
|
||||||
|
func getVariable(expr build.Expr, vars *map[string]*build.BinaryExpr) (varAssignment *build.BinaryExpr) {
|
||||||
|
if vars == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if literal, ok := expr.(*build.LiteralExpr); ok {
|
||||||
|
if varAssignment = (*vars)[literal.Token]; varAssignment != nil {
|
||||||
|
return varAssignment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddValueToList adds a value to a list. If the expression is
|
||||||
|
// not a list, a list with a single element is appended to the original
|
||||||
|
// expression.
|
||||||
|
func AddValueToList(oldList build.Expr, pkg string, item build.Expr, sorted bool) build.Expr {
|
||||||
|
if oldList == nil {
|
||||||
|
return &build.ListExpr{List: []build.Expr{item}}
|
||||||
|
}
|
||||||
|
|
||||||
|
str, ok := item.(*build.StringExpr)
|
||||||
|
if ok && ListFind(oldList, str.Value, pkg) != nil {
|
||||||
|
// The value is already in the list.
|
||||||
|
return oldList
|
||||||
|
}
|
||||||
|
li := FirstList(oldList)
|
||||||
|
if li != nil {
|
||||||
|
if sorted {
|
||||||
|
li.List = sortedInsert(li.List, item)
|
||||||
|
} else {
|
||||||
|
li.List = append(li.List, item)
|
||||||
|
}
|
||||||
|
return oldList
|
||||||
|
}
|
||||||
|
list := &build.ListExpr{List: []build.Expr{item}}
|
||||||
|
concat := &build.BinaryExpr{Op: "+", X: oldList, Y: list}
|
||||||
|
return concat
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddValueToListAttribute adds the given item to the list attribute identified by name and pkg.
|
||||||
|
func AddValueToListAttribute(r *build.Rule, name string, pkg string, item build.Expr, vars *map[string]*build.BinaryExpr) {
|
||||||
|
old := r.Attr(name)
|
||||||
|
sorted := !attributeMustNotBeSorted(r.Kind(), name)
|
||||||
|
if varAssignment := getVariable(old, vars); varAssignment != nil {
|
||||||
|
varAssignment.Y = AddValueToList(varAssignment.Y, pkg, item, sorted)
|
||||||
|
} else {
|
||||||
|
r.SetAttr(name, AddValueToList(old, pkg, item, sorted))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveAllListAttributeValues moves all values from list attribute oldAttr to newAttr,
|
||||||
|
// and deletes oldAttr.
|
||||||
|
func MoveAllListAttributeValues(rule *build.Rule, oldAttr, newAttr, pkg string, vars *map[string]*build.BinaryExpr) error {
|
||||||
|
if rule.Attr(oldAttr) == nil {
|
||||||
|
return fmt.Errorf("no attribute %s found in %s", oldAttr, rule.Name())
|
||||||
|
}
|
||||||
|
if rule.Attr(newAttr) == nil {
|
||||||
|
RenameAttribute(rule, oldAttr, newAttr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if listExpr, ok := rule.Attr(oldAttr).(*build.ListExpr); ok {
|
||||||
|
for _, val := range listExpr.List {
|
||||||
|
AddValueToListAttribute(rule, newAttr, pkg, val, vars)
|
||||||
|
}
|
||||||
|
rule.DelAttr(oldAttr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s already exists and %s is not a simple list", newAttr, oldAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DictionarySet looks for the key in the dictionary expression. If value is not nil,
|
||||||
|
// it replaces the current value with it. In all cases, it returns the current value.
|
||||||
|
func DictionarySet(dict *build.DictExpr, key string, value build.Expr) build.Expr {
|
||||||
|
for _, e := range dict.List {
|
||||||
|
kv, _ := e.(*build.KeyValueExpr)
|
||||||
|
if k, ok := kv.Key.(*build.StringExpr); ok && k.Value == key {
|
||||||
|
if value != nil {
|
||||||
|
kv.Value = value
|
||||||
|
}
|
||||||
|
return kv.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if value != nil {
|
||||||
|
kv := &build.KeyValueExpr{Key: &build.StringExpr{Value: key}, Value: value}
|
||||||
|
dict.List = append(dict.List, kv)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenameAttribute renames an attribute in a rule.
|
||||||
|
func RenameAttribute(r *build.Rule, oldName, newName string) error {
|
||||||
|
if r.Attr(newName) != nil {
|
||||||
|
return fmt.Errorf("attribute %s already exists in rule %s", newName, r.Name())
|
||||||
|
}
|
||||||
|
for _, kv := range r.Call.List {
|
||||||
|
as, ok := kv.(*build.BinaryExpr)
|
||||||
|
if !ok || as.Op != "=" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k, ok := as.X.(*build.LiteralExpr)
|
||||||
|
if !ok || k.Token != oldName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k.Token = newName
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("no attribute %s found in rule %s", oldName, r.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditFunction is a wrapper around build.Edit. The callback is called only on
|
||||||
|
// functions 'name'.
|
||||||
|
func EditFunction(v build.Expr, name string, f func(x *build.CallExpr, stk []build.Expr) build.Expr) build.Expr {
|
||||||
|
return build.Edit(v, func(expr build.Expr, stk []build.Expr) build.Expr {
|
||||||
|
call, ok := expr.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fct, ok := call.X.(*build.LiteralExpr)
|
||||||
|
if !ok || fct.Token != name {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f(call, stk)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsedSymbols returns the set of symbols used in the BUILD file (variables, function names).
|
||||||
|
func UsedSymbols(f *build.File) map[string]bool {
|
||||||
|
symbols := make(map[string]bool)
|
||||||
|
build.Walk(f, func(expr build.Expr, stack []build.Expr) {
|
||||||
|
literal, ok := expr.(*build.LiteralExpr)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check if we are on the left-side of an assignment
|
||||||
|
for _, e := range stack {
|
||||||
|
if as, ok := e.(*build.BinaryExpr); ok {
|
||||||
|
if as.Op == "=" && as.X == expr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
symbols[literal.Token] = true
|
||||||
|
})
|
||||||
|
return symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLoad(args []string) *build.CallExpr {
|
||||||
|
load := &build.CallExpr{
|
||||||
|
X: &build.LiteralExpr{
|
||||||
|
Token: "load",
|
||||||
|
},
|
||||||
|
List: []build.Expr{},
|
||||||
|
ForceCompact: true,
|
||||||
|
}
|
||||||
|
for _, a := range args {
|
||||||
|
load.List = append(load.List, &build.StringExpr{Value: a})
|
||||||
|
}
|
||||||
|
return load
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendLoad tries to find an existing load location and append symbols to it.
|
||||||
|
func appendLoad(stmts []build.Expr, args []string) bool {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
location := args[0]
|
||||||
|
symbolsToLoad := make(map[string]bool)
|
||||||
|
for _, s := range args[1:] {
|
||||||
|
symbolsToLoad[s] = true
|
||||||
|
}
|
||||||
|
var lastLoad *build.CallExpr
|
||||||
|
for _, s := range stmts {
|
||||||
|
call, ok := s.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if l, ok := call.X.(*build.LiteralExpr); !ok || l.Token != "load" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(call.List) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s, ok := call.List[0].(*build.StringExpr); !ok || s.Value != location {
|
||||||
|
continue // Loads a different file.
|
||||||
|
}
|
||||||
|
for _, arg := range call.List[1:] {
|
||||||
|
if s, ok := arg.(*build.StringExpr); ok {
|
||||||
|
delete(symbolsToLoad, s.Value) // Already loaded.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remember the last insert location, but potentially remove more symbols
|
||||||
|
// that are already loaded in other subsequent calls.
|
||||||
|
lastLoad = call
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastLoad == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the remaining loads to the last load location.
|
||||||
|
sortedSymbols := []string{}
|
||||||
|
for s := range symbolsToLoad {
|
||||||
|
sortedSymbols = append(sortedSymbols, s)
|
||||||
|
}
|
||||||
|
sort.Strings(sortedSymbols)
|
||||||
|
for _, s := range sortedSymbols {
|
||||||
|
lastLoad.List = append(lastLoad.List, &build.StringExpr{Value: s})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertLoad inserts a load statement at the top of the list of statements.
|
||||||
|
// The load statement is constructed using args. Symbols that are already loaded
|
||||||
|
// from the given filepath are ignored. If stmts already contains a load for the
|
||||||
|
// location in arguments, appends the symbols to load to it.
|
||||||
|
func InsertLoad(stmts []build.Expr, args []string) []build.Expr {
|
||||||
|
if appendLoad(stmts, args) {
|
||||||
|
return stmts
|
||||||
|
}
|
||||||
|
|
||||||
|
load := newLoad(args)
|
||||||
|
|
||||||
|
var all []build.Expr
|
||||||
|
added := false
|
||||||
|
for _, stmt := range stmts {
|
||||||
|
_, isComment := stmt.(*build.CommentBlock)
|
||||||
|
if isComment || added {
|
||||||
|
all = append(all, stmt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
all = append(all, load)
|
||||||
|
all = append(all, stmt)
|
||||||
|
added = true
|
||||||
|
}
|
||||||
|
if !added { // Empty file or just comments.
|
||||||
|
all = append(all, load)
|
||||||
|
}
|
||||||
|
return all
|
||||||
|
}
|
|
@ -0,0 +1,569 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
// Functions to clean and fix BUILD files
|
||||||
|
|
||||||
|
package edit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bazelbuild/buildtools/build"
|
||||||
|
)
|
||||||
|
|
||||||
|
// splitOptionsWithSpaces is a cleanup function.
|
||||||
|
// It splits options strings that contain a space. This change
|
||||||
|
// should be safe as Blaze is splitting those strings, but we will
|
||||||
|
// eventually get rid of this misfeature.
|
||||||
|
// eg. it converts from:
|
||||||
|
// copts = ["-Dfoo -Dbar"]
|
||||||
|
// to:
|
||||||
|
// copts = ["-Dfoo", "-Dbar"]
|
||||||
|
func splitOptionsWithSpaces(_ *build.File, r *build.Rule, _ string) bool {
|
||||||
|
var attrToRewrite = []string{
|
||||||
|
"copts",
|
||||||
|
"linkopts",
|
||||||
|
}
|
||||||
|
fixed := false
|
||||||
|
for _, attrName := range attrToRewrite {
|
||||||
|
attr := r.Attr(attrName)
|
||||||
|
if attr != nil {
|
||||||
|
for _, li := range AllLists(attr) {
|
||||||
|
fixed = splitStrings(li) || fixed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitStrings(list *build.ListExpr) bool {
|
||||||
|
var all []build.Expr
|
||||||
|
fixed := false
|
||||||
|
for _, e := range list.List {
|
||||||
|
str, ok := e.(*build.StringExpr)
|
||||||
|
if !ok {
|
||||||
|
all = append(all, e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(str.Value, " ") && !strings.Contains(str.Value, "'\"") {
|
||||||
|
fixed = true
|
||||||
|
for i, substr := range strings.Fields(str.Value) {
|
||||||
|
item := &build.StringExpr{Value: substr}
|
||||||
|
if i == 0 {
|
||||||
|
item.Comments = str.Comments
|
||||||
|
}
|
||||||
|
all = append(all, item)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
all = append(all, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.List = all
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
// shortenLabels rewrites the labels in the rule using the short notation.
|
||||||
|
func shortenLabels(_ *build.File, r *build.Rule, pkg string) bool {
|
||||||
|
fixed := false
|
||||||
|
for _, attr := range r.AttrKeys() {
|
||||||
|
e := r.Attr(attr)
|
||||||
|
if !ContainsLabels(attr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, li := range AllLists(e) {
|
||||||
|
for _, elem := range li.List {
|
||||||
|
str, ok := elem.(*build.StringExpr)
|
||||||
|
if ok && str.Value != ShortenLabel(str.Value, pkg) {
|
||||||
|
str.Value = ShortenLabel(str.Value, pkg)
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeVisibility removes useless visibility attributes.
|
||||||
|
func removeVisibility(f *build.File, r *build.Rule, pkg string) bool {
|
||||||
|
pkgDecl := PackageDeclaration(f)
|
||||||
|
defaultVisibility := pkgDecl.AttrStrings("default_visibility")
|
||||||
|
|
||||||
|
// If no default_visibility is given, it is implicitly private.
|
||||||
|
if len(defaultVisibility) == 0 {
|
||||||
|
defaultVisibility = []string{"//visibility:private"}
|
||||||
|
}
|
||||||
|
|
||||||
|
visibility := r.AttrStrings("visibility")
|
||||||
|
if len(visibility) == 0 || len(visibility) != len(defaultVisibility) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
sort.Strings(defaultVisibility)
|
||||||
|
sort.Strings(visibility)
|
||||||
|
for i, vis := range visibility {
|
||||||
|
if vis != defaultVisibility[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.DelAttr("visibility")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeTestOnly removes the useless testonly attributes.
|
||||||
|
func removeTestOnly(f *build.File, r *build.Rule, pkg string) bool {
|
||||||
|
pkgDecl := PackageDeclaration(f)
|
||||||
|
|
||||||
|
def := strings.HasSuffix(r.Kind(), "_test") || r.Kind() == "test_suite"
|
||||||
|
if !def {
|
||||||
|
if pkgDecl.Attr("default_testonly") == nil {
|
||||||
|
def = strings.HasPrefix(pkg, "javatests/")
|
||||||
|
} else if pkgDecl.AttrLiteral("default_testonly") == "1" {
|
||||||
|
def = true
|
||||||
|
} else if pkgDecl.AttrLiteral("default_testonly") != "0" {
|
||||||
|
// Non-literal value: it's not safe to do a change.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testonly := r.AttrLiteral("testonly")
|
||||||
|
if def && testonly == "1" {
|
||||||
|
r.DelAttr("testonly")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !def && testonly == "0" {
|
||||||
|
r.DelAttr("testonly")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func genruleRenameDepsTools(_ *build.File, r *build.Rule, _ string) bool {
|
||||||
|
return r.Kind() == "genrule" && RenameAttribute(r, "deps", "tools") == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicitHeuristicLabels adds $(location ...) for each label in the string s.
|
||||||
|
func explicitHeuristicLabels(s string, labels map[string]bool) string {
|
||||||
|
// Regexp comes from LABEL_CHAR_MATCHER in
|
||||||
|
// java/com/google/devtools/build/lib/analysis/LabelExpander.java
|
||||||
|
re := regexp.MustCompile("[a-zA-Z0-9:/_.+-]+|[^a-zA-Z0-9:/_.+-]+")
|
||||||
|
parts := re.FindAllString(s, -1)
|
||||||
|
changed := false
|
||||||
|
canChange := true
|
||||||
|
for i, part := range parts {
|
||||||
|
// We don't want to add $(location when it's already present.
|
||||||
|
// So we skip the next label when we see location(s).
|
||||||
|
if part == "location" || part == "locations" {
|
||||||
|
canChange = false
|
||||||
|
}
|
||||||
|
if !labels[part] {
|
||||||
|
if labels[":"+part] { // leading colon is often missing
|
||||||
|
part = ":" + part
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canChange {
|
||||||
|
canChange = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts[i] = "$(location " + part + ")"
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
return strings.Join(parts, "")
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLabels(r *build.Rule, attr string, labels map[string]bool) {
|
||||||
|
a := r.Attr(attr)
|
||||||
|
if a == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, li := range AllLists(a) {
|
||||||
|
for _, item := range li.List {
|
||||||
|
if str, ok := item.(*build.StringExpr); ok {
|
||||||
|
labels[str.Value] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// genruleFixHeuristicLabels modifies the cmd attribute of genrules, so
|
||||||
|
// that they don't rely on heuristic label expansion anymore.
|
||||||
|
// Label expansion is made explicit with the $(location ...) command.
|
||||||
|
func genruleFixHeuristicLabels(_ *build.File, r *build.Rule, _ string) bool {
|
||||||
|
if r.Kind() != "genrule" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := r.Attr("cmd")
|
||||||
|
if cmd == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
labels := make(map[string]bool)
|
||||||
|
addLabels(r, "tools", labels)
|
||||||
|
addLabels(r, "srcs", labels)
|
||||||
|
|
||||||
|
fixed := false
|
||||||
|
for _, str := range AllStrings(cmd) {
|
||||||
|
newVal := explicitHeuristicLabels(str.Value, labels)
|
||||||
|
if newVal != str.Value {
|
||||||
|
fixed = true
|
||||||
|
str.Value = newVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortExportsFiles sorts the first argument of exports_files if it is a list.
|
||||||
|
func sortExportsFiles(_ *build.File, r *build.Rule, _ string) bool {
|
||||||
|
if r.Kind() != "exports_files" || len(r.Call.List) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
build.SortStringList(r.Call.List[0])
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeVarref replaces all varref('x') with '$(x)'.
|
||||||
|
// The goal is to eventually remove varref from the build language.
|
||||||
|
func removeVarref(_ *build.File, r *build.Rule, _ string) bool {
|
||||||
|
fixed := false
|
||||||
|
EditFunction(r.Call, "varref", func(call *build.CallExpr, stk []build.Expr) build.Expr {
|
||||||
|
if len(call.List) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
str, ok := (call.List[0]).(*build.StringExpr)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fixed = true
|
||||||
|
str.Value = "$(" + str.Value + ")"
|
||||||
|
// Preserve suffix comments from the function call
|
||||||
|
str.Comment().Suffix = append(str.Comment().Suffix, call.Comment().Suffix...)
|
||||||
|
return str
|
||||||
|
})
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortGlob sorts the list argument to glob.
|
||||||
|
func sortGlob(_ *build.File, r *build.Rule, _ string) bool {
|
||||||
|
fixed := false
|
||||||
|
EditFunction(r.Call, "glob", func(call *build.CallExpr, stk []build.Expr) build.Expr {
|
||||||
|
if len(call.List) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
build.SortStringList(call.List[0])
|
||||||
|
fixed = true
|
||||||
|
return call
|
||||||
|
})
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateListConcatenation(expr build.Expr) build.Expr {
|
||||||
|
if _, ok := expr.(*build.ListExpr); ok {
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
bin, ok := expr.(*build.BinaryExpr)
|
||||||
|
if !ok || bin.Op != "+" {
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
li1, ok1 := evaluateListConcatenation(bin.X).(*build.ListExpr)
|
||||||
|
li2, ok2 := evaluateListConcatenation(bin.Y).(*build.ListExpr)
|
||||||
|
if !ok1 || !ok2 {
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
res := *li1
|
||||||
|
res.List = append(li1.List, li2.List...)
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeLiteralLists evaluates the concatenation of two literal lists.
|
||||||
|
// e.g. [1, 2] + [3, 4] -> [1, 2, 3, 4]
|
||||||
|
func mergeLiteralLists(_ *build.File, r *build.Rule, _ string) bool {
|
||||||
|
fixed := false
|
||||||
|
build.Edit(r.Call, func(expr build.Expr, stk []build.Expr) build.Expr {
|
||||||
|
newexpr := evaluateListConcatenation(expr)
|
||||||
|
fixed = fixed || (newexpr != expr)
|
||||||
|
return newexpr
|
||||||
|
})
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
// usePlusEqual replaces uses of extend and append with the += operator.
|
||||||
|
// e.g. foo.extend(bar) => foo += bar
|
||||||
|
// foo.append(bar) => foo += [bar]
|
||||||
|
func usePlusEqual(f *build.File) bool {
|
||||||
|
fixed := false
|
||||||
|
for i, stmt := range f.Stmt {
|
||||||
|
call, ok := stmt.(*build.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dot, ok := call.X.(*build.DotExpr)
|
||||||
|
if !ok || len(call.List) != 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj, ok := dot.X.(*build.LiteralExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var fix *build.BinaryExpr
|
||||||
|
if dot.Name == "extend" {
|
||||||
|
fix = &build.BinaryExpr{X: obj, Op: "+=", Y: call.List[0]}
|
||||||
|
} else if dot.Name == "append" {
|
||||||
|
list := &build.ListExpr{List: []build.Expr{call.List[0]}}
|
||||||
|
fix = &build.BinaryExpr{X: obj, Op: "+=", Y: list}
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fix.Comments = call.Comments // Keep original comments
|
||||||
|
f.Stmt[i] = fix
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNonemptyComment(comment *build.Comments) bool {
|
||||||
|
return len(comment.Before)+len(comment.Suffix)+len(comment.After) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether a call or any of its arguments have a comment
|
||||||
|
func hasComment(call *build.CallExpr) bool {
|
||||||
|
if isNonemptyComment(call.Comment()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, arg := range call.List {
|
||||||
|
if isNonemptyComment(arg.Comment()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanUnusedLoads removes symbols from load statements that are not used in the file.
|
||||||
|
// It also cleans symbols loaded multiple times, sorts symbol list, and removes load
|
||||||
|
// statements when the list is empty.
|
||||||
|
func cleanUnusedLoads(f *build.File) bool {
|
||||||
|
// If the file needs preprocessing, leave it alone.
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
if _, ok := stmt.(*build.PythonBlock); ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
symbols := UsedSymbols(f)
|
||||||
|
fixed := false
|
||||||
|
|
||||||
|
var all []build.Expr
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
rule, ok := ExprToRule(stmt, "load")
|
||||||
|
if !ok || len(rule.Call.List) == 0 || hasComment(rule.Call) {
|
||||||
|
all = append(all, stmt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var args []build.Expr
|
||||||
|
for _, arg := range rule.Call.List[1:] { // first argument is the path, we keep it
|
||||||
|
symbol, ok := loadedSymbol(arg)
|
||||||
|
if !ok || symbols[symbol] {
|
||||||
|
args = append(args, arg)
|
||||||
|
if ok {
|
||||||
|
// If the same symbol is loaded twice, we'll remove it.
|
||||||
|
delete(symbols, symbol)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(args) > 0 { // Keep the load statement if it loads at least one symbol.
|
||||||
|
li := &build.ListExpr{List: args}
|
||||||
|
build.SortStringList(li)
|
||||||
|
rule.Call.List = append(rule.Call.List[:1], li.List...)
|
||||||
|
all = append(all, rule.Call)
|
||||||
|
} else {
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.Stmt = all
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadedSymbol parses the symbol token from a load statement argument,
|
||||||
|
// supporting aliases.
|
||||||
|
func loadedSymbol(arg build.Expr) (string, bool) {
|
||||||
|
symbol, ok := arg.(*build.StringExpr)
|
||||||
|
if ok {
|
||||||
|
return symbol.Value, ok
|
||||||
|
}
|
||||||
|
// try an aliased symbol
|
||||||
|
if binExpr, ok := arg.(*build.BinaryExpr); ok && binExpr.Op == "=" {
|
||||||
|
if keyExpr, ok := binExpr.X.(*build.LiteralExpr); ok {
|
||||||
|
return keyExpr.Token, ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// movePackageDeclarationToTheTop ensures that the call to package() is done
|
||||||
|
// before everything else (except comments).
|
||||||
|
func movePackageDeclarationToTheTop(f *build.File) bool {
|
||||||
|
pkg := ExistingPackageDeclaration(f)
|
||||||
|
if pkg == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
all := []build.Expr{}
|
||||||
|
inserted := false // true when the package declaration has been inserted
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
_, isComment := stmt.(*build.CommentBlock)
|
||||||
|
_, isBinaryExpr := stmt.(*build.BinaryExpr) // e.g. variable declaration
|
||||||
|
_, isLoad := ExprToRule(stmt, "load")
|
||||||
|
if isComment || isBinaryExpr || isLoad {
|
||||||
|
all = append(all, stmt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if stmt == pkg.Call {
|
||||||
|
if inserted {
|
||||||
|
// remove the old package
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false // the file was ok
|
||||||
|
}
|
||||||
|
if !inserted {
|
||||||
|
all = append(all, pkg.Call)
|
||||||
|
inserted = true
|
||||||
|
}
|
||||||
|
all = append(all, stmt)
|
||||||
|
}
|
||||||
|
f.Stmt = all
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// moveToPackage is an auxilliary function used by moveLicensesAndDistribs.
|
||||||
|
// The function shouldn't appear more than once in the file (depot cleanup has
|
||||||
|
// been done).
|
||||||
|
func moveToPackage(f *build.File, attrname string) bool {
|
||||||
|
var all []build.Expr
|
||||||
|
fixed := false
|
||||||
|
for _, stmt := range f.Stmt {
|
||||||
|
rule, ok := ExprToRule(stmt, attrname)
|
||||||
|
if !ok || len(rule.Call.List) != 1 {
|
||||||
|
all = append(all, stmt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkgDecl := PackageDeclaration(f)
|
||||||
|
pkgDecl.SetAttr(attrname, rule.Call.List[0])
|
||||||
|
pkgDecl.AttrDefn(attrname).Comments = *stmt.Comment()
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
f.Stmt = all
|
||||||
|
return fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
// moveLicensesAndDistribs replaces the 'licenses' and 'distribs' functions
|
||||||
|
// with an attribute in package.
|
||||||
|
// Before: licenses(["notice"])
|
||||||
|
// After: package(licenses = ["notice"])
|
||||||
|
func moveLicensesAndDistribs(f *build.File) bool {
|
||||||
|
fixed1 := moveToPackage(f, "licenses")
|
||||||
|
fixed2 := moveToPackage(f, "distribs")
|
||||||
|
return fixed1 || fixed2
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllRuleFixes is a list of all Buildozer fixes that can be applied on a rule.
|
||||||
|
var AllRuleFixes = []struct {
|
||||||
|
Name string
|
||||||
|
Fn func(file *build.File, rule *build.Rule, pkg string) bool
|
||||||
|
Message string
|
||||||
|
}{
|
||||||
|
{"sortGlob", sortGlob,
|
||||||
|
"Sort the list in a call to glob"},
|
||||||
|
{"splitOptions", splitOptionsWithSpaces,
|
||||||
|
"Each option should be given separately in the list"},
|
||||||
|
{"shortenLabels", shortenLabels,
|
||||||
|
"Style: Use the canonical label notation"},
|
||||||
|
{"removeVisibility", removeVisibility,
|
||||||
|
"This visibility attribute is useless (it corresponds to the default value)"},
|
||||||
|
{"removeTestOnly", removeTestOnly,
|
||||||
|
"This testonly attribute is useless (it corresponds to the default value)"},
|
||||||
|
{"genruleRenameDepsTools", genruleRenameDepsTools,
|
||||||
|
"'deps' attribute in genrule has been renamed 'tools'"},
|
||||||
|
{"genruleFixHeuristicLabels", genruleFixHeuristicLabels,
|
||||||
|
"$(location) should be called explicitely"},
|
||||||
|
{"sortExportsFiles", sortExportsFiles,
|
||||||
|
"Files in exports_files should be sorted"},
|
||||||
|
{"varref", removeVarref,
|
||||||
|
"All varref('foo') should be replaced with '$foo'"},
|
||||||
|
{"mergeLiteralLists", mergeLiteralLists,
|
||||||
|
"Remove useless list concatenation"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileLevelFixes is a list of all Buildozer fixes that apply on the whole file.
|
||||||
|
var FileLevelFixes = []struct {
|
||||||
|
Name string
|
||||||
|
Fn func(file *build.File) bool
|
||||||
|
Message string
|
||||||
|
}{
|
||||||
|
{"movePackageToTop", movePackageDeclarationToTheTop,
|
||||||
|
"The package declaration should be the first rule in a file"},
|
||||||
|
{"usePlusEqual", usePlusEqual,
|
||||||
|
"Prefer '+=' over 'extend' or 'append'"},
|
||||||
|
{"unusedLoads", cleanUnusedLoads,
|
||||||
|
"Remove unused symbols from load statements"},
|
||||||
|
{"moveLicensesAndDistribs", moveLicensesAndDistribs,
|
||||||
|
"Move licenses and distribs to the package function"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixRule aims to fix errors in BUILD files, remove deprecated features, and
|
||||||
|
// simplify the code.
|
||||||
|
func FixRule(f *build.File, pkg string, rule *build.Rule, fixes []string) *build.File {
|
||||||
|
fixesAsMap := make(map[string]bool)
|
||||||
|
for _, fix := range fixes {
|
||||||
|
fixesAsMap[fix] = true
|
||||||
|
}
|
||||||
|
fixed := false
|
||||||
|
for _, fix := range AllRuleFixes {
|
||||||
|
if len(fixes) == 0 || fixesAsMap[fix.Name] {
|
||||||
|
fixed = fix.Fn(f, rule, pkg) || fixed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !fixed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixFile fixes everything it can in the BUILD file.
|
||||||
|
func FixFile(f *build.File, pkg string, fixes []string) *build.File {
|
||||||
|
fixesAsMap := make(map[string]bool)
|
||||||
|
for _, fix := range fixes {
|
||||||
|
fixesAsMap[fix] = true
|
||||||
|
}
|
||||||
|
fixed := false
|
||||||
|
for _, rule := range f.Rules("") {
|
||||||
|
res := FixRule(f, pkg, rule, fixes)
|
||||||
|
if res != nil {
|
||||||
|
fixed = true
|
||||||
|
f = res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, fix := range FileLevelFixes {
|
||||||
|
if len(fixes) == 0 || fixesAsMap[fix.Name] {
|
||||||
|
fixed = fix.Fn(f) || fixed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !fixed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
// Type information for attributes.
|
||||||
|
|
||||||
|
package edit
|
||||||
|
|
||||||
|
import (
|
||||||
|
buildpb "github.com/bazelbuild/buildtools/build_proto"
|
||||||
|
"github.com/bazelbuild/buildtools/lang"
|
||||||
|
)
|
||||||
|
|
||||||
|
var typeOf = lang.TypeOf
|
||||||
|
|
||||||
|
// IsList returns true for all attributes whose type is a list.
|
||||||
|
func IsList(attr string) bool {
|
||||||
|
ty := typeOf[attr]
|
||||||
|
return ty == buildpb.Attribute_STRING_LIST ||
|
||||||
|
ty == buildpb.Attribute_LABEL_LIST ||
|
||||||
|
ty == buildpb.Attribute_OUTPUT_LIST ||
|
||||||
|
ty == buildpb.Attribute_FILESET_ENTRY_LIST ||
|
||||||
|
ty == buildpb.Attribute_INTEGER_LIST ||
|
||||||
|
ty == buildpb.Attribute_LICENSE ||
|
||||||
|
ty == buildpb.Attribute_DISTRIBUTION_SET
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIntList returns true for all attributes whose type is an int list.
|
||||||
|
func IsIntList(attr string) bool {
|
||||||
|
return typeOf[attr] == buildpb.Attribute_INTEGER_LIST
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsString returns true for all attributes whose type is a string or a label.
|
||||||
|
func IsString(attr string) bool {
|
||||||
|
ty := typeOf[attr]
|
||||||
|
return ty == buildpb.Attribute_LABEL ||
|
||||||
|
ty == buildpb.Attribute_STRING ||
|
||||||
|
ty == buildpb.Attribute_OUTPUT
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsStringDict returns true for all attributes whose type is a string dictionary.
|
||||||
|
func IsStringDict(attr string) bool {
|
||||||
|
return typeOf[attr] == buildpb.Attribute_STRING_DICT
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsLabels returns true for all attributes whose type is a label or a label list.
|
||||||
|
func ContainsLabels(attr string) bool {
|
||||||
|
ty := typeOf[attr]
|
||||||
|
return ty == buildpb.Attribute_LABEL_LIST ||
|
||||||
|
ty == buildpb.Attribute_LABEL
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["file.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/github.com/bazelbuild/buildtools/file",
|
||||||
|
importpath = "github.com/bazelbuild/buildtools/file",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package file provides utility file operations.
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadFile is like ioutil.ReadFile.
|
||||||
|
func ReadFile(name string) ([]byte, os.FileInfo, error) {
|
||||||
|
fi, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(name)
|
||||||
|
return data, fi, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile is like ioutil.WriteFile
|
||||||
|
func WriteFile(name string, data []byte) error {
|
||||||
|
return ioutil.WriteFile(name, data, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenReadFile is like os.Open.
|
||||||
|
func OpenReadFile(name string) io.ReadCloser {
|
||||||
|
f, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Could not open %s\n", name)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["tables.gen.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/github.com/bazelbuild/buildtools/lang",
|
||||||
|
importpath = "github.com/bazelbuild/buildtools/lang",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = ["//vendor/github.com/bazelbuild/buildtools/build_proto:go_default_library"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
Binary file not shown.
|
@ -0,0 +1,245 @@
|
||||||
|
// Generated file, do not edit.
|
||||||
|
package lang
|
||||||
|
|
||||||
|
import buildpb "github.com/bazelbuild/buildtools/build_proto"
|
||||||
|
|
||||||
|
var TypeOf = map[string]buildpb.Attribute_Discriminator{
|
||||||
|
"aar": buildpb.Attribute_LABEL,
|
||||||
|
"actual": buildpb.Attribute_LABEL,
|
||||||
|
"aliases": buildpb.Attribute_STRING_LIST,
|
||||||
|
"all_files": buildpb.Attribute_LABEL,
|
||||||
|
"alwayslink": buildpb.Attribute_BOOLEAN,
|
||||||
|
"app_asset_catalogs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"app_bundle_id": buildpb.Attribute_STRING,
|
||||||
|
"app_deps": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"app_entitlements": buildpb.Attribute_LABEL,
|
||||||
|
"app_icon": buildpb.Attribute_STRING,
|
||||||
|
"app_infoplists": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"app_name": buildpb.Attribute_STRING,
|
||||||
|
"app_provisioning_profile": buildpb.Attribute_LABEL,
|
||||||
|
"app_resources": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"app_storyboards": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"app_strings": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"app_structured_resources": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"archives": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"args": buildpb.Attribute_STRING_LIST,
|
||||||
|
"artifact": buildpb.Attribute_STRING,
|
||||||
|
"asset_catalogs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"assets": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"assets_dir": buildpb.Attribute_STRING,
|
||||||
|
"avoid_deps": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"binary": buildpb.Attribute_LABEL,
|
||||||
|
"binary_type": buildpb.Attribute_STRING,
|
||||||
|
"blacklisted_protos": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"bootclasspath": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"build_file": buildpb.Attribute_STRING,
|
||||||
|
"build_file_content": buildpb.Attribute_STRING,
|
||||||
|
"bundle_id": buildpb.Attribute_STRING,
|
||||||
|
"bundle_imports": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"bundle_loader": buildpb.Attribute_LABEL,
|
||||||
|
"bundles": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"classpath_resources": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"cmd": buildpb.Attribute_STRING,
|
||||||
|
"command_line": buildpb.Attribute_STRING,
|
||||||
|
"commit": buildpb.Attribute_STRING,
|
||||||
|
"compatible_with": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"compiler_files": buildpb.Attribute_LABEL,
|
||||||
|
"constraints": buildpb.Attribute_STRING_LIST,
|
||||||
|
"copts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"cpu": buildpb.Attribute_STRING,
|
||||||
|
"create_executable": buildpb.Attribute_BOOLEAN,
|
||||||
|
"crunch_png": buildpb.Attribute_BOOLEAN,
|
||||||
|
"custom_package": buildpb.Attribute_STRING,
|
||||||
|
"data": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"datamodels": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"default": buildpb.Attribute_LABEL,
|
||||||
|
"default_copts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"default_deprecation": buildpb.Attribute_STRING,
|
||||||
|
"default_hdrs_check": buildpb.Attribute_STRING,
|
||||||
|
"default_ios_sdk_version": buildpb.Attribute_STRING,
|
||||||
|
"default_macosx_sdk_version": buildpb.Attribute_STRING,
|
||||||
|
"default_python_version": buildpb.Attribute_STRING,
|
||||||
|
"default_testonly": buildpb.Attribute_BOOLEAN,
|
||||||
|
"default_tvos_sdk_version": buildpb.Attribute_STRING,
|
||||||
|
"default_visibility": buildpb.Attribute_STRING_LIST,
|
||||||
|
"default_watchos_sdk_version": buildpb.Attribute_STRING,
|
||||||
|
"defines": buildpb.Attribute_STRING_LIST,
|
||||||
|
"densities": buildpb.Attribute_STRING_LIST,
|
||||||
|
"deploy_manifest_lines": buildpb.Attribute_STRING_LIST,
|
||||||
|
"deprecation": buildpb.Attribute_STRING,
|
||||||
|
"deps": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"dex_shards": buildpb.Attribute_INTEGER,
|
||||||
|
"dexopts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"distribs": buildpb.Attribute_DISTRIBUTION_SET,
|
||||||
|
"dwp_files": buildpb.Attribute_LABEL,
|
||||||
|
"dylibs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"dynamic_runtime_libs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"enable_modules": buildpb.Attribute_BOOLEAN,
|
||||||
|
"encoding": buildpb.Attribute_STRING,
|
||||||
|
"entitlements": buildpb.Attribute_LABEL,
|
||||||
|
"entry_classes": buildpb.Attribute_STRING_LIST,
|
||||||
|
"executable": buildpb.Attribute_BOOLEAN,
|
||||||
|
"exported_plugins": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"exports": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"exports_manifest": buildpb.Attribute_BOOLEAN,
|
||||||
|
"expression": buildpb.Attribute_STRING,
|
||||||
|
"ext_bundle_id": buildpb.Attribute_STRING,
|
||||||
|
"ext_entitlements": buildpb.Attribute_LABEL,
|
||||||
|
"ext_families": buildpb.Attribute_STRING_LIST,
|
||||||
|
"ext_infoplists": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"ext_provisioning_profile": buildpb.Attribute_LABEL,
|
||||||
|
"ext_resources": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"ext_strings": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"ext_structured_resources": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"extclasspath": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"extensions": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"extra_actions": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"extra_srcs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"families": buildpb.Attribute_STRING_LIST,
|
||||||
|
"features": buildpb.Attribute_STRING_LIST,
|
||||||
|
"flaky": buildpb.Attribute_BOOLEAN,
|
||||||
|
"framework_imports": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"genclass": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"generates_api": buildpb.Attribute_BOOLEAN,
|
||||||
|
"hdrs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"header_compiler": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"heuristic_label_expansion": buildpb.Attribute_BOOLEAN,
|
||||||
|
"idl_import_root": buildpb.Attribute_STRING,
|
||||||
|
"idl_parcelables": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"idl_srcs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"ijar": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"imports": buildpb.Attribute_STRING_LIST,
|
||||||
|
"includes": buildpb.Attribute_STRING_LIST,
|
||||||
|
"incremental_dexing": buildpb.Attribute_TRISTATE,
|
||||||
|
"infoplist": buildpb.Attribute_LABEL,
|
||||||
|
"infoplists": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"init_submodules": buildpb.Attribute_BOOLEAN,
|
||||||
|
"ios_device_arg": buildpb.Attribute_STRING_LIST,
|
||||||
|
"ios_test_target_device": buildpb.Attribute_LABEL,
|
||||||
|
"ios_version": buildpb.Attribute_STRING,
|
||||||
|
"ipa_post_processor": buildpb.Attribute_LABEL,
|
||||||
|
"is_dynamic": buildpb.Attribute_BOOLEAN,
|
||||||
|
"jars": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"javabuilder": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"javac": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"javac_supports_workers": buildpb.Attribute_BOOLEAN,
|
||||||
|
"javacopts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"jre_deps": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"jvm_flags": buildpb.Attribute_STRING_LIST,
|
||||||
|
"jvm_opts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"launch_image": buildpb.Attribute_STRING,
|
||||||
|
"launch_storyboard": buildpb.Attribute_LABEL,
|
||||||
|
"launcher": buildpb.Attribute_LABEL,
|
||||||
|
"licenses": buildpb.Attribute_LICENSE,
|
||||||
|
"linker_files": buildpb.Attribute_LABEL,
|
||||||
|
"linkopts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"linkshared": buildpb.Attribute_BOOLEAN,
|
||||||
|
"linkstamp": buildpb.Attribute_LABEL,
|
||||||
|
"linkstatic": buildpb.Attribute_BOOLEAN,
|
||||||
|
"local": buildpb.Attribute_BOOLEAN,
|
||||||
|
"main": buildpb.Attribute_LABEL,
|
||||||
|
"main_class": buildpb.Attribute_STRING,
|
||||||
|
"main_dex_list": buildpb.Attribute_LABEL,
|
||||||
|
"main_dex_list_opts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"main_dex_proguard_specs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"malloc": buildpb.Attribute_LABEL,
|
||||||
|
"manifest": buildpb.Attribute_LABEL,
|
||||||
|
"manifest_merger": buildpb.Attribute_STRING,
|
||||||
|
"manifest_values": buildpb.Attribute_STRING_DICT,
|
||||||
|
"message": buildpb.Attribute_STRING,
|
||||||
|
"misc": buildpb.Attribute_STRING_LIST,
|
||||||
|
"mnemonics": buildpb.Attribute_STRING_LIST,
|
||||||
|
"module_map": buildpb.Attribute_LABEL,
|
||||||
|
"multidex": buildpb.Attribute_STRING,
|
||||||
|
"name": buildpb.Attribute_STRING,
|
||||||
|
"neverlink": buildpb.Attribute_BOOLEAN,
|
||||||
|
"nocompress_extensions": buildpb.Attribute_STRING_LIST,
|
||||||
|
"nocopts": buildpb.Attribute_STRING,
|
||||||
|
"non_arc_srcs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"non_propagated_deps": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"objcopy_files": buildpb.Attribute_LABEL,
|
||||||
|
"options_file": buildpb.Attribute_LABEL,
|
||||||
|
"opts": buildpb.Attribute_STRING_LIST,
|
||||||
|
"out": buildpb.Attribute_STRING,
|
||||||
|
"out_templates": buildpb.Attribute_STRING_LIST,
|
||||||
|
"output_group": buildpb.Attribute_STRING,
|
||||||
|
"output_licenses": buildpb.Attribute_LICENSE,
|
||||||
|
"output_to_bindir": buildpb.Attribute_BOOLEAN,
|
||||||
|
"outs": buildpb.Attribute_STRING_LIST,
|
||||||
|
"path": buildpb.Attribute_STRING,
|
||||||
|
"pch": buildpb.Attribute_LABEL,
|
||||||
|
"per_proto_includes": buildpb.Attribute_BOOLEAN,
|
||||||
|
"platform_type": buildpb.Attribute_STRING,
|
||||||
|
"plugin": buildpb.Attribute_LABEL,
|
||||||
|
"plugins": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"portable_proto_filters": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"prefix": buildpb.Attribute_STRING,
|
||||||
|
"processor_class": buildpb.Attribute_STRING,
|
||||||
|
"proguard_apply_mapping": buildpb.Attribute_LABEL,
|
||||||
|
"proguard_generate_mapping": buildpb.Attribute_BOOLEAN,
|
||||||
|
"proguard_specs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"provisioning_profile": buildpb.Attribute_LABEL,
|
||||||
|
"pytype_deps": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"remote": buildpb.Attribute_STRING,
|
||||||
|
"repository": buildpb.Attribute_STRING,
|
||||||
|
"require_defined_version": buildpb.Attribute_BOOLEAN,
|
||||||
|
"requires_action_output": buildpb.Attribute_BOOLEAN,
|
||||||
|
"resource_configuration_filters": buildpb.Attribute_STRING_LIST,
|
||||||
|
"resource_files": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"resource_strip_prefix": buildpb.Attribute_STRING,
|
||||||
|
"resources": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"restricted_to": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"runtime": buildpb.Attribute_LABEL,
|
||||||
|
"runtime_deps": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"scope": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"sdk_dylibs": buildpb.Attribute_STRING_LIST,
|
||||||
|
"sdk_frameworks": buildpb.Attribute_STRING_LIST,
|
||||||
|
"sdk_includes": buildpb.Attribute_STRING_LIST,
|
||||||
|
"server": buildpb.Attribute_STRING,
|
||||||
|
"settings_file": buildpb.Attribute_STRING,
|
||||||
|
"sha1": buildpb.Attribute_STRING,
|
||||||
|
"sha256": buildpb.Attribute_STRING,
|
||||||
|
"shard_count": buildpb.Attribute_INTEGER,
|
||||||
|
"singlejar": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"size": buildpb.Attribute_STRING,
|
||||||
|
"source_version": buildpb.Attribute_STRING,
|
||||||
|
"srcjar": buildpb.Attribute_LABEL,
|
||||||
|
"srcs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"srcs_version": buildpb.Attribute_STRING,
|
||||||
|
"stamp": buildpb.Attribute_TRISTATE,
|
||||||
|
"static_runtime_libs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"storyboards": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"strict": buildpb.Attribute_BOOLEAN,
|
||||||
|
"strings": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"strip": buildpb.Attribute_BOOLEAN,
|
||||||
|
"strip_files": buildpb.Attribute_LABEL,
|
||||||
|
"strip_prefix": buildpb.Attribute_STRING,
|
||||||
|
"structured_resources": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"supports_header_parsing": buildpb.Attribute_BOOLEAN,
|
||||||
|
"supports_param_files": buildpb.Attribute_BOOLEAN,
|
||||||
|
"tag": buildpb.Attribute_STRING,
|
||||||
|
"tags": buildpb.Attribute_STRING_LIST,
|
||||||
|
"target_device": buildpb.Attribute_LABEL,
|
||||||
|
"target_version": buildpb.Attribute_STRING,
|
||||||
|
"test_class": buildpb.Attribute_STRING,
|
||||||
|
"testonly": buildpb.Attribute_BOOLEAN,
|
||||||
|
"tests": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"textual_hdrs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"timeout": buildpb.Attribute_STRING,
|
||||||
|
"toolchains": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"tools": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"type": buildpb.Attribute_STRING,
|
||||||
|
"url": buildpb.Attribute_STRING,
|
||||||
|
"use_objc_header_names": buildpb.Attribute_BOOLEAN,
|
||||||
|
"use_testrunner": buildpb.Attribute_BOOLEAN,
|
||||||
|
"values": buildpb.Attribute_STRING_DICT,
|
||||||
|
"version": buildpb.Attribute_STRING,
|
||||||
|
"versions": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"visibility": buildpb.Attribute_STRING_LIST,
|
||||||
|
"weak_sdk_frameworks": buildpb.Attribute_STRING_LIST,
|
||||||
|
"xcode": buildpb.Attribute_LABEL,
|
||||||
|
"xctest": buildpb.Attribute_BOOLEAN,
|
||||||
|
"xctest_app": buildpb.Attribute_LABEL,
|
||||||
|
"xibs": buildpb.Attribute_LABEL_LIST,
|
||||||
|
"xlint": buildpb.Attribute_STRING_LIST,
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["workspace.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/github.com/bazelbuild/buildtools/wspace",
|
||||||
|
importpath = "github.com/bazelbuild/buildtools/wspace",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = ["//vendor/github.com/bazelbuild/buildtools/build:go_default_library"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package wspace provides a method to find the root of the bazel tree.
|
||||||
|
package wspace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bazelbuild/buildtools/build"
|
||||||
|
)
|
||||||
|
|
||||||
|
const workspaceFile = "WORKSPACE"
|
||||||
|
|
||||||
|
func alwaysTrue(fi os.FileInfo) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var repoRootFiles = map[string]func(os.FileInfo) bool{
|
||||||
|
workspaceFile: alwaysTrue,
|
||||||
|
".buckconfig": alwaysTrue,
|
||||||
|
"pants": func(fi os.FileInfo) bool {
|
||||||
|
return fi.Mode()&os.ModeType == 0 && fi.Mode()&0100 == 0100
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// findContextPath finds the context path inside of a WORKSPACE-rooted source tree.
|
||||||
|
func findContextPath(rootDir string) (string, error) {
|
||||||
|
if rootDir == "" {
|
||||||
|
return os.Getwd()
|
||||||
|
}
|
||||||
|
return rootDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindWorkspaceRoot splits the current code context (the rootDir if present,
|
||||||
|
// the working directory if not.) It returns the path of the directory
|
||||||
|
// containing the WORKSPACE file, and the rest.
|
||||||
|
func FindWorkspaceRoot(rootDir string) (root string, rest string) {
|
||||||
|
wd, err := findContextPath(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
if root, err = Find(wd); err != nil {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
if len(wd) == len(root) {
|
||||||
|
return root, ""
|
||||||
|
}
|
||||||
|
return root, wd[len(root)+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find searches from the given dir and up for the WORKSPACE file
|
||||||
|
// returning the directory containing it, or an error if none found in the tree.
|
||||||
|
func Find(dir string) (string, error) {
|
||||||
|
if dir == "" || dir == "/" || dir == "." {
|
||||||
|
return "", os.ErrNotExist
|
||||||
|
}
|
||||||
|
for repoRootFile, fiFunc := range repoRootFiles {
|
||||||
|
if fi, err := os.Stat(filepath.Join(dir, repoRootFile)); err == nil && fiFunc(fi) {
|
||||||
|
return dir, nil
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Find(filepath.Dir(dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRepoBuildFiles parses the WORKSPACE to find BUILD files for non-Bazel
|
||||||
|
// external repositories, specifically those defined by one of these rules:
|
||||||
|
// new_local_repository(), new_git_repository(), new_http_archive()
|
||||||
|
func FindRepoBuildFiles(root string) (map[string]string, error) {
|
||||||
|
ws := filepath.Join(root, workspaceFile)
|
||||||
|
kinds := []string{
|
||||||
|
"new_local_repository",
|
||||||
|
"new_git_repository",
|
||||||
|
"new_http_archive",
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(ws)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ast, err := build.Parse(ws, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
files := make(map[string]string)
|
||||||
|
for _, kind := range kinds {
|
||||||
|
for _, r := range ast.Rules(kind) {
|
||||||
|
buildFile := r.AttrString("build_file")
|
||||||
|
if buildFile == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buildFile = strings.Replace(buildFile, ":", "/", -1)
|
||||||
|
files[r.Name()] = filepath.Join(root, buildFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
Loading…
Reference in New Issue