mirror of https://github.com/k3s-io/k3s
vendor buildozer
parent
d49953e8d0
commit
e070d9fd67
|
@ -472,16 +472,51 @@
|
|||
"Comment": "0.15.0",
|
||||
"Rev": "c728ce9f663e2bff26361ba5978ec5c9e6816a3c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bazelbuild/buildtools/api_proto",
|
||||
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bazelbuild/buildtools/build",
|
||||
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||
"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",
|
||||
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bazelbuild/buildtools/wspace",
|
||||
"Comment": "0.6.0-60-g1a9c38e0df9397",
|
||||
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||
"Rev": "3ac7bf7a47d159a033b107610db8a1b6575507a4"
|
||||
|
@ -4240,4 +4275,4 @@
|
|||
"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: =
|
||||
|
||||
|
@ -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: =
|
||||
|
||||
|
@ -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: =
|
||||
|
||||
|
|
|
@ -65,8 +65,15 @@ filegroup(
|
|||
"//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/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_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/wspace:all-srcs",
|
||||
"//vendor/github.com/beorn7/perks/quantile:all-srcs",
|
||||
"//vendor/github.com/blang/semver: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