buildtools-10.1.0/0000775000175000017500000000000015254573013013344 5ustar kenhyskenhysbuildtools-10.1.0/build/0000775000175000017500000000000015254573013014443 5ustar kenhyskenhysbuildtools-10.1.0/build/utils_test.go0000664000175000017500000000526415254573013017200 0ustar kenhyskenhys/* Copyright 2025 Google LLC 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 https://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 build import ( "testing" ) func TestIsMultiline(t *testing.T) { var tests = []struct { name string buildFile string exprToCheck func(Expr) bool wantIsMultiline bool }{ { name: "single_line_call", exprToCheck: func(expr Expr) bool { if call, ok := expr.(*CallExpr); ok { if calledIdent, ok := call.X.(*Ident); ok && calledIdent.Name == "foo" { return true } } return false }, buildFile: ` foo(name = "bar")`, wantIsMultiline: false, }, { name: "multiline_call", exprToCheck: func(expr Expr) bool { if call, ok := expr.(*CallExpr); ok { if calledIdent, ok := call.X.(*Ident); ok && calledIdent.Name == "foo" { return true } } return false }, buildFile: ` foo( name = "bar", )`, wantIsMultiline: true, }, { name: "single_line_attribute", exprToCheck: func(expr Expr) bool { if assign, ok := expr.(*AssignExpr); ok { if calledIdent, ok := assign.LHS.(*Ident); ok && calledIdent.Name == "foo_attr" { return true } } return false }, buildFile: ` foo( name = "bar", foo_attr = "on_a_single_line", )`, wantIsMultiline: false, }, { name: "multi_line_attribute", exprToCheck: func(expr Expr) bool { if assign, ok := expr.(*AssignExpr); ok { if calledIdent, ok := assign.LHS.(*Ident); ok && calledIdent.Name == "foo_attr" { return true } } return false }, buildFile: ` foo( name = "bar", foo_attr = [ "attribute", "which spans", "multiple lines", ], )`, wantIsMultiline: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { bld, err := Parse("BUILD", []byte(tc.buildFile)) if err != nil { t.Fatal(err) } var testedExpr *Expr Walk(bld, func(x Expr, _ []Expr) { if tc.exprToCheck(x) { testedExpr = &x } }) if testedExpr == nil { t.Fatal("Unable to find expression to test") } got := IsMultiLine(*testedExpr) if got != tc.wantIsMultiline { t.Fatalf("IsMultiline returned incorrect value, got: %t, expected: %t", got, tc.wantIsMultiline) } }) } } buildtools-10.1.0/build/quote_test.go0000664000175000017500000001113415254573013017166 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 build import ( "strings" "testing" ) var quoteTests = []struct { q string // quoted s string // unquoted (actual string) std bool // q is standard form for s }{ {`""`, "", true}, {`''`, "", false}, {`"hello"`, `hello`, true}, {`'hello'`, `hello`, false}, {`"quote\"here"`, `quote"here`, true}, {`'quote\"here'`, `quote"here`, false}, {`'quote"here'`, `quote"here`, false}, {`"quote'here"`, `quote'here`, true}, {`"quote\'here"`, `quote'here`, false}, {`'quote\'here'`, `quote'here`, false}, {`"""hello " ' world "" asdf ''' foo"""`, `hello " ' world "" asdf ''' foo`, true}, {`"foo\\(bar"`, `foo\(bar`, true}, {`"""hello world"""`, "hello\nworld", true}, {`"\\a\\b\\f\n\r\t\\v\000\377"`, "\\a\\b\\f\n\r\t\\v\000\xFF", true}, {`"\\a\\b\\f\n\r\t\\v\x00\xff"`, "\\a\\b\\f\n\r\t\\v\000\xFF", false}, {`"\\a\\b\\f\n\r\t\\v\000\xFF"`, "\\a\\b\\f\n\r\t\\v\000\xFF", false}, {`"\\a\\b\\f\n\r\t\\v\000\377\"'\\\003\200"`, "\\a\\b\\f\n\r\t\\v\x00\xFF\"'\\\x03\x80", true}, {`"\\a\\b\\f\n\r\t\\v\x00\xff\"'\\\x03\x80"`, "\\a\\b\\f\n\r\t\\v\x00\xFF\"'\\\x03\x80", false}, {`"\\a\\b\\f\n\r\t\\v\000\xFF\"'\\\x03\x80"`, "\\a\\b\\f\n\r\t\\v\x00\xFF\"'\\\x03\x80", false}, {`"\\a\\b\\f\n\r\t\\v\000\xFF\"\'\\\x03\x80"`, "\\a\\b\\f\n\r\t\\v\x00\xFF\"'\\\x03\x80", false}, { `"cat $(SRCS) | grep '\s*ip_block:' | sed -e 's/\s*ip_block: \"\([^ ]*\)\"/ \x27\\1\x27,/g' >> $@; "`, "cat $(SRCS) | grep '\\s*ip_block:' | sed -e 's/\\s*ip_block: \"\\([^ ]*\\)\"/ '\\1',/g' >> $@; ", false, }, { `"cat $(SRCS) | grep '\\s*ip_block:' | sed -e 's/\\s*ip_block: \"\\([^ ]*\\)\"/ \x27\\1\x27,/g' >> $@; "`, "cat $(SRCS) | grep '\\s*ip_block:' | sed -e 's/\\s*ip_block: \"\\([^ ]*\\)\"/ '\\1',/g' >> $@; ", false, }, { `"cat $(SRCS) | grep '\\s*ip_block:' | sed -e 's/\\s*ip_block: \"\\([^ ]*\\)\"/ '\\1',/g' >> $@; "`, "cat $(SRCS) | grep '\\s*ip_block:' | sed -e 's/\\s*ip_block: \"\\([^ ]*\\)\"/ '\\1',/g' >> $@; ", true, }, } var unquoteErrorTests = []struct { q string // quoted s string // unquoted, empty if unquote(s) will fail ok bool // true iff unquote(s) should succeed }{ {`"\1"`, "\u0001", true}, {`"\12"`, "\u000A", true}, {`"\123"`, "\u0053", true}, {`"\400"`, "", false}, {`"\x"`, "", false}, {`"\x1"`, "", false}, {`"\x12"`, "\u0012", true}, {`"\u"`, "", false}, {`"\u1"`, "", false}, {`"\u12"`, "", false}, {`"\u123"`, "", false}, {`"\u1234"`, "\u1234", true}, {`"\uD7FF"`, "\uD7FF", true}, {`"\uD800"`, "", false}, {`"\uDFFF"`, "", false}, {`"\uE000"`, "\uE000", true}, {`"\uFFFF"`, "\uFFFF", true}, {`"\u0000"`, "\u0000", true}, {`"\U"`, "", false}, {`"\U1"`, "", false}, {`"\U12"`, "", false}, {`"\U123"`, "", false}, {`"\U1234"`, "", false}, {`"\U12345"`, "", false}, {`"\U123456"`, "", false}, {`"\U1234567"`, "", false}, {`"\U00012345"`, "\U00012345", true}, {`"\U0000D7FF"`, "\uD7FF", true}, {`"\U0000D800"`, "", false}, {`"\U0000DFFF"`, "", false}, {`"\U0000E000"`, "\uE000", true}, {`"\U0000FFFF"`, "\uFFFF", true}, {`"\U00000000"`, "\u0000", true}, {`"\U0010FFFF"`, "\U0010FFFF", true}, {`"\U00110000"`, "", false}, {`"\UFFFFFFFF"`, "", false}, } func TestQuote(t *testing.T) { for _, tt := range quoteTests { if !tt.std { continue } q := quote(tt.s, strings.HasPrefix(tt.q, `"""`)) if q != tt.q { t.Errorf("quote(%#q) = %s, want %s", tt.s, q, tt.q) } } } func TestUnquote(t *testing.T) { for _, tt := range quoteTests { s, triple, err := Unquote(tt.q) wantTriple := strings.HasPrefix(tt.q, `"""`) || strings.HasPrefix(tt.q, `'''`) if s != tt.s || triple != wantTriple || err != nil { t.Errorf("unquote(%s) = %#q, %v, %v want %#q, %v, nil", tt.q, s, triple, err, tt.s, wantTriple) } } } func TestUnquoteErrors(t *testing.T) { for _, tt := range unquoteErrorTests { s, triple, err := Unquote(tt.q) if tt.ok && (s != tt.s || err != nil) { t.Errorf("Unquote(%s) = %#q, %v, %v want %#q, %v, nil", tt.q, s, triple, err, tt.s, triple) } else if !tt.ok && err == nil { t.Errorf("Unquote(%s) = %#q, %v, %v want %#q, %v, non-nil", tt.q, s, triple, err, tt.s, triple) } } } buildtools-10.1.0/build/walk_test.go0000664000175000017500000000523215254573013016771 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 build import "testing" func nodeToString(e Expr) string { if bin, ok := e.(*BinaryExpr); ok { return bin.Op } if assign, ok := e.(*AssignExpr); ok { return assign.Op } if lit, ok := e.(*LiteralExpr); ok { return lit.Token } return "unknown" } // (1 + 2) * (3 - 4) var binaryExprExample Expr = &BinaryExpr{ X: &BinaryExpr{ X: &LiteralExpr{Token: "1"}, Op: "+", Y: &LiteralExpr{Token: "2"}, }, Op: "*", Y: &BinaryExpr{ X: &LiteralExpr{Token: "3"}, Op: "-", Y: &LiteralExpr{Token: "4"}, }, } func TestWalk(t *testing.T) { var prefix []string Walk(binaryExprExample, func(e Expr, stk []Expr) { prefix = append(prefix, nodeToString(e)) }) compare(t, prefix, []string{"*", "+", "1", "2", "-", "3", "4"}) } func TestWalkOnce(t *testing.T) { var prefix []string var postfix []string var walk func(e *Expr) walk = func(e *Expr) { prefix = append(prefix, nodeToString(*e)) WalkOnce(*e, walk) postfix = append(postfix, nodeToString(*e)) } walk(&binaryExprExample) compare(t, prefix, []string{"*", "+", "1", "2", "-", "3", "4"}) compare(t, postfix, []string{"1", "2", "+", "3", "4", "-", "*"}) } func TestEdit(t *testing.T) { expr, _ := Parse("test", []byte("1 + 2")) compare(t, FormatString(expr), "1 + 2\n") Edit(expr, func(e Expr, stk []Expr) Expr { // Check if there are already parens if len(stk) > 0 { if _, ok := stk[len(stk)-1].(*ParenExpr); ok { return nil } } // Add parens around literal if lit, ok := e.(*LiteralExpr); ok { lit.Start = Position{} // workaround to avoid multiline formatting return &ParenExpr{X: e} } return nil }) compare(t, FormatString(expr), "(1) + (2)\n") } func TestRemoveParens(t *testing.T) { expr, _ := Parse("test", []byte("((((1))) + 2) + (3 + 4) * 5")) compare(t, FormatString(expr), "((((1))) + 2) + (3 + 4) * 5\n") // Remove all ParenExpr Edit(expr, func(e Expr, stk []Expr) Expr { for { if p, ok := e.(*ParenExpr); ok { e = p.X } else { return e } } }) // Parens are inserted in the output due to different precedence of operators. compare(t, FormatString(expr), "1 + 2 + (3 + 4) * 5\n") } buildtools-10.1.0/build/quote.go0000664000175000017500000001763015254573013016136 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // Python quoted strings. package build import ( "bytes" "fmt" "strconv" "strings" ) // unesc maps single-letter chars following \ to their actual values. var unesc = [256]byte{ 'a': '\a', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', 'v': '\v', '\\': '\\', '\'': '\'', '"': '"', } // esc maps escape-worthy bytes to the char that should follow \. var esc = [256]byte{ '\a': 'a', '\b': 'b', '\f': 'f', '\n': 'n', '\r': 'r', '\t': 't', '\v': 'v', '\\': '\\', '\'': '\'', '"': '"', } // escapable is a set of all character that may follow an unescaped backslash // in a string literal var escapable = [256]bool{ '\n': true, 'a': true, 'b': true, 'f': true, 'n': true, 'r': true, 't': true, 'u': true, 'U': true, 'v': true, 'x': true, '\'': true, '\\': true, '"': true, '0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true, '8': true, '9': true, } // Unquote unquotes the quoted string, returning the actual // string value, whether the original was triple-quoted, and // an error describing invalid input. func Unquote(quoted string) (s string, triple bool, err error) { // Check for raw prefix: means don't interpret the inner \. raw := false if strings.HasPrefix(quoted, "r") { raw = true quoted = quoted[1:] } if len(quoted) < 2 { err = fmt.Errorf("string literal too short") return } if quoted[0] != '"' && quoted[0] != '\'' || quoted[0] != quoted[len(quoted)-1] { err = fmt.Errorf("string literal has invalid quotes") } // Check for triple quoted string. quote := quoted[0] if len(quoted) >= 6 && quoted[1] == quote && quoted[2] == quote && quoted[:3] == quoted[len(quoted)-3:] { triple = true quoted = quoted[3 : len(quoted)-3] } else { quoted = quoted[1 : len(quoted)-1] } // Now quoted is the quoted data, but no quotes. // If we're in raw mode or there are no escapes, we're done. if raw || !strings.Contains(quoted, `\`) { s = quoted return } // Otherwise process quoted string. // Each iteration processes one escape sequence along with the // plain text leading up to it. var buf bytes.Buffer for { // Remove prefix before escape sequence. i := strings.Index(quoted, `\`) if i < 0 { i = len(quoted) } buf.WriteString(quoted[:i]) quoted = quoted[i:] if len(quoted) == 0 { break } // Process escape sequence. if len(quoted) == 1 { err = fmt.Errorf(`truncated escape sequence \`) return } switch quoted[1] { default: // In Python, if \z (for some byte z) is not a known escape sequence // then it appears as literal text in the string. buf.WriteString(quoted[:2]) quoted = quoted[2:] case '\n': // Ignore the escape and the line break. quoted = quoted[2:] case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"': // One-char escape buf.WriteByte(unesc[quoted[1]]) quoted = quoted[2:] case '0', '1', '2', '3', '4', '5', '6', '7': // Octal escape, up to 3 digits. n := int(quoted[1] - '0') quoted = quoted[2:] for i := 1; i < 3; i++ { if len(quoted) == 0 || quoted[0] < '0' || '7' < quoted[0] { break } n = n*8 + int(quoted[0]-'0') quoted = quoted[1:] } if n >= 256 { // NOTE: Python silently discards the high bit, // so that '\541' == '\141' == 'a'. // Let's see if we can avoid doing that in BUILD files. err = fmt.Errorf(`invalid escape sequence \%03o`, n) return } buf.WriteByte(byte(n)) case 'u': // Unicode escape, exactly 4 digits for a 16-bit Unicode code point. if len(quoted) < 6 { err = fmt.Errorf(`truncated escape sequence %s`, quoted) return } n, err1 := strconv.ParseInt(quoted[2:6], 16, 0) if err1 != nil { err = fmt.Errorf(`invalid escape sequence %s`, quoted[:6]) return } if n >= 0xD800 && n <= 0xDFFF { err = fmt.Errorf(`invalid dangling surrogate %s`, quoted[:6]) return } buf.WriteRune(rune(n)) quoted = quoted[6:] case 'U': // Unicode escape, exactly 8 digits for a 16-bit Unicode code point. if len(quoted) < 10 { err = fmt.Errorf(`truncated escape sequence %s`, quoted) return } n, err1 := strconv.ParseInt(quoted[2:10], 16, 0) if err1 != nil { err = fmt.Errorf(`invalid escape sequence %s`, quoted[:10]) return } if n >= 0xD800 && n <= 0xDFFF { err = fmt.Errorf(`invalid dangling surrogate %s`, quoted[:10]) return } if n > 0x10FFFF { err = fmt.Errorf(`Unicode value out of range %s`, quoted[:10]) return } buf.WriteRune(rune(n)) quoted = quoted[10:] case 'x': // Hexadecimal escape, exactly 2 digits. if len(quoted) < 4 { err = fmt.Errorf(`truncated escape sequence %s`, quoted) return } n, err1 := strconv.ParseInt(quoted[2:4], 16, 0) if err1 != nil { err = fmt.Errorf(`invalid escape sequence %s`, quoted[:4]) return } buf.WriteByte(byte(n)) quoted = quoted[4:] } } s = buf.String() return } // IsCorrectEscaping reports whether a string doesn't contain any incorrectly // escaped sequences such as "\a". func IsCorrectEscaping(value string) bool { escaped := false // This for-loop doesn't correctly check for a backlash at the end of the string literal, but // such string can't be parsed anyway, neither by Bazel nor by Buildifier. for _, ch := range value { if !escaped { if ch == '\\' { escaped = true } continue } if ok := escapable[ch]; !ok { return false } escaped = false } return true } // indexByte returns the index of the first instance of b in s, or else -1. func indexByte(s string, b byte) int { for i := 0; i < len(s); i++ { if s[i] == b { return i } } return -1 } // hex is a list of the hexadecimal digits, for use in quoting. // We always print lower-case hexadecimal. const hex = "0123456789abcdef" // quote returns the quoted form of the string value "x". // If triple is true, quote uses the triple-quoted form """x""". func quote(unquoted string, triple bool) string { q := `"` if triple { q = `"""` } var buf bytes.Buffer buf.WriteString(q) for i := 0; i < len(unquoted); i++ { c := unquoted[i] if c == '"' && triple && (i+1 < len(unquoted) && unquoted[i+1] != '"' || i+2 < len(unquoted) && unquoted[i+2] != '"') { // Can pass up to two quotes through, because they are followed by a non-quote byte. buf.WriteByte(c) if i+1 < len(unquoted) && unquoted[i+1] == '"' { buf.WriteByte(c) i++ } continue } if triple && c == '\n' { // Can allow newline in triple-quoted string. buf.WriteByte(c) continue } if c == '\'' { // Can allow ' since we always use ". buf.WriteByte(c) continue } if c == '\\' { // All backslashes should be escaped buf.WriteByte('\\') buf.WriteByte('\\') continue } if esc[c] != 0 { buf.WriteByte('\\') buf.WriteByte(esc[c]) continue } if c < 0x20 || c >= 0x80 { // BUILD files are supposed to be Latin-1, so escape all control and high bytes. // I'd prefer to use \x here, but Blaze does not implement // \x in quoted strings (b/7272572). buf.WriteByte('\\') buf.WriteByte(hex[c>>6]) // actually octal but reusing hex digits 0-7. buf.WriteByte(hex[(c>>3)&7]) buf.WriteByte(hex[c&7]) /* buf.WriteByte('\\') buf.WriteByte('x') buf.WriteByte(hex[c>>4]) buf.WriteByte(hex[c&0xF]) */ continue } buf.WriteByte(c) continue } buf.WriteString(q) return buf.String() } buildtools-10.1.0/build/print.go0000664000175000017500000010254415254573013016134 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // Printing of syntax trees. package build import ( "bytes" "fmt" "strings" "github.com/bazel-contrib/buildtools/v10/tables" ) const ( nestedIndentation = 4 // Indentation of nested blocks listIndentation = 4 // Indentation of multiline expressions defIndentation = 8 // Indentation of multiline function definitions ) // FormatWithoutRewriting returns the formatted form of the given Starlark file. // This function is mostly useful for tests only, please consider using `Format` instead. func FormatWithoutRewriting(f *File) []byte { pr := &printer{fileType: f.Type} pr.file(f) return pr.Bytes() } // Format rewrites the file and returns the formatted form of it. func Format(f *File) []byte { Rewrite(f) return FormatWithoutRewriting(f) } // FormatWithRewriter rewites the file with custom rewriter and returns the formatted form of it func FormatWithRewriter(w *Rewriter, f *File) []byte { w.Rewrite(f) return FormatWithoutRewriting(f) } // FormatString returns the string form of the given expression. func FormatString(x Expr) string { // Expr is an interface and can be nil if x == nil { return "" } fileType := TypeBuild // for compatibility if file, ok := x.(*File); ok { fileType = file.Type } pr := &printer{fileType: fileType} switch x := x.(type) { case *File: pr.file(x) default: pr.expr(x, precLow) } return pr.String() } // A printer collects the state during printing of a file or expression. type printer struct { fileType FileType // different rules can be applied to different file types. bytes.Buffer // output buffer comment []Comment // pending end-of-line comments margin int // left margin (indent), a number of spaces depth int // nesting depth inside ( ) [ ] { } level int // nesting level of def-, if-else- and for-blocks needsNewLine bool // true if the next statement needs a new line before it } // formattingMode returns the current file formatting mode. // Can be only TypeBuild or TypeDefault. func (p *printer) formattingMode() FileType { switch p.fileType { case TypeBuild, TypeWorkspace, TypeModule, TypeRepo, TypeVendor: return TypeBuild default: // TypeDefault, TypeBzl return TypeDefault } } // spaceString is a run of spaces used by spaces() to emit indentation without allocating each time. const spaceString = " " // spaces writes n spaces to the buffer. func (p *printer) spaces(n int) { for n >= len(spaceString) { p.prints(spaceString) n -= len(spaceString) } if n > 0 { p.prints(spaceString[:n]) } } // prints writes s to the buffer. func (p *printer) prints(s string) { p.WriteString(s) } // printc writes a single byte to the buffer. func (p *printer) printc(c byte) { p.WriteByte(c) } // indent returns the position on the current line, in bytes, 0-indexed. func (p *printer) indent() int { b := p.Bytes() n := 0 for n < len(b) && b[len(b)-1-n] != '\n' { n++ } return n } // newline ends the current line, flushing end-of-line comments. // It must only be called when printing a newline is known to be safe: // when not inside an expression or when p.depth > 0. // To break a line inside an expression that might not be enclosed // in brackets of some kind, use breakline instead. func (p *printer) newline() { p.needsNewLine = false p.flushComments() p.trim() p.printc('\n') p.spaces(p.margin) } // flushComments prints the queued end-of-line comments: the first one at the // end of the current line, any further ones on their own lines at the current // margin. func (p *printer) flushComments() { if len(p.comment) == 0 { return } p.prints(" ") for i, com := range p.comment { if i > 0 { p.trim() p.printc('\n') p.spaces(p.margin) } p.prints(strings.TrimSpace(com.Token)) } p.comment = p.comment[:0] } // softNewline postpones a call to newline to the next call of p.newlineIfNeeded() // If softNewline is called several times, just one newline is printed. // Usecase: if there are several nested blocks ending at the same time, for instance // // if True: // for a in b: // pass // foo() // // the last statement (`pass`) doesn't end with a newline, each block ends with a lazy newline // which actually gets printed only once when right before the next statement (`foo()`) is printed. func (p *printer) softNewline() { p.needsNewLine = true } // newlineIfNeeded calls newline if softNewline() has previously been called func (p *printer) newlineIfNeeded() { if p.needsNewLine == true { p.newline() } } // breakline breaks the current line, inserting a continuation \ if needed. // If no continuation \ is needed, breakline flushes end-of-line comments. func (p *printer) breakline() { if p.depth == 0 { // Cannot have both final \ and comments. p.prints(" \\\n") p.spaces(p.margin) return } // Safe to use newline. p.newline() } // trim removes trailing spaces from the current line. func (p *printer) trim() { // Remove trailing space from line we're about to end. b := p.Bytes() n := len(b) for n > 0 && b[n-1] == ' ' { n-- } p.Truncate(n) } // file formats the given file into the print buffer. func (p *printer) file(f *File) { for _, com := range f.Before { p.prints(strings.TrimSpace(com.Token)) p.newline() } p.statements(f.Stmt) for _, com := range f.After { p.prints(strings.TrimSpace(com.Token)) p.newline() } p.newlineIfNeeded() } func (p *printer) nestedStatements(stmts []Expr) { p.margin += nestedIndentation p.level++ p.newline() p.statements(stmts) p.margin -= nestedIndentation p.level-- } func (p *printer) statements(rawStmts []Expr) { // rawStmts may contain nils if a refactoring tool replaces an actual statement with nil. // It means the statements don't exist anymore, just ignore them. stmts := []Expr{} for _, stmt := range rawStmts { if stmt != nil { stmts = append(stmts, stmt) } } for i, stmt := range stmts { p.expr(stmt, precLow) // A CommentBlock is an empty statement without a body, // it doesn't need an line break after the body if _, ok := stmt.(*CommentBlock); !ok { p.softNewline() } for _, com := range stmt.Comment().After { p.newlineIfNeeded() p.prints(strings.TrimSpace(com.Token)) p.softNewline() } // Print an empty line break after the statement unless it's the last statement in the sequence. // In that case a line break should be printed when the block or the file ends. if i < len(stmts)-1 { p.newline() } if i+1 < len(stmts) && !p.compactStmt(stmt, stmts[i+1]) { p.newline() } } } // compactStmt reports whether the pair of statements s1, s2 // should be printed without an intervening blank line. // We omit the blank line when both are subinclude statements // and the second one has no leading comments. func (p *printer) compactStmt(s1, s2 Expr) bool { if len(s2.Comment().Before) > 0 || len(s1.Comment().After) > 0 { return false } else if isLoad(s1) && isLoad(s2) { // Load statements should be compact return true } else if isLoad(s1) || isLoad(s2) { // Load statements should be separated from anything else return false } else if p.fileType == TypeModule && areBazelDepsOfSameType(s1, s2) { // bazel_dep statements in MODULE files should be compressed if they are both dev deps or // both non-dev deps. return true } else if p.fileType == TypeModule && isBazelDepWithOverride(s1, s2) { // Do not separate an override from the bazel_dep it overrides. return true } else if p.fileType == TypeModule && useSameModuleExtensionProxy(s1, s2) { // Keep statements together that use the same module extension: // // foo_deps = use_extension("//:foo.bzl", "foo_deps") // foo_deps.module(path = "github.com/foo/bar") // use_repo(foo_deps, "com_github_foo_bar") return true } else if p.fileType == TypeModule && isInclude(s1) && isInclude(s2) { // include takes a single argument and there are typically multiple in // a row, so we want to keep them together. return true } else if isCommentBlock(s1) || isCommentBlock(s2) { // Standalone comment blocks shouldn't be attached to other statements return false } else if (p.formattingMode() == TypeBuild) && p.level == 0 { // Top-level statements in a BUILD or WORKSPACE file return false } else if isFunctionDefinition(s1) || isFunctionDefinition(s2) { // On of the statements is a function definition return false } else { // Depend on how the statements have been printed in the original file _, end := s1.Span() start, _ := s2.Span() return start.Line-end.Line <= 1 } } // isLoad reports whether x is a load statement. func isLoad(x Expr) bool { _, ok := x.(*LoadStmt) return ok } // areBazelDepsOfSameType reports whether x and y are bazel_dep statements that // are both dev dependencies or both regular dependencies. func areBazelDepsOfSameType(x, y Expr) bool { if !isBazelDep(x) || !isBazelDep(y) { return false } isXDevDep := getKeywordBoolArgument(x.(*CallExpr), "dev_dependency", false) isYDevDep := getKeywordBoolArgument(y.(*CallExpr), "dev_dependency", false) return isXDevDep == isYDevDep } func isBazelDep(x Expr) bool { call, ok := x.(*CallExpr) if !ok { return false } if ident, ok := call.X.(*Ident); ok && ident.Name == "bazel_dep" { return true } return false } func isUseRepoOrUseExtension(x Expr) bool { call, ok := x.(*CallExpr) if !ok { return false } if ident, ok := call.X.(*Ident); ok && (ident.Name == "use_repo" || ident.Name == "use_extension") { return true } return false } func isModuleOverride(x Expr) bool { call, ok := x.(*CallExpr) if !ok { return false } ident, ok := call.X.(*Ident) if !ok { return false } return tables.IsModuleOverride[ident.Name] } func getKeywordBoolArgument(call *CallExpr, keyword string, defaultValue bool) bool { arg := getKeywordArgument(call, keyword) if arg == nil { return defaultValue } ident, ok := arg.(*Ident) if !ok { // Assume that the specified more complex value does not evaluate to the default. return !defaultValue } return ident.Name == "True" } func getKeywordArgument(call *CallExpr, param string) Expr { for _, arg := range call.List { kwarg, ok := arg.(*AssignExpr) if !ok { continue } ident, ok := kwarg.LHS.(*Ident) if !ok { continue } if ident.Name == param { return kwarg.RHS } } return nil } func isBazelDepWithOverride(x, y Expr) bool { if !isBazelDep(x) || !isModuleOverride(y) { return false } bazelDepName, ok := getKeywordArgument(x.(*CallExpr), "name").(*StringExpr) if !ok { return false } overrideModuleName, ok := getKeywordArgument(y.(*CallExpr), "module_name").(*StringExpr) if !ok { return false } return bazelDepName.Value == overrideModuleName.Value } func useSameModuleExtensionProxy(x, y Expr) bool { extX, isUseRepoX := usedModuleExtensionProxy(x) if extX == "" { return false } extY, isUseRepoY := usedModuleExtensionProxy(y) // Switching from a use_repo to a non-use_repo statement should break the // sequence of statements. // // foo_deps.module(path = "github.com/foo/bar") // use_repo(foo_deps, "com_github_foo_bar") // // foo_deps.module(path = "github.com/foo/bar2") // use_repo(foo_deps, "com_github_foo_bar2") return extX == extY && (!isUseRepoX || isUseRepoY) } func usedModuleExtensionProxy(x Expr) (name string, isUseRepo bool) { if call, ok := x.(*CallExpr); ok { if callee, isIdent := call.X.(*Ident); isIdent && callee.Name == "use_repo" { // Handles: // use_repo(foo_deps, "com_github_foo_bar") if len(call.List) < 1 { return "", true } proxy, isIdent := call.List[0].(*Ident) if !isIdent { return "", true } return proxy.Name, true } else if dot, isDot := call.X.(*DotExpr); isDot { // Handles: // foo_deps.module(path = "github.com/foo/bar") extension, isIdent := dot.X.(*Ident) if !isIdent { return "", false } return extension.Name, false } else { return "", false } } else if assign, ok := x.(*AssignExpr); ok { // Handles: // foo_deps = use_extension("//:foo.bzl", "foo_deps") assignee, isIdent := assign.LHSIdent() if !isIdent { return "", false } call, isCall := assign.RHS.(*CallExpr) if !isCall { return "", false } callee, isIdent := call.X.(*Ident) if !isIdent || callee.Name != "use_extension" { return "", false } return assignee.Name, false } else { return "", false } } func isInclude(x Expr) bool { if call, ok := x.(*CallExpr); ok { if ident, ok := call.X.(*Ident); ok && ident.Name == "include" { return true } } return false } // isCommentBlock reports whether x is a comment block node. func isCommentBlock(x Expr) bool { _, ok := x.(*CommentBlock) return ok } // isFunctionDefinition checks if the statement is a def code block func isFunctionDefinition(x Expr) bool { _, ok := x.(*DefStmt) return ok } // isDifferentLines reports whether two positions belong to different lines. // If one of the positions is null (Line == 0), it's not a real position but probably an indicator // of manually inserted node. Return false in this case func isDifferentLines(p1, p2 *Position) bool { if p1.Line == 0 || p2.Line == 0 { return false } return p1.Line != p2.Line } // Expression formatting. // The expression formatter must introduce parentheses to force the // meaning described by the parse tree. We preserve parentheses in the // input, so extra parentheses are only needed if we have edited the tree. // // For example consider these expressions: // // (1) "x" "y" % foo // (2) "x" + "y" % foo // (3) "x" + ("y" % foo) // (4) ("x" + "y") % foo // // When we parse (1), we represent the concatenation as an addition. // However, if we print the addition back out without additional parens, // as in (2), it has the same meaning as (3), which is not the original // meaning. To preserve the original meaning we must add parens as in (4). // // To allow arbitrary rewrites to be formatted properly, we track full // operator precedence while printing instead of just handling this one // case of string concatenation. // // The precedences are assigned values low to high. A larger number // binds tighter than a smaller number. All binary operators bind // left-to-right. const ( precLow = iota precAssign precColon precIfElse precOr precAnd precCmp precBitwiseOr precBitwiseXor precBitwiseAnd precBitwiseShift precAdd precMultiply precUnary precSuffix ) // opPrec gives the precedence for operators found in a BinaryExpr. var opPrec = map[string]int{ "or": precOr, "and": precAnd, "in": precCmp, "not in": precCmp, "<": precCmp, ">": precCmp, "==": precCmp, "!=": precCmp, "<=": precCmp, ">=": precCmp, "+": precAdd, "-": precAdd, "*": precMultiply, "/": precMultiply, "//": precMultiply, "%": precMultiply, "|": precBitwiseOr, "&": precBitwiseAnd, "^": precBitwiseXor, "<<": precBitwiseShift, ">>": precBitwiseShift, } // expr prints the expression v to the print buffer. // The value outerPrec gives the precedence of the operator // outside expr. If that operator binds tighter than v's operator, // expr must introduce parentheses to preserve the meaning // of the parse tree (see above). func (p *printer) expr(v Expr, outerPrec int) { p.precedingComments(v) // Do we introduce parentheses? // The result depends on the kind of expression. // Each expression type that might need parentheses // calls addParen with its own precedence. // If parentheses are necessary, addParen prints the // opening parenthesis and sets parenthesized so that // the code after the switch can print the closing one. parenthesized := false addParen := func(prec int) { if prec < outerPrec { p.printc('(') p.depth++ parenthesized = true } } switch v := v.(type) { default: panic(fmt.Errorf("printer: unexpected type %T", v)) case *CommentBlock: // CommentBlock has no body case *LiteralExpr: p.prints(v.Token) case *Ident: p.prints(v.Name) case *TypedIdent: p.expr(v.Ident, precLow) p.prints(": ") p.expr(v.Type, precLow) case *TypeAppExpr: p.expr(v.Type, precSuffix) p.seq("[]", &v.ArgsStart, &v.Args, &v.End, modeTypeApp, v.ForceCompact, v.ForceMultiLine) case *EllipsisExpr: p.prints("...") case *BranchStmt: p.prints(v.Token) case *StringExpr: // If the Token is a correct quoting of Value and has double quotes, use it, // also use it if it has single quotes and the value itself contains a double quote symbol // or if it's a raw string literal (starts with "r"). // This preserves the specific escaping choices that BUILD authors have made. s, triple, err := Unquote(v.Token) if err == nil && s == v.Value && triple == v.TripleQuote { if strings.HasPrefix(v.Token, `r`) { // Raw string literal token := v.Token if strings.HasSuffix(v.Token, `'`) && !strings.ContainsRune(v.Value, '"') { // Single quotes but no double quotes inside the string, replace with double quotes if strings.HasSuffix(token, `'''`) { token = `r"""` + token[4:len(token)-3] + `"""` } else if strings.HasSuffix(token, `'`) { token = `r"` + token[2:len(token)-1] + `"` } } p.prints(token) break } // Non-raw string literal if strings.HasPrefix(v.Token, `"`) || strings.ContainsRune(v.Value, '"') { // Either double quoted or there are double-quotes inside the string if IsCorrectEscaping(v.Token) { p.prints(v.Token) break } } } p.prints(quote(v.Value, v.TripleQuote)) case *DotExpr: addParen(precSuffix) p.expr(v.X, precSuffix) _, xEnd := v.X.Span() isMultiline := isDifferentLines(&v.NamePos, &xEnd) if isMultiline { p.margin += listIndentation p.breakline() } p.printc('.') p.prints(v.Name) if isMultiline { p.margin -= listIndentation } case *IndexExpr: addParen(precSuffix) p.expr(v.X, precSuffix) p.printc('[') p.expr(v.Y, precLow) p.printc(']') case *KeyValueExpr: p.expr(v.Key, precLow) p.prints(": ") p.expr(v.Value, precLow) case *SliceExpr: addParen(precSuffix) p.expr(v.X, precSuffix) p.printc('[') if v.From != nil { p.expr(v.From, precLow) } p.printc(':') if v.To != nil { p.expr(v.To, precLow) } if v.SecondColon.Byte != 0 { p.printc(':') if v.Step != nil { p.expr(v.Step, precLow) } } p.printc(']') case *UnaryExpr: addParen(precUnary) if v.Op == "not" { p.prints("not ") // Requires a space after it. } else { p.prints(v.Op) } // Use the next precedence level (precSuffix), so that nested unary expressions are parenthesized, // for example: `not (-(+(~foo)))` instead of `not -+~foo` if v.X != nil { p.expr(v.X, precSuffix) } case *LambdaExpr: addParen(precColon) p.prints("lambda") for i, param := range v.Params { if i > 0 { p.printc(',') } p.printc(' ') p.expr(param, precLow) } p.prints(": ") p.expr(v.Body[0], precLow) // lambdas should have exactly one statement case *BinaryExpr: // Precedence: use the precedence of the operator. // Since all binary expressions FormatWithoutRewriting left-to-right, // it is okay for the left side to reuse the same operator // without parentheses, so we use prec for v.X. // For the same reason, the right side cannot reuse the same // operator, or else a parse tree for a + (b + c), where the ( ) are // not present in the source, will format as a + b + c, which // means (a + b) + c. Treat the right expression as appearing // in a context one precedence level higher: use prec+1 for v.Y. // // Line breaks: if we are to break the line immediately after // the operator, introduce a margin at the current column, // so that the second operand lines up with the first one and // also so that neither operand can use space to the left. // If the operator is an =, indent the right side another 4 spaces. prec := opPrec[v.Op] addParen(prec) m := p.margin if v.LineBreak { p.margin = p.indent() } p.expr(v.X, prec) p.printc(' ') p.prints(v.Op) if v.LineBreak { p.breakline() } else { p.printc(' ') } p.expr(v.Y, prec+1) p.margin = m case *AssignExpr: addParen(precAssign) m := p.margin if v.LineBreak { p.margin = p.indent() + listIndentation } p.expr(v.LHS, precAssign) p.printc(' ') p.prints(v.Op) if v.LineBreak { p.breakline() } else { p.printc(' ') } p.expr(v.RHS, precAssign+1) p.margin = m case *ParenExpr: p.seq("()", &v.Start, &[]Expr{v.X}, &v.End, modeParen, false, v.ForceMultiLine) case *CallExpr: forceCompact := v.ForceCompact if p.fileType == TypeModule && (isBazelDep(v) || isUseRepoOrUseExtension(v)) { start, end := v.Span() forceCompact = start.Line == end.Line } addParen(precSuffix) p.expr(v.X, precSuffix) p.seq("()", &v.ListStart, &v.List, &v.End, modeCall, forceCompact, v.ForceMultiLine) case *LoadStmt: addParen(precSuffix) p.prints("load") args := []Expr{v.Module} for i := range v.From { from := v.From[i] to := v.To[i] var arg Expr if from.Name == to.Name { // Suffix comments are attached to the `to` token, // Before comments are attached to the `from` token, // they need to be combined. arg = from.asString() arg.Comment().Before = to.Comment().Before } else { arg = &AssignExpr{ LHS: to, Op: "=", RHS: from.asString(), } } args = append(args, arg) } p.seq("()", &v.Load, &args, &v.Rparen, modeLoad, v.ForceCompact, false) case *ListExpr: p.seq("[]", &v.Start, &v.List, &v.End, modeList, false, v.ForceMultiLine) case *SetExpr: p.seq("{}", &v.Start, &v.List, &v.End, modeList, false, v.ForceMultiLine) case *TupleExpr: mode := modeTuple if v.NoBrackets { mode = modeSeq } p.seq("()", &v.Start, &v.List, &v.End, mode, v.ForceCompact, v.ForceMultiLine) case *DictExpr: var list []Expr for _, x := range v.List { list = append(list, x) } p.seq("{}", &v.Start, &list, &v.End, modeDict, false, v.ForceMultiLine) case *Comprehension: p.listFor(v) case *ConditionalExpr: addParen(precSuffix) p.expr(v.Then, precIfElse) p.prints(" if ") p.expr(v.Test, precIfElse) p.prints(" else ") p.expr(v.Else, precIfElse) case *ReturnStmt: p.prints("return") if v.Result != nil { p.printc(' ') p.expr(v.Result, precLow) } case *DefStmt: p.prints("def ") p.prints(v.Name) p.optionalTypeParams(v.TypeParams) // Line anchor for the opening '(' of the params list paramsAnchor := v.StartPos if v.TypeParams != nil { _, paramsAnchor = v.TypeParams.Span() } // Support printing partially-defined, code-created DefStmt-s. paramsEnd, _ := v.ParamsEnd.(*End) p.seq("()", ¶msAnchor, &v.Params, paramsEnd, modeDef, v.ForceCompact, v.ForceMultiLine) if v.Type != nil { p.prints(" -> ") p.expr(v.Type, precLow) } p.printc(':') p.queueEndOfLineComments(v.ColonPos) p.nestedStatements(v.Body) case *ForStmt: p.prints("for ") p.expr(v.Vars, precLow) p.prints(" in ") p.expr(v.X, precLow) p.printc(':') p.nestedStatements(v.Body) case *IfStmt: block := v isFirst := true needsEmptyLine := false for { p.newlineIfNeeded() if !isFirst { if needsEmptyLine { p.newline() } p.prints("el") } p.prints("if ") p.expr(block.Cond, precLow) p.printc(':') p.nestedStatements(block.True) isFirst = false _, end := block.True[len(block.True)-1].Span() needsEmptyLine = block.ElsePos.Pos.Line-end.Line > 1 // If the else-block contains just one statement which is an IfStmt, flatten it as a part // of if-elif chain. // Don't do it if the "else" statement has a suffix comment or if the next "if" statement // has a before-comment. if len(block.False) != 1 { break } next, ok := block.False[0].(*IfStmt) if !ok { break } if len(block.ElsePos.Comment().Suffix) == 0 && len(next.Comment().Before) == 0 { block = next continue } break } if len(block.False) > 0 { p.newlineIfNeeded() if needsEmptyLine { p.newline() } p.prints("else:") p.comment = append(p.comment, block.ElsePos.Comment().Suffix...) p.nestedStatements(block.False) } case *ForClause: p.prints("for ") p.expr(v.Vars, precLow) p.prints(" in ") p.expr(v.X, precLow) case *IfClause: p.prints("if ") p.expr(v.Cond, precLow) case *TypeAliasStmt: p.prints("type ") p.expr(v.Name, precLow) p.optionalTypeParams(v.TypeParams) p.prints(" =") if v.LineBreak { p.margin += listIndentation p.breakline() p.expr(v.Type, precLow) p.margin -= listIndentation } else { p.prints(" ") p.expr(v.Type, precLow) } } // Add closing parenthesis if needed. if parenthesized { p.depth-- p.printc(')') } p.queueEndOfLineComments(v) } // Emit line-comments preceding a given expression. func (p *printer) precedingComments(v Expr) { // If we are in the middle of an expression but not inside ( ) [ ] { } // then we cannot just break the line: we'd have to end it with a \. // However, even then we can't emit line comments since that would // end the expression. This is only a concern if we have rewritten // the parse tree. If comments were okay before this expression in // the original input they're still okay now, in the absence of rewrites. // // TODO(bazel-team): Check whether it is valid to emit comments right now, // and if not, insert them earlier in the output instead, at the most // recent \n not following a \ line. p.newlineIfNeeded() if before := v.Comment().Before; len(before) > 0 { // Want to print a line comment. // Line comments must be at the current margin. p.trim() if p.indent() > 0 { // There's other text on the line. Start a new line. p.printc('\n') } // Re-indent to margin. p.spaces(p.margin) for _, com := range before { p.prints(strings.TrimSpace(com.Token)) p.newline() } } } // Queue end-of-line comments for printing when we reach the end of the line. func (p *printer) queueEndOfLineComments(v Expr) { if v != nil { p.comment = append(p.comment, v.Comment().Suffix...) } } // A seqMode describes a formatting mode for a sequence of values, // like a list or call arguments. type seqMode int const ( _ seqMode = iota modeCall // f(x) modeList // [x] modeTuple // (x,) modeParen // (x) modeDict // {x:y} modeSeq // x, y modeDef // def f(x, y) modeLoad // load(a, b, c) modeTypeApp // dict[str, int] modeTypeParams // [T, U] ) // useCompactMode reports whether a sequence should be formatted in a compact mode func (p *printer) useCompactMode(start *Position, list *[]Expr, end *End, mode seqMode, forceCompact, forceMultiLine bool) bool { // If there are line comments, use multiline // so we can print the comments before the closing bracket. for _, x := range *list { if len(x.Comment().Before) > 0 || len(x.Comment().Suffix) > 0 { return false } } if end != nil && len(end.Before) > 0 { return false } // Implicit tuples are always compact if mode == modeSeq { return true } // Use compact mode for empty call expressions or type applications if ForceMultiLine is not set if (mode == modeCall || mode == modeTypeApp) && len(*list) == 0 && !forceMultiLine { return true } // In the Default and .bzl printing modes try to keep the original printing style. // Non-top-level statements and lists of arguments of a function definition // should also keep the original style regardless of the mode. if (p.level != 0 || p.formattingMode() == TypeDefault || mode == modeDef || mode == modeTypeParams) && mode != modeLoad { // If every element (including the brackets) ends on the same line where the next element starts, // use the compact mode, otherwise use multiline mode. // If an node's line number is 0, it means it doesn't appear in the original file, // its position shouldn't be taken into account. Unless a sequence is new, // then use multiline mode if ForceMultiLine mode was set. previousEnd := start isNewSeq := start.Line == 0 for _, x := range *list { start, end := x.Span() isNewSeq = isNewSeq && start.Line == 0 if isDifferentLines(&start, previousEnd) { return false } if end.Line != 0 { previousEnd = &end } } if end != nil { isNewSeq = isNewSeq && end.Pos.Line == 0 // In modeDef, we try to keep the closing parenthesis on the same line. if mode != modeDef && isDifferentLines(previousEnd, &end.Pos) { return false } } if !isNewSeq { return true } // Use the forceMultiline value for new sequences. return !forceMultiLine } // In Build mode, use the forceMultiline and forceCompact values if forceMultiLine { return false } if forceCompact { return true } // If neither of the flags are set, use compact mode only for empty or 1-element sequences return len(*list) <= 1 } // seq formats a list of values inside a given bracket pair (brack = "()", "[]", "{}"). // The end node holds any trailing comments to be printed just before the // closing bracket. // The mode parameter specifies the sequence mode (see above). // If multiLine is true, seq avoids the compact form even // for 0- and 1-element sequences. func (p *printer) seq(brack string, start *Position, list *[]Expr, end *End, mode seqMode, forceCompact, forceMultiLine bool) { args := &[]Expr{} for _, x := range *list { // nil arguments may be added by some linter checks, filter them out because // they may cause NPE. if x != nil { *args = append(*args, x) } } if mode != modeSeq { p.prints(brack[:1]) } p.depth++ defer func() { p.depth-- if mode != modeSeq { p.prints(brack[1:]) } }() if p.useCompactMode(start, args, end, mode, forceCompact, forceMultiLine) { for i, x := range *args { if i > 0 { p.prints(", ") } p.expr(x, precLow) } // Single-element tuple must end with comma, to mark it as a tuple. if len(*args) == 1 && mode == modeTuple { p.printc(',') } return } // Multi-line form. indentation := listIndentation if mode == modeDef { indentation = defIndentation } p.margin += indentation for i, x := range *args { // If we are about to break the line before the first // element and there are trailing end-of-line comments // waiting to be printed, delay them and print them as // whole-line comments preceding that element. // Do this by printing a newline ourselves and positioning // so that the end-of-line comment, with the two spaces added, // will line up with the current margin. if i == 0 && len(p.comment) > 0 { p.printc('\n') p.spaces(p.margin - 2) } p.newline() p.expr(x, precLow) if i+1 < len(*args) || needsTrailingComma(mode, x) { p.printc(',') } } // Final comments. printedFinalComments := false if end != nil { for _, com := range end.Before { p.newline() p.prints(strings.TrimSpace(com.Token)) printedFinalComments = true } } // In modeDef, keep the closing bracket on the same line unless doing so // would move a parameter's comment outside the parameter list. closeOnNewLine := mode != modeDef || printedFinalComments || len(p.comment) > 0 // Flush the last element's end-of-line comments before dedenting so that // any further comments line up with the element, not the closing bracket. p.flushComments() p.margin -= indentation if closeOnNewLine { p.newline() } } // optionalTypeParams formats a ListExpr of a type alias's or generic function's type params, // or does nothing if given a nil. func (p *printer) optionalTypeParams(typeParams Expr) { switch list := typeParams.(type) { case nil: return case *ListExpr: p.precedingComments(typeParams) p.seq("[]", &list.Start, &list.List, &list.End, modeTypeParams, false, list.ForceMultiLine) p.queueEndOfLineComments(typeParams) default: panic(fmt.Errorf("printer: expected ListExpr for type params, got %T", typeParams)) } } func needsTrailingComma(mode seqMode, v Expr) bool { switch mode { case modeDef: return false case modeParen: return false case modeTypeApp: // Syntax error! return false case modeCall: // *args and **kwargs in fn calls switch v := v.(type) { case *UnaryExpr: if v.Op == "*" || v.Op == "**" { return false } } } return true } // listFor formats a ListForExpr (list comprehension). // The single-line form is: // // [x for y in z if c] // // and the multi-line form is: // // [ // x // for y in z // if c // ] func (p *printer) listFor(v *Comprehension) { multiLine := v.ForceMultiLine || len(v.End.Before) > 0 // space breaks the line in multiline mode // or else prints a space. space := func() { if multiLine { p.breakline() } else { p.printc(' ') } } open, close := "[", "]" if v.Curly { open, close = "{", "}" } p.depth++ p.prints(open) if multiLine { p.margin += listIndentation p.newline() } p.expr(v.Body, precLow) for _, c := range v.Clauses { space() p.expr(c, precLow) } if multiLine { for _, com := range v.End.Before { p.newline() p.prints(strings.TrimSpace(com.Token)) } p.margin -= listIndentation p.newline() } p.prints(close) p.depth-- } func (p *printer) isTopLevel() bool { return p.margin == 0 } buildtools-10.1.0/build/testdata/0000775000175000017500000000000015254573013016254 5ustar kenhyskenhysbuildtools-10.1.0/build/testdata/058.in0000664000175000017500000000270615254573013017125 0ustar kenhyskenhysdef f(): if foo: return bar # these comments # belong to the next statement def f(): if foo: return bar # these comments # belong to the top level def f(): if foo: return bar # these comments # belong to the top level def f(): if foo: return bar # this comment belongs to the return statement # these comments are aligned # with the if-statement as after-comments # this should have the same indentation level as above # but this line belongs to the top level # and this belongs to the following for-loop for a in b: if foo: pass # this comment is aligned with pass # this comment is aligned with pass # this comment is aligned with pass pass elif bar: pass # this comment is aligned with pass # this comment is aligned with pass # this comment is aligned with elif # this comment is aligned with elif # this comment is aligned with for # this comment is aligned with for for a in b: if foo: pass elif bar: pass # all # of # these # comments # are # not # indented for a in b: if foo: pass elif bar: pass else: pass # This comment stays here # all # of # these # comments # are # aligned # with # else def foo(): return # This comment stays here # this comment belongs to the return statement # this comment belongs to the function # but these belong # to the top level buildtools-10.1.0/build/testdata/079.bzl.golden0000664000175000017500000000336315254573013020560 0ustar kenhyskenhys_: Any # Enable type checking. type _Numeric = int | float type _Setting[T] = dict[str, T] type _Transform[U] = \ Callable[[_Setting[U], str], _Setting[U]] type _OptionalDict[ # blah K, # key V, # value # blah blah ] = dict[K, V] | None type NumericResult = struct[{ "name": str, "data": Sequence[_Numeric] | None, }, ...] type UnionWithLineBreak = int | bool | \ float | str # End of union def concat(name: str, transform: _Transform[_Numeric] | None, *args: _Setting[_Numeric]) -> NumericResult: d: list[_Numeric] = [] for s in args: if transform != None: s = transform(s, "concat") if s.get("data") is not None: d.extend(s["data"]) return struct(name = name, data = d) def foo[T, U](a: T, b: U) -> dict[T, U]: return {a: b} def important_function[T](): # comment about important_function pass def lots_of_type_params[ T, U, V, ](x: int, y: str) -> None: pass def well_commented[ T, # comment about T U, # comment about U # comment after U ]( x: T, # comment about x y: U # comment about y # comment after y ) -> T: # comment after type # comment in body return x # buck2 tuple type syntax def parenthized_tuple_type(arg: (str) | ()) -> (str, int) | (str, str): if arg: return (arg[0], len(arg[0])) return "", "" # parentheses are allowed (but useless other than for readability) parenthesized: ((int) | (str | list[(bool)])) # ellipsis can be used in reified types provider_field(Callable[..., bool], default = lambda x: False) FOO = cast(struct[{"name": str}, ...], some(value) or another) BAR = isinstance(some(value) or another, struct[{"name": str}, ...]) buildtools-10.1.0/build/testdata/065.in0000664000175000017500000000012215254573013017111 0ustar kenhyskenhysx = 123 y = 0123 z = 0 t = 0x123, 0X456, 0o123, 0O456 foo(bar=[0, 00, 01, 02, 3]) buildtools-10.1.0/build/testdata/076.golden0000664000175000017500000000024615254573013017764 0ustar kenhyskenhys# Test collapse empty call expressions func() func() # before comment func() func() # after comment func( # line comment ) func( name = "not_empty", ) buildtools-10.1.0/build/testdata/077.build.golden0000664000175000017500000000023315254573013021057 0ustar kenhyskenhysvisibility([ "//foo", "//bar", ]) # Not modified due to leave-alone comment. # buildifier: leave-alone visibility([ "//foo", "//bar", ]) buildtools-10.1.0/build/testdata/075.in0000664000175000017500000000022115254573013017112 0ustar kenhyskenhysload("//:bac.bzl", "x", "y") load(":bab.bzl", "x", "y") load("//:baa.bzl", "x", "y") load("@foo//:foo.bzl", "x", "y") load("@bar.bzl", "x", "y") buildtools-10.1.0/build/testdata/047.in0000664000175000017500000000133515254573013017120 0ustar kenhyskenhys# Comprehensions with nested loops and conditions list_comprehension = [ a # item for b in c # first for if b # condition 1 if foo(b) # condition 2 for a in b # second for if a # condition 3 if bar(a) # condition 4 ] dict_comprehension = { a : a # item for b in c # first for if b # condition 1 if foo(b) # condition 2 for a in b # second for if a # condition 3 if bar(a) # condition 4 } # Comprehensions with tuple unpacking [a + b + c for a, b in c] [a + b + c for (a, b) in c] [a + b + c for a, (b, c) in d] [a + b + c for (a, b), c in d] [a + b + c for (a, (b, c)) in d] [a + b + c for ( a, (b, c) ) in d] [a for (a) in b] [a for (a,) in b] [a for ( a ) in b] [a for ( a, ) in b] buildtools-10.1.0/build/testdata/078.golden0000664000175000017500000000043315254573013017764 0ustar kenhyskenhysdef f(): pass # comment after a compact def def g(): if True: pass # comment after a nested compact if def h(): for x in [1]: pass # comment after a nested compact for def i(): pass # comment between a compact def and a statement x = 1 buildtools-10.1.0/build/testdata/002.in0000664000175000017500000000021015254573013017076 0ustar kenhyskenhyscc_test ( name = 'b\"ar\'"' , srcs = [ 'a.cc' , "b.cc" , "c.cc" ] , size = "small" , deps = [ "//base" , ":foo", "//util:map-util", ] ) buildtools-10.1.0/build/testdata/003.in0000664000175000017500000000046415254573013017112 0ustar kenhyskenhysnumbers = [ 0, 11, 123.456, 123., .456, 1.23e45, -1, +1, 0.0, -0.0, 1.0, -1.0, +1.0, 1e6, -1e6, -1.23e-45, 3.539537889086625e+24, 3.539537889086625E+24, 3.539537889086625e00024000, 3.539537889086625e+00024000, 3539537889086624823140625, 0x123, 0xE45, 0xe45, ] buildtools-10.1.0/build/testdata/015.golden0000664000175000017500000000031615254573013017753 0ustar kenhyskenhys# shouldn't add trailing spaces to the blank line after the first comment. cc_binary( deps = [ # comment before blank line # comment after blank line "y", "a", ], ) buildtools-10.1.0/build/testdata/079.build.golden0000664000175000017500000000361115254573013021064 0ustar kenhyskenhys_: Any # Enable type checking. type _Numeric = int | float type _Setting[T] = dict[str, T] type _Transform[U] = \ Callable[ [ _Setting[U], str, ], _Setting[U] ] type _OptionalDict[ # blah K, # key V, # value # blah blah ] = dict[K, V] | None type NumericResult = struct[ { "name": str, "data": Sequence[_Numeric] | None, }, ... ] type UnionWithLineBreak = int | bool | \ float | str # End of union def concat(name: str, transform: _Transform[_Numeric] | None, *args: _Setting[_Numeric]) -> NumericResult: d: list[_Numeric] = [] for s in args: if transform != None: s = transform(s, "concat") if s.get("data") is not None: d.extend(s["data"]) return struct(name = name, data = d) def foo[T, U](a: T, b: U) -> dict[T, U]: return {a: b} def important_function[T](): # comment about important_function pass def lots_of_type_params[ T, U, V, ](x: int, y: str) -> None: pass def well_commented[ T, # comment about T U, # comment about U # comment after U ]( x: T, # comment about x y: U # comment about y # comment after y ) -> T: # comment after type # comment in body return x # buck2 tuple type syntax def parenthized_tuple_type(arg: str | ()) -> (str, int) | (str, str): if arg: return (arg[0], len(arg[0])) return "", "" # parentheses are allowed (but useless other than for readability) parenthesized: (int | (str | list[bool])) # ellipsis can be used in reified types provider_field( Callable[..., bool], default = lambda x: False, ) FOO = cast( struct[ {"name": str}, ... ], some(value) or another, ) BAR = isinstance( some(value) or another, struct[ {"name": str}, ... ], ) buildtools-10.1.0/build/testdata/071.in0000664000175000017500000000010515254573013017107 0ustar kenhyskenhysfoo() # buildifier: disable=load-on-top load(":foo", "bar") bar() buildtools-10.1.0/build/testdata/079.in0000664000175000017500000000326115254573013017125 0ustar kenhyskenhys_: Any # Enable type checking. type _Numeric = int | float type _Setting[T] = dict[str, T] type _Transform[U] = \ Callable[[_Setting[U], str], _Setting[U]] type _OptionalDict[ # blah K, # key V, # value # blah blah ] = dict[K, V] | None type NumericResult = struct[{ "name": str, "data": Sequence[_Numeric] | None, }, ...] type UnionWithLineBreak = int | bool \ | float | str # End of union def concat(name: str, transform: _Transform[_Numeric]|None, *args: _Setting[_Numeric]) -> NumericResult: d: list[_Numeric] = [] for s in args: if transform != None: s = transform(s, "concat") if s.get("data") is not None: d.extend(s["data"]) return struct(name = name, data = d) def foo[T, U](a: T, b: U) -> dict[T, U]: return {a: b} def important_function[T](): # comment about important_function pass def lots_of_type_params[T, U, V, ](x: int, y: str) -> None: pass def well_commented[T, # comment about T U, # comment about U # comment after U ](x: T, # comment about x y: U, # comment about y # comment after y ) -> T: # comment after type # comment in body return x # buck2 tuple type syntax def parenthized_tuple_type(arg: (str,) | ()) -> (str, int) | (str, str,): if arg: return (arg[0], len(arg[0])) return "", "" # parentheses are allowed (but useless other than for readability) parenthesized: ((int) | (str | list[(bool)])) # ellipsis can be used in reified types provider_field(Callable[..., bool], default = lambda x: False) FOO = cast(struct[{"name": str}, ...], some(value) or another) BAR = isinstance(some(value) or another, struct[{"name": str}, ...]) buildtools-10.1.0/build/testdata/056.in0000664000175000017500000000012615254573013017115 0ustar kenhyskenhys# Comments oder shouldn't be broken def function( x, # 1 # 2 ) : # 3 # 4 pass buildtools-10.1.0/build/testdata/031.build.golden0000664000175000017500000000044215254573013021047 0ustar kenhyskenhysjava_test( name = "AllTestsCoverage", size = "large", data = TEST_SOURCES, tags = [ "local", # Cannot be run remotely. "manual", # Don't run coverage automatically. ], test_class = INSTRUMENTED_TEST_CLASS, deps = [":instrumented_js_runner"], ) buildtools-10.1.0/build/testdata/014.bzl.golden0000664000175000017500000000030615254573013020537 0ustar kenhyskenhysfoo = [x for x in (1, 2, 3)] bar(a = -1, b = not foo) cc_library( name = "bot_scorer", srcs = [ "bot_scorer.cc", "bot_scorer.h", ], deps = [":logdata_protolib"], ) buildtools-10.1.0/build/testdata/066.build.golden0000664000175000017500000000050315254573013021055 0ustar kenhyskenhysst1 = "abc" st2 = """ multiline""" st3 = ( "multi" ) n = 123 id = a fct1 = foo(1) fct2 = foo( arg = 1, ) fct3 = ( foo( arg = 1, ) ) macro( name = "foo", arg = ["a"], ) nested = 1 comments = [ "a", ("b"), # end of line comment # before comment ("c"), ] # comment buildtools-10.1.0/build/testdata/024.in0000664000175000017500000000077015254573013017115 0ustar kenhyskenhys# Test that single-line calls with no named arguments use the compact form. # Single-line tuples are also okay. str.rewrite(".", "/") str.rewrite(before=".", after="/") x = (1,2,3) x = (1,'2',-3,[],{},(),not b) y = (1, [2]) y = (1, {2: 3}) y = (1, (2, 3)) y = (1, -(a + b)) y = (1, not (a or b)) x = ( 1, 2, 3, ) x = (1,2, 3, 4) d.get(key, []) myfunc(1, 2, foo(x, key = y)) myfunc( 1, 2, foo(x, key = y)) myfunc(1, 2, foo(x, key = y) ) myfunc(1, 2, foo(x, key = y)) buildtools-10.1.0/build/testdata/054.in0000664000175000017500000000074015254573013017115 0ustar kenhyskenhys# If-else clauses if True: pass elif False: pass if foo in bar: for f in foo: # loop f() else if True: a = b else: if False: #assign b=a else: (a,b)=(b,a) if foo: bar if foo: # this is assigned to foo pass else: # this is assigned to "else" if bar: # this is assigned to bar pass else: # this is assigned to "else" too pass if foo: pass else: # comment if bar: pass if foo: pass else: # comment if bar: pass buildtools-10.1.0/build/testdata/031.in0000664000175000017500000000044615254573013017113 0ustar kenhyskenhysjava_test( name = 'AllTestsCoverage', test_class = INSTRUMENTED_TEST_CLASS, deps = [ ':instrumented_js_runner' ], data = TEST_SOURCES, tags = [ 'local', # Cannot be run remotely. 'manual' # Don't run coverage automatically. ], size = 'large') buildtools-10.1.0/build/testdata/039.build.golden0000664000175000017500000000022615254573013021057 0ustar kenhyskenhyscc_library( name = "foo", dict = { 1: 1, 2: 4, 3: 9, }, dict_comprehension = {i: i * i for i in range(10)}, ) buildtools-10.1.0/build/testdata/055.golden0000664000175000017500000000051315254573013017756 0ustar kenhyskenhysload(":bar.bzl", "bar") load(":baz.bzl", "baz") # after-comment load(":foo.bzl", "a", "d", "foo", b = "c", c = "b") load( ":foobar.bzl", "foobar", ) load( # 0 "foobar.bzl", # 1 # 5 "aaa", "bar", # 3 "foo", # 2 baz = "bazz", # 4 # 6 # 7 bbb = "ccc", # 8 ) # 9 foo(bar()) buildtools-10.1.0/build/testdata/073.in0000664000175000017500000000010515254573013017111 0ustar kenhyskenhys# disable=out-of-order-load load(":foo", "foo") load(":bar", "bar") buildtools-10.1.0/build/testdata/067.in0000664000175000017500000000104515254573013017120 0ustar kenhyskenhysload(,,, # A , ":foo.bzl",,, # B , "bar",,, ) load(,,,":a",,,"b",,,) cc_binary(,,, # A , name = "bin",,, # B , srcs = ["bin.cc"],,, # C , ) cc_binary(,,,name="wibble",,,srcs=["wibble.cc"],,,) my_list = [ 1,,, # A , 2 # B , ] my_1tuple = ( # A 1,,, # B , ) my_2tuple = ( # A 1,,, # B , 2,,, # C , ) my_dict = { 'a':1,,, # A , 'b':2,,, # B , } func(,,,a) func(b,,,) func(c,,,d) func(,,,e,,,f,,,) buildtools-10.1.0/build/testdata/041.bzl.golden0000664000175000017500000000036615254573013020545 0ustar kenhyskenhysboq_initial_css_modules( action_paths_dict = { "some.path.OneAction": [ "//some/path", ], "some.path.AnotherAction": [ "//some/other/path", ], }, visible_node_name = "SomeUi", ) buildtools-10.1.0/build/testdata/009.in0000664000175000017500000000206515254573013017117 0ustar kenhyskenhys# buildifier: leave-alone L1 = [ "C", "D", "A", "Z", "B", ] L2 = [ "C", "D", "A", "Z", "B", ] # All of the GSS, in the order they should be compiled in. # buildifier: leave-alone filegroup( name = "all_gss", srcs = [ "iphone_sprites.gss", ":iphone_imagedata_gss", ":multi_resolution_imagedata_gss", ":iphone_sprite_images_gss", "iphone/00_topstyles.gss", "iphone/avatar.gss", "iphone/chrome.gss", "iphone/conversation.gss", "iphone/error.gss", "iphone/installer.gss", "iphone/labels.gss", "iphone/menu.gss", "iphone/messageview.gss", "iphone/search.gss", "iphone/threadlist.gss", "iphone/toolbar.gss", "iphone/zz_bottomstyles.gss", "iphone/overlay.gss", "iphone/moremenu.gss", "iphone/settings.gss", "iphone/netstat.gss", "iphone/mouse.gss", "iphone/sprite_overrides.gss", "iphone/iconmenu.gss", "iphone/blpromo.gss", ], ) buildtools-10.1.0/build/testdata/070.build.golden0000664000175000017500000000077315254573013021061 0ustar kenhyskenhysfoo( name = "foo", srcs = [ "aaa", "bbb", ] + CONSTANT + [ "//ccc", "//ddd", ], data = [ "iii", "jjj", ] + bar([ "not", "sortable", "arguments", ]) + non_select({ "baz": [ "not", "sortable", "arguments", ], }), deps = select({ "bar": [ "//eee", "//ggg", ], }) + [ "fff", "hhh", ], ) buildtools-10.1.0/build/testdata/068.build.golden0000664000175000017500000000062015254573013021057 0ustar kenhyskenhys# Test sorting of call arguments. foo( name = "t", a = 0, z = 1, ) foo.bar.baz( name = "x", a = 3, b = 1, c = 0, d = 2, ) baz( name = "y", c = foo.bar( "positional1", 42, a = 3, b = 1, c = 0, d = 2, ), ) foo.bar( a = 2, z = 1, ).bar( name = "z", a = 3, b = 1, c = 0, d = 2, ) buildtools-10.1.0/build/testdata/042.bzl.golden0000664000175000017500000000045615254573013020546 0ustar kenhyskenhysfilegroup( name = "foo" + cpu, srcs = [ prefix + "/bin/" + triple + "-objcopy", ] + ([ prefix + "/bin/ccperf-collect", "what", ] if triple != "sparc" else []) + glob([ "moar_thing/*", ]), ) buildtools-10.1.0/build/testdata/064.build.golden0000664000175000017500000000026315254573013021056 0ustar kenhyskenhysfoo( a, b, ) foo( a, b, ) foo(a) foo( a, bar( a, b, ), ) def foo(): foo(a, b) def foo(): foo( a, b, ) buildtools-10.1.0/build/testdata/067.build.golden0000664000175000017500000000067415254573013021067 0ustar kenhyskenhysload(":a", "b") load( # A ":foo.bzl", # B "bar", ) cc_binary( # A name = "bin", # B srcs = ["bin.cc"], # C ) cc_binary( name = "wibble", srcs = ["wibble.cc"], ) my_list = [ 1, # A 2, # B ] my_1tuple = ( # A 1, # B ) my_2tuple = ( # A 1, # B 2, # C ) my_dict = { "a": 1, # A "b": 2, # B } func(a) func(b) func(c, d) func(e, f) buildtools-10.1.0/build/testdata/051.bzl.golden0000664000175000017500000000257715254573013020554 0ustar kenhyskenhysfoo( name = "foo", srcs = [bar], ) aaa bbb ccc ddd # multiline # comment eee fff ggg def function( # This shouldn't mess with indentation counting x, y = z): aaa bbb def g( a, s, d # this is d ): # this is function definition ccc # Misalingned comments # are not a syntax error ddd def h(a, s = d): # this is h eee def k(g): g += 1 return g for x in y: print(y) if x: print(x) elif y: print(y) else: print(z) print(y) print(x) if x: print(x) elif y: print(y) else: print(z) print(y) print(x) def i(): def j(): return h( [1], [2], ) # returns h return u() + w for b in a: # first for c, d in b: # second for ((e, f), g) in zip(c, d): # third pass if foo in bar: for f in foo: # loop f() elif True: a = b elif False: #assign b = a else: (a, b) = (b, a) if foo: bar if foo: bar #comment bar bar def a(name, foo = True, **kwargs): # comment here return buildtools-10.1.0/build/testdata/007.in0000664000175000017500000000030715254573013017112 0ustar kenhyskenhysexports_files([ "url_substring_denylist", "url_substring_denylist_short" ]) package(default_visibility = [ "//foo:bar" ]) package(default_visibility = [ "//foo:bar", "//bar:baz" ]) buildtools-10.1.0/build/testdata/060.in0000664000175000017500000000240215254573013017107 0ustar kenhyskenhysstrings = [ # empty '', "", '''''', """""", # ordinary 'foo', "foo", '''foo''', """foo""", # contain quotes of different style '"foo"', "'foo'", '''"""foo"""''', """'''foo'''""", # contain quotes of the same style '\'foo\'', "\"foo\"", '''\'\'\'foo\'\'\'''', """\"\"\"foo\"\"\"""", # contain quotes of both styles '\'"foo"\'', "'\"foo\"'", '''\'\'\'"""foo"""\'\'\'''', """'''\"\"\"foo\"\"\"'''""", # raw strings r'\foo', r'"\foo', r"\foo", r"'\foo", r'''\foo''', r'''"""\foo''', r"""\foo""", r"""'''\foo""", r"\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x43\y\z\0\1\2\3\4\5\6\7\8\9\uABCD\uD800\uDFFF\U0000ABCD\U0000D800\U0000DFFF\U00110000\\n", # contain incorrect escape sequences "\c'", '\c"', """\c'""", '''\c"''', "\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\v\w\x43\y\z\0\1\2\3\4\5\6\7\8\9", # correct escape sequences """\a\b\f\n\r\t\v\007\010\ """, '''\a\b\f\n\r\t\v\007\010\ ''', """ aa\\bb\n \ """, "\000\111\222", # correct Unicode escape sequences "\u0000", "\u0042", "\uABCD", "\uFFFF", "\u0123foo", "\u01234567", "\U00000000", "\U00000042", "\U0000ABCD", "\U0000FFFF", "\U0001F600", "\U0010ABCD", "\U0010ABCDfoo", "\U0010ABCDEF0", ] buildtools-10.1.0/build/testdata/069.build.golden0000664000175000017500000000052715254573013021066 0ustar kenhyskenhys# Test sortable denylists foobar( srcs = [ "bar.in", "foo.in", ], ) genrule( srcs = [ "foo.in", "bar.in", ], outs = [ "foo.in", "bar.in", ], foobar = [ "foo", "bar", ], ) cc_embed_data( srcs = [ "foo.in", "bar.in", ], ) buildtools-10.1.0/build/testdata/016.in0000664000175000017500000000054015254573013017111 0ustar kenhyskenhys# 1-element tuple with trailing comma must preserve it. x = (1,) y = ( 1, ) foo((1,)) bar(1,) baz([1,]) foo((1, )) bar(1, ) baz([1, ]) x = (1, 2, -3,) y = ( 1, 2, -3, ) foo((1, 2,)) bar(1, 2,) baz([1, 2,]) foo((1, 2, )) foo((1, 2, [3])) bar(1, 2, ) baz([1, 2, ]) # Implicit tuples True,False=False,True (a,b),c=d c=d,(e,f) buildtools-10.1.0/build/testdata/077.in0000664000175000017500000000023215254573013017116 0ustar kenhyskenhysvisibility([ "//foo", "//bar", ]) # Not modified due to leave-alone comment. # buildifier: leave-alone visibility([ "//foo", "//bar", ])buildtools-10.1.0/build/testdata/046.golden0000664000175000017500000000076015254573013017762 0ustar kenhyskenhys[ # These lines should all appear # inside the block cc_binary( name = "foo_%s" % lang, ) for lang in li # list comprehension for li in foo # nested list comprehension if lang in langs # condition if lang # another condition ] # List comprehension ends [ # These lines should all appear # inside the block cc_binary( # Here's a comment # about the target name = "foo_%s" % lang, ), # My binary ] # List ends buildtools-10.1.0/build/testdata/022.bzl.golden0000664000175000017500000000117515254573013020543 0ustar kenhyskenhyscc_library( name = "abc", srcs = [ # Animals or animal sounds. "yellowjacket.c", "buzz.c", "caribou.c", foo(), # Ocean phenomena. "yellowjacket.c", "wave.c", "emerald_sea.c", "caribou.c", # End of ocean phenomena. # Other stuff. ], ) cc_library( name = "x", # do not sort srcs = [ "x.cc", "y.cc", "x.cc", ], data = [ # do not sort ":x", ":y", ":x", ], # buildifier: leave-alone deps = [ ":x", ":y", ":x", ], ) buildtools-10.1.0/build/testdata/049.in0000664000175000017500000000022115254573013017113 0ustar kenhyskenhyscc_library( name = "foo", deps = [ "yet/another:dep", "//some/other/dep", ":bar", "//some:dep", ], ) buildtools-10.1.0/build/testdata/002.bzl.golden0000664000175000017500000000017215254573013020535 0ustar kenhyskenhyscc_test(name = 'b\"ar\'"', srcs = ["a.cc", "b.cc", "c.cc"], size = "small", deps = ["//base", ":foo", "//util:map-util"]) buildtools-10.1.0/build/testdata/042.build.golden0000664000175000017500000000036215254573013021052 0ustar kenhyskenhysfilegroup( name = "foo" + cpu, srcs = [ prefix + "/bin/" + triple + "-objcopy", ] + ([ prefix + "/bin/ccperf-collect", "what", ] if triple != "sparc" else []) + glob([ "moar_thing/*", ]), ) buildtools-10.1.0/build/testdata/052.golden0000664000175000017500000000150615254573013017756 0ustar kenhyskenhys# Unary operators -foo +foo ~foo -(+foo) -(+(~foo)) not foo # Binary operators foo = bar + baz + qux foo = bar - baz - qux foo = bar * baz * qux foo = bar / baz / qux foo = bar // baz // qux foo = bar % baz % qux foo = bar | baz | qux foo = bar & baz & qux foo = bar ^ baz ^ qux foo = bar << baz << qux foo = bar >> baz >> qux foo = ~bar | ~baz ^ -qux << quux >> baa # If-else operator precedence -f(1) if not a else 2 + 3 # Logic operators not foo < bar > baz <= foo >= bar != baz in foo == bar or baz x = "abc" in copts or "def" in copts x2 = "abc" not in copts and "def" not in copts y = "abc" in (copts or []) == False a = 1 + 2 if 3 + 4 else 5 + 6 # Augmented assignments foo += bar foo -= bar foo *= bar foo /= bar foo //= bar foo %= bar foo |= bar foo &= bar foo ^= bar foo <<= bar foo >>= bar buildtools-10.1.0/build/testdata/070.bzl.golden0000664000175000017500000000047615254573013020551 0ustar kenhyskenhysfoo( name = "foo", srcs = ["bbb", "aaa"] + CONSTANT + ["//ddd:ddd", "//ccc:ccc"], deps = select({ "bar": ["//ggg:ggg", "//eee:eee"], }) + ["hhh", "fff"], data = ["jjj", "iii"] + bar(["not", "sortable", "arguments"]) + non_select({ "baz": ["not", "sortable", "arguments"], }), ) buildtools-10.1.0/build/testdata/019.build.golden0000664000175000017500000001212615254573013021057 0ustar kenhyskenhyscc_library( name = "abc", srcs = ["abc.cc"], deps = [ ".zzz", " zzz", "!zzz", "\"zzz", "#zzz", "$zzz", "%zzz", "&zzz", "'zzz", "(zzz", ")zzz", "*zzz", "+zzz", ",zzz", "-zzz", "/zzz", "0zzz", "123", "1zzz", "2zzz", "3zzz", "4zzz", "5zzz", "6zzz", "7zzz", "8zzz", "9zzz", ";zzz", "zzz", "?zzz", "Azzz", "Bzzz", "Czzz", "Dzzz", "Ezzz", "FOO", "Fzzz", "Gzzz", "Hzzz", "Izzz", "Jzzz", "Kzzz", "Lzzz", "Mzzz", "Nzzz", "Ozzz", "Pzzz", "Qzzz", "Rzzz", "Szzz", "Tzzz", "Uzzz", "Vzzz", "Wzzz", "Xzzz", "Yzzz", "Zzzz", "[zzz", "\\zzz", "]zzz", "^zzz", "_zzz", "`zzz", "azzz", "bzzz", "czzz", "dzzz", "ezzz", "foo", "fzzz", "gzzz", "hzzz", "izzz", "jzzz", "kzzz", "lzzz", "mzzz", "nzzz", "ozzz", "pzzz", "qzzz", "rzzz", "szzz", "tzzz", "uzzz", "vzzz", "wzzz", "xzzz", "yzzz", "zzz.", "zzz:", "zzz ", "zzz!", "zzz#", "zzz$", "zzz%", "zzz&", "zzz'", "zzz(", "zzz)", "zzz*", "zzz+", "zzz,", "zzz-", "zzz/", "zzz0", "zzz1", "zzz2", "zzz3", "zzz4", "zzz5", "zzz6", "zzz7", "zzz8", "zzz9", "zzz;", "zzz<", "zzz=", "zzz>", "zzz?", "zzz@", "zzzA", "zzzB", "zzzC", "zzzD", "zzzE", "zzzF", "zzzG", "zzzH", "zzzI", "zzzJ", "zzzK", "zzzL", "zzzM", "zzzN", "zzzO", "zzzP", "zzzQ", "zzzR", "zzzS", "zzzT", "zzzU", "zzzV", "zzzW", "zzzX", "zzzY", "zzzZ", "zzz[", "zzz\\", "zzz]", "zzz^", "zzz_", "zzz`", "zzza", "zzzb", "zzzc", "zzzd", "zzze", "zzzf", "zzzg", "zzzh", "zzzi", "zzzj", "zzzk", "zzzl", "zzzm", "zzzn", "zzzo", "zzzp", "zzzq", "zzzr", "zzzs", "zzzt", "zzzu", "zzzv", "zzzw", "zzzx", "zzzy", "zzzz", "zzz{", "zzz|", "zzz}", "zzz~", "{asdf}", "{zzz", "|zzz", "}zzz", "~zzz", ":a", ":zzz", "//a", "//a:b", "//a.zzz", "//a:zzz", "//a zzz", "//a!zzz", "//a#zzz", "//a$zzz", "//a%zzz", "//a&zzz", "//a'zzz", "//a(zzz", "//a)zzz", "//a*zzz", "//a+zzz", "//a,zzz", "//a-zzz", "//a/b", "//a/b:c", "//a/b:d", "//a/b/c", "//a/b/c:d", "//a/zzz", "//a0zzz", "//a1zzz", "//a2", "//a2:a", "//a2:b", "//a2zzz", "//a3zzz", "//a4zzz", "//a5zzz", "//a6zzz", "//a7zzz", "//a8zzz", "//a9zzz", "//a;zzz", "//azzz", "//a?zzz", "//a@zzz", "//aAzzz", "//aBzzz", "//aCzzz", "//aDzzz", "//aEzzz", "//aFzzz", "//aGzzz", "//aHzzz", "//aIzzz", "//aJzzz", "//aKzzz", "//aLzzz", "//aMzzz", "//aNzzz", "//aOzzz", "//aPzzz", "//aQzzz", "//aRzzz", "//aSzzz", "//aTzzz", "//aUzzz", "//aVzzz", "//aWzzz", "//aXzzz", "//aYzzz", "//aZzzz", "//a[zzz", "//a\\zzz", "//a]zzz", "//a^zzz", "//a_zzz", "//a`zzz", "//aazzz", "//abzzz", "//aczzz", "//adzzz", "//aezzz", "//afzzz", "//agzzz", "//ahzzz", "//aizzz", "//ajzzz", "//akzzz", "//alzzz", "//amzzz", "//anzzz", "//aozzz", "//apzzz", "//aqzzz", "//arzzz", "//aszzz", "//atzzz", "//auzzz", "//avzzz", "//awzzz", "//axzzz", "//ayzzz", "//azzz", "//azzzz", "//a{zzz", "//a|zzz", "//a}zzz", "//a~zzz", "@abc", "@abc//:xyz", "@abc//foo", "@zzz", ], ) buildtools-10.1.0/build/testdata/054.golden0000664000175000017500000000102715254573013017756 0ustar kenhyskenhys# If-else clauses if True: pass elif False: pass if foo in bar: for f in foo: # loop f() elif True: a = b elif False: #assign b = a else: (a, b) = (b, a) if foo: bar if foo: # this is assigned to foo pass else: # this is assigned to "else" if bar: # this is assigned to bar pass else: # this is assigned to "else" too pass if foo: pass else: # comment if bar: pass if foo: pass else: # comment if bar: pass buildtools-10.1.0/build/testdata/048.in0000664000175000017500000000050215254573013017114 0ustar kenhyskenhyshas_sets(name="empty",labels={}) has_sets(name="one",labels={"foo"}) has_sets(name="one_trailing",labels={"bar",}) has_sets(name="two",labels={"foo","bar"}) has_sets(name="two_trailing",labels={"foo","bar",}) has_sets(name="three",labels={"foo","bar", "baz"}) has_sets(name="three_trailing",labels={"foo","bar","baz",}) buildtools-10.1.0/build/testdata/020.build.golden0000664000175000017500000000021315254573013021041 0ustar kenhyskenhyscc_library( srcs = [ "//hjk/asdf", ], cmd = "abc " + "//def" + " ghi", malloc = "//abc:def", ) buildtools-10.1.0/build/testdata/060.golden0000664000175000017500000000262115254573013017754 0ustar kenhyskenhysstrings = [ # empty "", "", """""", """""", # ordinary "foo", "foo", """foo""", """foo""", # contain quotes of different style '"foo"', "'foo'", '''"""foo"""''', """'''foo'''""", # contain quotes of the same style "'foo'", "\"foo\"", """'''foo'''""", """\"\"\"foo\"\"\"""", # contain quotes of both styles '\'"foo"\'', "'\"foo\"'", '''\'\'\'"""foo"""\'\'\'''', """'''\"\"\"foo\"\"\"'''""", # raw strings r"\foo", r'"\foo', r"\foo", r"'\foo", r"""\foo""", r'''"""\foo''', r"""\foo""", r"""'''\foo""", r"\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x43\y\z\0\1\2\3\4\5\6\7\8\9\uABCD\uD800\uDFFF\U0000ABCD\U0000D800\U0000DFFF\U00110000\\n", # contain incorrect escape sequences "\\c'", "\\c\"", """\\c'""", """\\c\"""", "\a\b\\c\\d\\e\f\\g\\h\\i\\j\\k\\l\\m\n\\o\\p\\q\r\\s\t\v\\wC\\y\\z\000\001\002\003\004\005\006\a\\8\\9", # correct escape sequences """\a\b\f\n\r\t\v\007\010\ """, """\a\b\f \r\t\v\a\b """, """ aa\\bb\n \ """, "\000\111\222", # correct Unicode escape sequences "\u0000", "\u0042", "\uABCD", "\uFFFF", "\u0123foo", "\u01234567", "\U00000000", "\U00000042", "\U0000ABCD", "\U0000FFFF", "\U0001F600", "\U0010ABCD", "\U0010ABCDfoo", "\U0010ABCDEF0", ] buildtools-10.1.0/build/testdata/050.build.golden0000664000175000017500000000016015254573013021045 0ustar kenhyskenhyscc_library( name = "foo", deps = [ "testdata", ":testdata", "//:root", ], ) buildtools-10.1.0/build/testdata/057.in0000664000175000017500000000047415254573013017124 0ustar kenhyskenhysdef foo1(x, y, **kwargs): bar1( x, y, **kwargs ) def foo2(x, y, *args): bar2( x, y, *args ) def foo3(x, y, **kwargs): bar2( x, y, **kwargs, ) def foo4(x, y, *args): bar4( x, y, *args, )buildtools-10.1.0/build/testdata/064.bzl.golden0000664000175000017500000000021615254573013020544 0ustar kenhyskenhysfoo(a, b) foo( a, b, ) foo(a) foo( a, bar(a, b), ) def foo(): foo(a, b) def foo(): foo( a, b, ) buildtools-10.1.0/build/testdata/069.bzl.golden0000664000175000017500000000052715254573013020556 0ustar kenhyskenhys# Test sortable denylists foobar( srcs = [ "foo.in", "bar.in", ], ) genrule( srcs = [ "foo.in", "bar.in", ], outs = [ "foo.in", "bar.in", ], foobar = [ "foo", "bar", ], ) cc_embed_data( srcs = [ "foo.in", "bar.in", ], ) buildtools-10.1.0/build/testdata/024.bzl.golden0000664000175000017500000000105415254573013020541 0ustar kenhyskenhys# Test that single-line calls with no named arguments use the compact form. # Single-line tuples are also okay. str.rewrite(".", "/") str.rewrite(before = ".", after = "/") x = (1, 2, 3) x = (1, "2", -3, [], {}, (), not b) y = (1, [2]) y = (1, {2: 3}) y = (1, (2, 3)) y = (1, -(a + b)) y = (1, not (a or b)) x = ( 1, 2, 3, ) x = ( 1, 2, 3, 4, ) d.get(key, []) myfunc(1, 2, foo(x, key = y)) myfunc( 1, 2, foo(x, key = y), ) myfunc( 1, 2, foo(x, key = y), ) myfunc(1, 2, foo( x, key = y, )) buildtools-10.1.0/build/testdata/029.in0000664000175000017500000000010115254573013017106 0ustar kenhyskenhys# c1 foo() # c2 bar() # c3 baz() # c4 quux1() #c6 # c5 quux2() buildtools-10.1.0/build/testdata/053.build.golden0000664000175000017500000000127215254573013021055 0ustar kenhyskenhys# Function definitions and calls def foo(a, b, c, *args, **kwargs): return bar(a, b, c, *args, **kwargs) def foo(*args, **kwargs): return bar(*args, **kwargs) def foo(*args): return bar(*args) def foo(**kwargs): return bar(**kwargs) def foo(a, *, b, **kwargs): return bar(a, b, **kwargs) def foo(a: str, b: int | float, *, c: Callable[ ..., dict[str, int] ], *args: int, **kwargs: bool) -> struct[ { "a": str, "b": int | float, }, ... ]: return bar(a, b, c, *args, **kwargs) def type_annot(): a: str = "test" b: int = 42 return a + str(b) bar(a, b, c, *args, **kwargs) bar(*args, **kwargs) bar(*args) bar(**kwargs) buildtools-10.1.0/build/testdata/001.build.golden0000664000175000017500000000036215254573013021045 0ustar kenhyskenhyscc_test( name = "bar", size = "small", srcs = [ "a.cc", "b.cc", "c.cc", ], data = ["datum"], datum = ["data"], deps = [ ":foo", "//base", "//util:map-util", ], ) buildtools-10.1.0/build/testdata/073.golden0000664000175000017500000000010515254573013017753 0ustar kenhyskenhys# disable=out-of-order-load load(":foo", "foo") load(":bar", "bar") buildtools-10.1.0/build/testdata/016.bzl.golden0000664000175000017500000000063015254573013020541 0ustar kenhyskenhys# 1-element tuple with trailing comma must preserve it. x = (1,) y = ( 1, ) foo((1,)) bar(1) baz([1]) foo(( 1, )) bar( 1, ) baz([ 1, ]) x = (1, 2, -3) y = ( 1, 2, -3, ) foo((1, 2)) bar(1, 2) baz([1, 2]) foo(( 1, 2, )) foo((1, 2, [3])) bar( 1, 2, ) baz([ 1, 2, ]) # Implicit tuples True, False = False, True (a, b), c = d c = d, (e, f) buildtools-10.1.0/build/testdata/063.build.golden0000664000175000017500000000045615254573013021061 0ustar kenhyskenhysfoo( 1, c + d, a = b, *f, **e ) foo( a, b = c, ) foo(a, *d) foo(a, **e) foo( b = c, *d ) foo( b = c, **e ) foo(*d, **e) foo( a, a2, b = c, b2 = c2, *d, **e ) foo( baz * 2, bar = bar( z, x = y, ), ) buildtools-10.1.0/build/testdata/062.in0000664000175000017500000000071715254573013017120 0ustar kenhyskenhysfoo( srcs = ["not", "sorted", "list"], # this should be sorted in the build mode only # keep sorted foo = ["not", "sorted", "list"], bar = [f(x) for x in [ # please keep sorted "not", "sorted", "list"] ], ) # buildifier: keep sorted x = ["not", "sorted", "list"] y = ["not", "sorted", "list"] # buildifier: keep sorted ["not", "sorted", "list"] ["not", "sorted", "list"] [ # buildifier: keep sorted "not", "sorted", "list" ] buildtools-10.1.0/build/testdata/002.build.golden0000664000175000017500000000031315254573013021042 0ustar kenhyskenhyscc_test( name = 'b\"ar\'"', size = "small", srcs = [ "a.cc", "b.cc", "c.cc", ], deps = [ ":foo", "//base", "//util:map-util", ], ) buildtools-10.1.0/build/testdata/050.stripslashes.golden0000664000175000017500000000013415254573013022473 0ustar kenhyskenhyscc_library( name = "foo", deps = [ ":testdata", "//:root", ], ) buildtools-10.1.0/build/testdata/041.in0000664000175000017500000000036615254573013017115 0ustar kenhyskenhysboq_initial_css_modules( action_paths_dict = { "some.path.OneAction": [ "//some/path", ], "some.path.AnotherAction": [ "//some/other/path", ], }, visible_node_name = "SomeUi", ) buildtools-10.1.0/build/testdata/071.golden0000664000175000017500000000010515254573013017751 0ustar kenhyskenhysfoo() # buildifier: disable=load-on-top load(":foo", "bar") bar() buildtools-10.1.0/build/testdata/062.build.golden0000664000175000017500000000123115254573013021050 0ustar kenhyskenhysfoo( srcs = [ "list", "not", "sorted", ], # this should be sorted in the build mode only bar = [ f(x) for x in [ # please keep sorted "list", "not", "sorted", ] ], # keep sorted foo = [ "list", "not", "sorted", ], ) # buildifier: keep sorted x = [ "list", "not", "sorted", ] y = [ "not", "sorted", "list", ] # buildifier: keep sorted [ "list", "not", "sorted", ] [ "not", "sorted", "list", ] [ # buildifier: keep sorted "list", "not", "sorted", ] buildtools-10.1.0/build/testdata/202.error0000664000175000017500000000002415254573013017626 0ustar kenhyskenhyswrong_float = 1.2.3 buildtools-10.1.0/build/testdata/005.in0000664000175000017500000000022015254573013017102 0ustar kenhyskenhysJAVA_FILES = [ # Comment regarding Foo.java "Foo.java", "Bar.java", "Baz.java", # Comment regarding Baz.java "Quux.java" ] buildtools-10.1.0/build/testdata/059.golden0000664000175000017500000000242615254573013017767 0ustar kenhyskenhysif foo: def f(): """This docstring should be moved 4 spaces to the right. @returns: nothing """ pass if bar: def g(): """This docstring should be moved 8 spaces to the left. This particular line has just 4 leading spaces, so it should be moved only 4 spaces to the left. @returns: nothing """ pass if foobar: def h(): """This docstring is already indented properly, therefore shouldn't be reindented, but the trailing spacees from this and the following lines should be removed. “Unicodeâ€� characters aren't escaped. """ if barfoo: def abc(): " Single-line docstrings aren't changed " pass def aaa(): """ Docstring for top-level def statements are also fixed. “Unicodeâ€� characters aren't escaped. Trailing backslashes \ are preserved """ var = """ This is not a docstring, should not be modified. """ def bbb(): """ Docstring for top-level def statements are also fixed. “Unicodeâ€� characters aren't escaped. Trailing backslashes \ are preserved """ var = """ This is not a docstring, should not be modified. """ buildtools-10.1.0/build/testdata/046.in0000664000175000017500000000073315254573013017120 0ustar kenhyskenhys[ # These lines should all appear # inside the block cc_binary( name = "foo_%s" % lang, ) for lang in li # list comprehension for li in foo # nested list comprehension if lang in langs # condition if lang # another condition ] # List comprehension ends [ # These lines should all appear # inside the block cc_binary( # Here's a comment # about the target name = "foo_%s" % lang, ) # My binary ] # List ends buildtools-10.1.0/build/testdata/015.in0000664000175000017500000000031415254573013017107 0ustar kenhyskenhys# shouldn't add trailing spaces to the blank line after the first comment. cc_binary( deps = [ # comment before blank line # comment after blank line "y", "a", ]) buildtools-10.1.0/build/testdata/048.bzl.golden0000664000175000017500000000054515254573013020553 0ustar kenhyskenhyshas_sets(name = "empty", labels = {}) has_sets(name = "one", labels = {"foo"}) has_sets(name = "one_trailing", labels = {"bar"}) has_sets(name = "two", labels = {"foo", "bar"}) has_sets(name = "two_trailing", labels = {"foo", "bar"}) has_sets(name = "three", labels = {"foo", "bar", "baz"}) has_sets(name = "three_trailing", labels = {"foo", "bar", "baz"}) buildtools-10.1.0/build/testdata/003.golden0000664000175000017500000000053415254573013017752 0ustar kenhyskenhysnumbers = [ 0, 11, 123.456, 123., 0.456, 1.23e45, -1, +1, 0.0, -0.0, 1.0, -1.0, +1.0, 1e6, -1e6, -1.23e-45, 3.539537889086625e24, 3.539537889086625e24, 3.539537889086625e24000, 3.539537889086625e24000, 3539537889086624823140625, 0x123, 0xE45, 0xe45, ] buildtools-10.1.0/build/testdata/022.in0000664000175000017500000000110415254573013017103 0ustar kenhyskenhyscc_library(name = "abc", srcs = [ # Animals or animal sounds. "yellowjacket.c", "buzz.c", "caribou.c", foo(), # Ocean phenomena. "yellowjacket.c", "wave.c", "emerald_sea.c", "caribou.c", # End of ocean phenomena. # Other stuff. ], ) cc_library( name = "x", # do not sort srcs = [ "x.cc", "y.cc", "x.cc", ], data = [ # do not sort ":x", ":y", ":x", ], # buildifier: leave-alone deps = [ ":x", ":y", ":x", ] )buildtools-10.1.0/build/testdata/041.build.golden0000664000175000017500000000036615254573013021055 0ustar kenhyskenhysboq_initial_css_modules( visible_node_name = "SomeUi", action_paths_dict = { "some.path.OneAction": [ "//some/path", ], "some.path.AnotherAction": [ "//some/other/path", ], }, ) buildtools-10.1.0/build/testdata/032.in0000664000175000017500000000047315254573013017114 0ustar kenhyskenhysbigrule( timeout = 1, testonly = 1, srcs = 1, srcdir = 1, src = 1, size = 1, quux = 1, package_name = 1, outs = 1, out = 1, of = 1, name = 1, include = 1, implements = 1, implementation = 1, hdrs = 1, has_services = 1, gwt_name = 1, foo = 1, destdir = 1, deps = 1, baz = 1, baseline = 1, bar = 1, alwayslink = 1, ) buildtools-10.1.0/build/testdata/057.golden0000664000175000017500000000047315254573013017765 0ustar kenhyskenhysdef foo1(x, y, **kwargs): bar1( x, y, **kwargs ) def foo2(x, y, *args): bar2( x, y, *args ) def foo3(x, y, **kwargs): bar2( x, y, **kwargs ) def foo4(x, y, *args): bar4( x, y, *args ) buildtools-10.1.0/build/testdata/044.build.golden0000664000175000017500000000277215254573013021063 0ustar kenhyskenhys# The comprehensions here shouldn't get collapsed to single lines. GLOB_resources_legacy_txt = glob(["resources/legacy/*.txt"]) GLOB_resources_legacy_txt2 = [ filename_and_some_extra_to_make_this_long for filename_and_some_extra_to_make_this_long in GLOB_resources_legacy_txt ] legacy_txt_name_to_filename = { filename.split("/")[-1][:-len(".txt")].lower(): filename for filename in GLOB_resources_legacy_txt_tuple } flags = [ flag for flag in [ _flag( name, type(default), values.pop(name, default), ) for name, default in defaults.items() ] if flag != None ] flags = [ flag # Example comment. for flag in [ _flag( name, type(default), values.pop(name, default), ) for name, default in defaults.items() ] # Clarififcation. if flag != None ] # Skip empty elements. flags = [ # Example comment. flag # Clarififcation. for flag in [ _flag( name, type(default), values.pop(name, default), ) for name, default in defaults.items() ] # Skip empty elements. if flag != None ] flags = [ # Example comment. flag # Clarififcation. for flag in [ _flag( name, type(default), values.pop(name, default), ) for name, default in defaults.items() ] # Skip empty elements. if flag != None ] buildtools-10.1.0/build/testdata/042.in0000664000175000017500000000033415254573013017111 0ustar kenhyskenhys filegroup( name = "foo" + cpu, srcs = [ prefix + "/bin/" + triple + "-objcopy" ] + ([ prefix + "/bin/ccperf-collect", "what"] if triple != 'sparc' else []) + glob([ "moar_thing/*"]) ) buildtools-10.1.0/build/testdata/078.in0000664000175000017500000000037715254573013017131 0ustar kenhyskenhysdef f(): pass # comment after a compact def def g(): if True: pass # comment after a nested compact if def h(): for x in [1]: pass # comment after a nested compact for def i(): pass # comment between a compact def and a statement x = 1 buildtools-10.1.0/build/testdata/049.bzl.golden0000664000175000017500000000022115254573013020543 0ustar kenhyskenhyscc_library( name = "foo", deps = [ "yet/another:dep", "//some/other/dep", ":bar", "//some:dep", ], ) buildtools-10.1.0/build/testdata/032.build.golden0000664000175000017500000000063715254573013021056 0ustar kenhyskenhysbigrule( name = 1, gwt_name = 1, package_name = 1, size = 1, timeout = 1, testonly = 1, src = 1, srcdir = 1, srcs = 1, out = 1, outs = 1, hdrs = 1, has_services = 1, include = 1, of = 1, baseline = 1, bar = 1, baz = 1, foo = 1, quux = 1, destdir = 1, deps = 1, implementation = 1, implements = 1, alwayslink = 1, ) buildtools-10.1.0/build/testdata/039.bzl.golden0000664000175000017500000000016715254573013020553 0ustar kenhyskenhyscc_library( name = "foo", dict_comprehension = {i: i * i for i in range(10)}, dict = {1: 1, 2: 4, 3: 9}, ) buildtools-10.1.0/build/testdata/035.bzl.golden0000664000175000017500000000047415254573013020550 0ustar kenhyskenhyscc_library( name = "abc", srcs = [ "spam.cc", "spam.cc", "eggs.cc", ], linkopts = [ "-l", "pthread", "-l", "apparmor", ], deps = [ "spam", "egg", "spam", "spam", "bacon", "spam", ], ) buildtools-10.1.0/build/testdata/043.golden0000664000175000017500000000036015254573013017753 0ustar kenhyskenhysbar = "bar" # This gets an empty expr_opt in the parser, causing a nil to appear. b = bar[:-2] # Test that slices and partial slices are parsed properly f = foo[-1:-2:-3] f = foo[1::] f = foo[:1:] f = foo[::1] f = foo[::] f = foo[:] buildtools-10.1.0/build/testdata/049.build.golden0000664000175000017500000000022115254573013021053 0ustar kenhyskenhyscc_library( name = "foo", deps = [ "yet/another:dep", ":bar", "//some:dep", "//some/other/dep", ], ) buildtools-10.1.0/build/testdata/063.bzl.golden0000664000175000017500000000026515254573013020547 0ustar kenhyskenhysfoo(1, c + d, a = b, *f, **e) foo(a, b = c) foo(a, *d) foo(a, **e) foo(b = c, *d) foo(b = c, **e) foo(*d, **e) foo(a, a2, b = c, b2 = c2, *d, **e) foo(baz * 2, bar = bar(z, x = y)) buildtools-10.1.0/build/testdata/049.stripslashes.golden0000664000175000017500000000021515254573013022503 0ustar kenhyskenhyscc_library( name = "foo", deps = [ ":bar", "some:dep", "some/other/dep", "yet/another:dep", ], ) buildtools-10.1.0/build/testdata/065.build.golden0000664000175000017500000000016315254573013021056 0ustar kenhyskenhysx = 123 y = 0o123 z = 0 t = 0x123, 0X456, 0o123, 0O456 foo(bar = [ 0, 0o0, 0o1, 0o2, 3, ]) buildtools-10.1.0/build/testdata/061.golden0000664000175000017500000000071315254573013017755 0ustar kenhyskenhysobject.string.replace("foo", "bar").replace("baz", "qux") object.string \ .replace("foo", "bar") \ .replace("baz", "qux") (object.string .replace("foo", "bar") .replace("baz", "qux")) (object.string # 1 .replace("foo", "bar") # 2 .replace("baz", "qux")) # 3 [ (object.string .replace("foo", "bar") .replace("baz", "qux")), (object.string .replace("foo", "bar") .replace("baz", "qux")), ] buildtools-10.1.0/build/testdata/019.in0000664000175000017500000001444515254573013017125 0ustar kenhyskenhyscc_library(name = "abc", srcs = [ "abc.cc" ], deps = [ "Wzzz", "//aEzzz", "6zzz", "[zzz", "//aSzzz", "//a?zzz", "//aLzzz", "//a%zzz", "//a|zzz", "Zzzz", "^zzz", "Kzzz", " zzz", "//aTzzz", "//aFzzz", "ezzz", "//a:a", ",zzz", "mzzz", "Xzzz", "lzzz", "//a^zzz", "fzzz", "//a/b/c:d", "$zzz", "//a0zzz", "foo", "jzzz", "4zzz", "}zzz", "Bzzz", "//aazzz", "//a5zzz", ":a", "\\zzz", "2zzz", "@abc//foo:foo", "//ahzzz", "yzzz", "//adzzz", "@abc//:xyz", "//a{zzz", "vzzz", ">zzz", "//aZzzz", "@zzz", "@abc//:abc", "//azzz", "Vzzz", "//a]zzz", "//a-zzz", "//a/b:c", "Yzzz", "//aAzzz", "&zzz", "//a>zzz", "bzzz", "//a", "Uzzz", "zzzu", "//a!zzz", "zzz*", "zzz[", "zzzy", "//aezzz", "zzzp", "//a&zzz", "xzzz", "zzz^", "//a1zzz", "zzz_", "zzzs", "zzzc", "zzzK", "//a/zzz", "zzzz", "zzz;", "zzzn", "zzzQ", "zzz\\", "123", "3zzz", "//a/b/c:c", "zzz7", "//avzzz", "Ezzz", "zzzT", "9zzz", "zzzg", "//a4zzz", "/zzz", "//a6zzz", "zzz9", "czzz", "//a2:b", "{zzz", "//aJzzz", "zzz2", "zzz%", "zzzx", "dzzz", "zzz)", "//a)zzz", "zzz.", "//aOzzz", "zzzb", "Hzzz", "=zzz", "zzz}", "zzzh", ";zzz", "zzz:", "//a*zzz", "5zzz", "zzzY", "zzz1", "-zzz", "//a}zzz", "zzz/", "{asdf}", "zzzG", "zzzk", "//arzzz", "zzzj", "zzzl", "zzz|", "(zzz", "zzz5", "zzz8", "zzzX", "zzzP", "zzz0", "zzzU", "zzz$", "zzzN", "zzz!", "FOO", "//a:b", "zzz-", "//aizzz", "zzzV", "zzzz", "zzz`", "zzzm", "zzz?", "tzzz", "//a$zzz", "zzz,", "zzzC", "zzz'", "zzzr", "zzzw", "zzzM", "+zzz", "zzzL", "zzzq", "'zzz", "Pzzz", "zzzJ", "zzz=", "zzzW", "Nzzz", "zzzR", "zzzZ", "zzz4", "zzzB", "zzzA", "zzz#", "struct[{"a": str, "b":int|float}, ...]: return bar(a,b,c,* args,** kwargs) def type_annot(): a: str = "test" b: int = 42 return a + str(b) bar(a,b,c,* args,** kwargs) bar(* args,** kwargs) bar(* args) bar(** kwargs) buildtools-10.1.0/build/testdata/006.bzl.golden0000664000175000017500000000113615254573013020542 0ustar kenhyskenhysset = {1, 2, 3} multiline_set = { 4, 5, [6], } plus = lambda x, y: x + y two = (lambda x: x(x))(lambda z: lambda y: z)(1)(2)(3) make_one = lambda: 1 l = lambda x, y, *args, **kwargs: f(y, key = x, *args, **kwargs) TriggerActionAfterProbe( probe = ("http", "response"), predicate = lambda State, response: all([ response["request"]["method"] == "GET", response["request"]["url"].find("/posts/") != -1, response["request"]["url"].endswith("/comments"), response["status_code"] in range(200, 299), ]), action = lambda State, response: None, ) buildtools-10.1.0/build/testdata/020.in0000664000175000017500000000024115254573013017102 0ustar kenhyskenhyscc_library( malloc = "//abc" + ":def", srcs = [ "//hjk/as" + "df:asdf", ], cmd = "abc " + "//def" + " ghi", ) buildtools-10.1.0/build/testdata/072.in0000664000175000017500000000035115254573013017113 0ustar kenhyskenhysload(":a.bzl", "one") # disable=same-origin-load load(":a.bzl", "two") load(":b.bzl", "one") load(":b.bzl", "two") # buildifier: disable=same-origin-load load(":c.bzl", "one", "x", y = "three") load(":c.bzl", "x", "two", y = "four") buildtools-10.1.0/build/testdata/053.bzl.golden0000664000175000017500000000121415254573013020541 0ustar kenhyskenhys# Function definitions and calls def foo(a, b, c, *args, **kwargs): return bar(a, b, c, *args, **kwargs) def foo(*args, **kwargs): return bar(*args, **kwargs) def foo(*args): return bar(*args) def foo(**kwargs): return bar(**kwargs) def foo(a, *, b, **kwargs): return bar(a, b, **kwargs) def foo(a: str, b: int | float, *, c: Callable[..., dict[str, int]], *args: int, **kwargs: bool) -> struct[{"a": str, "b": int | float}, ...]: return bar(a, b, c, *args, **kwargs) def type_annot(): a: str = "test" b: int = 42 return a + str(b) bar(a, b, c, *args, **kwargs) bar(*args, **kwargs) bar(*args) bar(**kwargs) buildtools-10.1.0/build/testdata/080.bzl.golden0000664000175000017500000000037615254573013020551 0ustar kenhyskenhys# Test that `type` soft keyword can be used as ordinary identifier in valid contexts. def _type_test_cases(): a = type b = type.foo() c = foo.type[0] d = type(1, 2, 3) e = type(type = 1) def type(type = ""): return type buildtools-10.1.0/build/testdata/050.in0000664000175000017500000000023615254573013017111 0ustar kenhyskenhyscc_library( name = "foo", deps = [ ":testdata", "testdata", "//testdata", "//testdata:testdata", "//:root", ], ) buildtools-10.1.0/build/testdata/080.in0000664000175000017500000000037615254573013017121 0ustar kenhyskenhys# Test that `type` soft keyword can be used as ordinary identifier in valid contexts. def _type_test_cases(): a = type b = type.foo() c = foo.type[0] d = type(1, 2, 3) e = type(type = 1) def type(type = ""): return type buildtools-10.1.0/build/testdata/009.golden0000664000175000017500000000206515254573013017761 0ustar kenhyskenhys# buildifier: leave-alone L1 = [ "C", "D", "A", "Z", "B", ] L2 = [ "C", "D", "A", "Z", "B", ] # All of the GSS, in the order they should be compiled in. # buildifier: leave-alone filegroup( name = "all_gss", srcs = [ "iphone_sprites.gss", ":iphone_imagedata_gss", ":multi_resolution_imagedata_gss", ":iphone_sprite_images_gss", "iphone/00_topstyles.gss", "iphone/avatar.gss", "iphone/chrome.gss", "iphone/conversation.gss", "iphone/error.gss", "iphone/installer.gss", "iphone/labels.gss", "iphone/menu.gss", "iphone/messageview.gss", "iphone/search.gss", "iphone/threadlist.gss", "iphone/toolbar.gss", "iphone/zz_bottomstyles.gss", "iphone/overlay.gss", "iphone/moremenu.gss", "iphone/settings.gss", "iphone/netstat.gss", "iphone/mouse.gss", "iphone/sprite_overrides.gss", "iphone/iconmenu.gss", "iphone/blpromo.gss", ], ) buildtools-10.1.0/build/testdata/019.bzl.golden0000664000175000017500000001217115254573013020547 0ustar kenhyskenhyscc_library( name = "abc", srcs = ["abc.cc"], deps = [ "Wzzz", "//aEzzz", "6zzz", "[zzz", "//aSzzz", "//a?zzz", "//aLzzz", "//a%zzz", "//a|zzz", "Zzzz", "^zzz", "Kzzz", " zzz", "//aTzzz", "//aFzzz", "ezzz", "//a:a", ",zzz", "mzzz", "Xzzz", "lzzz", "//a^zzz", "fzzz", "//a/b/c:d", "$zzz", "//a0zzz", "foo", "jzzz", "4zzz", "}zzz", "Bzzz", "//aazzz", "//a5zzz", ":a", "\\zzz", "2zzz", "@abc//foo:foo", "//ahzzz", "yzzz", "//adzzz", "@abc//:xyz", "//a{zzz", "vzzz", ">zzz", "//aZzzz", "@zzz", "@abc//:abc", "//azzz", "Vzzz", "//a]zzz", "//a-zzz", "//a/b:c", "Yzzz", "//aAzzz", "&zzz", "//a>zzz", "bzzz", "//a", "Uzzz", "zzzu", "//a!zzz", "zzz*", "zzz[", "zzzy", "//aezzz", "zzzp", "//a&zzz", "xzzz", "zzz^", "//a1zzz", "zzz_", "zzzs", "zzzc", "zzzK", "//a/zzz", "zzzz", "zzz;", "zzzn", "zzzQ", "zzz\\", "123", "3zzz", "//a/b/c:c", "zzz7", "//avzzz", "Ezzz", "zzzT", "9zzz", "zzzg", "//a4zzz", "/zzz", "//a6zzz", "zzz9", "czzz", "//a2:b", "{zzz", "//aJzzz", "zzz2", "zzz%", "zzzx", "dzzz", "zzz)", "//a)zzz", "zzz.", "//aOzzz", "zzzb", "Hzzz", "=zzz", "zzz}", "zzzh", ";zzz", "zzz:", "//a*zzz", "5zzz", "zzzY", "zzz1", "-zzz", "//a}zzz", "zzz/", "{asdf}", "zzzG", "zzzk", "//arzzz", "zzzj", "zzzl", "zzz|", "(zzz", "zzz5", "zzz8", "zzzX", "zzzP", "zzz0", "zzzU", "zzz$", "zzzN", "zzz!", "FOO", "//a:b", "zzz-", "//aizzz", "zzzV", "zzzz", "zzz`", "zzzm", "zzz?", "tzzz", "//a$zzz", "zzz,", "zzzC", "zzz'", "zzzr", "zzzw", "zzzM", "+zzz", "zzzL", "zzzq", "'zzz", "Pzzz", "zzzJ", "zzz=", "zzzW", "Nzzz", "zzzR", "zzzZ", "zzz4", "zzzB", "zzzA", "zzz#", ">baz>>qux foo=~bar|~baz^-qux<>baa # If-else operator precedence - f(1) if not a else 2+3 # Logic operators not foobaz<=foo>=bar!=baz in foo==bar or baz x = "abc" in copts or "def" in copts x2 = "abc" not in copts and "def" not in copts y = "abc" in (copts or []) == False a = 1 + 2 if 3 + 4 else 5 + 6 # Augmented assignments foo+=bar foo-=bar foo*=bar foo/=bar foo//=bar foo%=bar foo|=bar foo&=bar foo^=bar foo<<=bar foo>>=bar buildtools-10.1.0/build/testdata/070.in0000664000175000017500000000043615254573013017115 0ustar kenhyskenhysfoo( name='foo', srcs=['bbb', 'aaa'] + CONSTANT + ['//ddd:ddd', '//ccc:ccc'], deps=select({ 'bar': ['//ggg:ggg', '//eee:eee'], }) + ['hhh', 'fff'], data=['jjj', 'iii']+bar(['not', 'sortable', 'arguments'])+non_select({ 'baz': ['not', 'sortable', 'arguments'], }), ) buildtools-10.1.0/build/testdata/067.bzl.golden0000664000175000017500000000065515254573013020556 0ustar kenhyskenhysload(":a", "b") load( # A ":foo.bzl", # B "bar", ) cc_binary( # A name = "bin", # B srcs = ["bin.cc"], # C ) cc_binary(name = "wibble", srcs = ["wibble.cc"]) my_list = [ 1, # A 2, # B ] my_1tuple = ( # A 1, # B ) my_2tuple = ( # A 1, # B 2, # C ) my_dict = { "a": 1, # A "b": 2, # B } func(a) func(b) func(c, d) func(e, f) buildtools-10.1.0/build/testdata/074.golden0000664000175000017500000000007115254573013017756 0ustar kenhyskenhysload("bar", "baz") load("foo", "x", "y", "z") package() buildtools-10.1.0/build/testdata/035.in0000664000175000017500000000036615254573013017120 0ustar kenhyskenhyscc_library(name = "abc", srcs = [ "spam.cc", "spam.cc", "eggs.cc", ], linkopts = [ "-l", "pthread", "-l", "apparmor" ], deps = [ "spam", "egg", "spam", "spam", "bacon", "spam", ], ) buildtools-10.1.0/build/testdata/007.bzl.golden0000664000175000017500000000027415254573013020545 0ustar kenhyskenhysexports_files([ "url_substring_denylist", "url_substring_denylist_short", ]) package(default_visibility = ["//foo:bar"]) package(default_visibility = ["//foo:bar", "//bar:baz"]) buildtools-10.1.0/build/testdata/077.default.golden0000664000175000017500000000023315254573013021404 0ustar kenhyskenhysvisibility([ "//foo", "//bar", ]) # Not modified due to leave-alone comment. # buildifier: leave-alone visibility([ "//foo", "//bar", ]) buildtools-10.1.0/build/testdata/004.golden0000664000175000017500000000012215254573013017744 0ustar kenhyskenhysJAVA_FILES = [ "Foo.java", "Bar.java", "Baz.java", "Quux.java", ] buildtools-10.1.0/build/testdata/031.bzl.golden0000664000175000017500000000044215254573013020537 0ustar kenhyskenhysjava_test( name = "AllTestsCoverage", test_class = INSTRUMENTED_TEST_CLASS, deps = [":instrumented_js_runner"], data = TEST_SOURCES, tags = [ "local", # Cannot be run remotely. "manual", # Don't run coverage automatically. ], size = "large", ) buildtools-10.1.0/build/testdata/006.in0000664000175000017500000000112015254573013017103 0ustar kenhyskenhysset = {1, 2, 3} multiline_set = { 4, 5, [6] } plus = lambda x, y: x + y two = (lambda x:x(x)) (lambda z:lambda y:z)( 1 ) ( 2 ) ( 3 ) make_one = lambda: 1 l = lambda x,y,*args,**kwargs:f(y,key=x,*args,**kwargs) TriggerActionAfterProbe( probe = ("http", "response"), predicate = lambda State, response: all([ response["request"]["method"] == "GET", response["request"]["url"].find("/posts/") != -1, response["request"]["url"].endswith("/comments"), response["status_code"] in range(200, 299), ]), action = lambda State, response: None, ) buildtools-10.1.0/build/testdata/061.in0000664000175000017500000000063515254573013017116 0ustar kenhyskenhysobject.string.replace("foo", "bar").replace("baz", "qux") object.string.\ replace("foo", "bar")\ .replace("baz", "qux") (object.string. \ replace("foo", "bar") \ .replace("baz", "qux")) (object.string. # 1 replace("foo", "bar") # 2 .replace("baz", "qux")) # 3 [(object.string. replace("foo", "bar") .replace("baz", "qux")), (object.string. replace("foo", "bar") .replace("baz", "qux"))] buildtools-10.1.0/build/testdata/027.in0000664000175000017500000000021415254573013017111 0ustar kenhyskenhyspy_binary(name = "crawlcoverage", srcs = []) # gratuitous1 py_binary(name = "crawlcoverage2", srcs = []) # gratuitous2 buildtools-10.1.0/build/testdata/063.in0000664000175000017500000000026415254573013017116 0ustar kenhyskenhysfoo(1, a = b, c + d, **e, *f) foo(b = c, a) foo(*d, a) foo(**e, a) foo(*d, b = c) foo(**e, b = c) foo(**e, *d) foo(**e, *d, b = c, b2 = c2, a, a2) foo(bar = bar(x = y, z), baz * 2)buildtools-10.1.0/build/testdata/050.bzl.golden0000664000175000017500000000024515254573013020541 0ustar kenhyskenhyscc_library( name = "foo", deps = [ ":testdata", "testdata", "//testdata", "//testdata:testdata", "//:root", ], ) buildtools-10.1.0/build/testdata/032.bzl.golden0000664000175000017500000000063715254573013020546 0ustar kenhyskenhysbigrule( timeout = 1, testonly = 1, srcs = 1, srcdir = 1, src = 1, size = 1, quux = 1, package_name = 1, outs = 1, out = 1, of = 1, name = 1, include = 1, implements = 1, implementation = 1, hdrs = 1, has_services = 1, gwt_name = 1, foo = 1, destdir = 1, deps = 1, baz = 1, baseline = 1, bar = 1, alwayslink = 1, ) buildtools-10.1.0/build/testdata/045.golden0000664000175000017500000000025715254573013017762 0ustar kenhyskenhys[ cc_library( name = "Prefix" + suffix, deps = [dep] if dep else [], ) for suffix, dep in [ ("s1", None), ("s1", "//dep"), ] ] buildtools-10.1.0/build/testdata/024.build.golden0000664000175000017500000000134615254573013021055 0ustar kenhyskenhys# Test that single-line calls with no named arguments use the compact form. # Single-line tuples are also okay. str.rewrite(".", "/") str.rewrite( after = "/", before = ".", ) x = (1, 2, 3) x = (1, "2", -3, [], {}, (), not b) y = ( 1, [2], ) y = ( 1, {2: 3}, ) y = ( 1, (2, 3), ) y = ( 1, -(a + b), ) y = ( 1, not (a or b), ) x = ( 1, 2, 3, ) x = ( 1, 2, 3, 4, ) d.get(key, []) myfunc( 1, 2, foo( x, key = y, ), ) myfunc( 1, 2, foo( x, key = y, ), ) myfunc( 1, 2, foo( x, key = y, ), ) myfunc( 1, 2, foo( x, key = y, ), ) buildtools-10.1.0/build/testdata/022.build.golden0000664000175000017500000000106015254573013021044 0ustar kenhyskenhyscc_library( name = "abc", srcs = [ # Animals or animal sounds. "yellowjacket.c", "buzz.c", "caribou.c", foo(), # Ocean phenomena. "wave.c", "emerald_sea.c", # End of ocean phenomena. # Other stuff. ], ) cc_library( name = "x", # do not sort srcs = [ "x.cc", "y.cc", ], data = [ # do not sort ":x", ":y", ], # buildifier: leave-alone deps = [ ":x", ":y", ":x", ], ) buildtools-10.1.0/build/testdata/043.in0000664000175000017500000000036715254573013017120 0ustar kenhyskenhysbar = "bar" # This gets an empty expr_opt in the parser, causing a nil to appear. b = bar[:-2] # Test that slices and partial slices are parsed properly f = foo[-1:-2:-3] f = foo[1 : :] f = foo[: 1 :] f = foo[: : 1] f = foo[: :] f = foo[ : ]buildtools-10.1.0/build/testdata/001.bzl.golden0000664000175000017500000000023115254573013020530 0ustar kenhyskenhyscc_test(name = "bar", size = "small", srcs = ["b.cc", "a.cc", "c.cc"], deps = ["//base", ":foo", "//util:map-util"], data = ["datum"], datum = ["data"]) buildtools-10.1.0/build/testdata/077.bzl.golden0000664000175000017500000000023315254573013020547 0ustar kenhyskenhysvisibility([ "//bar", "//foo", ]) # Not modified due to leave-alone comment. # buildifier: leave-alone visibility([ "//foo", "//bar", ]) buildtools-10.1.0/build/testdata/201.error0000664000175000017500000000004415254573013017627 0ustar kenhyskenhys[ "aaa", "bbb" "ccc" ] buildtools-10.1.0/build/testdata/065.bzl.golden0000664000175000017500000000013015254573013020540 0ustar kenhyskenhysx = 123 y = 0o123 z = 0 t = 0x123, 0X456, 0o123, 0O456 foo(bar = [0, 0o0, 0o1, 0o2, 3]) buildtools-10.1.0/build/testdata/051.build.golden0000664000175000017500000000260315254573013021052 0ustar kenhyskenhysfoo( name = "foo", srcs = [bar], ) aaa bbb ccc ddd # multiline # comment eee fff ggg def function( # This shouldn't mess with indentation counting x, y = z): aaa bbb def g( a, s, d # this is d ): # this is function definition ccc # Misalingned comments # are not a syntax error ddd def h(a, s = d): # this is h eee def k(g): g += 1 return g for x in y: print(y) if x: print(x) elif y: print(y) else: print(z) print(y) print(x) if x: print(x) elif y: print(y) else: print(z) print(y) print(x) def i(): def j(): return h( [1], [2], ) # returns h return u() + w for b in a: # first for c, d in b: # second for ((e, f), g) in zip(c, d): # third pass if foo in bar: for f in foo: # loop f() elif True: a = b elif False: #assign b = a else: (a, b) = (b, a) if foo: bar if foo: bar #comment bar bar def a(name, foo = True, **kwargs): # comment here return buildtools-10.1.0/build/testdata/029.golden0000664000175000017500000000010315254573013017752 0ustar kenhyskenhys# c1 foo() # c2 bar() # c3 baz() # c4 quux1() #c6 # c5 quux2() buildtools-10.1.0/build/testdata/004.in0000664000175000017500000000013515254573013017106 0ustar kenhyskenhysJAVA_FILES = [ "Foo.java", "Bar.java", "Baz.java", "Quux.java" ] buildtools-10.1.0/build/testdata/001.in0000664000175000017500000000021715254573013017104 0ustar kenhyskenhyscc_test(name="bar",size="small",srcs=["b.cc","a.cc","c.cc"],deps=["//base",":foo","//util:map-util"], data = [ "datum" ], datum = [ "data", ]) buildtools-10.1.0/build/rule_test.go0000664000175000017500000001145515254573013017006 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 build import ( "testing" ) var simpleCall *CallExpr = &CallExpr{ X: &Ident{ Name: "java_library", }, List: []Expr{ &AssignExpr{ LHS: &Ident{ Name: "name", }, Op: "=", RHS: &StringExpr{ Value: "x", }, }, }, } var simpleRule *Rule = &Rule{simpleCall, ""} var structCall *CallExpr = &CallExpr{ X: &DotExpr{ X: &DotExpr{ X: &Ident{ Name: "foo", }, Name: "bar", }, Name: "baz", }, List: []Expr{ &AssignExpr{ LHS: &Ident{ Name: "name", }, Op: "=", RHS: &StringExpr{ Value: "x", }, }, }, } var structRule *Rule = &Rule{structCall, ""} func TestKind(t *testing.T) { if simpleRule.Kind() != "java_library" { t.Errorf(`simpleRule.Kind() = %v, want "java_library"`, simpleRule.Kind()) } if structRule.Kind() != "foo.bar.baz" { t.Errorf(`structRule.Kind() = %v, want "foo.bar.baz"`, structRule.Kind()) } } func TestSetKind(t *testing.T) { rule := &Rule{ &CallExpr{ X: &Ident{ Name: "java_library", }, List: []Expr{ &AssignExpr{ LHS: &Ident{ Name: "name", }, Op: "=", RHS: &StringExpr{ Value: "x", }, }, }, }, "", } rule.SetKind("java_binary") compare(t, rule.Call.X, &Ident{Name: "java_binary"}) rule.SetKind("foo.bar.baz") compare(t, rule.Call.X, &DotExpr{ X: &DotExpr{ X: &Ident{ Name: "foo", }, Name: "bar", }, Name: "baz", }) } func TestRules(t *testing.T) { f := &File{ Stmt: []Expr{ simpleCall, structCall, }, } compare(t, f.Rules(""), []*Rule{simpleRule, structRule}) compare(t, f.Rules("java_binary"), []*Rule(nil)) compare(t, f.Rules("java_library"), []*Rule{simpleRule}) compare(t, f.Rules("foo.bar.baz"), []*Rule{structRule}) } func TestRulesNested(t *testing.T) { f := &File{ Stmt: []Expr{ &ListExpr{ List: []Expr{ simpleCall, structCall, }, }, }, } compare(t, f.Rules(""), []*Rule{simpleRule, structRule}) compare(t, f.Rules("java_binary"), []*Rule(nil)) compare(t, f.Rules("java_library"), []*Rule{simpleRule}) compare(t, f.Rules("foo.bar.baz"), []*Rule{structRule}) } func TestRulesDoubleNested(t *testing.T) { var doubleNested *CallExpr = &CallExpr{ X: &Ident{ Name: "java_library", }, List: []Expr{ &AssignExpr{ LHS: &Ident{ Name: "name", }, Op: "=", RHS: &CallExpr{ X: &Ident{Name: "varref"}, List: []Expr{&StringExpr{Value: "x"}}, }, }, }, } var doubleNestedRule *Rule = &Rule{doubleNested, ""} f := &File{ Stmt: []Expr{ &ListExpr{ List: []Expr{ doubleNested, }, }, }, } compare(t, f.Rules(""), []*Rule{doubleNestedRule}) compare(t, f.Rules("java_library"), []*Rule{doubleNestedRule}) } func TestRuleNamed(t *testing.T) { tests := []struct { name string rules []Expr want *Rule description string }{ {"", []Expr{simpleCall}, nil, "Empty name matches no rules."}, {"x", []Expr{simpleCall}, simpleRule, "Find single rule by name."}, {"does_not_exist", []Expr{simpleCall}, nil, "Returns nil when no rules have specified name."}, {"foo.bar.baz", []Expr{simpleCall, structCall}, nil, "Returns nil when multiple rules have specified name."}, } for _, tst := range tests { f := &File{Stmt: tst.rules} compare(t, f.RuleNamed(tst.name), tst.want) } } func TestImplicitName(t *testing.T) { tests := []struct { path string input string want string description string }{ {"foo/BUILD", `rule()`, "foo", `Use an implicit name for one rule.`}, {"foo/BUILD", `rule(name="a") rule(name="b") rule()`, "foo", `Use an implicit name for the one unnamed rule`}, {"foo/BUILD", `rule() rule() rule()`, "", `No implicit name for multiple unnamed rules`}, {"foo/BUILD", `load(":foo.bzl", "bar") rule(name="a") rule(name="b")`, "", `No implicit name for load`}, {"foo/BUILD", `load(":foo.bzl", "bar") rule()`, "foo", `Use an implicit name for one unnamed rule with load`}, {"BUILD", `rule()`, "", `No implicit name for root package`}, } for _, tst := range tests { file, err := Parse(tst.path, []byte(tst.input)) if err != nil { t.Error(tst.description, err) continue } if got := file.implicitRuleName(); got != tst.want { t.Errorf("TestImplicitName(%s): got %s, want %s.", tst.description, got, tst.want) } } } buildtools-10.1.0/build/syntax.go0000664000175000017500000005723115254573013016330 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 build implements parsing and printing of BUILD files. package build // Syntax data structure definitions. import ( "fmt" "strings" "unicode/utf8" ) // A Position describes the position between two bytes of input. type Position struct { Line int // line in input (starting at 1) LineRune int // rune in line (starting at 1) Byte int // byte in input (starting at 0) } // add returns the position at the end of s, assuming it starts at p. func (p Position) add(s string) Position { p.Byte += len(s) if n := strings.Count(s, "\n"); n > 0 { p.Line += n s = s[strings.LastIndex(s, "\n")+1:] p.LineRune = 1 } p.LineRune += utf8.RuneCountInString(s) return p } // An Expr represents an input element. type Expr interface { // Span returns the start and end position of the expression, // excluding leading or trailing comments. Span() (start, end Position) // Comment returns the comments attached to the expression. // This method would normally be named 'Comments' but that // would interfere with embedding a type of the same name. Comment() *Comments // Copy returns a non-deep copy of the node. Can be useful if // the actual node type is hidden by the Expr interface and // not relevant. Copy() Expr } // A Comment represents a single # comment. type Comment struct { Start Position Token string // without trailing newline } // Span returns the start and end positions of the node func (c Comment) Span() (start, end Position) { return c.Start, c.Start.add(c.Token) } // Comments collects the comments associated with an expression. type Comments struct { Before []Comment // whole-line comments before this expression Suffix []Comment // end-of-line comments after this expression // For top-level expressions only, After lists whole-line // comments following the expression. After []Comment } // Comment returns the receiver. This isn't useful by itself, but // a Comments struct is embedded into all the expression // implementation types, and this gives each of those a Comment // method to satisfy the Expr interface. func (c *Comments) Comment() *Comments { return c } // stmtsEnd returns the end position of the last non-nil statement func stmtsEnd(stmts []Expr) Position { for i := len(stmts) - 1; i >= 0; i-- { if stmts[i] != nil { _, end := stmts[i].Span() return end } } return Position{} } // A File represents an entire BUILD or .bzl file. type File struct { Path string // absolute file path Pkg string // optional; the package of the file (always forward slashes) Label string // optional; file path relative to the package name (always forward slashes) WorkspaceRoot string // optional; path to the directory containing the WORKSPACE file Type FileType Comments Stmt []Expr } // DisplayPath returns the filename if it's not empty, "" otherwise func (f *File) DisplayPath() string { if f.Path == "" { return "" } return f.Path } // CanonicalPath returns the path of a file relative to the workspace root with forward slashes only func (f *File) CanonicalPath() string { if f.Pkg == "" { return "//" + f.Label } return fmt.Sprintf("//%s/%s", f.Pkg, f.Label) } // Span returns the start and end positions of the node func (f *File) Span() (start, end Position) { if len(f.Stmt) == 0 { p := Position{Line: 1, LineRune: 1} return p, p } start = Position{} end = stmtsEnd(f.Stmt) return start, end } // Copy creates and returns a non-deep copy of File func (f *File) Copy() Expr { n := *f return &n } // A CommentBlock represents a top-level block of comments separate // from any rule. type CommentBlock struct { Comments Start Position } // Span returns the start and end positions of the node func (x *CommentBlock) Span() (start, end Position) { return x.Start, x.Start } // Copy creates and returns a non-deep copy of CommentBlock func (x *CommentBlock) Copy() Expr { n := *x return &n } // An Ident represents an identifier. type Ident struct { Comments NamePos Position Name string } // Span returns the start and end positions of the node func (x *Ident) Span() (start, end Position) { return x.NamePos, x.NamePos.add(x.Name) } // Copy creates and returns a non-deep copy of Ident func (x *Ident) Copy() Expr { n := *x return &n } func (x *Ident) asString() *StringExpr { _, end := x.Span() return &StringExpr{ Comments: x.Comments, Start: x.NamePos, Value: x.Name, End: end, } } // An TypedIdent represents an identifier with type annotation: "foo: int". // // May occur as a statement ("var statement") or as the LHS of AssignExpr when used as a statement. type TypedIdent struct { Comments Ident Expr // must be *Ident Type Expr } // Span returns the start and end positions of the node func (x *TypedIdent) Span() (start, end Position) { start, _ = x.Ident.Span() _, end = x.Type.Span() return start, end } // Copy creates and returns a non-deep copy of TypedIdent func (x *TypedIdent) Copy() Expr { n := *x return &n } // GetIdent returns TypedIdent's variable func (x *TypedIdent) GetIdent() *Ident { ident, ok := x.Ident.(*Ident) if !ok { panic(fmt.Errorf("expected *build.Ident for variable of typed identifier, got %T", x.Ident)) } return ident } // A TypeAppExpr represents a type application: a type_name followed by a type_list: struct[{"name": str}, ...], typing.Sequence[int]. type TypeAppExpr struct { Comments Type Expr // *Ident or *DotExpr ArgsStart Position // position of [ Args []Expr End // position of ] ForceCompact bool // force compact (non-multiline) form when printing ForceMultiLine bool // force multiline form when printing } // Span returns the start and end positions of the node func (x *TypeAppExpr) Span() (start, end Position) { start, _ = x.Type.Span() return start, x.End.Pos.add("]") } // Copy creates and returns a non-deep copy of TypeAppExpr func (x *TypeAppExpr) Copy() Expr { n := *x return &n } // An EllipsisExpr represents the '...' token in a type_arg. type EllipsisExpr struct { Comments Pos Position } // Span returns the start and end positions of the node func (x *EllipsisExpr) Span() (start, end Position) { return x.Pos, x.Pos.add("...") } // Copy creates and returns a non-deep copy of EllipsisExpr func (x *EllipsisExpr) Copy() Expr { n := *x return &n } // BranchStmt represents a `pass`, `break`, or `continue` statement. type BranchStmt struct { Comments Token string // pass, break, continue TokenPos Position } // Span returns the start and end positions of the node func (x *BranchStmt) Span() (start, end Position) { return x.TokenPos, x.TokenPos.add(x.Token) } // Copy creates and returns a non-deep copy of BranchStmt func (x *BranchStmt) Copy() Expr { n := *x return &n } // A LiteralExpr represents a literal number. type LiteralExpr struct { Comments Start Position Token string // identifier token } // Span returns the start and end positions of the node func (x *LiteralExpr) Span() (start, end Position) { return x.Start, x.Start.add(x.Token) } // Copy creates and returns a non-deep copy of LiteralExpr func (x *LiteralExpr) Copy() Expr { n := *x return &n } // A StringExpr represents a single literal string. type StringExpr struct { Comments Start Position Value string // string value (decoded) TripleQuote bool // triple quote output End Position // To allow specific formatting of string literals, // at least within our requirements, record the // preferred form of Value. This field is a hint: // it is only used if it is a valid quoted form for Value. Token string } // Span returns the start and end positions of the node func (x *StringExpr) Span() (start, end Position) { return x.Start, x.End } // Copy creates and returns a non-deep copy of StringExpr func (x *StringExpr) Copy() Expr { n := *x return &n } // An End represents the end of a parenthesized or bracketed expression. // It is a place to hang comments. type End struct { Comments Pos Position } // Span returns the start and end positions of the node func (x *End) Span() (start, end Position) { return x.Pos, x.Pos.add(")") } // Copy creates and returns a non-deep copy of End func (x *End) Copy() Expr { n := *x return &n } // CallExprKind describes special kinds of call expression nodes. type CallExprKind int // List of CallExprKind values. const ( CallExprOrdinary CallExprKind = iota // ordinary call expression CallExprCast // cast(t, val), where the first arg (t) is a type expression CallExprIsInstance // isinstance(val, t), where the second arg (t) is a type expression ) // A CallExpr represents a function call expression: X(List). type CallExpr struct { Comments X Expr ListStart Position // position of ( List []Expr End // position of ) ForceCompact bool // force compact (non-multiline) form when printing ForceMultiLine bool // force multiline form when printing CallExprKind } // Span returns the start and end positions of the node func (x *CallExpr) Span() (start, end Position) { start, _ = x.X.Span() return start, x.End.Pos.add(")") } // Copy creates and returns a non-deep copy of CallExpr func (x *CallExpr) Copy() Expr { n := *x return &n } // A DotExpr represents a field selector: X.Name. type DotExpr struct { Comments X Expr Dot Position NamePos Position Name string } // Span returns the start and end positions of the node func (x *DotExpr) Span() (start, end Position) { start, _ = x.X.Span() return start, x.NamePos.add(x.Name) } // Copy creates and returns a non-deep copy of DotExpr func (x *DotExpr) Copy() Expr { n := *x return &n } // A Comprehension represents a list comprehension expression: [X for ... if ...]. type Comprehension struct { Comments Curly bool // curly braces (as opposed to square brackets) Lbrack Position Body Expr Clauses []Expr // = *ForClause | *IfClause ForceMultiLine bool // split expression across multiple lines End } // Span returns the start and end positions of the node func (x *Comprehension) Span() (start, end Position) { return x.Lbrack, x.End.Pos.add("]") } // Copy creates and returns a non-deep copy of Comprehension func (x *Comprehension) Copy() Expr { n := *x return &n } // A ForClause represents a for clause in a list comprehension: for Var in Expr. type ForClause struct { Comments For Position Vars Expr In Position X Expr } // Span returns the start and end positions of the node func (x *ForClause) Span() (start, end Position) { _, end = x.X.Span() return x.For, end } // Copy creates and returns a non-deep copy of ForClause func (x *ForClause) Copy() Expr { n := *x return &n } // An IfClause represents an if clause in a list comprehension: if Cond. type IfClause struct { Comments If Position Cond Expr } // Span returns the start and end positions of the node func (x *IfClause) Span() (start, end Position) { _, end = x.Cond.Span() return x.If, end } // Copy creates and returns a non-deep copy of IfClause func (x *IfClause) Copy() Expr { n := *x return &n } // A KeyValueExpr represents a dictionary entry: Key: Value. type KeyValueExpr struct { Comments Key Expr Colon Position Value Expr } // Span returns the start and end positions of the node func (x *KeyValueExpr) Span() (start, end Position) { start, _ = x.Key.Span() _, end = x.Value.Span() return start, end } // Copy creates and returns a non-deep copy of KeyValueExpr func (x *KeyValueExpr) Copy() Expr { n := *x return &n } // A DictExpr represents a dictionary literal: { List }. type DictExpr struct { Comments Start Position List []*KeyValueExpr End ForceMultiLine bool // force multiline form when printing } // Span returns the start and end positions of the node func (x *DictExpr) Span() (start, end Position) { return x.Start, x.End.Pos.add("}") } // Copy creates and returns a non-deep copy of DictExpr func (x *DictExpr) Copy() Expr { n := *x return &n } // A ListExpr represents a list literal: [ List ]. type ListExpr struct { Comments Start Position List []Expr End ForceMultiLine bool // force multiline form when printing } // Span returns the start and end positions of the node func (x *ListExpr) Span() (start, end Position) { return x.Start, x.End.Pos.add("]") } // Copy creates and returns a non-deep copy of ListExpr func (x *ListExpr) Copy() Expr { n := *x return &n } // A SetExpr represents a set literal: { List }. type SetExpr struct { Comments Start Position List []Expr End ForceMultiLine bool // force multiline form when printing } // Span returns the start and end positions of the node func (x *SetExpr) Span() (start, end Position) { return x.Start, x.End.Pos.add("}") } // Copy creates and returns a non-deep copy of SetExpr func (x *SetExpr) Copy() Expr { n := *x return &n } // A TupleExpr represents a tuple literal: (List) type TupleExpr struct { Comments NoBrackets bool // true if a tuple has no brackets, e.g. `a, b = x` Start Position List []Expr End ForceCompact bool // force compact (non-multiline) form when printing ForceMultiLine bool // force multiline form when printing } // Span returns the start and end positions of the node func (x *TupleExpr) Span() (start, end Position) { if !x.NoBrackets { return x.Start, x.End.Pos.add(")") } start, _ = x.List[0].Span() _, end = x.List[len(x.List)-1].Span() return start, end } // Copy creates and returns a non-deep copy of TupleExpr func (x *TupleExpr) Copy() Expr { n := *x return &n } // A UnaryExpr represents a unary expression: Op X. type UnaryExpr struct { Comments OpStart Position Op string X Expr } // Span returns the start and end positions of the node func (x *UnaryExpr) Span() (start, end Position) { if x.X == nil { return x.OpStart, x.OpStart } _, end = x.X.Span() return x.OpStart, end } // Copy creates and returns a non-deep copy of UnaryExpr func (x *UnaryExpr) Copy() Expr { n := *x return &n } // A BinaryExpr represents a binary expression: X Op Y. type BinaryExpr struct { Comments X Expr OpStart Position Op string LineBreak bool // insert line break between Op and Y Y Expr } // Span returns the start and end positions of the node func (x *BinaryExpr) Span() (start, end Position) { start, _ = x.X.Span() _, end = x.Y.Span() return start, end } // Copy creates and returns a non-deep copy of BinaryExpr func (x *BinaryExpr) Copy() Expr { n := *x return &n } // An AssignExpr represents a binary expression with `=`: LHS = RHS. type AssignExpr struct { Comments // LHS is the left hand side operation. For example, if the expression is // "default_visibility = [:foo]", the LHS is the Ident "default_visibility". LHS Expr // OpPos is the position of the Op. OpPos Position // Op is the operation of the expression. For example, if the expression // is "default_visibility = [:foo]", the Op is "="". Op string // LineBreak is whether there is a linebreak between Op and RHS. LineBreak bool // RHS is the right hand side operation. For example, if the expression is // "default_visibility = [":foo"]", the RHS is the ListExpr [":foo"]. RHS Expr } // Span returns the start and end positions of the node func (x *AssignExpr) Span() (start, end Position) { start, _ = x.LHS.Span() _, end = x.RHS.Span() return start, end } // Copy creates and returns a non-deep copy of AssignExpr func (x *AssignExpr) Copy() Expr { n := *x return &n } // LHSIdent returns the identifier of the LHS if it is a single identifier // (or a single typed identifier). Otherwise, returns nil, false. func (x *AssignExpr) LHSIdent() (*Ident, bool) { switch node := x.LHS.(type) { case *Ident: return node, true case *TypedIdent: return node.GetIdent(), true default: return nil, false } } // A ParenExpr represents a parenthesized expression: (X). type ParenExpr struct { Comments Start Position X Expr End ForceMultiLine bool // insert line break after opening ( and before closing ) } // Span returns the start and end positions of the node func (x *ParenExpr) Span() (start, end Position) { return x.Start, x.End.Pos.add(")") } // Copy creates and returns a non-deep copy of ParenExpr func (x *ParenExpr) Copy() Expr { n := *x return &n } // A SliceExpr represents a slice expression: expr[from:to] or expr[from:to:step] . type SliceExpr struct { Comments X Expr SliceStart Position From Expr FirstColon Position To Expr SecondColon Position Step Expr End Position } // Span returns the start and end positions of the node func (x *SliceExpr) Span() (start, end Position) { start, _ = x.X.Span() return start, x.End.add("]") } // Copy creates and returns a non-deep copy of SliceExpr func (x *SliceExpr) Copy() Expr { n := *x return &n } // An IndexExpr represents an index expression: X[Y]. type IndexExpr struct { Comments X Expr IndexStart Position Y Expr End Position } // Span returns the start and end positions of the node func (x *IndexExpr) Span() (start, end Position) { start, _ = x.X.Span() return start, x.End.add("]") } // Copy creates and returns a non-deep copy of IndexExpr func (x *IndexExpr) Copy() Expr { n := *x return &n } // A Function represents the common parts of LambdaExpr and DefStmt type Function struct { Comments StartPos Position // position of DEF or LAMBDA token Params []Expr Body []Expr } // Span returns the start and end positions of the node func (x *Function) Span() (start, end Position) { _, end = x.Body[len(x.Body)-1].Span() return x.StartPos, end } // Copy creates and returns a non-deep copy of Function func (x *Function) Copy() Expr { n := *x return &n } // A LambdaExpr represents a lambda expression: lambda Var: Expr. type LambdaExpr struct { Comments Function } // Span returns the start and end positions of the node func (x *LambdaExpr) Span() (start, end Position) { return x.Function.Span() } // Copy creates and returns a non-deep copy of LambdaExpr func (x *LambdaExpr) Copy() Expr { n := *x return &n } // ConditionalExpr represents the conditional: X if TEST else ELSE. type ConditionalExpr struct { Comments Then Expr IfStart Position Test Expr ElseStart Position Else Expr } // Span returns the start and end position of the expression, // excluding leading or trailing comments. // Span returns the start and end positions of the node func (x *ConditionalExpr) Span() (start, end Position) { start, _ = x.Then.Span() _, end = x.Else.Span() return start, end } // Copy creates and returns a non-deep copy of ConditionalExpr func (x *ConditionalExpr) Copy() Expr { n := *x return &n } // A LoadStmt loads another module and binds names from it: // load(Module, "x", y="foo"). // // The AST is slightly unfaithful to the concrete syntax here because // Skylark's load statement, so that it can be implemented in Python, // binds some names (like y above) with an identifier and some (like x) // without. For consistency we create fake identifiers for all the // strings. type LoadStmt struct { Comments Load Position Module *StringExpr From []*Ident // name defined in loading module To []*Ident // name in loaded module Rparen End ForceCompact bool // force compact (non-multiline) form when printing } // Span returns the start and end positions of the node func (x *LoadStmt) Span() (start, end Position) { return x.Load, x.Rparen.Pos.add(")") } // Copy creates and returns a non-deep copy of LoadStmt func (x *LoadStmt) Copy() Expr { n := *x return &n } // A TypeAliasStmt represents a type alias definition: "type Foo = int" or "type Foo[T, U] = dict[T, U]". type TypeAliasStmt struct { Comments TypePos Position // position of "type" soft keyword Name Expr // name of the type alias; must be an *Ident TypeParams Expr // optional type parameters [T, U]. If non-nil, must be a non-empty ListExpr of Ident elements. EqualPos Position // position of "=" Type Expr // definition/target type expression LineBreak bool // insert line break between '=' and Type } // Span returns the start and end positions of the node func (x *TypeAliasStmt) Span() (start, end Position) { start = x.TypePos _, end = x.Type.Span() return start, end } // Copy creates and returns a non-deep copy of TypeAliasStmt func (x *TypeAliasStmt) Copy() Expr { n := *x return &n } // GetIdent returns the variable defined by TypeAliasStmt func (x *TypeAliasStmt) GetIdent() *Ident { ident, ok := x.Name.(*Ident) if !ok { panic(fmt.Errorf("expected *build.Ident for variable of a type alias, got %T", x.Name)) } return ident } // A DefStmt represents a function definition expression: def foo(List):. type DefStmt struct { Comments Function Name string TypeParams Expr // optional type parameters [T, U]. If non-nil, must be a non-empty ListExpr of Ident elements. ParamsEnd Expr // an End representing the ")" that closes the params list ColonPos Expr // an End representing the ":" ForceCompact bool // force compact (non-multiline) form when printing the arguments ForceMultiLine bool // force multiline form when printing the arguments Type Expr // type annotation } // Span returns the start and end positions of the node func (x *DefStmt) Span() (start, end Position) { return x.Function.Span() } // Copy creates and returns a non-deep copy of DefStmt func (x *DefStmt) Copy() Expr { n := *x return &n } // HeaderSpan returns the span of the function header `def f(...):` func (x *DefStmt) HeaderSpan() (start, end Position) { start = x.Function.StartPos // Attempt to support printing a partially-defined DefStmt if x.ColonPos != nil { _, end = x.ColonPos.Span() } return } // A ReturnStmt represents a return statement: return f(x). type ReturnStmt struct { Comments Return Position Result Expr // may be nil } // Span returns the start and end positions of the node func (x *ReturnStmt) Span() (start, end Position) { if x.Result == nil { return x.Return, x.Return.add("return") } _, end = x.Result.Span() return x.Return, end } // Copy creates and returns a non-deep copy of ReturnStmt func (x *ReturnStmt) Copy() Expr { n := *x return &n } // A ForStmt represents a for loop block: for x in range(10):. type ForStmt struct { Comments Function For Position // position of for Vars Expr X Expr Body []Expr } // Span returns the start and end positions of the node func (x *ForStmt) Span() (start, end Position) { end = stmtsEnd(x.Body) return x.For, end } // Copy creates and returns a non-deep copy of ForStmt func (x *ForStmt) Copy() Expr { n := *x return &n } // An IfStmt represents an if-else block: if x: ... else: ... . // `elif`s are treated as a chain of `IfStmt`s. type IfStmt struct { Comments If Position // position of if Cond Expr True []Expr ElsePos End // position of else or elif False []Expr // optional } // Span returns the start and end positions of the node func (x *IfStmt) Span() (start, end Position) { body := x.False if body == nil { body = x.True } end = stmtsEnd(body) return x.If, end } // Copy creates and returns a non-deep copy of IfStmt func (x *IfStmt) Copy() Expr { n := *x return &n } buildtools-10.1.0/build/lex.go0000664000175000017500000006267115254573013015576 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // Lexical scanning for BUILD file parser. package build import ( "bytes" "fmt" "path/filepath" "sort" "strings" "unicode/utf8" ) // FileType represents a type of a file (default (for .bzl files), BUILD, or WORKSPACE). // Certain formatting or refactoring rules can be applied to several file types, so they support // bitwise operations: `type1 | type2` can represent a scope (e.g. BUILD and WORKSPACE files) and // `scope & fileType` can be used to check whether a file type belongs to a scope. type FileType int const ( // TypeDefault represents general Starlark files TypeDefault FileType = 1 << iota // TypeBuild represents BUILD files TypeBuild // TypeWorkspace represents WORKSPACE files TypeWorkspace // TypeBzl represents .bzl files TypeBzl // TypeModule represents MODULE.bazel and *.MODULE.bazel files TypeModule // TypeRepo represents REPO.bazel files TypeRepo // TypeVendor represents VENDOR.bazel files TypeVendor ) func (t FileType) String() string { switch t { case TypeDefault: return "default" case TypeBuild: return "BUILD" case TypeWorkspace: return "WORKSPACE" case TypeBzl: return ".bzl" case TypeModule: return "MODULE.bazel" case TypeRepo: return "REPO.bazel" case TypeVendor: return "VENDOR.bazel" } return "unknown" } // ParseBuild parses a file, marks it as a BUILD file and returns the corresponding parse tree. // // The filename is used only for generating error messages. func ParseBuild(filename string, data []byte) (*File, error) { in := newInput(filename, data) f, err := in.parse() if f != nil { f.Type = TypeBuild } return f, err } // ParseWorkspace parses a file, marks it as a WORKSPACE file and returns the corresponding parse tree. // // The filename is used only for generating error messages. func ParseWorkspace(filename string, data []byte) (*File, error) { in := newInput(filename, data) f, err := in.parse() if f != nil { f.Type = TypeWorkspace } return f, err } // ParseModule parses a file, marks it as a MODULE.bazel file and returns the corresponding parse tree. // // The filename is used only for generating error messages. func ParseModule(filename string, data []byte) (*File, error) { in := newInput(filename, data) f, err := in.parse() if f != nil { f.Type = TypeModule } return f, err } // ParseRepo parses a file, marks it as a REPO.bazel file and returns the corresponding parse tree. // // The filename is used only for generating error messages. func ParseRepo(filename string, data []byte) (*File, error) { in := newInput(filename, data) f, err := in.parse() if f != nil { f.Type = TypeRepo } return f, err } // ParseVendor parses a file, marks it as a VENDOR.bazel file and returns the corresponding parse tree. // // The filename is used only for generating error messages. func ParseVendor(filename string, data []byte) (*File, error) { in := newInput(filename, data) f, err := in.parse() if f != nil { f.Type = TypeVendor } return f, err } // ParseBzl parses a file, marks it as a .bzl file and returns the corresponding parse tree. // // The filename is used only for generating error messages. func ParseBzl(filename string, data []byte) (*File, error) { in := newInput(filename, data) f, err := in.parse() if f != nil { f.Type = TypeBzl } return f, err } // ParseDefault parses a file, marks it as a generic Starlark file and returns the corresponding parse tree. // // The filename is used only for generating error messages. func ParseDefault(filename string, data []byte) (*File, error) { in := newInput(filename, data) f, err := in.parse() if f != nil { f.Type = TypeDefault } return f, err } func getFileType(filename string) FileType { if filename == "" { // stdin return TypeDefault } basename := strings.ToLower(filepath.Base(filename)) if strings.HasSuffix(basename, ".oss") { basename = basename[:len(basename)-4] } if basename == "module.bazel" || strings.HasSuffix(basename, ".module.bazel") { return TypeModule } if basename == "repo.bazel" { return TypeRepo } if basename == "vendor.bazel" { return TypeVendor } ext := filepath.Ext(basename) switch ext { case ".bzl": return TypeBzl case ".sky": return TypeDefault } base := basename[:len(basename)-len(ext)] switch { case ext == ".build" || base == "build" || strings.HasPrefix(base, "build.") || strings.HasSuffix(basename, ".build.bazel"): return TypeBuild case ext == ".workspace" || base == "workspace" || strings.HasPrefix(base, "workspace."): return TypeWorkspace } return TypeDefault } // Parse parses the input data and returns the corresponding parse tree. // // Uses the filename to detect the formatting type (build, workspace, or default) and calls // ParseBuild, ParseWorkspace, or ParseDefault correspondingly. func Parse(filename string, data []byte) (*File, error) { switch getFileType(filename) { case TypeBuild: return ParseBuild(filename, data) case TypeWorkspace: return ParseWorkspace(filename, data) case TypeModule: return ParseModule(filename, data) case TypeRepo: return ParseRepo(filename, data) case TypeVendor: return ParseVendor(filename, data) case TypeBzl: return ParseBzl(filename, data) } return ParseDefault(filename, data) } // ParseError contains information about the error encountered during parsing. type ParseError struct { Message string Filename string Pos Position } // Error returns a string representation of the parse error. func (e ParseError) Error() string { filename := e.Filename if filename == "" { filename = "" } return fmt.Sprintf("%s:%d:%d: %v", filename, e.Pos.Line, e.Pos.LineRune, e.Message) } // An input represents a single input file being parsed. type input struct { // Lexing state. filename string // name of input file, for errors complete []byte // entire input remaining []byte // remaining input token []byte // token being scanned lastToken string // most recently returned token, for error messages pos Position // current input position lineComments []Comment // accumulated line comments suffixComments []Comment // accumulated suffix comments depth int // nesting of [ ] { } ( ) cleanLine bool // true if the current line only contains whitespace before the current position indent int // current line indentation in spaces indents []int // stack of indentation levels in spaces // Parser state. file *File // returned top-level syntax tree parseError error // error encountered during parsing // Comment assignment state. pre []Expr // all expressions, in preorder traversal post []Expr // all expressions, in postorder traversal } func newInput(filename string, data []byte) *input { // The syntax requires that each simple statement ends with '\n', however it's optional at EOF. // If `data` doesn't end with '\n' we add it here to keep parser simple. // It shouldn't affect neither the parsed tree nor its formatting. data = append(data, '\n') return &input{ filename: filename, complete: data, remaining: data, pos: Position{Line: 1, LineRune: 1, Byte: 0}, cleanLine: true, indents: []int{0}, } } func (in *input) currentIndent() int { return in.indents[len(in.indents)-1] } // parse parses the input file. func (in *input) parse() (f *File, err error) { // The parser panics for both routine errors like syntax errors // and for programmer bugs like array index errors. // Turn both into error returns. Catching bug panics is // especially important when processing many files. defer func() { if e := recover(); e != nil { if e == in.parseError { err = in.parseError } else { err = ParseError{Message: fmt.Sprintf("internal error: %v", e), Filename: in.filename, Pos: in.pos} } } }() // Invoke the parser generated from parse.y. yyParse(in) if in.parseError != nil { return nil, in.parseError } in.file.Path = in.filename // Assign comments to nearby syntax. in.assignComments() if err := checkTypeAlias(in.file); err != nil { return nil, err } return in.file, nil } // checkTypeAlias checks that type alias statements occur only at top-level. func checkTypeAlias(file *File) error { var typeAliasErr error WalkStatements(file, func(x Expr, stk []Expr) error { if typeAliasErr != nil { return &StopTraversalError{} } if typeAliasStmt, ok := x.(*TypeAliasStmt); ok && len(stk) > 1 { typeAliasErr = ParseError{ Message: "syntax error: type alias not at top level", Filename: file.Path, Pos: typeAliasStmt.TypePos, } return &StopTraversalError{} } return nil }) return typeAliasErr } // Error is called to report an error. // When called by the generated code s is always "syntax error". // Error does not return: it panics. func (in *input) Error(s string) { in.ErrorAt(in.pos, s) } // ErrorAt is called to report an error at a specific position. // ErrorAt does not return: it panics. func (in *input) ErrorAt(pos Position, s string) { if s == "syntax error" && in.lastToken != "" { s += " near " + in.lastToken } in.parseError = ParseError{Message: s, Filename: in.filename, Pos: pos} panic(in.parseError) } // eof reports whether the input has reached end of file. func (in *input) eof() bool { return len(in.remaining) == 0 } // peekRune returns the next rune in the input without consuming it. func (in *input) peekRune() int { if len(in.remaining) == 0 { return 0 } r, _ := utf8.DecodeRune(in.remaining) return int(r) } // readRune consumes and returns the next rune in the input. func (in *input) readRune() int { if len(in.remaining) == 0 { in.Error("internal lexer error: readRune at EOF") } r, size := utf8.DecodeRune(in.remaining) in.remaining = in.remaining[size:] if r == '\n' { in.pos.Line++ in.pos.LineRune = 1 } else { in.pos.LineRune++ } in.pos.Byte += size return int(r) } // startToken marks the beginning of the next input token. // It must be followed by a call to endToken, once the token has // been consumed using readRune. func (in *input) startToken(val *yySymType) { in.token = in.remaining val.tok = "" val.pos = in.pos } // yySymType (used in the next few functions) is defined by the // generated parser. It is a struct containing all the fields listed // in parse.y's %union [sic] section. // endToken marks the end of an input token. // It records the actual token string in val.tok if the caller // has not done that already. func (in *input) endToken(val *yySymType) { if val.tok == "" { tok := string(in.peekToken()) val.tok = tok in.lastToken = val.tok } } // peekToken returns the bytes comprising the current token being scanned. func (in *input) peekToken() []byte { return in.token[:len(in.token)-len(in.remaining)] } // Lex is called from the generated parser to obtain the next input token. // It returns the token value (either a rune like '+' or a symbolic token _FOR) // and sets val to the data associated with the token. // // For all our input tokens, the associated data is // val.Pos (the position where the token begins) // and val.Token (the input string corresponding to the token). func (in *input) Lex(val *yySymType) int { // Skip past spaces, stopping at non-space or EOF. countNL := 0 // number of newlines we've skipped past for !in.eof() { // Skip over spaces. Count newlines so we can give the parser // information about where top-level blank lines are, // for top-level comment assignment. c := in.peekRune() if c == ' ' || c == '\t' || c == '\r' || c == '\n' { if c == '\n' { in.indent = 0 in.cleanLine = true if in.depth == 0 { // Not in a statememt. Tell parser about top-level blank line. in.startToken(val) in.readRune() in.endToken(val) return '\n' } countNL++ } else if c == ' ' && in.cleanLine { in.indent++ } in.readRune() continue } // Comment runs to end of line. if c == '#' { // If a line contains just a comment its indentation level doesn't matter. // Reset it to zero. in.indent = 0 isLineComment := in.cleanLine in.cleanLine = true // Is this comment the only thing on its line? // Find the last \n before this # and see if it's all // spaces from there to here. // If it's a suffix comment but the last non-space symbol before // it is one of (, [, or {, treat it as a line comment that should be // put inside the corresponding block. i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n")) prefix := bytes.TrimSpace(in.complete[i+1 : in.pos.Byte]) isSuffix := true if len(prefix) == 0 || prefix[len(prefix)-1] == '[' || prefix[len(prefix)-1] == '(' || prefix[len(prefix)-1] == '{' { isSuffix = false } // Consume comment without the \n it ends with. in.startToken(val) for len(in.remaining) > 0 && in.peekRune() != '\n' { in.readRune() } in.endToken(val) val.tok = strings.TrimRight(val.tok, "\n") in.lastToken = "comment" // If we are at top level (not in a rule), hand the comment to // the parser as a _COMMENT token. The grammar is written // to handle top-level comments itself. if in.depth == 0 && isLineComment { // Not in a statement. Tell parser about top-level comment. return _COMMENT } // Otherwise, save comment for later attachment to syntax tree. if countNL > 1 { in.lineComments = append(in.lineComments, Comment{val.pos, ""}) } if isSuffix { in.suffixComments = append(in.suffixComments, Comment{val.pos, val.tok}) } else { in.lineComments = append(in.lineComments, Comment{val.pos, val.tok}) } countNL = 0 continue } if c == '\\' && len(in.remaining) >= 2 && in.remaining[1] == '\n' { // We can ignore a trailing \ at end of line together with the \n. in.readRune() in.readRune() continue } // Found non-space non-comment. break } // Check for changes in indentation // Skip if we're inside a statement, or if there were non-space // characters before in the current line. if in.depth == 0 && in.cleanLine { if in.indent > in.currentIndent() { // A new indentation block starts in.indents = append(in.indents, in.indent) in.lastToken = "indent" in.cleanLine = false return _INDENT } else if in.indent < in.currentIndent() { // An indentation block ends in.indents = in.indents[:len(in.indents)-1] // It's a syntax error if the current line indentation level in now greater than // currentIndent(), should be either equal (a parent block continues) or still less // (need to unindent more). if in.indent > in.currentIndent() { in.pos = val.pos in.Error("unexpected indentation") } in.lastToken = "unindent" return _UNINDENT } } in.cleanLine = false // If the file ends with an indented block, return the corresponding amounts of unindents. if in.eof() && in.currentIndent() > 0 { in.indents = in.indents[:len(in.indents)-1] in.lastToken = "unindent" return _UNINDENT } // Found the beginning of the next token. in.startToken(val) defer in.endToken(val) // End of file. if in.eof() { in.lastToken = "EOF" return _EOF } // Punctuation tokens. switch c := in.peekRune(); c { case '[', '(', '{': in.depth++ in.readRune() return c case ']', ')', '}': in.depth-- in.readRune() return c case '.': in.readRune() if bytes.HasPrefix(in.remaining, []byte("..")) { in.readRune() in.readRune() return _ELLIPSIS } return '.' case ':', ';', ',': // single-char tokens in.readRune() return c case '<', '>', '=', '!', '+', '-', '*', '/', '%', '|', '&', '~', '^': // possibly followed by = in.readRune() if c == '~' { // unary bitwise not, shouldn't be followed by anything return c } if c == '*' && in.peekRune() == '*' { // double asterisk in.readRune() return _STAR_STAR } if c == '-' && in.peekRune() == '>' { // functions type annotation in.readRune() return _ARROW } if c == in.peekRune() { switch c { case '/': // integer division in.readRune() c = _INT_DIV case '<': // left shift in.readRune() c = _BIT_LSH case '>': // right shift in.readRune() c = _BIT_RSH } } if in.peekRune() == '=' { in.readRune() switch c { case '<': return _LE case '>': return _GE case '=': return _EQ case '!': return _NE default: return _AUGM } } return c case 'r': // possible beginning of raw quoted string if len(in.remaining) < 2 || in.remaining[1] != '"' && in.remaining[1] != '\'' { break } in.readRune() c = in.peekRune() fallthrough case '"', '\'': // quoted string quote := c if len(in.remaining) >= 3 && in.remaining[0] == byte(quote) && in.remaining[1] == byte(quote) && in.remaining[2] == byte(quote) { // Triple-quoted string. in.readRune() in.readRune() in.readRune() var c1, c2, c3 int for { if in.eof() { in.pos = val.pos in.Error("unexpected EOF in string") } c1, c2, c3 = c2, c3, in.readRune() if c1 == quote && c2 == quote && c3 == quote { break } if c3 == '\\' { if in.eof() { in.pos = val.pos in.Error("unexpected EOF in string") } in.readRune() } } } else { in.readRune() for { if in.eof() { in.pos = val.pos in.Error("unexpected EOF in string") } if in.peekRune() == '\n' { in.Error("unexpected newline in string") } c := in.readRune() if c == quote { break } if c == '\\' { if in.eof() { in.pos = val.pos in.Error("unexpected EOF in string") } in.readRune() } } } in.endToken(val) s, triple, err := Unquote(val.tok) if err != nil { in.Error(fmt.Sprint(err)) } val.str = s val.triple = triple return _STRING } // Checked all punctuation. Must be identifier token. if c := in.peekRune(); !isIdent(c) { in.Error(fmt.Sprintf("unexpected input character %#q", rune(c))) } // Scan over alphanumeric identifier. for { c := in.peekRune() if !isIdent(c) { if c == '+' || c == '-' { t := in.peekToken() // Parse 12.3e-4 and 12.3E+4 as a single token. if !(len(t) > 0 && t[0] >= '0' && t[0] <= '9' && (t[len(t)-1] == 'e' || t[len(t)-1] == 'E')) { break } } else { break } } in.readRune() } // Call endToken to set val.tok to identifier we just scanned, // so we can look to see if val.tok is a keyword. in.endToken(val) if k := keywordToken[val.tok]; k != 0 { return k } switch val.tok { case "pass": return _PASS case "break": return _BREAK case "continue": return _CONTINUE } if len(val.tok) > 0 && val.tok[0] >= '0' && val.tok[0] <= '9' { return _INT } return _IDENT } // isIdent reports whether c is an identifier rune. // We treat all non-ASCII runes as identifier runes. func isIdent(c int) bool { return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '_' || c >= 0x80 } // keywordToken records the special tokens for // strings that should not be treated as ordinary identifiers. var keywordToken = map[string]int{ "and": _AND, "cast": _CAST, "for": _FOR, "if": _IF, "else": _ELSE, "elif": _ELIF, "in": _IN, "is": _IS, "isinstance": _ISINSTANCE, "lambda": _LAMBDA, "load": _LOAD, "not": _NOT, "or": _OR, "def": _DEF, "return": _RETURN, } // Comment assignment. // We build two lists of all subexpressions, preorder and postorder. // The preorder list is ordered by start location, with outer expressions first. // The postorder list is ordered by end location, with outer expressions last. // We use the preorder list to assign each whole-line comment to the syntax // immediately following it, and we use the postorder list to assign each // end-of-line comment to the syntax immediately preceding it. // order walks the expression adding it and its subexpressions to the // preorder and postorder lists. func (in *input) order(v Expr) { if len(in.lineComments) > 0 && v != nil { in.pre = append(in.pre, v) } switch v := v.(type) { default: panic(fmt.Errorf("order: unexpected type %T", v)) case nil: // nothing case *End: // nothing case *File: for _, stmt := range v.Stmt { in.order(stmt) } case *CommentBlock: // nothing case *CallExpr: in.order(v.X) for _, x := range v.List { in.order(x) } in.order(&v.End) case *LoadStmt: in.order(v.Module) for i := range v.From { in.order(v.To[i]) in.order(v.From[i]) } in.order(&v.Rparen) case *LiteralExpr: // nothing case *StringExpr: // nothing case *Ident: // nothing case *TypedIdent: in.order(v.Ident) in.order(v.Type) case *TypeAppExpr: in.order(v.Type) for _, x := range v.Args { in.order(x) } in.order(&v.End) case *EllipsisExpr: // nothing case *BranchStmt: // nothing case *DotExpr: in.order(v.X) case *ListExpr: for _, x := range v.List { in.order(x) } in.order(&v.End) case *Comprehension: in.order(v.Body) for _, c := range v.Clauses { in.order(c) } in.order(&v.End) case *SetExpr: for _, x := range v.List { in.order(x) } in.order(&v.End) case *ForClause: in.order(v.Vars) in.order(v.X) case *IfClause: in.order(v.Cond) case *KeyValueExpr: in.order(v.Key) in.order(v.Value) case *DictExpr: for _, x := range v.List { in.order(x) } in.order(&v.End) case *TupleExpr: for _, x := range v.List { in.order(x) } if !v.NoBrackets { in.order(&v.End) } case *UnaryExpr: in.order(v.X) case *BinaryExpr: in.order(v.X) in.order(v.Y) case *AssignExpr: in.order(v.LHS) in.order(v.RHS) case *ConditionalExpr: in.order(v.Then) in.order(v.Test) in.order(v.Else) case *ParenExpr: in.order(v.X) in.order(&v.End) case *SliceExpr: in.order(v.X) in.order(v.From) in.order(v.To) in.order(v.Step) case *IndexExpr: in.order(v.X) in.order(v.Y) case *LambdaExpr: for _, param := range v.Params { in.order(param) } for _, expr := range v.Body { in.order(expr) } case *ReturnStmt: if v.Result != nil { in.order(v.Result) } case *DefStmt: if v.TypeParams != nil { in.order(v.TypeParams) } for _, x := range v.Params { in.order(x) } in.order(v.ParamsEnd) if v.Type != nil { in.order(v.Type) } in.order(v.ColonPos) for _, x := range v.Body { in.order(x) } case *ForStmt: in.order(v.Vars) in.order(v.X) for _, x := range v.Body { in.order(x) } case *IfStmt: in.order(v.Cond) for _, s := range v.True { in.order(s) } if len(v.False) > 0 { in.order(&v.ElsePos) } for _, s := range v.False { in.order(s) } case *TypeAliasStmt: in.order(v.Name) if v.TypeParams != nil { in.order(v.TypeParams) } in.order(v.Type) } if len(in.suffixComments) > 0 && v != nil { in.post = append(in.post, v) } } // assignComments attaches comments to nearby syntax. func (in *input) assignComments() { // Line comments are attached using the preorder list, suffix comments using // the postorder list (order() builds only the list(s) for the comment kinds // present). If the file has neither kind, skip the whole-tree walk entirely. hasLine := len(in.lineComments) > 0 hasSuffix := len(in.suffixComments) > 0 if !hasLine && !hasSuffix { return } in.order(in.file) if hasSuffix { in.assignSuffixComments() } if hasLine { in.assignLineComments() } } func (in *input) assignSuffixComments() { // Assign suffix comments to syntax immediately before. suffix := in.suffixComments for i := len(in.post) - 1; i >= 0; i-- { x := in.post[i] // Do not assign suffix comments to file or to block statements switch x.(type) { case *File, *DefStmt, *IfStmt, *ForStmt, *CommentBlock: continue } _, end := x.Span() xcom := x.Comment() for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte { xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1]) suffix = suffix[:len(suffix)-1] } } // We assigned suffix comments in reverse. // If multiple suffix comments were appended to the same // expression node, they are now in reverse. Fix that. for _, x := range in.post { reverseComments(x.Comment().Suffix) } // Remaining suffix comments go at beginning of file. in.file.Before = append(in.file.Before, suffix...) } func (in *input) assignLineComments() { // Assign line comments to syntax immediately following. line := in.lineComments for _, x := range in.pre { start, _ := x.Span() xcom := x.Comment() for len(line) > 0 && start.Byte >= line[0].Start.Byte { xcom.Before = append(xcom.Before, line[0]) line = line[1:] } // Line comments can be sorted in a wrong order because they get assigned from different // parts of the lexer and the parser. Restore the original order. sort.SliceStable(xcom.Before, func(i, j int) bool { return xcom.Before[i].Start.Byte < xcom.Before[j].Start.Byte }) } // Remaining line comments go at end of file. in.file.After = append(in.file.After, line...) } // reverseComments reverses the []Comment list. func reverseComments(list []Comment) { for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { list[i], list[j] = list[j], list[i] } } buildtools-10.1.0/build/lex_test.go0000664000175000017500000000402415254573013016621 0ustar kenhyskenhys/* Copyright 2018 Google LLC 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 https://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 build import ( "testing" ) func TestIsBuildFilename(t *testing.T) { cases := map[string]FileType{ "BUILD": TypeBuild, "build": TypeBuild, "bUIld": TypeBuild, "BUILD.oss": TypeBuild, "BUILD.bazel": TypeBuild, "BUILD.bazel.oss": TypeBuild, "BUILD.foo.bazel": TypeBuild, "BUILD.foo.oss": TypeBuild, "foo.BUILD.bazel": TypeBuild, "build.bzl": TypeBzl, "build.sky": TypeDefault, "WORKSPACE": TypeWorkspace, "WORKSPACE.oss": TypeWorkspace, "WORKSPACE.bazel": TypeWorkspace, "WORKSPACE.foo.bazel": TypeWorkspace, "WORKSPACE.foo.oss": TypeWorkspace, "external.BUILD": TypeBuild, "BUILD.external": TypeBuild, "aBUILD": TypeDefault, "thing.sky": TypeDefault, "my.WORKSPACE": TypeWorkspace, "thing.bzl": TypeBzl, "thing.bzl.oss": TypeBzl, "thing.bzl.exe": TypeDefault, "workspace.bazel": TypeWorkspace, "workspace.bzl": TypeBzl, "foo.bar": TypeDefault, "MODULE.bazel": TypeModule, "module.bazel": TypeModule, "module.bzl": TypeBzl, "MODULE": TypeDefault, "REPO.bazel": TypeRepo, "VENDOR.bazel": TypeVendor, } for name, fileType := range cases { res := getFileType(name) if res != fileType { t.Errorf("isBuildFilename(%q) should be %v but was %v", name, fileType, res) } } } buildtools-10.1.0/build/parse.y.go0000664000175000017500000017624015254573013016365 0ustar kenhyskenhys//line build/parse.y:29 package build import __yyfmt__ "fmt" //line build/parse.y:29 //line build/parse.y:34 type yySymType struct { yys int // input tokens tok string // raw input syntax str string // decoding of quoted string pos Position // position of token triple bool // was string triple quoted? // partial syntax trees expr Expr exprs []Expr kv *KeyValueExpr kvs []*KeyValueExpr string *StringExpr ifstmt *IfStmt loadarg *struct { from Ident to Ident } loadargs []*struct { from Ident to Ident } def_header *DefStmt // partially filled in def statement, without the body // supporting information comma Position // position of trailing comma in list, if present lastStmt Expr // most recent rule, to attach line comments to } const _AUGM = 57346 const _AND = 57347 const _CAST = 57348 const _COMMENT = 57349 const _EOF = 57350 const _EQ = 57351 const _FOR = 57352 const _GE = 57353 const _IDENT = 57354 const _INT = 57355 const _IF = 57356 const _ELSE = 57357 const _ELIF = 57358 const _IN = 57359 const _IS = 57360 const _ISINSTANCE = 57361 const _LAMBDA = 57362 const _LOAD = 57363 const _LE = 57364 const _NE = 57365 const _STAR_STAR = 57366 const _INT_DIV = 57367 const _BIT_LSH = 57368 const _BIT_RSH = 57369 const _ARROW = 57370 const _NOT = 57371 const _OR = 57372 const _STRING = 57373 const _DEF = 57374 const _RETURN = 57375 const _PASS = 57376 const _BREAK = 57377 const _CONTINUE = 57378 const _INDENT = 57379 const _UNINDENT = 57380 const _ELLIPSIS = 57381 const ShiftInstead = 57382 const _ASSERT = 57383 const _UNARY = 57384 var yyToknames = [...]string{ "$end", "error", "$unk", "'%'", "'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'", "':'", "'<'", "'='", "'>'", "'['", "']'", "'{'", "'}'", "'|'", "'&'", "'^'", "'~'", "_AUGM", "_AND", "_CAST", "_COMMENT", "_EOF", "_EQ", "_FOR", "_GE", "_IDENT", "_INT", "_IF", "_ELSE", "_ELIF", "_IN", "_IS", "_ISINSTANCE", "_LAMBDA", "_LOAD", "_LE", "_NE", "_STAR_STAR", "_INT_DIV", "_BIT_LSH", "_BIT_RSH", "_ARROW", "_NOT", "_OR", "_STRING", "_DEF", "_RETURN", "_PASS", "_BREAK", "_CONTINUE", "_INDENT", "_UNINDENT", "_ELLIPSIS", "ShiftInstead", "'\\n'", "_ASSERT", "_UNARY", "';'", } var yyStatenames = [...]string{} const yyEofCode = 1 const yyErrCode = 2 const yyInitialStackSize = 16 //line build/parse.y:1340 // Go helper code. // unary returns a unary expression with the given // position, operator, and subexpression. func unary(pos Position, op string, x Expr) Expr { return &UnaryExpr{ OpStart: pos, Op: op, X: x, } } // binary returns a binary expression with the given // operands, position, and operator. func binary(x Expr, pos Position, op string, y Expr) Expr { _, xend := x.Span() ystart, _ := y.Span() switch op { case "=", "+=", "-=", "*=", "/=", "//=", "%=", "&=", "|=", "^=", "<<=", ">>=": return &AssignExpr{ LHS: x, OpPos: pos, Op: op, LineBreak: xend.Line < ystart.Line, RHS: y, } } return &BinaryExpr{ X: x, OpStart: pos, Op: op, LineBreak: xend.Line < ystart.Line, Y: y, } } // typed returns a TypedIdent expression func typed(x, y Expr) *TypedIdent { return &TypedIdent{ Ident: x, Type: y, } } // isSimpleExpression returns whether an expression is simple and allowed to exist in // compact forms of sequences. // The formal criteria are the following: an expression is considered simple if it's // a literal (variable, string or a number), a literal with a unary operator or an empty sequence. func isSimpleExpression(expr *Expr) bool { switch x := (*expr).(type) { case *LiteralExpr, *StringExpr, *Ident: return true case *UnaryExpr: _, literal := x.X.(*LiteralExpr) _, ident := x.X.(*Ident) return literal || ident case *ListExpr: return len(x.List) == 0 case *TupleExpr: return len(x.List) == 0 case *DictExpr: return len(x.List) == 0 case *SetExpr: return len(x.List) == 0 case *EllipsisExpr: return true default: return false } } // forceCompact returns the setting for the ForceCompact field for a call or tuple. // // NOTE 1: The field is called ForceCompact, not ForceSingleLine, // because it only affects the formatting associated with the call or tuple syntax, // not the formatting of the arguments. For example: // // call([ // 1, // 2, // 3, // ]) // // is still a compact call even though it runs on multiple lines. // // In contrast the multiline form puts a linebreak after the (. // // call( // [ // 1, // 2, // 3, // ], // ) // // NOTE 2: Because of NOTE 1, we cannot use start and end on the // same line as a signal for compact mode: the formatting of an // embedded list might move the end to a different line, which would // then look different on rereading and cause buildifier not to be // idempotent. Instead, we have to look at properties guaranteed // to be preserved by the reformatting, namely that the opening // paren and the first expression are on the same line and that // each subsequent expression begins on the same line as the last // one ended (no line breaks after comma). func forceCompact(start Position, list []Expr, end Position) bool { if len(list) <= 1 { // The call or tuple will probably be compact anyway; don't force it. return false } // If there are any named arguments or non-string, non-literal // arguments, cannot force compact mode. line := start.Line for _, x := range list { start, end := x.Span() if start.Line != line { return false } line = end.Line if !isSimpleExpression(&x) { return false } } return end.Line == line } // forceMultiLine returns the setting for the ForceMultiLine field. func forceMultiLine(start Position, list []Expr, end Position) bool { if len(list) > 1 { // The call will be multiline anyway, because it has multiple elements. Don't force it. return false } if len(list) == 0 { // Empty list: use position of brackets. return start.Line != end.Line } // Single-element list. // Check whether opening bracket is on different line than beginning of // element, or closing bracket is on different line than end of element. elemStart, elemEnd := list[0].Span() return start.Line != elemStart.Line || end.Line != elemEnd.Line } // forceMultiLineComprehension returns the setting for the ForceMultiLine field for a comprehension. func forceMultiLineComprehension(start Position, expr Expr, clauses []Expr, end Position) bool { // Return true if there's at least one line break between start, expr, each clause, and end exprStart, exprEnd := expr.Span() if start.Line != exprStart.Line { return true } previousEnd := exprEnd for _, clause := range clauses { clauseStart, clauseEnd := clause.Span() if previousEnd.Line != clauseStart.Line { return true } previousEnd = clauseEnd } return previousEnd.Line != end.Line } // isBlockStmt reports whether x is a statement with an indentable body // (def, for, or if). Line comments that follow such statements should form // standalone CommentBlock statements rather than being attached to the block // as After-comments, matching how the same comments are parsed when the // block is written in its expanded form (see extractTrailingComments). func isBlockStmt(x Expr) bool { switch x.(type) { case *DefStmt, *ForStmt, *IfStmt: return true } return false } // extractTrailingComments extracts trailing comments of an indented block starting with the first // comment line with indentation less than the block indentation. // The comments can either belong to CommentBlock statements or to the last non-comment statement // as After-comments. func extractTrailingComments(stmt Expr) []Expr { body := getLastBody(stmt) var comments []Expr if body != nil && len(*body) > 0 { // Get the current indentation level start, _ := (*body)[0].Span() indentation := start.LineRune // Find the last non-comment statement lastNonCommentIndex := -1 for i, stmt := range *body { if _, ok := stmt.(*CommentBlock); !ok { lastNonCommentIndex = i } } if lastNonCommentIndex == -1 { return comments } // Iterate over the trailing comments, find the first comment line that's not indented enough, // dedent it and all the following comments. for i := lastNonCommentIndex; i < len(*body); i++ { stmt := (*body)[i] if comment := extractDedentedComment(stmt, indentation); comment != nil { // This comment and all the following CommentBlock statements are to be extracted. comments = append(comments, comment) comments = append(comments, (*body)[i+1:]...) *body = (*body)[:i+1] // If the current statement is a CommentBlock statement without any comment lines // it should be removed too. if i > lastNonCommentIndex && len(stmt.Comment().After) == 0 { *body = (*body)[:i] } } } } return comments } // extractDedentedComment extract the first comment line from `stmt` which indentation is smaller // than `indentation`, and all following comment lines, and returns them in a newly created // CommentBlock statement. func extractDedentedComment(stmt Expr, indentation int) Expr { for i, line := range stmt.Comment().After { // line.Start.LineRune == 0 can't exist in parsed files, it indicates that the comment line // has been added by an AST modification. Don't take such lines into account. if line.Start.LineRune > 0 && line.Start.LineRune < indentation { // This and all the following lines should be dedented cb := &CommentBlock{ Start: line.Start, Comments: Comments{After: stmt.Comment().After[i:]}, } stmt.Comment().After = stmt.Comment().After[:i] return cb } } return nil } // getLastBody returns the last body of a block statement (the only body for For- and DefStmt // objects, the last in a if-elif-else chain func getLastBody(stmt Expr) *[]Expr { switch block := stmt.(type) { case *DefStmt: return &block.Body case *ForStmt: return &block.Body case *IfStmt: if len(block.False) == 0 { return &block.True } else if len(block.False) == 1 { if next, ok := block.False[0].(*IfStmt); ok { // Recursively find the last block of the chain return getLastBody(next) } } return &block.False } return nil } // Expose lex.ErrorAt to the parser. type yyLexerWithErrorAt interface { ErrorAt(pos Position, s string) } func errorAt(yylex yyLexer, pos Position, s string) { if lex, ok := yylex.(yyLexerWithErrorAt); ok { lex.ErrorAt(pos, s) } else { yylex.Error(s) } } //line yacctab:1 var yyExca = [...]int16{ -1, 1, 1, -1, -2, 0, -1, 90, 6, 67, -2, 140, -1, 197, 20, 137, -2, 138, } const yyPrivate = 57344 const yyLast = 1186 var yyAct = [...]int16{ 22, 310, 35, 259, 281, 298, 275, 258, 168, 121, 2, 128, 7, 9, 119, 181, 174, 108, 276, 49, 187, 188, 34, 118, 251, 27, 85, 290, 253, 202, 98, 99, 100, 101, 131, 46, 45, 50, 228, 58, 106, 111, 114, 260, 169, 150, 264, 94, 265, 228, 242, 105, 201, 63, 250, 126, 62, 66, 252, 67, 278, 64, 23, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 131, 151, 152, 153, 154, 155, 156, 157, 158, 159, 23, 23, 264, 44, 265, 228, 45, 113, 123, 65, 81, 82, 279, 107, 127, 123, 133, 116, 23, 182, 45, 96, 56, 51, 23, 179, 15, 131, 163, 23, 192, 135, 63, 60, 61, 62, 66, 288, 67, 57, 64, 195, 23, 193, 122, 44, 203, 23, 45, 78, 79, 80, 95, 136, 110, 23, 131, 257, 165, 97, 228, 180, 87, 207, 216, 217, 191, 190, 245, 196, 63, 199, 15, 62, 65, 81, 82, 209, 64, 190, 132, 240, 132, 182, 23, 321, 170, 246, 194, 214, 225, 233, 220, 223, 190, 292, 234, 337, 227, 238, 239, 63, 340, 209, 62, 66, 244, 67, 232, 64, 162, 244, 65, 247, 249, 86, 54, 318, 219, 271, 177, 178, 52, 241, 243, 170, 226, 132, 183, 241, 50, 248, 53, 270, 332, 256, 267, 209, 186, 235, 236, 182, 15, 65, 269, 282, 308, 208, 263, 54, 182, 307, 286, 209, 90, 132, 280, 287, 229, 15, 89, 210, 237, 176, 328, 284, 91, 211, 304, 160, 176, 327, 268, 54, 54, 289, 54, 254, 215, 324, 164, 230, 299, 291, 132, 173, 295, 48, 15, 115, 331, 170, 183, 283, 285, 303, 54, 228, 221, 95, 311, 263, 197, 315, 175, 302, 341, 313, 314, 334, 333, 317, 301, 7, 13, 266, 224, 293, 319, 200, 322, 222, 104, 282, 325, 103, 300, 329, 102, 55, 263, 117, 330, 132, 205, 132, 1, 10, 317, 15, 299, 88, 336, 335, 20, 272, 277, 342, 311, 343, 183, 309, 344, 204, 320, 112, 323, 263, 109, 183, 132, 263, 326, 47, 59, 21, 12, 124, 125, 8, 4, 33, 189, 172, 134, 297, 63, 296, 15, 62, 66, 262, 67, 294, 64, 338, 339, 41, 132, 261, 129, 130, 132, 43, 79, 80, 213, 212, 161, 39, 16, 40, 305, 306, 24, 273, 171, 312, 274, 37, 92, 93, 166, 15, 167, 23, 42, 132, 65, 81, 82, 0, 38, 0, 36, 0, 0, 277, 132, 0, 0, 0, 0, 0, 45, 41, 0, 206, 31, 0, 30, 43, 44, 0, 132, 0, 0, 39, 132, 40, 0, 132, 132, 0, 32, 312, 0, 37, 6, 0, 0, 11, 0, 23, 42, 26, 0, 0, 0, 0, 38, 28, 36, 0, 0, 0, 0, 0, 0, 0, 29, 0, 45, 25, 14, 17, 18, 19, 231, 316, 44, 41, 5, 0, 31, 0, 30, 43, 0, 0, 0, 0, 0, 39, 0, 40, 0, 0, 0, 0, 32, 0, 0, 37, 6, 3, 0, 11, 0, 23, 42, 26, 0, 255, 0, 0, 38, 28, 36, 0, 0, 0, 0, 0, 0, 0, 29, 0, 45, 25, 14, 17, 18, 19, 41, 0, 44, 31, 5, 30, 43, 0, 0, 0, 0, 0, 39, 0, 40, 0, 0, 0, 0, 32, 0, 0, 37, 0, 0, 0, 0, 0, 23, 42, 0, 0, 0, 0, 0, 38, 28, 36, 0, 0, 0, 0, 0, 0, 0, 29, 0, 45, 0, 14, 17, 18, 19, 41, 0, 44, 31, 120, 30, 43, 0, 0, 0, 0, 0, 39, 0, 40, 0, 0, 0, 0, 32, 0, 0, 37, 0, 0, 0, 0, 0, 23, 42, 0, 0, 0, 0, 0, 38, 28, 36, 0, 0, 0, 0, 0, 0, 0, 29, 0, 45, 0, 14, 17, 18, 19, 0, 41, 44, 184, 31, 228, 30, 43, 0, 0, 0, 0, 0, 39, 0, 40, 0, 0, 0, 0, 32, 0, 0, 37, 0, 0, 0, 0, 0, 23, 42, 0, 0, 0, 0, 0, 38, 28, 36, 0, 0, 185, 0, 0, 0, 0, 29, 41, 45, 184, 31, 0, 30, 43, 0, 0, 44, 0, 0, 39, 0, 40, 0, 0, 0, 0, 32, 0, 0, 37, 0, 0, 0, 0, 0, 23, 42, 0, 0, 0, 0, 0, 38, 28, 36, 41, 0, 185, 31, 228, 30, 43, 29, 0, 45, 0, 0, 39, 0, 40, 0, 0, 44, 0, 32, 0, 0, 37, 0, 0, 0, 0, 0, 23, 42, 0, 0, 0, 0, 0, 38, 28, 36, 41, 0, 0, 31, 0, 30, 43, 29, 0, 45, 0, 0, 39, 0, 40, 0, 0, 44, 0, 32, 0, 0, 37, 0, 0, 0, 0, 0, 23, 42, 0, 0, 0, 0, 0, 38, 28, 36, 0, 0, 63, 0, 0, 62, 66, 29, 67, 45, 64, 198, 68, 0, 69, 0, 0, 44, 0, 78, 79, 80, 0, 0, 77, 0, 0, 0, 70, 0, 73, 0, 0, 84, 0, 0, 74, 83, 0, 0, 0, 71, 72, 0, 65, 81, 82, 63, 75, 76, 62, 66, 0, 67, 0, 64, 0, 68, 0, 69, 0, 0, 0, 0, 78, 79, 80, 0, 0, 77, 0, 0, 0, 70, 0, 73, 0, 0, 84, 218, 0, 74, 83, 0, 0, 0, 71, 72, 0, 65, 81, 82, 63, 75, 76, 62, 66, 0, 67, 0, 64, 0, 68, 0, 69, 0, 0, 0, 0, 78, 79, 80, 0, 0, 77, 0, 0, 0, 70, 190, 73, 0, 0, 84, 0, 0, 74, 83, 0, 0, 0, 71, 72, 0, 65, 81, 82, 63, 75, 76, 62, 66, 0, 67, 0, 64, 0, 68, 0, 69, 0, 0, 0, 0, 78, 79, 80, 0, 0, 77, 0, 0, 0, 70, 0, 73, 0, 0, 84, 0, 0, 74, 83, 0, 0, 0, 71, 72, 0, 65, 81, 82, 63, 75, 76, 62, 66, 0, 67, 0, 64, 0, 68, 0, 69, 0, 0, 0, 0, 78, 79, 80, 0, 0, 77, 0, 0, 0, 70, 0, 73, 0, 0, 0, 0, 0, 74, 83, 0, 0, 0, 71, 72, 0, 65, 81, 82, 63, 75, 76, 62, 66, 0, 67, 0, 64, 0, 68, 0, 69, 0, 0, 0, 0, 78, 79, 80, 0, 0, 77, 0, 0, 0, 70, 0, 73, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 71, 72, 0, 65, 81, 82, 63, 75, 76, 62, 66, 0, 67, 0, 64, 0, 68, 0, 69, 0, 0, 0, 0, 78, 79, 80, 0, 0, 77, 0, 0, 0, 70, 0, 73, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 71, 72, 0, 65, 81, 82, 63, 75, 0, 62, 66, 0, 67, 0, 64, 0, 68, 0, 69, 0, 0, 0, 0, 78, 79, 80, 0, 0, 0, 0, 0, 0, 70, 63, 73, 0, 62, 66, 0, 67, 74, 64, 0, 0, 0, 71, 72, 0, 65, 81, 82, 79, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 81, 82, } var yyPact = [...]int16{ -32768, -32768, 472, -32768, -32768, -32768, -27, -32768, -32768, -32768, 260, 368, -32768, 193, 754, 95, -32768, -32768, -32768, -32768, -10, 83, 930, -32768, 184, 115, 754, 235, 100, 754, 754, 754, 754, -32768, -32768, -32768, 309, 306, 303, 754, 754, 754, 264, 69, -32768, -32768, -32768, -42, 525, 92, 235, -32768, 754, 754, 754, 273, 108, -32768, 108, 754, 104, -32768, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 7, 754, 754, 754, 754, 754, 754, 754, 754, 754, 240, 77, 184, 253, 111, 268, 754, 258, 281, -32768, 234, 77, 77, -32768, -32768, -32768, -32768, 268, 108, 678, 206, 885, 268, 122, 154, 279, 795, 268, 299, 930, 18, -32768, -33, 578, -32768, -32768, -32768, 754, 368, 273, 273, 975, 218, -32768, 236, -32768, 108, -32768, 142, 251, 525, -32768, -32768, -32768, -32768, -32768, 152, 152, 114, 114, 114, 114, 114, 114, 114, 754, 1065, 1110, 358, 49, 1137, 183, 183, 1020, 840, 108, 275, -32768, 302, 525, -32768, 296, 268, 678, 274, -32768, 226, 254, 754, -32768, 100, 754, -32768, -32768, -16, 202, 268, 930, 233, 754, 754, -32768, 149, 15, -32768, 368, 716, -32768, 134, -32768, 153, 716, -32768, 754, 716, -32768, -32768, -32768, -32768, -4, -34, 250, 235, 754, 108, 110, 71, 295, 268, 142, 525, -32768, 114, 754, 142, 187, 77, 53, -32768, -32768, -32768, 632, -32768, -32768, -32768, 754, -32768, -32768, 930, 268, 632, 137, 754, 930, 930, -32768, 15, 754, 85, 930, -32768, -32768, 930, -32768, 795, -32768, -35, -32768, -32768, 525, 273, -32768, -32768, 163, -32768, 142, -32768, -32768, -32768, 71, -16, -32768, -32768, 137, -32768, 975, -32768, -32768, 292, 272, -32768, -32768, 241, 77, 77, -32768, 219, 930, 82, 268, 202, 930, 975, 754, 416, -32768, -32768, -32768, 29, 185, 268, 151, 268, -32768, 252, 142, -32768, -32768, 53, 108, 244, 237, -32768, 754, 267, -32768, -32768, 205, 290, 289, 975, -32768, -32768, -32768, -32768, 29, -32768, -32768, 40, 71, -32768, 168, 108, 108, 170, 286, 54, -16, -32768, -32768, -32768, -32768, 754, 142, 142, -32768, -32768, -32768, -32768, 930, } var yyPgo = [...]int16{ 0, 16, 44, 8, 15, 400, 398, 18, 397, 396, 6, 394, 391, 390, 386, 26, 384, 383, 382, 43, 11, 377, 376, 375, 369, 7, 3, 367, 363, 361, 5, 0, 4, 51, 25, 300, 359, 99, 19, 358, 21, 20, 109, 357, 22, 10, 356, 355, 352, 351, 350, 9, 13, 349, 17, 344, 341, 2, 14, 339, 1, 337, 330, 323, 322, 320, 317, } var yyR1 = [...]int8{ 0, 64, 58, 58, 65, 65, 59, 59, 59, 45, 45, 45, 45, 46, 46, 62, 63, 63, 47, 47, 47, 49, 49, 48, 48, 50, 50, 51, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 13, 14, 15, 15, 16, 16, 66, 66, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 6, 6, 5, 5, 4, 4, 4, 4, 61, 61, 60, 60, 9, 9, 12, 12, 8, 8, 11, 11, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10, 35, 35, 36, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 37, 37, 32, 32, 33, 33, 1, 1, 2, 2, 3, 3, 54, 56, 56, 55, 55, 55, 38, 38, 57, 42, 43, 43, 43, 43, 44, 39, 40, 40, 41, 41, 17, 17, 18, 18, 19, 19, 20, 20, 20, 22, 22, 21, 23, 24, 24, 25, 25, 26, 26, 26, 26, 27, 28, 28, 29, 29, 30, } var yyR2 = [...]int8{ 0, 2, 5, 2, 0, 2, 0, 3, 2, 0, 2, 2, 3, 1, 1, 6, 1, 3, 3, 6, 1, 4, 5, 1, 4, 2, 1, 4, 0, 3, 1, 2, 1, 3, 5, 3, 1, 3, 1, 1, 1, 2, 4, 0, 4, 1, 3, 0, 1, 1, 1, 1, 1, 3, 8, 7, 7, 4, 4, 6, 8, 3, 4, 4, 3, 4, 3, 0, 2, 2, 3, 1, 3, 2, 2, 1, 3, 1, 3, 0, 2, 0, 2, 1, 3, 1, 3, 1, 3, 2, 1, 2, 1, 3, 5, 4, 4, 1, 3, 0, 1, 1, 4, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 5, 1, 3, 0, 1, 0, 2, 0, 1, 1, 2, 0, 1, 3, 1, 3, 0, 1, 2, 1, 3, 1, 1, 3, 2, 2, 1, 1, 4, 1, 3, 1, 2, 0, 2, 1, 3, 1, 3, 1, 1, 3, 1, 3, 4, 3, 0, 2, 1, 3, 1, 1, 1, 1, 3, 0, 2, 1, 3, 3, } var yyChk = [...]int16{ -32768, -64, -45, 29, -46, 62, 28, -51, -47, -52, -63, 31, -48, -35, 54, -42, -14, 55, 56, 57, -62, -49, -31, 33, -13, 53, 35, -34, 41, 50, 10, 8, 24, -43, -44, -57, 42, 27, 40, 17, 19, 5, 34, 11, 60, 52, 62, -53, 13, -38, -34, -42, 15, 25, 9, -35, 13, -42, 49, -50, 36, 37, 7, 4, 12, 46, 8, 10, 14, 16, 30, 43, 44, 32, 38, 50, 51, 26, 21, 22, 23, 47, 48, 39, 35, -15, 17, 33, -35, 11, 5, 17, -9, -8, -7, -42, 7, 45, -31, -31, -31, -31, 5, 5, 5, -33, -31, -37, -54, -55, -37, -31, -56, -33, -31, 11, 34, -66, 65, -58, 62, -51, 38, 9, -35, -35, -31, -19, -20, -22, -21, 5, -42, -19, -35, 13, 35, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, 38, -31, -31, -31, -31, -31, -31, -31, -31, -31, 15, -16, -42, -15, 13, 33, -6, -5, -3, -2, 9, -35, -36, 13, -1, 9, 15, -42, -42, -3, -19, -4, -31, -42, 7, 45, 18, -41, -40, -39, 31, -2, -3, -41, 20, -1, -2, 9, 13, -2, 6, 34, 62, -52, -59, -65, -35, -34, 15, 21, 11, 17, -17, -18, -19, 13, -58, -31, 36, -19, -1, 9, 5, -58, 6, -3, -2, -4, 9, 18, 13, -35, -7, -31, -57, -2, -2, 15, -31, -31, 18, -40, 35, -38, -31, 20, 20, -31, -54, -31, 58, 28, 62, 62, 13, -35, -20, 33, -25, -26, -19, -23, -27, -44, 17, 19, 6, -3, -2, -58, -31, 18, -42, -12, -11, -10, -7, -42, 7, 45, -4, -32, -31, -2, -4, -19, -31, -31, 38, -45, 62, -58, 18, -2, -24, -25, -28, -29, -30, -57, -19, 6, -1, 9, 13, -42, -42, 18, 13, -61, -60, -57, -42, -3, -3, -31, 59, -26, 18, -3, -2, 20, -3, -2, 13, -10, -19, 13, 13, -32, -3, 9, 15, 6, 6, -30, -26, 15, -19, -19, 18, 6, -60, -57, -31, } var yyDef = [...]int16{ 9, -2, 0, 1, 10, 11, 0, 13, 14, 28, 0, 0, 20, 30, 32, 49, 36, 38, 39, 40, 16, 23, 97, 151, 43, 0, 0, 101, 79, 0, 0, 0, 0, 50, 51, 52, 0, 0, 0, 134, 145, 134, 155, 0, 156, 150, 12, 47, 0, 0, 148, 49, 0, 0, 0, 31, 0, 41, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 0, 0, -2, 99, 0, 136, 83, 87, 90, 0, 103, 104, 105, 106, 140, 0, 0, 0, 130, 140, 143, 0, 136, 130, 146, 0, 130, 153, 154, 0, 48, 18, 6, 4, 0, 0, 33, 37, 98, 35, 166, 168, 169, 162, 171, 17, 0, 0, 25, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 0, 121, 122, 123, 124, 125, 126, 127, 128, 0, 0, 136, 45, 0, 0, 53, 0, 140, 0, 141, 138, 100, 0, 0, 80, 137, 0, 89, 91, 0, 0, 0, 71, 49, 0, 0, 61, 0, 160, 158, 0, 141, 135, 0, 64, 0, 0, -2, 0, 147, 66, 152, 27, 29, 0, 3, 0, 149, 0, 0, 0, 0, 0, 140, 164, 0, 24, 120, 0, 42, 0, 137, 81, 21, 57, 68, 141, 69, 139, 58, 132, 102, 84, 88, 0, 0, 0, 0, 73, 74, 62, 161, 0, 0, 131, 63, 65, 142, 144, 0, 9, 0, 8, 5, 0, 34, 167, 172, 0, 177, 179, 180, 181, 182, 175, 184, 170, 163, 141, 22, 129, 44, 46, 0, 136, 85, 92, 87, 90, 0, 70, 0, 133, 0, 140, 140, 72, 159, 0, 0, 7, 19, 173, 0, 0, 140, 0, 140, 186, 0, 165, 15, 82, 137, 0, 89, 91, 59, 132, 140, 75, 77, 0, 0, 0, 157, 2, 178, 174, 176, 141, 183, 185, 141, 0, 86, 93, 0, 0, 0, 0, 138, 0, 55, 56, 187, 188, 0, 95, 96, 60, 54, 76, 78, 94, } var yyTok1 = [...]int8{ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 62, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 22, 3, 5, 6, 7, 8, 9, 10, 11, 12, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 13, 65, 14, 15, 16, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 17, 3, 18, 23, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19, 21, 20, 24, } var yyTok2 = [...]int8{ 2, 3, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, 64, } var yyTok3 = [...]int8{ 0, } var yyErrorMessages = [...]struct { state int token int msg string }{} //line yaccpar:1 /* parser for yacc output */ var ( yyDebug = 0 yyErrorVerbose = false ) type yyLexer interface { Lex(lval *yySymType) int Error(s string) } type yyParser interface { Parse(yyLexer) int Lookahead() int } type yyParserImpl struct { lval yySymType stack [yyInitialStackSize]yySymType char int } func (p *yyParserImpl) Lookahead() int { return p.char } func yyNewParser() yyParser { return &yyParserImpl{} } const yyFlag = -32768 func yyTokname(c int) string { if c >= 1 && c-1 < len(yyToknames) { if yyToknames[c-1] != "" { return yyToknames[c-1] } } return __yyfmt__.Sprintf("tok-%v", c) } func yyStatname(s int) string { if s >= 0 && s < len(yyStatenames) { if yyStatenames[s] != "" { return yyStatenames[s] } } return __yyfmt__.Sprintf("state-%v", s) } func yyErrorMessage(state, lookAhead int) string { const TOKSTART = 4 if !yyErrorVerbose { return "syntax error" } for _, e := range yyErrorMessages { if e.state == state && e.token == lookAhead { return "syntax error: " + e.msg } } res := "syntax error: unexpected " + yyTokname(lookAhead) // To match Bison, suggest at most four expected tokens. expected := make([]int, 0, 4) // Look for shiftable tokens. base := int(yyPact[state]) for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { if n := base + tok; n >= 0 && n < yyLast && int(yyChk[int(yyAct[n])]) == tok { if len(expected) == cap(expected) { return res } expected = append(expected, tok) } } if yyDef[state] == -2 { i := 0 for yyExca[i] != -1 || int(yyExca[i+1]) != state { i += 2 } // Look for tokens that we accept or reduce. for i += 2; yyExca[i] >= 0; i += 2 { tok := int(yyExca[i]) if tok < TOKSTART || yyExca[i+1] == 0 { continue } if len(expected) == cap(expected) { return res } expected = append(expected, tok) } // If the default action is to accept or reduce, give up. if yyExca[i+1] != 0 { return res } } for i, tok := range expected { if i == 0 { res += ", expecting " } else { res += " or " } res += yyTokname(tok) } return res } func yylex1(lex yyLexer, lval *yySymType) (char, token int) { token = 0 char = lex.Lex(lval) if char <= 0 { token = int(yyTok1[0]) goto out } if char < len(yyTok1) { token = int(yyTok1[char]) goto out } if char >= yyPrivate { if char < yyPrivate+len(yyTok2) { token = int(yyTok2[char-yyPrivate]) goto out } } for i := 0; i < len(yyTok3); i += 2 { token = int(yyTok3[i+0]) if token == char { token = int(yyTok3[i+1]) goto out } } out: if token == 0 { token = int(yyTok2[1]) /* unknown char */ } if yyDebug >= 3 { __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) } return char, token } func yyParse(yylex yyLexer) int { return yyNewParser().Parse(yylex) } func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int { var yyn int var yyVAL yySymType var yyDollar []yySymType _ = yyDollar // silence set and not used yyS := yyrcvr.stack[:] Nerrs := 0 /* number of errors */ Errflag := 0 /* error recovery flag */ yystate := 0 yyrcvr.char = -1 yytoken := -1 // yyrcvr.char translated into internal numbering defer func() { // Make sure we report no lookahead when not parsing. yystate = -1 yyrcvr.char = -1 yytoken = -1 }() yyp := -1 goto yystack ret0: return 0 ret1: return 1 yystack: /* put a state and value onto the stack */ if yyDebug >= 4 { __yyfmt__.Printf("char %v in %v\n", yyTokname(yytoken), yyStatname(yystate)) } yyp++ if yyp >= len(yyS) { nyys := make([]yySymType, len(yyS)*2) copy(nyys, yyS) yyS = nyys } yyS[yyp] = yyVAL yyS[yyp].yys = yystate yynewstate: yyn = int(yyPact[yystate]) if yyn <= yyFlag { goto yydefault /* simple state */ } if yyrcvr.char < 0 { yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) } yyn += yytoken if yyn < 0 || yyn >= yyLast { goto yydefault } yyn = int(yyAct[yyn]) if int(yyChk[yyn]) == yytoken { /* valid shift */ yyrcvr.char = -1 yytoken = -1 yyVAL = yyrcvr.lval yystate = yyn if Errflag > 0 { Errflag-- } goto yystack } yydefault: /* default state action */ yyn = int(yyDef[yystate]) if yyn == -2 { if yyrcvr.char < 0 { yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) } /* look through exception table */ xi := 0 for { if yyExca[xi+0] == -1 && int(yyExca[xi+1]) == yystate { break } xi += 2 } for xi += 2; ; xi += 2 { yyn = int(yyExca[xi+0]) if yyn < 0 || yyn == yytoken { break } } yyn = int(yyExca[xi+1]) if yyn < 0 { goto ret0 } } if yyn == 0 { /* error ... attempt to resume parsing */ switch Errflag { case 0: /* brand new error */ yylex.Error(yyErrorMessage(yystate, yytoken)) Nerrs++ if yyDebug >= 1 { __yyfmt__.Printf("%s", yyStatname(yystate)) __yyfmt__.Printf(" saw %s\n", yyTokname(yytoken)) } fallthrough case 1, 2: /* incompletely recovered error ... try again */ Errflag = 3 /* find a state where "error" is a legal shift action */ for yyp >= 0 { yyn = int(yyPact[yyS[yyp].yys]) + yyErrCode if yyn >= 0 && yyn < yyLast { yystate = int(yyAct[yyn]) /* simulate a shift of "error" */ if int(yyChk[yystate]) == yyErrCode { goto yystack } } /* the current p has no shift on "error", pop stack */ if yyDebug >= 2 { __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) } yyp-- } /* there is no state on the stack with an error shift ... abort */ goto ret1 case 3: /* no shift yet; clobber input char */ if yyDebug >= 2 { __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yytoken)) } if yytoken == yyEofCode { goto ret1 } yyrcvr.char = -1 yytoken = -1 goto yynewstate /* try again in the same state */ } } /* reduction by production yyn */ if yyDebug >= 2 { __yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate)) } yynt := yyn yypt := yyp _ = yypt // guard against "declared and not used" yyp -= int(yyR2[yyn]) // yyp is now the index of $0. Perform the default action. Iff the // reduced production is ε, $1 is possibly out of range. if yyp+1 >= len(yyS) { nyys := make([]yySymType, len(yyS)*2) copy(nyys, yyS) yyS = nyys } yyVAL = yyS[yyp+1] /* consult goto table to find next state */ yyn = int(yyR1[yyn]) yyg := int(yyPgo[yyn]) yyj := yyg + yyS[yyp].yys + 1 if yyj >= yyLast { yystate = int(yyAct[yyg]) } else { yystate = int(yyAct[yyj]) if int(yyChk[yystate]) != -yyn { yystate = int(yyAct[yyg]) } } // dummy call; replaced with literal code switch yynt { case 1: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:240 { yylex.(*input).file = &File{Stmt: yyDollar[1].exprs} return 0 } case 2: yyDollar = yyS[yypt-5 : yypt+1] //line build/parse.y:247 { statements := yyDollar[4].exprs if yyDollar[2].exprs != nil { // $2 can only contain *CommentBlock objects, each of them contains a non-empty After slice cb := yyDollar[2].exprs[len(yyDollar[2].exprs)-1].(*CommentBlock) // $4 can't be empty and can't start with a comment stmt := yyDollar[4].exprs[0] start, _ := stmt.Span() if start.Line-cb.After[len(cb.After)-1].Start.Line == 1 { // The first statement of $4 starts on the next line after the last comment of $2. // Attach the last comment to the first statement stmt.Comment().Before = cb.After yyDollar[2].exprs = yyDollar[2].exprs[:len(yyDollar[2].exprs)-1] } statements = append(yyDollar[2].exprs, yyDollar[4].exprs...) } yyVAL.exprs = statements yyVAL.lastStmt = yyDollar[4].lastStmt } case 3: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:267 { yyVAL.exprs = yyDollar[1].exprs } case 6: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:275 { yyVAL.exprs = nil yyVAL.lastStmt = nil } case 7: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:280 { yyVAL.exprs = yyDollar[1].exprs yyVAL.lastStmt = yyDollar[1].lastStmt if yyVAL.lastStmt == nil { cb := &CommentBlock{Start: yyDollar[2].pos} yyVAL.exprs = append(yyVAL.exprs, cb) yyVAL.lastStmt = cb } com := yyVAL.lastStmt.Comment() com.After = append(com.After, Comment{Start: yyDollar[2].pos, Token: yyDollar[2].tok}) } case 8: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:292 { yyVAL.exprs = yyDollar[1].exprs yyVAL.lastStmt = nil } case 9: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:298 { yyVAL.exprs = nil yyVAL.lastStmt = nil } case 10: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:303 { // If this statement follows a comment block, // attach the comments to the statement. if cb, ok := yyDollar[1].lastStmt.(*CommentBlock); ok { yyVAL.exprs = append(yyDollar[1].exprs[:len(yyDollar[1].exprs)-1], yyDollar[2].exprs...) yyDollar[2].exprs[0].Comment().Before = cb.After yyVAL.lastStmt = yyDollar[2].lastStmt break } // Otherwise add to list. yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[2].exprs...) yyVAL.lastStmt = yyDollar[2].lastStmt // Consider this input: // // foo() // # bar // baz() // // If we've just parsed baz(), the # bar is attached to // foo() as an After comment. Make it a Before comment // for baz() instead. if x := yyDollar[1].lastStmt; x != nil { com := x.Comment() // stmt is never empty yyDollar[2].exprs[0].Comment().Before = com.After com.After = nil } } case 11: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:334 { // Blank line; sever last rule from future comments. yyVAL.exprs = yyDollar[1].exprs yyVAL.lastStmt = nil } case 12: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:340 { yyVAL.exprs = yyDollar[1].exprs yyVAL.lastStmt = yyDollar[1].lastStmt if yyVAL.lastStmt == nil || isBlockStmt(yyVAL.lastStmt) { // Comments after a block statement (e.g. a compact `def f(): pass`) // must become a standalone CommentBlock, matching how the same // comment is parsed when the block is written in its expanded, // indented form (see extractTrailingComments). Attaching it to the // block's After list instead makes the printer emit it without the // blank line that separates a block from a trailing comment, so // formatting would need a second pass to become stable. cb := &CommentBlock{Start: yyDollar[2].pos} yyVAL.exprs = append(yyVAL.exprs, cb) yyVAL.lastStmt = cb } com := yyVAL.lastStmt.Comment() com.After = append(com.After, Comment{Start: yyDollar[2].pos, Token: yyDollar[2].tok}) } case 13: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:361 { yyVAL.exprs = yyDollar[1].exprs yyVAL.lastStmt = yyDollar[1].exprs[len(yyDollar[1].exprs)-1] } case 14: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:366 { yyVAL.exprs = []Expr{yyDollar[1].expr} yyVAL.lastStmt = yyDollar[1].expr if cbs := extractTrailingComments(yyDollar[1].expr); len(cbs) > 0 { yyVAL.exprs = append(yyVAL.exprs, cbs...) yyVAL.lastStmt = cbs[len(cbs)-1] if yyDollar[1].lastStmt == nil { yyVAL.lastStmt = nil } } } case 15: yyDollar = yyS[yypt-6 : yypt+1] //line build/parse.y:380 { yyVAL.def_header = &DefStmt{ Function: Function{ StartPos: yyDollar[1].pos, Params: yyDollar[5].exprs, }, Name: yyDollar[2].tok, TypeParams: yyDollar[3].expr, ParamsEnd: &End{Pos: yyDollar[6].pos}, ForceCompact: forceCompact(yyDollar[4].pos, yyDollar[5].exprs, yyDollar[6].pos), ForceMultiLine: forceMultiLine(yyDollar[4].pos, yyDollar[5].exprs, yyDollar[6].pos), } } case 17: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:397 { yyDollar[1].def_header.Type = yyDollar[3].expr yyVAL.def_header = yyDollar[1].def_header } case 18: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:404 { yyDollar[1].def_header.Function.Body = yyDollar[3].exprs yyDollar[1].def_header.ColonPos = &End{Pos: yyDollar[2].pos} yyVAL.expr = yyDollar[1].def_header yyVAL.lastStmt = yyDollar[3].lastStmt } case 19: yyDollar = yyS[yypt-6 : yypt+1] //line build/parse.y:411 { yyVAL.expr = &ForStmt{ For: yyDollar[1].pos, Vars: yyDollar[2].expr, X: yyDollar[4].expr, Body: yyDollar[6].exprs, } yyVAL.lastStmt = yyDollar[6].lastStmt } case 20: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:421 { yyVAL.expr = yyDollar[1].ifstmt yyVAL.lastStmt = yyDollar[1].lastStmt } case 21: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:429 { yyVAL.ifstmt = &IfStmt{ If: yyDollar[1].pos, Cond: yyDollar[2].expr, True: yyDollar[4].exprs, } yyVAL.lastStmt = yyDollar[4].lastStmt } case 22: yyDollar = yyS[yypt-5 : yypt+1] //line build/parse.y:438 { yyVAL.ifstmt = yyDollar[1].ifstmt inner := yyDollar[1].ifstmt for len(inner.False) == 1 { inner = inner.False[0].(*IfStmt) } inner.ElsePos = End{Pos: yyDollar[2].pos} inner.False = []Expr{ &IfStmt{ If: yyDollar[2].pos, Cond: yyDollar[3].expr, True: yyDollar[5].exprs, }, } yyVAL.lastStmt = yyDollar[5].lastStmt } case 24: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:459 { yyVAL.ifstmt = yyDollar[1].ifstmt inner := yyDollar[1].ifstmt for len(inner.False) == 1 { inner = inner.False[0].(*IfStmt) } inner.ElsePos = End{Pos: yyDollar[2].pos} inner.False = yyDollar[4].exprs yyVAL.lastStmt = yyDollar[4].lastStmt } case 27: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:476 { yyVAL.exprs = append([]Expr{yyDollar[1].expr}, yyDollar[2].exprs...) yyVAL.lastStmt = yyVAL.exprs[len(yyVAL.exprs)-1] } case 28: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:482 { yyVAL.exprs = []Expr{} } case 29: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:486 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 31: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:493 { yyVAL.expr = &ReturnStmt{ Return: yyDollar[1].pos, Result: yyDollar[2].expr, } } case 32: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:500 { yyVAL.expr = &ReturnStmt{ Return: yyDollar[1].pos, } } case 33: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:505 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 34: yyDollar = yyS[yypt-5 : yypt+1] //line build/parse.y:506 { yyVAL.expr = binary(typed(yyDollar[1].expr, yyDollar[3].expr), yyDollar[4].pos, yyDollar[4].tok, yyDollar[5].expr) } case 35: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:507 { yyVAL.expr = typed(yyDollar[1].expr, yyDollar[3].expr) } case 37: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:509 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 38: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:511 { yyVAL.expr = &BranchStmt{ Token: yyDollar[1].tok, TokenPos: yyDollar[1].pos, } } case 39: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:518 { yyVAL.expr = &BranchStmt{ Token: yyDollar[1].tok, TokenPos: yyDollar[1].pos, } } case 40: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:525 { yyVAL.expr = &BranchStmt{ Token: yyDollar[1].tok, TokenPos: yyDollar[1].pos, } } case 41: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:536 { if yyDollar[1].expr.(*Ident).Name != "type" { // two idents can be adjacent only if the first one is `type`. _, end := yyDollar[2].expr.Span() errorAt(yylex, end, "syntax error near "+yyDollar[2].expr.(*Ident).Name) } yyVAL.expr = &TypeAliasStmt{ TypePos: yyDollar[1].expr.(*Ident).NamePos, Name: yyDollar[2].expr, // Rest of fields will be filled in by type_alias_stmt } } case 42: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:551 { typeStart, _ := yyDollar[4].expr.Span() // Modify $1 in-place to fill in the remaining fields. typeAlisStmt := yyDollar[1].expr.(*TypeAliasStmt) typeAlisStmt.TypeParams = yyDollar[2].expr typeAlisStmt.EqualPos = yyDollar[3].pos typeAlisStmt.Type = yyDollar[4].expr typeAlisStmt.LineBreak = yyDollar[3].pos.Line < typeStart.Line yyVAL.expr = typeAlisStmt } case 43: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:563 { yyVAL.expr = nil } case 44: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:567 { yyVAL.expr = &ListExpr{ Start: yyDollar[1].pos, List: yyDollar[2].exprs, End: End{Pos: yyDollar[4].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[4].pos), } } case 45: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:578 { yyVAL.exprs = []Expr{yyDollar[1].expr} } case 46: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:582 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 52: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:594 { yyVAL.expr = yyDollar[1].string } case 53: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:598 { yyVAL.expr = &DotExpr{ X: yyDollar[1].expr, Dot: yyDollar[2].pos, NamePos: yyDollar[3].pos, Name: yyDollar[3].tok, } } case 54: yyDollar = yyS[yypt-8 : yypt+1] //line build/parse.y:607 { load := &LoadStmt{ Load: yyDollar[1].pos, Module: yyDollar[4].string, Rparen: End{Pos: yyDollar[8].pos}, ForceCompact: yyDollar[2].pos.Line == yyDollar[8].pos.Line, } for _, arg := range yyDollar[6].loadargs { load.From = append(load.From, &arg.from) load.To = append(load.To, &arg.to) } yyVAL.expr = load } case 55: yyDollar = yyS[yypt-7 : yypt+1] //line build/parse.y:621 { args := []Expr{yyDollar[3].expr, yyDollar[5].expr} yyVAL.expr = &CallExpr{ CallExprKind: CallExprCast, X: &Ident{NamePos: yyDollar[1].pos, Name: yyDollar[1].tok}, ListStart: yyDollar[2].pos, List: args, End: End{Pos: yyDollar[7].pos}, ForceCompact: forceCompact(yyDollar[2].pos, args, yyDollar[7].pos), ForceMultiLine: forceMultiLine(yyDollar[2].pos, args, yyDollar[7].pos), } } case 56: yyDollar = yyS[yypt-7 : yypt+1] //line build/parse.y:634 { args := []Expr{yyDollar[3].expr, yyDollar[5].expr} yyVAL.expr = &CallExpr{ CallExprKind: CallExprIsInstance, X: &Ident{NamePos: yyDollar[1].pos, Name: yyDollar[1].tok}, ListStart: yyDollar[2].pos, List: args, End: End{Pos: yyDollar[7].pos}, ForceCompact: forceCompact(yyDollar[2].pos, args, yyDollar[7].pos), ForceMultiLine: forceMultiLine(yyDollar[2].pos, args, yyDollar[7].pos), } } case 57: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:647 { yyVAL.expr = &CallExpr{ X: yyDollar[1].expr, ListStart: yyDollar[2].pos, List: yyDollar[3].exprs, End: End{Pos: yyDollar[4].pos}, ForceCompact: forceCompact(yyDollar[2].pos, yyDollar[3].exprs, yyDollar[4].pos), ForceMultiLine: forceMultiLine(yyDollar[2].pos, yyDollar[3].exprs, yyDollar[4].pos), } } case 58: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:658 { yyVAL.expr = &IndexExpr{ X: yyDollar[1].expr, IndexStart: yyDollar[2].pos, Y: yyDollar[3].expr, End: yyDollar[4].pos, } } case 59: yyDollar = yyS[yypt-6 : yypt+1] //line build/parse.y:667 { yyVAL.expr = &SliceExpr{ X: yyDollar[1].expr, SliceStart: yyDollar[2].pos, From: yyDollar[3].expr, FirstColon: yyDollar[4].pos, To: yyDollar[5].expr, End: yyDollar[6].pos, } } case 60: yyDollar = yyS[yypt-8 : yypt+1] //line build/parse.y:678 { yyVAL.expr = &SliceExpr{ X: yyDollar[1].expr, SliceStart: yyDollar[2].pos, From: yyDollar[3].expr, FirstColon: yyDollar[4].pos, To: yyDollar[5].expr, SecondColon: yyDollar[6].pos, Step: yyDollar[7].expr, End: yyDollar[8].pos, } } case 61: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:691 { yyVAL.expr = &ListExpr{ Start: yyDollar[1].pos, List: yyDollar[2].exprs, End: End{Pos: yyDollar[3].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), } } case 62: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:700 { yyVAL.expr = &Comprehension{ Curly: false, Lbrack: yyDollar[1].pos, Body: yyDollar[2].expr, Clauses: yyDollar[3].exprs, End: End{Pos: yyDollar[4].pos}, ForceMultiLine: forceMultiLineComprehension(yyDollar[1].pos, yyDollar[2].expr, yyDollar[3].exprs, yyDollar[4].pos), } } case 63: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:711 { yyVAL.expr = &Comprehension{ Curly: true, Lbrack: yyDollar[1].pos, Body: yyDollar[2].kv, Clauses: yyDollar[3].exprs, End: End{Pos: yyDollar[4].pos}, ForceMultiLine: forceMultiLineComprehension(yyDollar[1].pos, yyDollar[2].kv, yyDollar[3].exprs, yyDollar[4].pos), } } case 64: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:722 { exprValues := make([]Expr, 0, len(yyDollar[2].kvs)) for _, kv := range yyDollar[2].kvs { exprValues = append(exprValues, Expr(kv)) } yyVAL.expr = &DictExpr{ Start: yyDollar[1].pos, List: yyDollar[2].kvs, End: End{Pos: yyDollar[3].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, exprValues, yyDollar[3].pos), } } case 65: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:735 { yyVAL.expr = &SetExpr{ Start: yyDollar[1].pos, List: yyDollar[2].exprs, End: End{Pos: yyDollar[4].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[4].pos), } } case 66: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:744 { if len(yyDollar[2].exprs) == 1 && yyDollar[2].comma.Line == 0 { // Just a parenthesized expression, not a tuple. yyVAL.expr = &ParenExpr{ Start: yyDollar[1].pos, X: yyDollar[2].exprs[0], End: End{Pos: yyDollar[3].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), } } else { yyVAL.expr = &TupleExpr{ Start: yyDollar[1].pos, List: yyDollar[2].exprs, End: End{Pos: yyDollar[3].pos}, ForceCompact: forceCompact(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), } } } case 67: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:765 { yyVAL.exprs = nil } case 68: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:769 { yyVAL.exprs = yyDollar[1].exprs } case 69: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:775 { yyVAL.exprs = []Expr{yyDollar[2].expr} } case 70: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:779 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 72: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:786 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 73: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:790 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 74: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:794 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 75: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:799 { yyVAL.loadargs = []*struct { from Ident to Ident }{yyDollar[1].loadarg} } case 76: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:803 { yyDollar[1].loadargs = append(yyDollar[1].loadargs, yyDollar[3].loadarg) yyVAL.loadargs = yyDollar[1].loadargs } case 77: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:809 { start := yyDollar[1].string.Start.add("'") if yyDollar[1].string.TripleQuote { start = start.add("''") } yyVAL.loadarg = &struct { from Ident to Ident }{ from: Ident{ Name: yyDollar[1].string.Value, NamePos: start, }, to: Ident{ Name: yyDollar[1].string.Value, NamePos: start, }, } } case 78: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:826 { start := yyDollar[3].string.Start.add("'") if yyDollar[3].string.TripleQuote { start = start.add("''") } yyVAL.loadarg = &struct { from Ident to Ident }{ from: Ident{ Name: yyDollar[3].string.Value, NamePos: start, }, to: *yyDollar[1].expr.(*Ident), } } case 79: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:841 { yyVAL.exprs = nil } case 80: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:845 { yyVAL.exprs = yyDollar[1].exprs } case 81: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:850 { yyVAL.exprs = nil } case 82: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:854 { yyVAL.exprs = yyDollar[1].exprs } case 83: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:860 { yyVAL.exprs = []Expr{yyDollar[1].expr} } case 84: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:864 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 85: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:871 { yyVAL.exprs = []Expr{yyDollar[1].expr} } case 86: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:875 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 88: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:882 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 89: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:886 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 90: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:890 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, nil) } case 91: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:894 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 93: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:903 { yyVAL.expr = typed(yyDollar[1].expr, yyDollar[3].expr) } case 94: yyDollar = yyS[yypt-5 : yypt+1] //line build/parse.y:907 { yyVAL.expr = binary(typed(yyDollar[1].expr, yyDollar[3].expr), yyDollar[4].pos, yyDollar[4].tok, yyDollar[5].expr) } case 95: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:911 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, typed(yyDollar[2].expr, yyDollar[4].expr)) } case 96: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:915 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, typed(yyDollar[2].expr, yyDollar[4].expr)) } case 98: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:922 { tuple, ok := yyDollar[1].expr.(*TupleExpr) if !ok || !tuple.NoBrackets { tuple = &TupleExpr{ List: []Expr{yyDollar[1].expr}, NoBrackets: true, ForceCompact: true, ForceMultiLine: false, } } tuple.List = append(tuple.List, yyDollar[3].expr) yyVAL.expr = tuple } case 99: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:937 { yyVAL.expr = nil } case 102: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:945 { yyVAL.expr = &LambdaExpr{ Function: Function{ StartPos: yyDollar[1].pos, Params: yyDollar[2].exprs, Body: []Expr{yyDollar[4].expr}, }, } } case 103: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:954 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 104: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:955 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 105: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:956 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 106: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:957 { yyVAL.expr = unary(yyDollar[1].pos, yyDollar[1].tok, yyDollar[2].expr) } case 107: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:958 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 108: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:959 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 109: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:960 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 110: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:961 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 111: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:962 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 112: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:963 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 113: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:964 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 114: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:965 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 115: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:966 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 116: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:967 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 117: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:968 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 118: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:969 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 119: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:970 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 120: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:971 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, "not in", yyDollar[4].expr) } case 121: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:972 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 122: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:973 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 123: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:974 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 124: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:975 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 125: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:976 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 126: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:977 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 127: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:978 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 128: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:980 { if b, ok := yyDollar[3].expr.(*UnaryExpr); ok && b.Op == "not" { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, "is not", b.X) } else { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } } case 129: yyDollar = yyS[yypt-5 : yypt+1] //line build/parse.y:988 { yyVAL.expr = &ConditionalExpr{ Then: yyDollar[1].expr, IfStart: yyDollar[2].pos, Test: yyDollar[3].expr, ElseStart: yyDollar[4].pos, Else: yyDollar[5].expr, } } case 130: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1000 { yyVAL.exprs = []Expr{yyDollar[1].expr} } case 131: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1004 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 132: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1009 { yyVAL.expr = nil } case 134: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1015 { yyVAL.exprs, yyVAL.comma = nil, Position{} } case 135: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1019 { yyVAL.exprs, yyVAL.comma = yyDollar[1].exprs, yyDollar[2].pos } case 136: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1029 { yyVAL.pos = Position{} } case 139: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1040 { yyVAL.pos = yyDollar[1].pos } case 140: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1048 { yyVAL.pos = Position{} } case 142: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1055 { yyVAL.kv = &KeyValueExpr{ Key: yyDollar[1].expr, Colon: yyDollar[2].pos, Value: yyDollar[3].expr, } } case 143: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1065 { yyVAL.kvs = []*KeyValueExpr{yyDollar[1].kv} } case 144: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1069 { yyVAL.kvs = append(yyDollar[1].kvs, yyDollar[3].kv) } case 145: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1074 { yyVAL.kvs = nil } case 146: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1078 { yyVAL.kvs = yyDollar[1].kvs } case 147: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1082 { yyVAL.kvs = yyDollar[1].kvs } case 149: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1089 { tuple, ok := yyDollar[1].expr.(*TupleExpr) if !ok || !tuple.NoBrackets { tuple = &TupleExpr{ List: []Expr{yyDollar[1].expr}, NoBrackets: true, ForceCompact: true, ForceMultiLine: false, } } tuple.List = append(tuple.List, yyDollar[3].expr) yyVAL.expr = tuple } case 150: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1105 { yyVAL.string = &StringExpr{ Start: yyDollar[1].pos, Value: yyDollar[1].str, TripleQuote: yyDollar[1].triple, End: yyDollar[1].pos.add(yyDollar[1].tok), Token: yyDollar[1].tok, } } case 151: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1117 { yyVAL.expr = &Ident{NamePos: yyDollar[1].pos, Name: yyDollar[1].tok} } case 152: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1123 { yyVAL.expr = &LiteralExpr{Start: yyDollar[1].pos, Token: yyDollar[1].tok + "." + yyDollar[3].tok} } case 153: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1127 { yyVAL.expr = &LiteralExpr{Start: yyDollar[1].pos, Token: yyDollar[1].tok + "."} } case 154: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1131 { yyVAL.expr = &LiteralExpr{Start: yyDollar[1].pos, Token: "." + yyDollar[2].tok} } case 155: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1135 { yyVAL.expr = &LiteralExpr{Start: yyDollar[1].pos, Token: yyDollar[1].tok} } case 156: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1141 { yyVAL.expr = &EllipsisExpr{ Pos: yyDollar[1].pos, } } case 157: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:1149 { yyVAL.expr = &ForClause{ For: yyDollar[1].pos, Vars: yyDollar[2].expr, In: yyDollar[3].pos, X: yyDollar[4].expr, } } case 158: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1160 { yyVAL.exprs = []Expr{yyDollar[1].expr} } case 159: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1164 { yyVAL.exprs = append(yyDollar[1].exprs, &IfClause{ If: yyDollar[2].pos, Cond: yyDollar[3].expr, }) } case 160: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1173 { yyVAL.exprs = yyDollar[1].exprs } case 161: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1177 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[2].exprs...) } case 162: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1182 { yyVAL.exprs = nil } case 163: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1186 { yyVAL.exprs = yyDollar[1].exprs } case 164: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1192 { yyVAL.exprs = []Expr{yyDollar[1].expr} } case 165: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1196 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 166: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1202 { yyVAL.expr = yyDollar[1].expr } case 167: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1206 { yyVAL.expr = binary(yyDollar[1].expr, yyDollar[2].pos, yyDollar[2].tok, yyDollar[3].expr) } case 170: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1214 { if len(yyDollar[2].exprs) == 1 && yyDollar[2].comma.Line == 0 { // Just a parenthesized type expression, not a tuple; // useless in type syntax, but permitted. yyVAL.expr = &ParenExpr{ Start: yyDollar[1].pos, X: yyDollar[2].exprs[0], End: End{Pos: yyDollar[3].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), } } else { yyVAL.expr = &TupleExpr{ Start: yyDollar[1].pos, List: yyDollar[2].exprs, End: End{Pos: yyDollar[3].pos}, ForceCompact: forceCompact(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), } } } case 172: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1238 { yyVAL.expr = &DotExpr{ X: yyDollar[1].expr, Dot: yyDollar[2].pos, NamePos: yyDollar[3].pos, Name: yyDollar[3].tok, } } case 173: yyDollar = yyS[yypt-4 : yypt+1] //line build/parse.y:1249 { yyVAL.expr = &TypeAppExpr{ Type: yyDollar[1].expr, ArgsStart: yyDollar[2].pos, Args: yyDollar[3].exprs, End: End{Pos: yyDollar[4].pos}, ForceCompact: forceCompact(yyDollar[2].pos, yyDollar[3].exprs, yyDollar[4].pos), ForceMultiLine: forceMultiLine(yyDollar[2].pos, yyDollar[3].exprs, yyDollar[4].pos), } } case 174: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1262 { yyVAL.expr = &ListExpr{ Start: yyDollar[1].pos, List: yyDollar[2].exprs, End: End{Pos: yyDollar[3].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, yyDollar[2].exprs, yyDollar[3].pos), } } case 175: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1272 { yyVAL.exprs = nil } case 176: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1276 { yyVAL.exprs = yyDollar[1].exprs } case 177: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1282 { yyVAL.exprs = []Expr{yyDollar[1].expr} } case 178: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1286 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 183: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1298 { exprValues := make([]Expr, 0, len(yyDollar[2].kvs)) for _, kv := range yyDollar[2].kvs { exprValues = append(exprValues, Expr(kv)) } yyVAL.expr = &DictExpr{ Start: yyDollar[1].pos, List: yyDollar[2].kvs, End: End{Pos: yyDollar[3].pos}, ForceMultiLine: forceMultiLine(yyDollar[1].pos, exprValues, yyDollar[3].pos), } } case 184: yyDollar = yyS[yypt-0 : yypt+1] //line build/parse.y:1312 { yyVAL.kvs = nil } case 185: yyDollar = yyS[yypt-2 : yypt+1] //line build/parse.y:1316 { yyVAL.kvs = yyDollar[1].kvs } case 186: yyDollar = yyS[yypt-1 : yypt+1] //line build/parse.y:1322 { yyVAL.kvs = []*KeyValueExpr{yyDollar[1].kv} } case 187: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1326 { yyVAL.kvs = append(yyDollar[1].kvs, yyDollar[3].kv) } case 188: yyDollar = yyS[yypt-3 : yypt+1] //line build/parse.y:1332 { yyVAL.kv = &KeyValueExpr{ Key: yyDollar[1].string, Colon: yyDollar[2].pos, Value: yyDollar[3].expr, } } } goto yystack /* stack new state and value */ } buildtools-10.1.0/build/walk.go0000664000175000017500000001406215254573013015733 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 build // StopTraversalError is a special error that tells the walker to not traverse // further and visit child nodes of the current node. type StopTraversalError struct{} func (m *StopTraversalError) Error() string { return "Stop traversal" } // Walk walks the expression tree v, calling f on all subexpressions // in a preorder traversal. // // The stk argument is the stack of expressions in the recursion above x, // from outermost to innermost. func Walk(v Expr, f func(x Expr, stk []Expr)) { var stack []Expr walk1(&v, &stack, func(x *Expr, stk []Expr) (Expr, error) { f(*x, stk) return nil, nil }) } // WalkPointers is the same as Walk but calls the callback function with pointers to nodes. func WalkPointers(v Expr, f func(x *Expr, stk []Expr)) { var stack []Expr walk1(&v, &stack, func(x *Expr, stk []Expr) (Expr, error) { f(x, stk) return nil, nil }) } // WalkInterruptable is the same as Walk but allows traversal to be interrupted. func WalkInterruptable(v Expr, f func(x Expr, stk []Expr) error) { var stack []Expr walk1(&v, &stack, func(x *Expr, stk []Expr) (Expr, error) { err := f(*x, stk) return nil, err }) } // Edit walks the expression tree v, calling f on all subexpressions // in a preorder traversal. If f returns a non-nil value, the tree is mutated. // The new value replaces the old one. // // The stk argument is the stack of expressions in the recursion above x, // from outermost to innermost. func Edit(v Expr, f func(x Expr, stk []Expr) Expr) Expr { var stack []Expr return walk1(&v, &stack, func(x *Expr, stk []Expr) (Expr, error) { return f(*x, stk), nil }) } // EditChildren is similar to Edit but doesn't visit the initial node, instead goes // directly to its children. func EditChildren(v Expr, f func(x Expr, stk []Expr) Expr) { stack := []Expr{v} WalkOnce(v, func(x *Expr) { walk1(x, &stack, func(x *Expr, stk []Expr) (Expr, error) { return f(*x, stk), nil }) }) } // walk1 is a helper function for Walk, WalkWithPostfix, and Edit. func walk1(v *Expr, stack *[]Expr, f func(x *Expr, stk []Expr) (Expr, error)) Expr { if v == nil || *v == nil { return nil } res, err := f(v, *stack) if res != nil { *v = res } if err != nil { if _, ok := err.(*StopTraversalError); ok { // Don't traverse inside return nil } } *stack = append(*stack, *v) WalkOnce(*v, func(x *Expr) { walk1(x, stack, f) }) *stack = (*stack)[:len(*stack)-1] return *v } // WalkOnce calls f on every child of v. func WalkOnce(v Expr, f func(x *Expr)) { switch v := v.(type) { case *File: for i := range v.Stmt { f(&v.Stmt[i]) } case *DotExpr: f(&v.X) case *IndexExpr: f(&v.X) f(&v.Y) case *KeyValueExpr: f(&v.Key) f(&v.Value) case *SliceExpr: f(&v.X) if v.From != nil { f(&v.From) } if v.To != nil { f(&v.To) } if v.Step != nil { f(&v.Step) } case *ParenExpr: f(&v.X) case *UnaryExpr: f(&v.X) case *BinaryExpr: f(&v.X) f(&v.Y) case *AssignExpr: f(&v.LHS) f(&v.RHS) case *TypeAliasStmt: f(&v.Name) if v.TypeParams != nil { f(&v.TypeParams) } f(&v.Type) case *TypedIdent: f(&v.Ident) f(&v.Type) case *TypeAppExpr: f(&v.Type) for i := range v.Args { f(&v.Args[i]) } case *LambdaExpr: for i := range v.Params { f(&v.Params[i]) } for i := range v.Body { f(&v.Body[i]) } case *CallExpr: f(&v.X) for i := range v.List { f(&v.List[i]) } case *ListExpr: for i := range v.List { f(&v.List[i]) } case *SetExpr: for i := range v.List { f(&v.List[i]) } case *TupleExpr: for i := range v.List { f(&v.List[i]) } case *DictExpr: for i := range v.List { e := Expr(v.List[i]) f(&e) } case *Comprehension: f(&v.Body) for _, c := range v.Clauses { f(&c) } case *IfClause: f(&v.Cond) case *ForClause: f(&v.Vars) f(&v.X) case *ConditionalExpr: f(&v.Then) f(&v.Test) f(&v.Else) case *LoadStmt: module := (Expr)(v.Module) f(&module) v.Module = module.(*StringExpr) for i := range v.From { from := (Expr)(v.From[i]) f(&from) v.From[i] = from.(*Ident) to := (Expr)(v.To[i]) f(&to) v.To[i] = to.(*Ident) } case *DefStmt: if v.TypeParams != nil { f(&v.TypeParams) } for i := range v.Params { f(&v.Params[i]) } f(&v.ParamsEnd) // for comments if v.Type != nil { f(&v.Type) } f(&v.ColonPos) // for comments for i := range v.Body { f(&v.Body[i]) } case *IfStmt: f(&v.Cond) for i := range v.True { f(&v.True[i]) } for i := range v.False { f(&v.False[i]) } case *ForStmt: f(&v.Vars) f(&v.X) for i := range v.Body { f(&v.Body[i]) } case *ReturnStmt: if v.Result != nil { f(&v.Result) } } } // walkStatements is a helper function for WalkStatements func walkStatements(v Expr, stack *[]Expr, f func(x Expr, stk []Expr) error) { if v == nil { return } err := f(v, *stack) if err != nil { if _, ok := err.(*StopTraversalError); ok { return } } *stack = append(*stack, v) traverse := func(x Expr) { walkStatements(x, stack, f) } switch expr := v.(type) { case *File: for _, s := range expr.Stmt { traverse(s) } case *DefStmt: for _, s := range expr.Body { traverse(s) } case *IfStmt: for _, s := range expr.True { traverse(s) } for _, s := range expr.False { traverse(s) } case *ForStmt: for _, s := range expr.Body { traverse(s) } } *stack = (*stack)[:len(*stack)-1] } // WalkStatements traverses sub statements (not all nodes) func WalkStatements(v Expr, f func(x Expr, stk []Expr) error) { var stack []Expr walkStatements(v, &stack, f) } buildtools-10.1.0/build/rewrite.go0000664000175000017500000011754415254573013016467 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // Rewriting of high-level (not purely syntactic) BUILD constructs. package build import ( "path" "path/filepath" "regexp" "sort" "strings" "github.com/bazel-contrib/buildtools/v10/labels" "github.com/bazel-contrib/buildtools/v10/tables" ) // DisableRewrites disables certain rewrites (for debugging). var DisableRewrites []string // disabled reports whether the named rewrite is disabled. func disabled(name string) bool { for _, x := range DisableRewrites { if name == x { return true } } return false } // AllowSort allows sorting of these lists even with sorting otherwise disabled (for debugging). var AllowSort []string // allowedSort reports whether sorting is allowed in the named context. func allowedSort(name string) bool { for _, x := range AllowSort { if name == x { return true } } return false } // Rewriter controls the rewrites to be applied. // // If non-nil, the rewrites with the specified names will be run. If // nil, a default set of rewrites will be used that is determined by // the type (BUILD vs default starlark) of the file being rewritten. type Rewriter struct { RewriteSet []string IsLabelArg map[string]bool LabelDenyList map[string]bool IsSortableListArg map[string]bool SortableDenylist map[string]bool SortableAllowlist map[string]bool NamePriority map[string]int StripLabelLeadingSlashes bool ShortenAbsoluteLabelsToRelative bool } // Rewrite applies rewrites to a file func Rewrite(f *File) { var rewriter = &Rewriter{ IsLabelArg: tables.IsLabelArg, LabelDenyList: tables.LabelDenylist, IsSortableListArg: tables.IsSortableListArg, SortableDenylist: tables.SortableDenylist, SortableAllowlist: tables.SortableAllowlist, NamePriority: tables.NamePriority, StripLabelLeadingSlashes: tables.StripLabelLeadingSlashes, ShortenAbsoluteLabelsToRelative: tables.ShortenAbsoluteLabelsToRelative, } rewriter.Rewrite(f) } // Rewrite applies the rewrites to a file func (w *Rewriter) Rewrite(f *File) { for _, r := range rewrites { // f.Type&r.scope is a bitwise comparison. Because starlark files result in a scope that will // not be changed by rewrites, we have included another check looking on the right side. // If we have an empty rewrite set, we do not want any rewrites to happen. if (!disabled(r.name) && (f.Type&r.scope != 0) && w.RewriteSet == nil) || (w.RewriteSet != nil && rewriteSetContains(w, r.name)) { r.fn(f, w) } } } func rewriteSetContains(w *Rewriter, name string) bool { for _, value := range w.RewriteSet { if value == name { return true } } return false } // Each rewrite function can be either applied for BUILD files, other files (such as .bzl), // or all files. const ( scopeDefault = TypeDefault | TypeBzl // .bzl and generic Starlark files scopeBuild = TypeBuild | TypeWorkspace | TypeModule | TypeRepo | TypeVendor // BUILD, WORKSPACE, MODULE, REPO, and VENDOR files scopeBoth = scopeDefault | scopeBuild ) // rewrites is the list of all Buildifier rewrites, in the order in which they are applied. // The order here matters: for example, label canonicalization must happen // before sorting lists of strings. var rewrites = []struct { name string fn func(*File, *Rewriter) scope FileType }{ {"removeParens", removeParens, scopeBuild}, {"callsort", sortCallArgs, scopeBuild}, {"label", fixLabels, scopeBuild}, {"listsort", sortStringLists, scopeBoth}, {"multiplus", fixMultilinePlus, scopeBuild}, {"loadTop", moveLoadOnTop, scopeBoth}, {"sameOriginLoad", compressSameOriginLoads, scopeBoth}, {"sortLoadStatements", sortLoadStatements, scopeBoth}, {"loadsort", sortAllLoadArgs, scopeBoth}, {"useRepoPositionalsSort", sortUseRepoPositionals, TypeModule}, {"formatdocstrings", formatDocstrings, scopeBoth}, {"reorderarguments", reorderArguments, scopeBoth}, {"editoctal", editOctals, scopeBoth}, {"editfloat", editFloats, scopeBoth}, {"collapseEmpty", collapseEmpty, scopeBoth}, } // leaveAlone reports whether any of the nodes on the stack are marked // with a comment containing "buildifier: leave-alone". func leaveAlone(stk []Expr, final Expr) bool { for _, x := range stk { if leaveAlone1(x) { return true } } if final != nil && leaveAlone1(final) { return true } return false } // hasComment reports whether x is marked with a comment that // after being converted to lower case, contains the specified text. func hasComment(x Expr, text string) bool { if x == nil { return false } for _, com := range x.Comment().Before { if strings.Contains(strings.ToLower(com.Token), text) { return true } } for _, com := range x.Comment().After { if strings.Contains(strings.ToLower(com.Token), text) { return true } } for _, com := range x.Comment().Suffix { if strings.Contains(strings.ToLower(com.Token), text) { return true } } return false } // isCommentAnywhere checks whether there's a comment containing the given text // anywhere in the file. func isCommentAnywhere(f *File, text string) bool { commentExists := false WalkInterruptable(f, func(node Expr, stack []Expr) (err error) { if hasComment(node, text) { commentExists = true return &StopTraversalError{} } return nil }) return commentExists } // leaveAlone1 reports whether x is marked with a comment containing // "buildifier: leave-alone", case-insensitive. func leaveAlone1(x Expr) bool { return hasComment(x, "buildifier: leave-alone") } // doNotSort reports whether x is marked with a comment containing // "do not sort", case-insensitive. func doNotSort(x Expr) bool { return hasComment(x, "do not sort") } // keepSorted reports whether x is marked with a comment containing // "keep sorted", case-insensitive. func keepSorted(x Expr) bool { return hasComment(x, "keep sorted") } // labelRE matches label strings, e.g. @r//x/y/z:abc // where $1 is @r//x/y/z, $2 is @r//, $3 is r, $4 is z, $5 is abc. func makeLabelRe(labelPrefix string) *regexp.Regexp { return regexp.MustCompile(`^(((?:@(\w+))?//|` + labelPrefix + `)(?:.+/)?([^:]*))(?::([^:]+))?$`) } var leadingSlashesLabelRe = makeLabelRe("//") var stripLeadingSlashesLabelRe = makeLabelRe("") // fixLabels rewrites labels into a canonical form. // // First, it joins labels written as string addition, turning // "//x" + ":y" (usually split across multiple lines) into "//x:y". // // Second, it removes redundant target qualifiers, turning labels like // "//third_party/m4:m4" into "//third_party/m4" as well as ones like // "@foo//:foo" into "@foo". func fixLabels(f *File, w *Rewriter) { joinLabel := func(p *Expr) { add, ok := (*p).(*BinaryExpr) if !ok || add.Op != "+" { return } str1, ok := add.X.(*StringExpr) if !ok || !strings.HasPrefix(str1.Value, "//") || strings.Contains(str1.Value, " ") { return } str2, ok := add.Y.(*StringExpr) if !ok || strings.Contains(str2.Value, " ") { return } str1.Value += str2.Value // Deleting nodes add and str2. // Merge comments from add, str1, and str2 and save in str1. com1 := add.Comment() com2 := str1.Comment() com3 := str2.Comment() com1.Before = append(com1.Before, com2.Before...) com1.Before = append(com1.Before, com3.Before...) com1.Suffix = append(com1.Suffix, com2.Suffix...) com1.Suffix = append(com1.Suffix, com3.Suffix...) *str1.Comment() = *com1 *p = str1 } labelPrefix := "//" labelRE := leadingSlashesLabelRe if w.StripLabelLeadingSlashes { labelPrefix = "" labelRE = stripLeadingSlashesLabelRe } shortenLabel := func(v Expr) { str, ok := v.(*StringExpr) if !ok { return } if w.StripLabelLeadingSlashes && strings.HasPrefix(str.Value, "//") { if filepath.Dir(f.Path) == "." || !strings.HasPrefix(str.Value, "//:") { str.Value = str.Value[2:] } } if w.ShortenAbsoluteLabelsToRelative { thisPackage := labelPrefix + filepath.Dir(f.Path) // filepath.Dir on Windows uses backslashes as separators, while labels always have slashes. if filepath.Separator != '/' { thisPackage = strings.Replace(thisPackage, string(filepath.Separator), "/", -1) } if str.Value == thisPackage { str.Value = ":" + path.Base(str.Value) } else if strings.HasPrefix(str.Value, thisPackage+":") { str.Value = str.Value[len(thisPackage):] } } m := labelRE.FindStringSubmatch(str.Value) if m == nil { return } if m[4] != "" && m[4] == m[5] { // e.g. //foo:foo str.Value = m[1] } else if m[3] != "" && m[4] == "" && m[3] == m[5] { // e.g. @foo//:foo str.Value = "@" + m[3] } } // Join and shorten labels within a container of labels (which can be a single // label, e.g. a single string expression or a concatenation of them). // Gracefully finish if the argument is of a different type. fixLabelsWithinAContainer := func(e *Expr) { if list, ok := (*e).(*ListExpr); ok { for i := range list.List { if leaveAlone1(list.List[i]) { continue } joinLabel(&list.List[i]) shortenLabel(list.List[i]) } } if set, ok := (*e).(*SetExpr); ok { for i := range set.List { if leaveAlone1(set.List[i]) { continue } joinLabel(&set.List[i]) shortenLabel(set.List[i]) } } else { joinLabel(e) shortenLabel(*e) } } Walk(f, func(v Expr, stk []Expr) { switch v := v.(type) { case *CallExpr: if leaveAlone(stk, v) { return } for i := range v.List { if leaveAlone1(v.List[i]) { continue } as, ok := v.List[i].(*AssignExpr) if !ok { continue } key, ok := as.LHS.(*Ident) if !ok || !w.IsLabelArg[key.Name] || w.LabelDenyList[callName(v)+"."+key.Name] { continue } if leaveAlone1(as.RHS) { continue } findAndModifyStrings(&as.RHS, fixLabelsWithinAContainer) } } }) } // callName returns the name of the rule being called by call. // If the call is not to a literal rule name or a dot expression, callName // returns "". func callName(call *CallExpr) string { return (&Rule{call, ""}).Kind() } // sortCallArgs sorts lists of named arguments to a call. func sortCallArgs(f *File, w *Rewriter) { Walk(f, func(v Expr, stk []Expr) { call, ok := v.(*CallExpr) if !ok { return } if leaveAlone(stk, call) { return } rule := callName(call) if rule == "" { rule = "" } // Find the tail of the argument list with named arguments. start := len(call.List) for start > 0 && argName(call.List[start-1]) != "" { start-- } // Record information about each arg into a sortable list. var args namedArgs for i, x := range call.List[start:] { name := argName(x) args = append(args, namedArg{ruleNamePriority(w, rule, name), name, i, x}) } // Sort the list and put the args back in the new order. if sort.IsSorted(args) { return } sort.Sort(args) for i, x := range args { call.List[start+i] = x.expr } }) } // ruleNamePriority maps a rule argument name to its sorting priority. // It could use the auto-generated per-rule tables but for now it just // falls back to the original list. func ruleNamePriority(w *Rewriter, rule, arg string) int { ruleArg := rule + "." + arg if val, ok := w.NamePriority[ruleArg]; ok { return val } return w.NamePriority[arg] /* list := ruleArgOrder[rule] if len(list) == 0 { return tables.NamePriority[arg] } for i, x := range list { if x == arg { return i } } return len(list) */ } // If x is of the form key=value, argName returns the string key. // Otherwise argName returns "". func argName(x Expr) string { if as, ok := x.(*AssignExpr); ok { if id, ok := as.LHS.(*Ident); ok { return id.Name } } return "" } // A namedArg records information needed for sorting // a named call argument into its proper position. type namedArg struct { priority int // kind of name; first sort key name string // name; second sort key index int // original index; final sort key expr Expr // name=value argument } // namedArgs is a slice of namedArg that implements sort.Interface type namedArgs []namedArg func (x namedArgs) Len() int { return len(x) } func (x namedArgs) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x namedArgs) Less(i, j int) bool { p := x[i] q := x[j] if p.priority != q.priority { return p.priority < q.priority } if p.name != q.name { return p.name < q.name } return p.index < q.index } // sortStringLists sorts lists of string literals used as specific rule or function arguments. func sortStringLists(f *File, w *Rewriter) { sortStringList := func(x *Expr) { SortStringList(*x) } Walk(f, func(e Expr, stk []Expr) { switch v := e.(type) { case *CallExpr: if f.Type == TypeDefault { // Rule parameters, not applicable to default file types return } if leaveAlone(stk, v) { return } if f.Type == TypeBzl { if name, ok := v.X.(*Ident); ok && name.Name == "visibility" { // Call args to the native "visibility function should be sorted per default. for _, arg := range v.List { findAndModifyStrings(&arg, sortStringList) } } return } rule := callName(v) for _, arg := range v.List { if leaveAlone1(arg) { continue } as, ok := arg.(*AssignExpr) if !ok || leaveAlone1(as) { continue } key, ok := as.LHS.(*Ident) if !ok { continue } context := rule + "." + key.Name if w.SortableDenylist[context] { continue } if w.IsSortableListArg[key.Name] || w.SortableAllowlist[context] || (!disabled("unsafesort") && allowedSort(context)) { if doNotSort(as) { deduplicateStringList(as.RHS) } else { findAndModifyStrings(&as.RHS, sortStringList) } } } case *AssignExpr: if disabled("unsafesort") { return } // "keep sorted" comment on x = list forces sorting of list. if keepSorted(v) { findAndModifyStrings(&v.RHS, sortStringList) } case *KeyValueExpr: if disabled("unsafesort") { return } // "keep sorted" before key: list also forces sorting of list. if keepSorted(v) { findAndModifyStrings(&v.Value, sortStringList) } case *ListExpr: if disabled("unsafesort") { return } // "keep sorted" comment above first list element also forces sorting of list. if len(v.List) > 0 && (keepSorted(v) || keepSorted(v.List[0])) { findAndModifyStrings(&e, sortStringList) } } }) } // deduplicateStringList removes duplicates from a list with string expressions // without reordering its elements. // Any suffix-comments are lost, any before- and after-comments are preserved. func deduplicateStringList(x Expr) { list, ok := x.(*ListExpr) if !ok { return } list.List = deduplicateStringExprs(list.List) } // deduplicateStringExprs removes duplicate string expressions from a slice // without reordering its elements. // Any suffix-comments are lost, any before- and after-comments are preserved. func deduplicateStringExprs(list []Expr) []Expr { var comments []Comment alreadySeen := make(map[string]bool) var deduplicated []Expr for _, value := range list { str, ok := value.(*StringExpr) if !ok { deduplicated = append(deduplicated, value) continue } strVal := str.Value if _, ok := alreadySeen[strVal]; ok { // This is a duplicate of a string above. // Collect comments so that they're not lost. comments = append(comments, str.Comment().Before...) comments = append(comments, str.Comment().After...) continue } alreadySeen[strVal] = true if len(comments) > 0 { comments = append(comments, value.Comment().Before...) value.Comment().Before = comments comments = nil } deduplicated = append(deduplicated, value) } return deduplicated } // SortStringList sorts x, a list of strings. // The list is broken by non-strings and by blank lines and comments into chunks. // Each chunk is sorted in place. func SortStringList(x Expr) { list, ok := x.(*ListExpr) if !ok || len(list.List) < 2 { return } if doNotSort(list.List[0]) { list.List = deduplicateStringExprs(list.List) return } forceSort := keepSorted(list) || keepSorted(list.List[0]) // TODO(bazel-team): Decide how to recognize lists that cannot // be sorted. Avoiding all lists with comments avoids sorting // lists that say explicitly, in some form or another, why they // cannot be sorted. For example, many cc_test rules require // certain order in their deps attributes. if !forceSort { if line, _ := hasComments(list); line { deduplicateStringList(list) return } } list.List = sortStringExprs(list.List) } // findAndModifyStrings finds and modifies string lists with a callback // function recursively within the given expression. It doesn't touch all // string lists it can find, but only top-level lists, lists that are parts of // concatenated expressions and lists within select statements. // It calls the callback on the root node and on all relevant inner lists. // The callback function should gracefully return if called with not appropriate // arguments. func findAndModifyStrings(x *Expr, callback func(*Expr)) { callback(x) switch x := (*x).(type) { case *BinaryExpr: if x.Op != "+" { return } findAndModifyStrings(&x.X, callback) findAndModifyStrings(&x.Y, callback) case *CallExpr: if ident, ok := x.X.(*Ident); !ok || ident.Name != "select" { return } if len(x.List) == 0 { return } dict, ok := x.List[0].(*DictExpr) if !ok { return } for _, kv := range dict.List { findAndModifyStrings(&kv.Value, callback) } } } func sortStringExprs(list []Expr) []Expr { if len(list) < 2 { return list } // Sort chunks of the list with no intervening blank lines or comments. for i := 0; i < len(list); { if _, ok := list[i].(*StringExpr); !ok { i++ continue } j := i + 1 for ; j < len(list); j++ { if str, ok := list[j].(*StringExpr); !ok || len(str.Before) > 0 { break } } chunk := make([]stringSortKey, 0, j-i) for index, x := range list[i:j] { chunk = append(chunk, makeSortKey(index, x.(*StringExpr))) } if !sort.IsSorted(byStringExpr(chunk)) || !isUniq(chunk) { before := chunk[0].x.Comment().Before chunk[0].x.Comment().Before = nil sort.Sort(byStringExpr(chunk)) chunk = uniq(chunk) chunk[0].x.Comment().Before = before for offset, key := range chunk { list[i+offset] = key.x } list = append(list[:(i+len(chunk))], list[j:]...) } i = j } return list } // uniq removes duplicates from a list, which must already be sorted. // It edits the list in place. func uniq(sortedList []stringSortKey) []stringSortKey { out := sortedList[:0] for _, sk := range sortedList { if len(out) == 0 || sk.value != out[len(out)-1].value { out = append(out, sk) } } return out } // isUniq reports whether the sorted list only contains unique elements. func isUniq(list []stringSortKey) bool { for i := range list { if i+1 < len(list) && list[i].value == list[i+1].value { return false } } return true } // A stringSortKey records information about a single string literal to be // sorted. The strings are first grouped into four phases: most strings, // strings beginning with ":", strings beginning with "//", and strings // beginning with "@". The next significant part of the comparison is the list // of elements in the value, where elements are split at `.' and `:'. Finally // we compare by value and break ties by original index. type stringSortKey struct { phase int split []string value string original int x Expr } func makeSortKey(index int, x *StringExpr) stringSortKey { key := stringSortKey{ value: x.Value, original: index, x: x, } switch { case strings.HasPrefix(x.Value, ":"): key.phase = 1 case strings.HasPrefix(x.Value, "//") || (tables.StripLabelLeadingSlashes && !strings.HasPrefix(x.Value, "@")): key.phase = 2 case strings.HasPrefix(x.Value, "@"): key.phase = 3 } key.split = strings.Split(strings.Replace(x.Value, ":", ".", -1), ".") return key } // byStringExpr implements sort.Interface for a list of stringSortKey. type byStringExpr []stringSortKey func (x byStringExpr) Len() int { return len(x) } func (x byStringExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byStringExpr) Less(i, j int) bool { xi := x[i] xj := x[j] if xi.phase != xj.phase { return xi.phase < xj.phase } for k := 0; k < len(xi.split) && k < len(xj.split); k++ { if xi.split[k] != xj.split[k] { return xi.split[k] < xj.split[k] } } if len(xi.split) != len(xj.split) { return len(xi.split) < len(xj.split) } if xi.value != xj.value { return xi.value < xj.value } return xi.original < xj.original } // fixMultilinePlus turns // // ... + // [ ... ] // // ... + // call(...) // // into // // ... + [ // ... // ] // // ... + call( // ... // ) // // which typically works better with our aggressively compact formatting. func fixMultilinePlus(f *File, _ *Rewriter) { // List manipulation helpers. // As a special case, we treat f([...]) as a list, mainly // for glob. // isList reports whether x is a list. var isList func(x Expr) bool isList = func(x Expr) bool { switch x := x.(type) { case *ListExpr: return true case *CallExpr: if len(x.List) == 1 { return isList(x.List[0]) } } return false } // isMultiLine reports whether x is a multiline list. var isMultiLine func(Expr) bool isMultiLine = func(x Expr) bool { switch x := x.(type) { case *ListExpr: return x.ForceMultiLine || len(x.List) > 1 case *CallExpr: if x.ForceMultiLine || len(x.List) > 1 && !x.ForceCompact { return true } if len(x.List) == 1 { return isMultiLine(x.List[0]) } } return false } // forceMultiLine tries to force the list x to use a multiline form. // It reports whether it was successful. var forceMultiLine func(Expr) bool forceMultiLine = func(x Expr) bool { switch x := x.(type) { case *ListExpr: // Already multi line? if x.ForceMultiLine { return true } // If this is a list containing a list, force the // inner list to be multiline instead. if len(x.List) == 1 && forceMultiLine(x.List[0]) { return true } x.ForceMultiLine = true return true case *CallExpr: if len(x.List) == 1 { return forceMultiLine(x.List[0]) } } return false } skip := map[Expr]bool{} Walk(f, func(v Expr, stk []Expr) { if skip[v] { return } bin, ok := v.(*BinaryExpr) if !ok || bin.Op != "+" { return } // Found a +. // w + x + y + z parses as ((w + x) + y) + z, // so chase down the left side to make a list of // all the things being added together, separated // by the BinaryExprs that join them. // Mark them as "skip" so that when Walk recurses // into the subexpressions, we won't reprocess them. var all []Expr for { all = append(all, bin.Y, bin) bin1, ok := bin.X.(*BinaryExpr) if !ok || bin1.Op != "+" { break } bin = bin1 skip[bin] = true } all = append(all, bin.X) // Because the outermost expression was the // rightmost one, the list is backward. Reverse it. for i, j := 0, len(all)-1; i < j; i, j = i+1, j-1 { all[i], all[j] = all[j], all[i] } // The 'all' slice is alternating addends and BinaryExpr +'s: // w, +, x, +, y, +, z // If there are no lists involved, don't rewrite anything. haveList := false for i := 0; i < len(all); i += 2 { if isList(all[i]) { haveList = true break } } if !haveList { return } // Okay, there are lists. // Consider each + next to a line break. for i := 1; i < len(all); i += 2 { bin := all[i].(*BinaryExpr) if !bin.LineBreak { continue } // We're going to break the line after the +. // If it is followed by a list, force that to be // multiline instead. if forceMultiLine(all[i+1]) { bin.LineBreak = false continue } // If the previous list was multiline already, // don't bother with the line break after // the +. if isMultiLine(all[i-1]) { bin.LineBreak = false continue } } }) } // isFunctionCall checks whether expr is a call of a function with a given name func isFunctionCall(expr Expr, name string) bool { call, ok := expr.(*CallExpr) if !ok { return false } if ident, ok := call.X.(*Ident); ok && ident.Name == name { return true } return false } // moveLoadOnTop moves all load statements to the top of the file func moveLoadOnTop(f *File, _ *Rewriter) { if f.Type == TypeWorkspace { // Moving load statements in Workspace files can break the semantics return } if isCommentAnywhere(f, "disable=load-on-top") { // For backward compatibility. This rewrite used to be a suppressible warning, // in some cases it's hard to maintain the position of load statements (e.g. // when the file is automatically generated or has automatic transformations // applied to it). The rewrite checks for the comment anywhere in the file // because it's hard to determine which statement is out of order. return } // Find the misplaced load statements misplacedLoads := make(map[int]*LoadStmt) firstStmtIndex := -1 // index of the first seen non-load statement for i := 0; i < len(f.Stmt); i++ { stmt := f.Stmt[i] _, isString := stmt.(*StringExpr) // typically a docstring _, isComment := stmt.(*CommentBlock) isWorkspace := isFunctionCall(stmt, "workspace") if isString || isComment || isWorkspace || stmt == nil { continue } load, ok := stmt.(*LoadStmt) if !ok { if firstStmtIndex == -1 { firstStmtIndex = i } continue } if firstStmtIndex == -1 { continue } misplacedLoads[i] = load } // Calculate a fix if firstStmtIndex == -1 { firstStmtIndex = 0 } offset := len(misplacedLoads) if offset == 0 { return } stmtCopy := append([]Expr{}, f.Stmt...) for i := range f.Stmt { if i < firstStmtIndex { // Docstring or a comment in the beginning, skip continue } else if _, ok := misplacedLoads[i]; ok { // A misplaced load statement, should be moved up to the `firstStmtIndex` position f.Stmt[firstStmtIndex] = stmtCopy[i] firstStmtIndex++ offset-- if offset == 0 { // No more statements should be moved break } } else { // An actual statement (not a docstring or a comment in the beginning), should be moved // `offset` positions down. f.Stmt[i+offset] = stmtCopy[i] } } } // compressSameOriginLoads merges loads from the same source into one load statement func compressSameOriginLoads(f *File, _ *Rewriter) { // Load statements grouped by their source files loads := make(map[string]*LoadStmt) for stmtIndex := 0; stmtIndex < len(f.Stmt); stmtIndex++ { load, ok := f.Stmt[stmtIndex].(*LoadStmt) if !ok { continue } previousLoad, ok := loads[load.Module.Value] if !ok { loads[load.Module.Value] = load continue } if hasComment(previousLoad, "disable=same-origin-load") || hasComment(load, "disable=same-origin-load") { continue } // Move loaded symbols to the existing load statement previousLoad.From = append(previousLoad.From, load.From...) previousLoad.To = append(previousLoad.To, load.To...) // Remove the current statement f.Stmt[stmtIndex] = nil } } // comparePaths compares two strings as if they were paths (the path delimiter, // '/', should be treated as smallest symbol). func comparePaths(path1, path2 string) bool { if path1 == path2 { return false } chunks1 := strings.Split(path1, "/") chunks2 := strings.Split(path2, "/") for i, chunk1 := range chunks1 { if i >= len(chunks2) { return false } chunk2 := chunks2[i] // Compare case-insensitively chunk1Lower := strings.ToLower(chunk1) chunk2Lower := strings.ToLower(chunk2) if chunk1Lower != chunk2Lower { return chunk1Lower < chunk2Lower } } // No case-insensitive difference detected. Likely the difference is just in // the case of some symbols, compare case-sensitively for the determinism. return path1 <= path2 } // compareLoadLabels compares two module names // If one label has explicit repository path (starts with @), it goes first // If the packages are different, labels are sorted by package name (empty package goes first) // If the packages are the same, labels are sorted by their name // Relative labels go last func compareLoadLabels(load1Label, load2Label string) bool { // handle absolute labels with explicit repositories separately to // make sure they precede absolute and relative labels without repos isExplicitRepo1 := strings.HasPrefix(load1Label, "@") isExplicitRepo2 := strings.HasPrefix(load2Label, "@") if isExplicitRepo1 != isExplicitRepo2 { // Exactly one label has an explicit repository name, it should be the first one. return isExplicitRepo1 } // ensure that relative labels go last by giving them a fake package name // that sorts after all valid package names label1 := labels.ParseRelative(load1Label, string(rune(0x7F))) label2 := labels.ParseRelative(load2Label, string(rune(0x7F))) if label1.Repository != label2.Repository { return label1.Repository < label2.Repository } // in case one of the packages is empty, the empty one goes first if (len(label1.Package) == 0) != (len(label2.Package) == 0) { return len(label1.Package) == 0 } // both packages are non-empty and not equal, so compare them if label1.Package != label2.Package { return comparePaths(label1.Package, label2.Package) } // in case both packages are the same, use file names to break ties return comparePaths(label1.Target, label2.Target) } // sortLoadStatements reorders sorts loads lexicographically by the source file, // but absolute loads have priority over local loads. func sortLoadStatements(f *File, _ *Rewriter) { if isCommentAnywhere(f, "disable=out-of-order-load") { // For backward compatibility. This rewrite used to be a suppressible warning, // in some cases it's hard to maintain the position of load statements (e.g. // when the file is automatically generated or has automatic transformations // applied to it). The rewrite checks for the comment anywhere in the file // because it's hard to determine which statement is out of order. return } // Consequent chunks of load statements (i.e. without statements of other types between them) var loadsChunks [][]*LoadStmt newChunk := true // Create a new chunk for the next seen load statement for _, stmt := range f.Stmt { if stmt == nil { // nil statements are no-op and shouldn't break consecutive chains of // load statements continue } load, ok := stmt.(*LoadStmt) if !ok { newChunk = true continue } if newChunk { // There's a non-load statement between this load and the previous load loadsChunks = append(loadsChunks, []*LoadStmt{}) newChunk = false } loadsChunks[len(loadsChunks)-1] = append(loadsChunks[len(loadsChunks)-1], load) } // Sort and flatten the chunks var sortedLoads []*LoadStmt for _, chunk := range loadsChunks { sortedChunk := append([]*LoadStmt{}, chunk...) sort.SliceStable(sortedChunk, func(i, j int) bool { load1Label := (sortedChunk)[i].Module.Value load2Label := (sortedChunk)[j].Module.Value return compareLoadLabels(load1Label, load2Label) }) sortedLoads = append(sortedLoads, sortedChunk...) } // Calculate the replacements loadIndex := 0 for stmtIndex := range f.Stmt { if _, ok := f.Stmt[stmtIndex].(*LoadStmt); !ok { continue } f.Stmt[stmtIndex] = sortedLoads[loadIndex] loadIndex++ } } // sortAllLoadArgs sorts all load arguments in the file func sortAllLoadArgs(f *File, _ *Rewriter) { Walk(f, func(v Expr, stk []Expr) { if load, ok := v.(*LoadStmt); ok { SortLoadArgs(load) } }) } func sortUseRepoPositionals(f *File, _ *Rewriter) { Walk(f, func(v Expr, stk []Expr) { if call, ok := v.(*CallExpr); ok { // The first argument of a valid use_repo call is always a module extension proxy, so we // do not need to sort calls with less than three arguments. if ident, ok := call.X.(*Ident); !ok || ident.Name != "use_repo" || len(call.List) < 3 { return } // Respect the "do not sort" comment on both the first argument and the first repository // name. if doNotSort(call) || doNotSort(call.List[0]) || doNotSort(call.List[1]) { call.List = deduplicateStringExprs(call.List) } else { // Keyword arguments do not have to be sorted here as this has already been done by // the generic callsort rewriter pass. call.List = sortStringExprs(call.List) } } }) } // hasComments reports whether any comments are associated with // the list or its elements. func hasComments(list *ListExpr) (line, suffix bool) { com := list.Comment() if len(com.Before) > 0 || len(com.After) > 0 || len(list.End.Before) > 0 { line = true } if len(com.Suffix) > 0 { suffix = true } for _, elem := range list.List { com := elem.Comment() if len(com.Before) > 0 { line = true } if len(com.Suffix) > 0 { suffix = true } } return } // A wrapper for a LoadStmt's From and To slices for consistent sorting of their contents. // It's assumed that the following slices have the same length. The contents are sorted by // the `To` attribute, but all items with equal "From" and "To" parts are placed before the items // with different parts. type loadArgs struct { From []*Ident To []*Ident modified bool } func (args *loadArgs) Len() int { return len(args.From) } func (args *loadArgs) Swap(i, j int) { args.From[i], args.From[j] = args.From[j], args.From[i] args.To[i], args.To[j] = args.To[j], args.To[i] args.modified = true } func (args *loadArgs) Less(i, j int) bool { // Arguments with equal "from" and "to" parts are prioritized equalI := args.From[i].Name == args.To[i].Name equalJ := args.From[j].Name == args.To[j].Name if equalI != equalJ { // If equalI and !equalJ, return true, otherwise false. // Equivalently, return equalI. return equalI } return args.To[i].Name < args.To[j].Name } // deduplicate assumes that the args are sorted and prioritize arguments that // are defined later (e.g. `a = "x", a = "y"` is shortened to `a = "y"`). func (args *loadArgs) deduplicate() { var from, to []*Ident for i := range args.To { if len(to) > 0 && to[len(to)-1].Name == args.To[i].Name { // Overwrite from[len(from)-1] = args.From[i] to[len(to)-1] = args.To[i] args.modified = true } else { // Just append from = append(from, args.From[i]) to = append(to, args.To[i]) } } args.From = from args.To = to } // SortLoadArgs sorts a load statement arguments (lexicographically, but positional first) func SortLoadArgs(load *LoadStmt) bool { args := loadArgs{From: load.From, To: load.To} sort.Sort(&args) args.deduplicate() load.From = args.From load.To = args.To return args.modified } // formatDocstrings fixes the indentation and trailing whitespace of docstrings func formatDocstrings(f *File, _ *Rewriter) { Walk(f, func(v Expr, stk []Expr) { def, ok := v.(*DefStmt) if !ok || len(def.Body) == 0 { return } docstring, ok := def.Body[0].(*StringExpr) if !ok || !docstring.TripleQuote { return } oldIndentation := docstring.Start.LineRune - 1 // LineRune starts with 1 newIndentation := nestedIndentation * len(stk) // Operate on Token, not Value, because their line breaks can be different if a line ends with // a backslash. updatedToken := formatString(docstring.Token, oldIndentation, newIndentation) if updatedToken != docstring.Token { docstring.Token = updatedToken // Update the value to keep it consistent with Token docstring.Value, _, _ = Unquote(updatedToken) } }) } // formatString modifies a string value of a docstring to match the new indentation level and // to remove trailing whitespace from its lines. func formatString(value string, oldIndentation, newIndentation int) string { difference := newIndentation - oldIndentation lines := strings.Split(value, "\n") for i, line := range lines { if i == 0 { // The first line shouldn't be touched because it starts right after ''' or """ continue } if difference > 0 { line = strings.Repeat(" ", difference) + line } else { for i, rune := range line { if i == -difference || rune != ' ' { line = line[i:] break } } } if i != len(lines)-1 { // Remove trailing space from the line unless it's the last line that's responsible // for the indentation of the closing `"""` line = strings.TrimRight(line, " ") } lines[i] = line } return strings.Join(lines, "\n") } // argumentType returns an integer by which funcall arguments can be sorted: // 1 for positional, 2 for named, 3 for *args, 4 for **kwargs func argumentType(expr Expr) int { switch expr := expr.(type) { case *UnaryExpr: switch expr.Op { case "**": return 4 case "*": return 3 } case *AssignExpr: return 2 } return 1 } // reorderArguments fixes the order of arguments of a function call // (positional, named, *args, **kwargs) func reorderArguments(f *File, _ *Rewriter) { Walk(f, func(expr Expr, stack []Expr) { call, ok := expr.(*CallExpr) if !ok { return } compare := func(i, j int) bool { // Keep nil nodes at their place. They are no-op for the formatter but can // be useful for the linter which expects them to not move. if call.List[i] == nil || call.List[j] == nil { return false } return argumentType(call.List[i]) < argumentType(call.List[j]) } if !sort.SliceIsSorted(call.List, compare) { sort.SliceStable(call.List, compare) } }) } // editOctals inserts 'o' into octal numbers to make it more obvious they are octal // 0123 -> 0o123 func editOctals(f *File, _ *Rewriter) { Walk(f, func(expr Expr, stack []Expr) { l, ok := expr.(*LiteralExpr) if !ok { return } if len(l.Token) > 1 && l.Token[0] == '0' && l.Token[1] >= '0' && l.Token[1] <= '9' { l.Token = "0o" + l.Token[1:] } }) } // editFloats inserts '0' before the decimal point in floats and normalizes the // exponent part to lowercase, no plus sign, and no leading zero. func editFloats(f *File, _ *Rewriter) { Walk(f, func(expr Expr, stack []Expr) { l, ok := expr.(*LiteralExpr) if !ok { return } if !strings.ContainsRune(l.Token, '.') { return } if strings.HasPrefix(l.Token, ".") { l.Token = "0" + l.Token } if !strings.ContainsAny(l.Token, "eE") { return } parts := strings.SplitN(l.Token, "e", 2) if len(parts) != 2 { parts = strings.SplitN(l.Token, "E", 2) } if len(parts) != 2 { // Invalid float, skip rewriting. return } l.Token = parts[0] + "e" + strings.TrimLeft(parts[1], "0+") }) } // removeParens removes trivial parens func removeParens(f *File, _ *Rewriter) { var simplify func(expr Expr, stack []Expr) Expr simplify = func(expr Expr, stack []Expr) Expr { // Look for parenthesized expressions, ignoring those with // comments and those that are intentionally multiline. pa, ok := expr.(*ParenExpr) if !ok || pa.ForceMultiLine { return expr } if len(pa.Comment().Before) > 0 || len(pa.Comment().After) > 0 || len(pa.Comment().Suffix) > 0 { return expr } switch x := pa.X.(type) { case *Comprehension, *DictExpr, *Ident, *ListExpr, *LiteralExpr, *ParenExpr, *SetExpr, *StringExpr: // These expressions don't need parens, remove them (recursively). return Edit(x, simplify) case *CallExpr: // Parens might be needed if the callable is multiline. start, end := x.X.Span() if start.Line == end.Line { return Edit(x, simplify) } } return expr } Edit(f, simplify) } // collapseEmpty unsets ForceMultiLine for empty call expressions. func collapseEmpty(f *File, _ *Rewriter) { Walk(f, func(expr Expr, stack []Expr) { if c, ok := expr.(*CallExpr); ok && len(c.List) == 0 { // No arguments c.ForceMultiLine = false } }) } buildtools-10.1.0/build/parse.y0000664000175000017500000010375415254573013015761 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // BUILD file parser. // This is a yacc grammar. Its lexer is in lex.go. // // For a good introduction to writing yacc grammars, see // Kernighan and Pike's book The Unix Programming Environment. // // The definitive yacc manual is // Stephen C. Johnson and Ravi Sethi, "Yacc: A Parser Generator", // online at http://plan9.bell-labs.com/sys/doc/yacc.pdf. %{ package build %} // The generated parser puts these fields in a struct named yySymType. // (The name %union is historical, but it is inaccurate for Go.) %union { // input tokens tok string // raw input syntax str string // decoding of quoted string pos Position // position of token triple bool // was string triple quoted? // partial syntax trees expr Expr exprs []Expr kv *KeyValueExpr kvs []*KeyValueExpr string *StringExpr ifstmt *IfStmt loadarg *struct{from Ident; to Ident} loadargs []*struct{from Ident; to Ident} def_header *DefStmt // partially filled in def statement, without the body // supporting information comma Position // position of trailing comma in list, if present lastStmt Expr // most recent rule, to attach line comments to } // These declarations set the type for a $ reference ($$, $1, $2, ...) // based on the kind of symbol it refers to. Other fields can be referred // to explicitly, as in $1. // // %token is for input tokens generated by the lexer. // %type is for higher-level grammar rules defined here. // // It is possible to put multiple tokens per line, but it is easier to // keep ordered using a sparser one-per-line list. %token '%' %token '(' %token ')' %token '*' %token '+' %token ',' %token '-' %token '.' %token '/' %token ':' %token '<' %token '=' %token '>' %token '[' %token ']' %token '{' %token '}' %token '|' %token '&' %token '^' %token '~' // By convention, yacc token names are all caps. // However, we do not want to export them from the Go package // we are creating, so prefix them all with underscores. %token _AUGM // augmented assignment %token _AND // keyword and %token _CAST // keyword cast %token _COMMENT // top-level # comment %token _EOF // end of file %token _EQ // operator == %token _FOR // keyword for %token _GE // operator >= %token _IDENT // non-keyword identifier %token _INT // integer number %token _IF // keyword if %token _ELSE // keyword else %token _ELIF // keyword elif %token _IN // keyword in %token _IS // keyword is %token _ISINSTANCE // keyword isinstance %token _LAMBDA // keyword lambda %token _LOAD // keyword load %token _LE // operator <= %token _NE // operator != %token _STAR_STAR // operator ** %token _INT_DIV // operator // %token _BIT_LSH // bitwise operator << %token _BIT_RSH // bitwise operator >> %token _ARROW // functions type annotation -> %token _NOT // keyword not %token _OR // keyword or %token _STRING // quoted string %token _DEF // keyword def %token _RETURN // keyword return %token _PASS // keyword pass %token _BREAK // keyword break %token _CONTINUE // keyword continue %token _INDENT // indentation %token _UNINDENT // unindentation %token _ELLIPSIS // ... token %type comma_opt %type commas %type commas_opt %type argument %type arguments %type arguments_opt %type parameter %type parameters %type parameters_opt %type parameter_type %type parameters_type %type parameters_type_opt %type type_alias_stmt_start %type type_alias_stmt %type type_params_opt %type type_params_idents %type type_exprs_opt %type type_exprs %type type_expr %type type_atom %type type_application %type type_name %type type_list %type type_args_opt %type type_args %type type_arg %type type_dict %type type_keyvalues_opt %type type_keyvalues %type type_keyvalue %type test %type test_opt %type tests_opt %type primary_expr %type expr %type expr_opt %type tests %type loop_vars %type for_clause %type for_clause_with_if_clauses_opt %type for_clauses_with_if_clauses_opt %type ident %type number %type ellipsis %type stmts %type stmt // a simple_stmt or a for/if/def block %type block_stmt // a single for/if/def statement %type if_else_block // a complete if-elif-else block %type if_chain // an elif-elif-else chain %type elif // `elif` or `else if` token(s) %type simple_stmt // One or many small_stmts on one line, e.g. 'a = f(x); return str(a)' %type small_stmt // A single statement, e.g. 'a = f(x)' %type small_stmts_continuation // A sequence of `';' small_stmt` %type keyvalue %type keyvalues %type keyvalues_no_comma %type string %type suite %type comments %type load_argument %type load_arguments %type def_header %type def_header_type_opt // Operator precedence. // Operators listed lower in the table bind tighter. // We tag rules with this fake, low precedence to indicate // that when the rule is involved in a shift/reduce // conflict, we prefer that the parser shift (try for a longer parse). // Shifting is the default resolution anyway, but stating it explicitly // silences yacc's warning for that specific case. %left ShiftInstead %left '\n' %left _ASSERT // '=' and augmented assignments have the lowest precedence // e.g. "x = a if c > 0 else 'bar'" // followed by // 'if' and 'else' which have lower precedence than all other operators. // e.g. "a, b if c > 0 else 'foo'" is either a tuple of (a,b) or 'foo' // and not a tuple of "(a, (b if ... ))" %left '=' _AUGM %left _IF _ELSE _ELIF %left ',' %left ':' %left _IS %left _OR %left _AND %left '<' '>' _EQ _NE _LE _GE _NOT _IN %left '|' %left '^' %left '&' %left _BIT_LSH _BIT_RSH %left '+' '-' %left '*' '/' '%' _INT_DIV %left '.' '[' '(' %left _STRING %right _UNARY %% // Grammar rules. // // A note on names: if foo is a rule, then foos is a sequence of foos // (with interleaved commas or other syntax as appropriate) // and foo_opt is an optional foo. file: stmts _EOF { yylex.(*input).file = &File{Stmt: $1} return 0 } suite: '\n' comments _INDENT stmts _UNINDENT { statements := $4 if $2 != nil { // $2 can only contain *CommentBlock objects, each of them contains a non-empty After slice cb := $2[len($2)-1].(*CommentBlock) // $4 can't be empty and can't start with a comment stmt := $4[0] start, _ := stmt.Span() if start.Line - cb.After[len(cb.After)-1].Start.Line == 1 { // The first statement of $4 starts on the next line after the last comment of $2. // Attach the last comment to the first statement stmt.Comment().Before = cb.After $2 = $2[:len($2)-1] } statements = append($2, $4...) } $$ = statements $$ = $4 } | simple_stmt linebreaks_opt %prec ShiftInstead { $$ = $1 } linebreaks_opt: | linebreaks_opt '\n' comments: { $$ = nil $$ = nil } | comments _COMMENT '\n' { $$ = $1 $$ = $1 if $$ == nil { cb := &CommentBlock{Start: $2} $$ = append($$, cb) $$ = cb } com := $$.Comment() com.After = append(com.After, Comment{Start: $2, Token: $2}) } | comments '\n' { $$ = $1 $$ = nil } stmts: { $$ = nil $$ = nil } | stmts stmt { // If this statement follows a comment block, // attach the comments to the statement. if cb, ok := $1.(*CommentBlock); ok { $$ = append($1[:len($1)-1], $2...) $2[0].Comment().Before = cb.After $$ = $2 break } // Otherwise add to list. $$ = append($1, $2...) $$ = $2 // Consider this input: // // foo() // # bar // baz() // // If we've just parsed baz(), the # bar is attached to // foo() as an After comment. Make it a Before comment // for baz() instead. if x := $1; x != nil { com := x.Comment() // stmt is never empty $2[0].Comment().Before = com.After com.After = nil } } | stmts '\n' { // Blank line; sever last rule from future comments. $$ = $1 $$ = nil } | stmts _COMMENT '\n' { $$ = $1 $$ = $1 if $$ == nil || isBlockStmt($$) { // Comments after a block statement (e.g. a compact `def f(): pass`) // must become a standalone CommentBlock, matching how the same // comment is parsed when the block is written in its expanded, // indented form (see extractTrailingComments). Attaching it to the // block's After list instead makes the printer emit it without the // blank line that separates a block from a trailing comment, so // formatting would need a second pass to become stable. cb := &CommentBlock{Start: $2} $$ = append($$, cb) $$ = cb } com := $$.Comment() com.After = append(com.After, Comment{Start: $2, Token: $2}) } stmt: simple_stmt { $$ = $1 $$ = $1[len($1)-1] } | block_stmt { $$ = []Expr{$1} $$ = $1 if cbs := extractTrailingComments($1); len(cbs) > 0 { $$ = append($$, cbs...) $$ = cbs[len(cbs)-1] if $1 == nil { $$ = nil } } } def_header: _DEF _IDENT type_params_opt '(' parameters_type_opt ')' { $$ = &DefStmt{ Function: Function{ StartPos: $1, Params: $5, }, Name: $2, TypeParams: $3, ParamsEnd: &End{Pos: $6}, ForceCompact: forceCompact($4, $5, $6), ForceMultiLine: forceMultiLine($4, $5, $6), } } def_header_type_opt: def_header | def_header _ARROW type_expr { $1.Type = $3 $$ = $1 } block_stmt: def_header_type_opt ':' suite { $1.Function.Body = $3 $1.ColonPos = &End{Pos: $2} $$ = $1 $$ = $3 } | _FOR loop_vars _IN expr ':' suite { $$ = &ForStmt{ For: $1, Vars: $2, X: $4, Body: $6, } $$ = $6 } | if_else_block { $$ = $1 $$ = $1 } // One or several if-elif-elif statements if_chain: _IF expr ':' suite { $$ = &IfStmt{ If: $1, Cond: $2, True: $4, } $$ = $4 } | if_chain elif expr ':' suite { $$ = $1 inner := $1 for len(inner.False) == 1 { inner = inner.False[0].(*IfStmt) } inner.ElsePos = End{Pos: $2} inner.False = []Expr{ &IfStmt{ If: $2, Cond: $3, True: $5, }, } $$ = $5 } // A complete if-elif-elif-else chain if_else_block: if_chain | if_chain _ELSE ':' suite { $$ = $1 inner := $1 for len(inner.False) == 1 { inner = inner.False[0].(*IfStmt) } inner.ElsePos = End{Pos: $2} inner.False = $4 $$ = $4 } elif: _ELSE _IF | _ELIF simple_stmt: small_stmt small_stmts_continuation semi_opt '\n' { $$ = append([]Expr{$1}, $2...) $$ = $$[len($$)-1] } small_stmts_continuation: { $$ = []Expr{} } | small_stmts_continuation ';' small_stmt { $$ = append($1, $3) } small_stmt: expr %prec ShiftInstead | _RETURN expr { $$ = &ReturnStmt{ Return: $1, Result: $2, } } | _RETURN { $$ = &ReturnStmt{ Return: $1, } } | expr '=' expr { $$ = binary($1, $2, $2, $3) } | ident ':' type_expr '=' expr { $$ = binary(typed($1, $3), $4, $4, $5) } | ident ':' type_expr { $$ = typed($1, $3) } | type_alias_stmt | expr _AUGM expr { $$ = binary($1, $2, $2, $3) } | _PASS { $$ = &BranchStmt{ Token: $1, TokenPos: $1, } } | _BREAK { $$ = &BranchStmt{ Token: $1, TokenPos: $1, } } | _CONTINUE { $$ = &BranchStmt{ Token: $1, TokenPos: $1, } } // Split out of type_alias_stmt to improve error handling: // on two adjacent idents, e.g. "foo bar", we want to emit the error at "bar". type_alias_stmt_start: ident ident { if $1.(*Ident).Name != "type" { // two idents can be adjacent only if the first one is `type`. _, end := $2.Span() errorAt(yylex, end, "syntax error near " + $2.(*Ident).Name) } $$ = &TypeAliasStmt{ TypePos: $1.(*Ident).NamePos, Name: $2, // Rest of fields will be filled in by type_alias_stmt } } type_alias_stmt: type_alias_stmt_start type_params_opt '=' type_expr { typeStart, _ := $4.Span() // Modify $1 in-place to fill in the remaining fields. typeAlisStmt := $1.(*TypeAliasStmt) typeAlisStmt.TypeParams = $2 typeAlisStmt.EqualPos = $3 typeAlisStmt.Type = $4 typeAlisStmt.LineBreak = $3.Line < typeStart.Line $$ = typeAlisStmt } type_params_opt: { $$ = nil } | '[' type_params_idents comma_opt ']' { $$ = &ListExpr{ Start: $1, List: $2, End: End{Pos: $4}, ForceMultiLine: forceMultiLine($1, $2, $4), } } type_params_idents: ident { $$ = []Expr{$1} } | type_params_idents ',' ident { $$ = append($1, $3) } semi_opt: | ';' primary_expr: ident | number | ellipsis | string { $$ = $1 } | primary_expr '.' _IDENT { $$ = &DotExpr{ X: $1, Dot: $2, NamePos: $3, Name: $3, } } | _LOAD '(' commas_opt string commas load_arguments commas_opt ')' { load := &LoadStmt{ Load: $1, Module: $4, Rparen: End{Pos: $8}, ForceCompact: $2.Line == $8.Line, } for _, arg := range $6 { load.From = append(load.From, &arg.from) load.To = append(load.To, &arg.to) } $$ = load } | _CAST '(' type_expr commas argument commas_opt ')' { args := []Expr{$3, $5} $$ = &CallExpr{ CallExprKind: CallExprCast, X: &Ident{NamePos: $1, Name: $1}, ListStart: $2, List: args, End: End{Pos: $7}, ForceCompact: forceCompact($2, args, $7), ForceMultiLine: forceMultiLine($2, args, $7), } } | _ISINSTANCE '(' argument commas type_expr commas_opt ')' { args := []Expr{$3, $5} $$ = &CallExpr{ CallExprKind: CallExprIsInstance, X: &Ident{NamePos: $1, Name: $1}, ListStart: $2, List: args, End: End{Pos: $7}, ForceCompact: forceCompact($2, args, $7), ForceMultiLine: forceMultiLine($2, args, $7), } } | primary_expr '(' arguments_opt ')' { $$ = &CallExpr{ X: $1, ListStart: $2, List: $3, End: End{Pos: $4}, ForceCompact: forceCompact($2, $3, $4), ForceMultiLine: forceMultiLine($2, $3, $4), } } | primary_expr '[' expr ']' { $$ = &IndexExpr{ X: $1, IndexStart: $2, Y: $3, End: $4, } } | primary_expr '[' expr_opt ':' test_opt ']' { $$ = &SliceExpr{ X: $1, SliceStart: $2, From: $3, FirstColon: $4, To: $5, End: $6, } } | primary_expr '[' expr_opt ':' test_opt ':' test_opt ']' { $$ = &SliceExpr{ X: $1, SliceStart: $2, From: $3, FirstColon: $4, To: $5, SecondColon: $6, Step: $7, End: $8, } } | '[' tests_opt ']' { $$ = &ListExpr{ Start: $1, List: $2, End: End{Pos: $3}, ForceMultiLine: forceMultiLine($1, $2, $3), } } | '[' test for_clauses_with_if_clauses_opt ']' { $$ = &Comprehension{ Curly: false, Lbrack: $1, Body: $2, Clauses: $3, End: End{Pos: $4}, ForceMultiLine: forceMultiLineComprehension($1, $2, $3, $4), } } | '{' keyvalue for_clauses_with_if_clauses_opt '}' { $$ = &Comprehension{ Curly: true, Lbrack: $1, Body: $2, Clauses: $3, End: End{Pos: $4}, ForceMultiLine: forceMultiLineComprehension($1, $2, $3, $4), } } | '{' keyvalues '}' { exprValues := make([]Expr, 0, len($2)) for _, kv := range $2 { exprValues = append(exprValues, Expr(kv)) } $$ = &DictExpr{ Start: $1, List: $2, End: End{Pos: $3}, ForceMultiLine: forceMultiLine($1, exprValues, $3), } } | '{' tests comma_opt '}' { $$ = &SetExpr{ Start: $1, List: $2, End: End{Pos: $4}, ForceMultiLine: forceMultiLine($1, $2, $4), } } | '(' tests_opt ')' { if len($2) == 1 && $2.Line == 0 { // Just a parenthesized expression, not a tuple. $$ = &ParenExpr{ Start: $1, X: $2[0], End: End{Pos: $3}, ForceMultiLine: forceMultiLine($1, $2, $3), } } else { $$ = &TupleExpr{ Start: $1, List: $2, End: End{Pos: $3}, ForceCompact: forceCompact($1, $2, $3), ForceMultiLine: forceMultiLine($1, $2, $3), } } } arguments_opt: { $$ = nil } | arguments commas_opt { $$ = $1 } arguments: commas_opt argument { $$ = []Expr{$2} } | arguments commas argument { $$ = append($1, $3) } argument: test | ident '=' test { $$ = binary($1, $2, $2, $3) } | '*' test { $$ = unary($1, $1, $2) } | _STAR_STAR test { $$ = unary($1, $1, $2) } load_arguments: load_argument { $$ = []*struct{from Ident; to Ident}{$1} } | load_arguments ',' load_argument { $1 = append($1, $3) $$ = $1 } load_argument: string { start := $1.Start.add("'") if $1.TripleQuote { start = start.add("''") } $$ = &struct{from Ident; to Ident}{ from: Ident{ Name: $1.Value, NamePos: start, }, to: Ident{ Name: $1.Value, NamePos: start, }, } } | ident '=' string { start := $3.Start.add("'") if $3.TripleQuote { start = start.add("''") } $$ = &struct{from Ident; to Ident}{ from: Ident{ Name: $3.Value, NamePos: start, }, to: *$1.(*Ident), } } parameters_opt: { $$ = nil } | parameters comma_opt { $$ = $1 } parameters_type_opt: { $$ = nil } | parameters_type comma_opt { $$ = $1 } parameters: parameter { $$ = []Expr{$1} } | parameters ',' parameter { $$ = append($1, $3) } // Parameters with optional type annotations parameters_type: parameter_type { $$ = []Expr{$1} } | parameters_type ',' parameter_type { $$ = append($1, $3) } parameter: ident | ident '=' test { $$ = binary($1, $2, $2, $3) } | '*' ident { $$ = unary($1, $1, $2) } | '*' { $$ = unary($1, $1, nil) } | _STAR_STAR ident { $$ = unary($1, $1, $2) } // Parameter with optional type annotation parameter_type: parameter | ident ':' type_expr { $$ = typed($1, $3) } | ident ':' type_expr '=' test { $$ = binary(typed($1, $3), $4, $4, $5) } | '*' ident ':' type_expr { $$ = unary($1, $1, typed($2, $4)) } | _STAR_STAR ident ':' type_expr { $$ = unary($1, $1, typed($2, $4)) } expr: test %prec ShiftInstead | expr ',' test { tuple, ok := $1.(*TupleExpr) if !ok || !tuple.NoBrackets { tuple = &TupleExpr{ List: []Expr{$1}, NoBrackets: true, ForceCompact: true, ForceMultiLine: false, } } tuple.List = append(tuple.List, $3) $$ = tuple } expr_opt: { $$ = nil } | expr test: primary_expr | _LAMBDA parameters_opt ':' expr { $$ = &LambdaExpr{ Function: Function{ StartPos: $1, Params: $2, Body: []Expr{$4}, }, } } | _NOT test %prec _UNARY { $$ = unary($1, $1, $2) } | '-' test %prec _UNARY { $$ = unary($1, $1, $2) } | '+' test %prec _UNARY { $$ = unary($1, $1, $2) } | '~' test %prec _UNARY { $$ = unary($1, $1, $2) } | test '*' test { $$ = binary($1, $2, $2, $3) } | test '%' test { $$ = binary($1, $2, $2, $3) } | test '/' test { $$ = binary($1, $2, $2, $3) } | test _INT_DIV test { $$ = binary($1, $2, $2, $3) } | test '+' test { $$ = binary($1, $2, $2, $3) } | test '-' test { $$ = binary($1, $2, $2, $3) } | test '<' test { $$ = binary($1, $2, $2, $3) } | test '>' test { $$ = binary($1, $2, $2, $3) } | test _EQ test { $$ = binary($1, $2, $2, $3) } | test _LE test { $$ = binary($1, $2, $2, $3) } | test _NE test { $$ = binary($1, $2, $2, $3) } | test _GE test { $$ = binary($1, $2, $2, $3) } | test _IN test { $$ = binary($1, $2, $2, $3) } | test _NOT _IN test { $$ = binary($1, $2, "not in", $4) } | test _OR test { $$ = binary($1, $2, $2, $3) } | test _AND test { $$ = binary($1, $2, $2, $3) } | test '|' test { $$ = binary($1, $2, $2, $3) } | test '&' test { $$ = binary($1, $2, $2, $3) } | test '^' test { $$ = binary($1, $2, $2, $3) } | test _BIT_LSH test { $$ = binary($1, $2, $2, $3) } | test _BIT_RSH test { $$ = binary($1, $2, $2, $3) } | test _IS test { if b, ok := $3.(*UnaryExpr); ok && b.Op == "not" { $$ = binary($1, $2, "is not", b.X) } else { $$ = binary($1, $2, $2, $3) } } | test _IF test _ELSE test { $$ = &ConditionalExpr{ Then: $1, IfStart: $2, Test: $3, ElseStart: $4, Else: $5, } } tests: test { $$ = []Expr{$1} } | tests commas test { $$ = append($1, $3) } test_opt: { $$ = nil } | test tests_opt: { $$, $$ = nil, Position{} } | tests commas_opt { $$, $$ = $1, $2 } // comma_opt is an optional comma. If the comma is present, // the rule's value is the position of the comma. Otherwise // the rule's value is the zero position. Tracking this // lets us distinguish (x) and (x,). comma_opt: { $$ = Position{} } | ',' // commas allows us to treat multiple consecutive commas as if they are a single // comma token. This is a syntax error in bazel, but a common user error, so it // is convenient to automatically fix it. commas: ',' | commas ',' { $$ = $1 } // commas_opt is the one-or-more comma equivalent of comma_opt, and is used // where trailing commas have some significance. Like commas they squash down // to a single comma if present to fix a common user error. commas_opt: { $$ = Position{} } | commas keyvalue: test ':' test { $$ = &KeyValueExpr{ Key: $1, Colon: $2, Value: $3, } } keyvalues_no_comma: keyvalue { $$ = []*KeyValueExpr{$1} } | keyvalues_no_comma commas keyvalue { $$ = append($1, $3) } keyvalues: { $$ = nil } | keyvalues_no_comma { $$ = $1 } | keyvalues_no_comma commas { $$ = $1 } loop_vars: primary_expr | loop_vars ',' primary_expr { tuple, ok := $1.(*TupleExpr) if !ok || !tuple.NoBrackets { tuple = &TupleExpr{ List: []Expr{$1}, NoBrackets: true, ForceCompact: true, ForceMultiLine: false, } } tuple.List = append(tuple.List, $3) $$ = tuple } string: _STRING { $$ = &StringExpr{ Start: $1, Value: $1, TripleQuote: $1, End: $1.add($1), Token: $1, } } ident: _IDENT { $$ = &Ident{NamePos: $1, Name: $1} } number: _INT '.' _INT { $$ = &LiteralExpr{Start: $1, Token: $1 + "." + $3} } | _INT '.' { $$ = &LiteralExpr{Start: $1, Token: $1 + "."} } | '.' _INT { $$ = &LiteralExpr{Start: $1, Token: "." + $2} } | _INT %prec ShiftInstead { $$ = &LiteralExpr{Start: $1, Token: $1} } ellipsis: _ELLIPSIS { $$ = &EllipsisExpr{ Pos: $1, } } for_clause: _FOR loop_vars _IN test { $$ = &ForClause{ For: $1, Vars: $2, In: $3, X: $4, } } for_clause_with_if_clauses_opt: for_clause { $$ = []Expr{$1} } | for_clause_with_if_clauses_opt _IF test { $$ = append($1, &IfClause{ If: $2, Cond: $3, }) } for_clauses_with_if_clauses_opt: for_clause_with_if_clauses_opt { $$ = $1 } | for_clauses_with_if_clauses_opt for_clause_with_if_clauses_opt { $$ = append($1, $2...) } type_exprs_opt: { $$ = nil } | type_exprs commas_opt { $$ = $1 } type_exprs: type_expr { $$ = []Expr{$1} } | type_exprs commas type_expr { $$ = append($1, $3) } type_expr: type_atom { $$ = $1 } | type_expr '|' type_atom { $$ = binary($1, $2, $2, $3) } type_atom: type_name | type_application | '(' type_exprs_opt ')' { if len($2) == 1 && $2.Line == 0 { // Just a parenthesized type expression, not a tuple; // useless in type syntax, but permitted. $$ = &ParenExpr{ Start: $1, X: $2[0], End: End{Pos: $3}, ForceMultiLine: forceMultiLine($1, $2, $3), } } else { $$ = &TupleExpr{ Start: $1, List: $2, End: End{Pos: $3}, ForceCompact: forceCompact($1, $2, $3), ForceMultiLine: forceMultiLine($1, $2, $3), } } } type_name: ident | type_name '.' _IDENT { $$ = &DotExpr{ X: $1, Dot: $2, NamePos: $3, Name: $3, } } type_application: type_name '[' type_args ']' { $$ = &TypeAppExpr{ Type: $1, ArgsStart: $2, Args: $3, End: End{Pos: $4}, ForceCompact: forceCompact($2, $3, $4), ForceMultiLine: forceMultiLine($2, $3, $4), } } type_list: '[' type_args_opt ']' { $$ = &ListExpr{ Start: $1, List: $2, End: End{Pos: $3}, ForceMultiLine: forceMultiLine($1, $2, $3), } } type_args_opt: { $$ = nil } | type_args commas_opt { $$ = $1 } type_args: type_arg { $$ = []Expr{$1} } | type_args commas type_arg { $$ = append($1, $3) } type_arg: type_expr | type_list | type_dict | ellipsis type_dict: '{' type_keyvalues_opt '}' { exprValues := make([]Expr, 0, len($2)) for _, kv := range $2 { exprValues = append(exprValues, Expr(kv)) } $$ = &DictExpr{ Start: $1, List: $2, End: End{Pos: $3}, ForceMultiLine: forceMultiLine($1, exprValues, $3), } } type_keyvalues_opt: { $$ = nil } | type_keyvalues commas_opt { $$ = $1 } type_keyvalues: type_keyvalue { $$ = []*KeyValueExpr{$1} } | type_keyvalues commas type_keyvalue { $$ = append($1, $3) } type_keyvalue: string ':' type_arg { $$ = &KeyValueExpr{ Key: $1, Colon: $2, Value: $3, } } %% // Go helper code. // unary returns a unary expression with the given // position, operator, and subexpression. func unary(pos Position, op string, x Expr) Expr { return &UnaryExpr{ OpStart: pos, Op: op, X: x, } } // binary returns a binary expression with the given // operands, position, and operator. func binary(x Expr, pos Position, op string, y Expr) Expr { _, xend := x.Span() ystart, _ := y.Span() switch op { case "=", "+=", "-=", "*=", "/=", "//=", "%=", "&=", "|=", "^=", "<<=", ">>=": return &AssignExpr{ LHS: x, OpPos: pos, Op: op, LineBreak: xend.Line < ystart.Line, RHS: y, } } return &BinaryExpr{ X: x, OpStart: pos, Op: op, LineBreak: xend.Line < ystart.Line, Y: y, } } // typed returns a TypedIdent expression func typed(x, y Expr) *TypedIdent { return &TypedIdent{ Ident: x, Type: y, } } // isSimpleExpression returns whether an expression is simple and allowed to exist in // compact forms of sequences. // The formal criteria are the following: an expression is considered simple if it's // a literal (variable, string or a number), a literal with a unary operator or an empty sequence. func isSimpleExpression(expr *Expr) bool { switch x := (*expr).(type) { case *LiteralExpr, *StringExpr, *Ident: return true case *UnaryExpr: _, literal := x.X.(*LiteralExpr) _, ident := x.X.(*Ident) return literal || ident case *ListExpr: return len(x.List) == 0 case *TupleExpr: return len(x.List) == 0 case *DictExpr: return len(x.List) == 0 case *SetExpr: return len(x.List) == 0 case *EllipsisExpr: return true default: return false } } // forceCompact returns the setting for the ForceCompact field for a call or tuple. // // NOTE 1: The field is called ForceCompact, not ForceSingleLine, // because it only affects the formatting associated with the call or tuple syntax, // not the formatting of the arguments. For example: // // call([ // 1, // 2, // 3, // ]) // // is still a compact call even though it runs on multiple lines. // // In contrast the multiline form puts a linebreak after the (. // // call( // [ // 1, // 2, // 3, // ], // ) // // NOTE 2: Because of NOTE 1, we cannot use start and end on the // same line as a signal for compact mode: the formatting of an // embedded list might move the end to a different line, which would // then look different on rereading and cause buildifier not to be // idempotent. Instead, we have to look at properties guaranteed // to be preserved by the reformatting, namely that the opening // paren and the first expression are on the same line and that // each subsequent expression begins on the same line as the last // one ended (no line breaks after comma). func forceCompact(start Position, list []Expr, end Position) bool { if len(list) <= 1 { // The call or tuple will probably be compact anyway; don't force it. return false } // If there are any named arguments or non-string, non-literal // arguments, cannot force compact mode. line := start.Line for _, x := range list { start, end := x.Span() if start.Line != line { return false } line = end.Line if !isSimpleExpression(&x) { return false } } return end.Line == line } // forceMultiLine returns the setting for the ForceMultiLine field. func forceMultiLine(start Position, list []Expr, end Position) bool { if len(list) > 1 { // The call will be multiline anyway, because it has multiple elements. Don't force it. return false } if len(list) == 0 { // Empty list: use position of brackets. return start.Line != end.Line } // Single-element list. // Check whether opening bracket is on different line than beginning of // element, or closing bracket is on different line than end of element. elemStart, elemEnd := list[0].Span() return start.Line != elemStart.Line || end.Line != elemEnd.Line } // forceMultiLineComprehension returns the setting for the ForceMultiLine field for a comprehension. func forceMultiLineComprehension(start Position, expr Expr, clauses []Expr, end Position) bool { // Return true if there's at least one line break between start, expr, each clause, and end exprStart, exprEnd := expr.Span() if start.Line != exprStart.Line { return true } previousEnd := exprEnd for _, clause := range clauses { clauseStart, clauseEnd := clause.Span() if previousEnd.Line != clauseStart.Line { return true } previousEnd = clauseEnd } return previousEnd.Line != end.Line } // isBlockStmt reports whether x is a statement with an indentable body // (def, for, or if). Line comments that follow such statements should form // standalone CommentBlock statements rather than being attached to the block // as After-comments, matching how the same comments are parsed when the // block is written in its expanded form (see extractTrailingComments). func isBlockStmt(x Expr) bool { switch x.(type) { case *DefStmt, *ForStmt, *IfStmt: return true } return false } // extractTrailingComments extracts trailing comments of an indented block starting with the first // comment line with indentation less than the block indentation. // The comments can either belong to CommentBlock statements or to the last non-comment statement // as After-comments. func extractTrailingComments(stmt Expr) []Expr { body := getLastBody(stmt) var comments []Expr if body != nil && len(*body) > 0 { // Get the current indentation level start, _ := (*body)[0].Span() indentation := start.LineRune // Find the last non-comment statement lastNonCommentIndex := -1 for i, stmt := range *body { if _, ok := stmt.(*CommentBlock); !ok { lastNonCommentIndex = i } } if lastNonCommentIndex == -1 { return comments } // Iterate over the trailing comments, find the first comment line that's not indented enough, // dedent it and all the following comments. for i := lastNonCommentIndex; i < len(*body); i++ { stmt := (*body)[i] if comment := extractDedentedComment(stmt, indentation); comment != nil { // This comment and all the following CommentBlock statements are to be extracted. comments = append(comments, comment) comments = append(comments, (*body)[i+1:]...) *body = (*body)[:i+1] // If the current statement is a CommentBlock statement without any comment lines // it should be removed too. if i > lastNonCommentIndex && len(stmt.Comment().After) == 0 { *body = (*body)[:i] } } } } return comments } // extractDedentedComment extract the first comment line from `stmt` which indentation is smaller // than `indentation`, and all following comment lines, and returns them in a newly created // CommentBlock statement. func extractDedentedComment(stmt Expr, indentation int) Expr { for i, line := range stmt.Comment().After { // line.Start.LineRune == 0 can't exist in parsed files, it indicates that the comment line // has been added by an AST modification. Don't take such lines into account. if line.Start.LineRune > 0 && line.Start.LineRune < indentation { // This and all the following lines should be dedented cb := &CommentBlock{ Start: line.Start, Comments: Comments{After: stmt.Comment().After[i:]}, } stmt.Comment().After = stmt.Comment().After[:i] return cb } } return nil } // getLastBody returns the last body of a block statement (the only body for For- and DefStmt // objects, the last in a if-elif-else chain func getLastBody(stmt Expr) *[]Expr { switch block := stmt.(type) { case *DefStmt: return &block.Body case *ForStmt: return &block.Body case *IfStmt: if len(block.False) == 0 { return &block.True } else if len(block.False) == 1 { if next, ok := block.False[0].(*IfStmt); ok { // Recursively find the last block of the chain return getLastBody(next) } } return &block.False } return nil } // Expose lex.ErrorAt to the parser. type yyLexerWithErrorAt interface { ErrorAt(pos Position, s string) } func errorAt(yylex yyLexer, pos Position, s string) { if lex, ok := yylex.(yyLexerWithErrorAt); ok { lex.ErrorAt(pos, s) } else { yylex.Error(s) } } buildtools-10.1.0/build/print_test.go0000664000175000017500000004305415254573013017173 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 build import ( "bytes" "fmt" "os" "path/filepath" "reflect" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/tables" "github.com/bazel-contrib/buildtools/v10/testutils" ) // List of test files that have irreversible changes (e.g. if a list is sorted only for Bzl, // the tests TestPrintBuildAsBzl and TestPrintBzlAsBuild will fail as the list will not be // un-sorted by formatting it in the reverse order). var hasIrreversibleChanges = map[string]bool{ "testdata/077": true, } // exists reports whether the named file exists. func exists(name string) bool { _, err := os.Stat(name) return err == nil } // setFlags sets flags based on the test file name (except the --type flag) func setFlags(file string) func() { if strings.Contains(file, ".stripslashes.") { tables.StripLabelLeadingSlashes = true } // Test file 050 tests the ShortenAbsoluteLabelsToRelative behavior, all other tests assume that ShortenAbsoluteLabelsToRelative is false. if strings.Contains(file, string(os.PathSeparator)+"050.") { tables.ShortenAbsoluteLabelsToRelative = true } return func() { tables.StripLabelLeadingSlashes = false tables.ShortenAbsoluteLabelsToRelative = false } } func testIdempotence(t *testing.T, file string, fileType FileType) { defer setFlags(file)() testPrint(t, file, file, fileType) } func testFormat(t *testing.T, input, output string, fileType FileType) { defer setFlags(output)() testPrint(t, input, output, fileType) } // Test that reading and then writing the golden files // does not change their output. func TestPrintGolden(t *testing.T) { outs, chdir := findTests(t, ".golden") defer chdir() // Run the tests with --type=build for _, out := range outs { if !strings.HasSuffix(out, ".build.golden") { continue } testIdempotence(t, out, TypeBuild) } // Run the same tests with --type=bzl for _, out := range outs { if !strings.HasSuffix(out, ".bzl.golden") { continue } testIdempotence(t, out, TypeBzl) } } // Test that formatting the input files produces the golden files. func TestPrintRewrite(t *testing.T) { ins, chdir := findTests(t, ".in") defer chdir() for _, in := range ins { prefix := in[:len(in)-len(".in")] if out := prefix + ".golden"; exists(out) { // Tests with a single .golden suffix test that build, bzl and default // all result in same output. testFormat(t, in, out, TypeDefault) testFormat(t, in, out, TypeBzl) testFormat(t, in, out, TypeBuild) } if out := prefix + ".default.golden"; exists(out) { testFormat(t, in, out, TypeDefault) } if out := prefix + ".bzl.golden"; exists(out) { testFormat(t, in, out, TypeBzl) } if out := prefix + ".build.golden"; exists(out) { testFormat(t, in, out, TypeBuild) } if out := prefix + ".stripslashes.golden"; exists(out) { // Stripslashes in BUILD mode only. testFormat(t, in, out, TypeBuild) } } } // Test that attempting to format an incorrect file throws a syntax error func TestSyntaxError(t *testing.T) { ins, chdir := findTests(t, ".error") defer chdir() parsers := map[string]func(string, []byte) (*File, error){ "bzl": ParseBzl, "default": ParseDefault, } for _, in := range ins { data, err := os.ReadFile(in) if err != nil { t.Error(err) return } for name, parser := range parsers { _, err := parser(in, data) if err == nil { t.Errorf("Expected a syntax error for %q (type %q)", in, name) return } } } } // Test that golden files for the build mode aren't modified if reformatted in bzl mode func TestPrintBuildAsBzl(t *testing.T) { ins, chdir := findTests(t, ".in") defer chdir() for _, in := range ins { prefix := in[:len(in)-len(".in")] if hasIrreversibleChanges[prefix] { continue } outBuild := prefix + ".build.golden" if !exists(outBuild) { continue } testIdempotence(t, outBuild, TypeBzl|TypeDefault) } } // Test that golden files for the bzl become golden files for the build mode after reformatting // in the build mode func TestPrintBzlAsBuild(t *testing.T) { ins, chdir := findTests(t, ".in") defer chdir() for _, in := range ins { prefix := in[:len(in)-len(".in")] if hasIrreversibleChanges[prefix] { continue } outBuild := prefix + ".build.golden" outBzl := prefix + ".bzl.golden" if !exists(outBuild) { continue } testFormat(t, outBzl, outBuild, TypeBuild) } } // findTests finds all files of the passed suffix in the build/testdata directory. // It changes the working directory to be the directory containing the `testdata` directory, // and returns a function to call to change back to the current directory. // This allows tests to assert on alias finding between absolute and relative labels. func findTests(t *testing.T, suffix string) ([]string, func()) { return testutils.FindTests(t, "build", "testdata/*"+suffix) } // testPrint is a helper for testing the printer. // It reads the file named in, reformats it, and compares // the result to the file named out. func testPrint(t *testing.T, in, out string, fileType FileType) { data, err := os.ReadFile(in) if err != nil { t.Error(err) return } golden, err := os.ReadFile(out) if err != nil { t.Error(err) return } base := "testdata/" + filepath.Base(in) parsers := make(map[string]func(string, []byte) (*File, error)) if (fileType & TypeBzl) != 0 { parsers["bzl"] = ParseBzl } if (fileType & TypeBuild) != 0 { parsers["build"] = ParseBuild } if (fileType & TypeDefault) != 0 { parsers["default"] = ParseDefault } for name, parser := range parsers { file, err := parser(base, data) if err != nil { t.Error(err) return } ndata := Format(file) if !bytes.Equal(ndata, golden) { t.Errorf("formatted %s with parser %s incorrectly: diff shows -%s, +ours", base, name, filepath.Base(out)) testutils.Tdiff(t, golden, ndata) return } } } // Test that when files in the testdata directory are parsed // and printed and parsed again, we get the same parse tree // both times. func TestPrintParse(t *testing.T) { outs, chdir := findTests(t, "") defer chdir() for _, out := range outs { if strings.HasSuffix(out, ".error") { // Incorrect starlark file, skip continue } data, err := os.ReadFile(out) if err != nil { t.Error(err) continue } base := "testdata/" + filepath.Base(out) f, err := Parse(base, data) if err != nil { t.Errorf("parsing original: %v", err) } ndata := FormatWithoutRewriting(f) f2, err := Parse(base, ndata) if err != nil { t.Errorf("parsing reformatted: %v", err) } eq := eqchecker{file: base} if err := eq.check(f, f2); err != nil { t.Errorf("not equal: %v", err) } } } // Test that sequences created in code and then written to file // are properly formatted. func TestPrintNewSequences(t *testing.T) { outs, chdir := findTests(t, "064.*") defer chdir() for _, out := range outs { golden, err := os.ReadFile(out) if err != nil { t.Error(err) return } var fileTypes []FileType if strings.HasSuffix(out, "build.golden") { fileTypes = []FileType{TypeBuild} } else { fileTypes = []FileType{TypeBzl, TypeDefault} } newSequences := []Expr{ &CallExpr{ X: &Ident{Name: "foo"}, List: []Expr{ &LiteralExpr{ Token: "a", }, &LiteralExpr{ Token: "b", }, }, }, &CallExpr{ X: &Ident{Name: "foo"}, List: []Expr{ &LiteralExpr{ Token: "a", }, &LiteralExpr{ Token: "b", }, }, ForceMultiLine: true, }, &CallExpr{ X: &Ident{Name: "foo"}, List: []Expr{ &LiteralExpr{ Token: "a", }, }, }, &CallExpr{ X: &Ident{Name: "foo"}, List: []Expr{ &LiteralExpr{ Token: "a", }, &CallExpr{ X: &Ident{Name: "bar"}, List: []Expr{ &LiteralExpr{ Token: "a", }, &LiteralExpr{ Token: "b", }, }, }, }, ForceMultiLine: true, }, &DefStmt{ Name: "foo", Function: Function{ Body: []Expr{ &CallExpr{ X: &Ident{Name: "foo"}, List: []Expr{ &LiteralExpr{ Token: "a", }, &LiteralExpr{ Token: "b", }, }, }, }, }, }, &DefStmt{ Name: "foo", Function: Function{ Body: []Expr{ &CallExpr{ X: &Ident{Name: "foo"}, List: []Expr{ &LiteralExpr{ Token: "a", }, &LiteralExpr{ Token: "b", }, }, ForceMultiLine: true, }, }, }, }, } for _, fileType := range fileTypes { file := &File{ Type: fileType, Stmt: newSequences, } ndata := Format(file) if !bytes.Equal(ndata, golden) { t.Errorf("formatted file incorrectly: diff shows -%s, +ours", filepath.Base(out)) testutils.Tdiff(t, golden, ndata) return } } } } // An eqchecker holds state for checking the equality of two parse trees. type eqchecker struct { file string pos Position } // errorf returns an error described by the printf-style format and arguments, // inserting the current file position before the error text. func (eq *eqchecker) errorf(format string, args ...interface{}) error { return fmt.Errorf("%s:%d: %s", eq.file, eq.pos.Line, fmt.Sprintf(format, args...)) } // check checks that v and w represent the same parse tree. // If not, it returns an error describing the first difference. func (eq *eqchecker) check(v, w interface{}) error { return eq.checkValue(reflect.ValueOf(v), reflect.ValueOf(w)) } var ( posType = reflect.TypeOf(Position{}) commentsType = reflect.TypeOf(Comments{}) parenType = reflect.TypeOf((*ParenExpr)(nil)) stringExprType = reflect.TypeOf(StringExpr{}) ) // checkValue checks that v and w represent the same parse tree. // If not, it returns an error describing the first difference. func (eq *eqchecker) checkValue(v, w reflect.Value) error { // inner returns the innermost expression for v. // If v is a parenthesized expression (X) it returns x. // if v is a non-nil interface value, it returns the concrete // value in the interface. inner := func(v reflect.Value) reflect.Value { for v.IsValid() { if v.Type() == parenType { v = v.Elem().FieldByName("X") continue } if v.Kind() == reflect.Interface && !v.IsNil() { v = v.Elem() continue } break } return v } v = inner(v) w = inner(w) if v.Kind() != w.Kind() { return eq.errorf("%s became %s", v.Kind(), w.Kind()) } // There is nothing to compare for zero values, so exit early. if !v.IsValid() { return nil } if v.Type() != w.Type() { return eq.errorf("%s became %s", v.Type(), w.Type()) } if p, ok := v.Interface().(Expr); ok { eq.pos, _ = p.Span() } switch v.Kind() { default: return eq.errorf("unexpected type %s", v.Type()) case reflect.Bool, reflect.Int, reflect.String: vi := v.Interface() wi := w.Interface() if vi != wi { return eq.errorf("%v became %v", vi, wi) } case reflect.Slice: vl := v.Len() wl := w.Len() for i := 0; i < vl || i < wl; i++ { if i >= vl { return eq.errorf("unexpected %s", w.Index(i).Type()) } if i >= wl { return eq.errorf("missing %s", v.Index(i).Type()) } if err := eq.checkValue(v.Index(i), w.Index(i)); err != nil { return err } } case reflect.Struct: // Fields in struct must match. t := v.Type() n := t.NumField() for i := 0; i < n; i++ { tf := t.Field(i) switch { default: if err := eq.checkValue(v.Field(i), w.Field(i)); err != nil { return err } case tf.Type == posType: // ignore positions case tf.Type == commentsType: // ignore comment assignment case tf.Name == "MultiLine": // ignore multiline setting case tf.Name == "LineBreak": // ignore line break setting case t == stringExprType && tf.Name == "Token": // ignore raw string token } } case reflect.Ptr, reflect.Interface: if v.IsNil() != w.IsNil() { if v.IsNil() { return eq.errorf("unexpected %s", w.Elem().Type()) } return eq.errorf("missing %s", v.Elem().Type()) } if err := eq.checkValue(v.Elem(), w.Elem()); err != nil { return err } } return nil } // TestPrintDefParameterAndHeaderComments checks that comments on the final // parameter and comments on the function header remain distinct after // formatting and reparsing. func TestPrintDefParameterAndHeaderComments(t *testing.T) { for _, param := range []string{"x", "x = None", "x: int", "x: int = 0", "*args", "**kwargs"} { for _, returnType := range []string{"", " -> int"} { for _, headerComment := range []string{"", " # header"} { for _, beforeParam := range []string{"", "\n "} { input := "def f(" + beforeParam + param + ", # @unused\n)" + returnType + ":" + headerComment + "\n pass\n" want := "def f(\n " + param + " # @unused\n)" + returnType + ":" + headerComment + "\n pass\n" t.Run(input, func(t *testing.T) { f, err := ParseBzl("test.bzl", []byte(input)) if err != nil { t.Fatal(err) } got := Format(f) if string(got) != want { testutils.Tdiff(t, []byte(want), got) } // Formatting must not turn a parameter comment into a header comment. reparsed, err := ParseBzl("test.bzl", got) if err != nil { t.Fatal(err) } def := reparsed.Stmt[0].(*DefStmt) if comments := def.Params[0].Comment().Suffix; len(comments) != 1 || comments[0].Token != "# @unused" { t.Errorf("parameter suffix comments = %v, want # @unused", comments) } comments := def.ColonPos.Comment().Suffix if headerComment == "" && len(comments) != 0 { t.Errorf("unexpected header suffix comments: %v", comments) } else if headerComment != "" && (len(comments) != 1 || comments[0].Token != "# header") { t.Errorf("header suffix comments = %v, want # header", comments) } if reformatted := Format(reparsed); !bytes.Equal(got, reformatted) { t.Error("formatting is not idempotent") testutils.Tdiff(t, got, reformatted) } }) } } } } } // TestPrintMultipleEndOfLineComments checks that when the last parameter // carries more than one end-of-line comment, the additional comments are // aligned with the parameter rather than with the closing parenthesis, so that // formatting is idempotent. func TestPrintMultipleEndOfLineComments(t *testing.T) { tests := []struct { input string want string }{ { input: "def f(x: # type\n int # param\n):\n pass\n", want: "def f(\n x: int # type\n # param\n):\n pass\n", }, } for _, tc := range tests { t.Run(tc.input, func(t *testing.T) { f, err := ParseBzl("test.bzl", []byte(tc.input)) if err != nil { t.Fatal(err) } got := Format(f) if string(got) != tc.want { testutils.Tdiff(t, []byte(tc.want), got) } reparsed, err := ParseBzl("test.bzl", got) if err != nil { t.Fatal(err) } if reformatted := Format(reparsed); !bytes.Equal(got, reformatted) { t.Error("formatting is not idempotent") testutils.Tdiff(t, got, reformatted) } }) } } func TestPrintTypeExprForceMultiLine(t *testing.T) { tests := []struct { name string input string want string }{ { name: "multiline type application", input: `type T = list[ int, str ] `, want: `type T = list[ int, str ] `, }, { name: "multiline type application with comments", input: `x: list[ # comment 1 int, # comment 2 str # comment 3 ] = [123, "four"] `, want: `x: list[ # comment 1 int, # comment 2 str # comment 3 ] = [123, "four"] `, }, { name: "multiline type list in compact type application", input: `type T = Callable[[ int, str, ], int] `, want: `type T = Callable[[ int, str, ], int] `, }, { name: "multiline type dict in compact type application", input: `type NumericResult = struct[{ "name": str, "data": Sequence[_Numeric] | None, }, ...] `, want: `type NumericResult = struct[{ "name": str, "data": Sequence[_Numeric] | None, }, ...] `, }, { name: "multiline type dict in non-compact type application", input: `type T = struct[ { "a": int, "b": str, }, ... ] `, want: `type T = struct[ { "a": int, "b": str, }, ... ] `, }, { name: "dotted type name", input: "type T = typing.Sequence\n", want: "type T = typing.Sequence\n", }, { name: "dotted type name in type application", input: "type T = typing.Sequence[int]\n", want: "type T = typing.Sequence[int]\n", }, { name: "multi-dotted type name in type application", input: "type T = a.b.c.D[int, str]\n", want: "type T = a.b.c.D[int, str]\n", }, { name: "nested dotted type names", input: "type T = typing.Mapping[str, typing.Sequence[int]]\n", want: "type T = typing.Mapping[str, typing.Sequence[int]]\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { f, err := Parse("test.bzl", []byte(tt.input)) if err != nil { t.Fatalf("Parse failed: %v", err) } got := string(Format(f)) if got != tt.want { t.Errorf("Format() = %q, want %q", got, tt.want) } }) } } buildtools-10.1.0/build/build_defs.bzl0000664000175000017500000001316615254573013017263 0ustar kenhyskenhys"""Provides go_yacc and genfile_check_test 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. """ load( "@io_bazel_rules_go//go:def.bzl", "GoSource", ) load("@rules_shell//shell:sh_binary.bzl", "sh_binary") load("@rules_shell//shell:sh_test.bzl", "sh_test") _GO_YACC_TOOL = "@org_golang_x_tools//cmd/goyacc" def _go_yacc_impl(ctx): args = ctx.actions.args() args.add("-o", ctx.outputs.out) args.add(ctx.file.src) goroot = "%s/.." % ctx.executable._go_yacc_tool.dirname ctx.actions.run( executable = ctx.executable._go_yacc_tool, arguments = [args], inputs = [ctx.file.src], outputs = [ctx.outputs.out], env = { "GOROOT": goroot, }, ) return DefaultInfo( files = depset([ctx.outputs.out]), ) _go_yacc = rule( implementation = _go_yacc_impl, attrs = { "src": attr.label( allow_single_file = True, ), "out": attr.output(), "_go_yacc_tool": attr.label( default = _GO_YACC_TOOL, allow_single_file = True, executable = True, cfg = "exec", ), }, ) # buildifier: disable=unnamed-macro def go_yacc(src, out, visibility = None): """Runs go tool yacc -o $out $src.""" _go_yacc( name = src + ".go_yacc", src = src, out = out, visibility = visibility, ) def _extract_go_src(ctx): """Thin rule that exposes the GoSource from a go_library.""" return [DefaultInfo(files = depset(ctx.attr.library[GoSource].srcs))] extract_go_src = rule( implementation = _extract_go_src, attrs = { "library": attr.label( providers = [GoSource], ), }, ) # buildifier: disable=unnamed-macro def genfile_check_test(src, gen, allowed_diffs = []): """ Asserts that any checked-in generated code matches bazel gen. Args: src: checked in file gen: generated file allowed_diffs: optional list of regexes to ignore in diff """ if not src: fail("src is required", "src") if not gen: fail("gen is required", "gen") diff_args = "" for d in allowed_diffs: diff_args += " -I '%s'" % d native.genrule( name = src + "_checksh", outs = [src + "_check.sh"], cmd = r"""cat >$@ <<'eof' #!/bin/bash # Script generated by @com_github_bazelbuild_buildtools//build:build_defs.bzl # --- begin runfiles.bash initialization --- # Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash). set -euo pipefail if [[ ! -d "$${RUNFILES_DIR:-/dev/null}" && ! -f "$${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then if [[ -f "$$0.runfiles_manifest" ]]; then export RUNFILES_MANIFEST_FILE="$$0.runfiles_manifest" elif [[ -f "$$0.runfiles/MANIFEST" ]]; then export RUNFILES_MANIFEST_FILE="$$0.runfiles/MANIFEST" elif [[ -f "$$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then export RUNFILES_DIR="$$0.runfiles" fi fi if [[ -f "$${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then source "$${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" elif [[ -f "$${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then source "$$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ "$$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" else echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" exit 1 fi # --- end runfiles.bash initialization --- [[ "$$1" = external/* ]] && F1="$${1#external/}" || F1="$$TEST_WORKSPACE/$$1" [[ "$$2" = external/* ]] && F2="$${2#external/}" || F2="$$TEST_WORKSPACE/$$2" F1="$$(rlocation "$$F1")" F2="$$(rlocation "$$F2")" diff -q%s "$$F1" "$$F2" eof """ % diff_args, ) sh_test( name = src + "_checkshtest", size = "small", srcs = [src + "_check.sh"], deps = ["@bazel_tools//tools/bash/runfiles"], data = [src, gen], args = ["$(rootpath " + src + ")", "$(rootpath " + gen + ")"], ) # magic copy rule used to update the checked-in version native.genrule( name = src + "_copysh", srcs = [gen], outs = [src + "copy.sh"], cmd = "echo 'cp $${BUILD_WORKSPACE_DIRECTORY}/$(execpath " + gen + ") $${BUILD_WORKSPACE_DIRECTORY}/" + native.package_name() + "/" + src + "' > $@", ) sh_binary( name = src + "_copy", srcs = [src + "_copysh"], data = [gen], ) # buildifier: disable=unnamed-macro def go_proto_checkedin_test(src, proto = "go_default_library", allowed_diffs = []): """ Asserts that any checked-in .pb.go code matches bazel gen. Args: src: checked in file proto: generated file allowed_diffs: optional list of regexes to ignore in diff """ genfile = src + "_genfile" extract_go_src( name = genfile + "go", library = proto, ) # TODO(pmbethe09): why is the extra copy needed? native.genrule( name = genfile, srcs = [genfile + "go"], outs = [genfile + ".go"], cmd = "cp $< $@", ) genfile_check_test(src, genfile, allowed_diffs = allowed_diffs) buildtools-10.1.0/build/rewrite_test_files/0000775000175000017500000000000015254573013020345 5ustar kenhyskenhysbuildtools-10.1.0/build/rewrite_test_files/original_formatted.star0000664000175000017500000000021215254573013025104 0ustar kenhyskenhys""" Test file for rewrite.go """ bazel.builder( name = "test", a = "test", abbb = "test", bi = "test", bo = "test", ) buildtools-10.1.0/build/rewrite_test_files/original.star0000664000175000017500000000021215254573013023037 0ustar kenhyskenhys""" Test file for rewrite.go """ bazel.builder( bo = "test", a = "test", bi = "test", name = "test", abbb = "test", ) buildtools-10.1.0/build/rule.go0000664000175000017500000002210015254573013015734 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // Rule-level API for inspecting and modifying a build.File syntax tree. package build import ( "path/filepath" "strings" ) // A Rule represents a single BUILD rule. type Rule struct { Call *CallExpr ImplicitName string // The name which should be used if the name attribute is not set. See the comment on File.implicitRuleName. } // NewRule is a simple constructor for Rule. func NewRule(call *CallExpr) *Rule { return &Rule{call, ""} } func (f *File) Rule(call *CallExpr) *Rule { r := &Rule{call, ""} if r.AttrString("name") == "" { r.ImplicitName = f.implicitRuleName() } return r } func (f *File) rules(p func(r *Rule) bool) []*Rule { var all []*Rule for _, stmt := range f.Stmt { Walk(stmt, func(x Expr, stk []Expr) { call, ok := x.(*CallExpr) if !ok { return } // Skip nested calls. for _, frame := range stk { if _, ok := frame.(*CallExpr); ok { return } } // Check if the rule is correct. rule := f.Rule(call) if !p(rule) { return } all = append(all, rule) }) } return all } // Rules returns the rules in the file of the given kind (such as "go_library"). // If kind == "", Rules returns all rules in the file. func (f *File) Rules(kind string) []*Rule { return f.rules(func(rule *Rule) bool { // Check if the rule kind is correct. return kind == "" || rule.Kind() == kind }) } // RuleAt returns the rule in the file that starts at the specified line, or null if no such rule. func (f *File) RuleAt(linenum int) *Rule { all := f.rules(func(rule *Rule) bool { start, end := rule.Call.X.Span() return start.Line <= linenum && linenum <= end.Line }) if len(all) != 1 { return nil } return all[0] } // RuleNamed returns the rule in the file that has the specified name, or null if no such rule. func (f *File) RuleNamed(name string) *Rule { all := f.rules(func(rule *Rule) bool { return rule.Name() == name }) if len(all) != 1 { return nil } return all[0] } // DelRules removes rules with the given kind and name from the file. // An empty kind matches all kinds; an empty name matches all names. // It returns the number of rules that were deleted. func (f *File) DelRules(kind, name string) int { var i int for _, stmt := range f.Stmt { if call, ok := stmt.(*CallExpr); ok { r := f.Rule(call) if (kind == "" || r.Kind() == kind) && (name == "" || r.Name() == name) { continue } } f.Stmt[i] = stmt i++ } n := len(f.Stmt) - i f.Stmt = f.Stmt[:i] return n } // If a build file contains exactly one unnamed rule, and no rules in the file explicitly have the // same name as the name of the directory the build file is in, we treat the unnamed rule as if it // had the name of the directory containing the BUILD file. // This is following a convention used in the Pants build system to cut down on boilerplate. func (f *File) implicitRuleName() string { // We disallow empty names in the top-level BUILD files. dir := filepath.Dir(f.Path) if dir == "." { return "" } sawAnonymousRule := false possibleImplicitName := filepath.Base(dir) for _, stmt := range f.Stmt { call, ok := stmt.(*CallExpr) if !ok { continue } temp := &Rule{call, ""} if temp.AttrString("name") == possibleImplicitName { // A target explicitly has the name of the dir, so no implicit targets are allowed. return "" } if temp.Kind() != "" && temp.AttrString("name") == "" { if sawAnonymousRule { return "" } sawAnonymousRule = true } } if sawAnonymousRule { return possibleImplicitName } return "" } // Kind returns the rule's kind (such as "go_library"). // The kind of the rule may be given by a literal or it may be a sequence of dot expressions that // begins with a literal, if the call expression does not conform to either of these forms, an // empty string will be returned func (r *Rule) Kind() string { var names []string expr := r.Call.X for { x, ok := expr.(*DotExpr) if !ok { break } names = append(names, x.Name) expr = x.X } x, ok := expr.(*Ident) if !ok { return "" } names = append(names, x.Name) // Reverse the elements since the deepest expression contains the leading literal for l, r := 0, len(names)-1; l < r; l, r = l+1, r-1 { names[l], names[r] = names[r], names[l] } return strings.Join(names, ".") } // SetKind changes rule's kind (such as "go_library"). func (r *Rule) SetKind(kind string) { names := strings.Split(kind, ".") var startPos Position if x := r.Call.X; x != nil { startPos, _ = x.Span() } var expr Expr expr = &Ident{Name: names[0], NamePos: startPos} for _, name := range names[1:] { expr = &DotExpr{X: expr, Name: name, NamePos: startPos} } r.Call.X = expr } // ExplicitName returns the rule's target name if it's explicitly provided as a string value, "" otherwise. func (r *Rule) ExplicitName() string { return r.AttrString("name") } // Name returns the rule's target name. // If the rule has no explicit target name, Name returns the implicit name if there is one, else the empty string. func (r *Rule) Name() string { explicitName := r.ExplicitName() if explicitName == "" && r.Kind() != "package" { return r.ImplicitName } return explicitName } // AttrKeys returns the keys of all the rule's attributes. func (r *Rule) AttrKeys() []string { var keys []string for _, expr := range r.Call.List { if as, ok := expr.(*AssignExpr); ok { if keyExpr, ok := as.LHS.(*Ident); ok { keys = append(keys, keyExpr.Name) } } } return keys } // AttrDefn returns the AssignExpr defining the rule's attribute with the given key. // If the rule has no such attribute, AttrDefn returns nil. func (r *Rule) AttrDefn(key string) *AssignExpr { for _, kv := range r.Call.List { as, ok := kv.(*AssignExpr) if !ok { continue } k, ok := as.LHS.(*Ident) if !ok || k.Name != key { continue } return as } return nil } // Attr returns the value of the rule's attribute with the given key // (such as "name" or "deps"). // If the rule has no such attribute, Attr returns nil. func (r *Rule) Attr(key string) Expr { as := r.AttrDefn(key) if as == nil { return nil } return as.RHS } // DelAttr deletes the rule's attribute with the named key. // It returns the old value of the attribute, or nil if the attribute was not found. func (r *Rule) DelAttr(key string) Expr { list := r.Call.List for i, kv := range list { as, ok := kv.(*AssignExpr) if !ok { continue } k, ok := as.LHS.(*Ident) if !ok || k.Name != key { continue } copy(list[i:], list[i+1:]) r.Call.List = list[:len(list)-1] return as.RHS } return nil } // SetAttr sets the rule's attribute with the given key to value. // If the rule has no attribute with the key, SetAttr appends // one to the end of the rule's attribute list. func (r *Rule) SetAttr(key string, val Expr) { as := r.AttrDefn(key) if as != nil { as.RHS = val return } r.Call.List = append(r.Call.List, &AssignExpr{ LHS: &Ident{Name: key}, Op: "=", RHS: val, }, ) } // AttrLiteral returns the literal form of the rule's attribute // with the given key (such as "cc_api_version"), only when // that value is an identifier or number. // If the rule has no such attribute or the attribute is not an identifier or number, // AttrLiteral returns "". func (r *Rule) AttrLiteral(key string) string { value := r.Attr(key) if ident, ok := value.(*Ident); ok { return ident.Name } if literal, ok := value.(*LiteralExpr); ok { return literal.Token } return "" } // AttrString returns the value of the rule's attribute // with the given key (such as "name"), as a string. // If the rule has no such attribute or the attribute has a non-string value, // Attr returns the empty string. func (r *Rule) AttrString(key string) string { str, ok := r.Attr(key).(*StringExpr) if !ok { return "" } return str.Value } // AttrStrings returns the value of the rule's attribute // with the given key (such as "srcs"), as a []string. // If the rule has no such attribute or the attribute is not // a list of strings, AttrStrings returns a nil slice. func (r *Rule) AttrStrings(key string) []string { return Strings(r.Attr(key)) } // Strings returns expr as a []string. // If expr is not a list of string literals, // Strings returns a nil slice instead. // If expr is an empty list of string literals, // returns a non-nil empty slice. // (this allows differentiating between these two cases) func Strings(expr Expr) []string { list, ok := expr.(*ListExpr) if !ok { return nil } all := []string{} // not nil for _, l := range list.List { str, ok := l.(*StringExpr) if !ok { return nil } all = append(all, str.Value) } return all } buildtools-10.1.0/build/parse_test.go0000664000175000017500000002417115254573013017150 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 build import ( "encoding/json" "os" "path/filepath" "reflect" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/testutils" ) func TestParse(t *testing.T) { for i, tt := range parseTests { name := "test" if tt.out != nil { name = tt.out.Path } p, err := Parse(name, []byte(tt.in)) if err != nil { t.Errorf("#%d: %v", i, err) continue } if tt.out != nil { compare(t, p, tt.out) } } } func TestParseTestdata(t *testing.T) { // Test that files in the testdata directory can all be parsed. // For this test we don't bother checking what the tree looks like. // The printing tests will exercise that information. testdata := os.Getenv("TEST_SRCDIR") + "/" + os.Getenv("TEST_WORKSPACE") + "/build/testdata" outs, err := filepath.Glob(testdata + "/*") if err != nil { t.Fatal(err) } if len(outs) == 0 { t.Fatal("Data set is empty:", testdata) } for _, out := range outs { if strings.HasSuffix(out, ".error") { // Incorrect starlark file, skip continue } data, err := os.ReadFile(out) if err != nil { t.Error(err) continue } _, err = Parse(filepath.Base(out), data) if err != nil { t.Error(err) } } } // toJSON returns human-readable json for the given syntax tree. // It is used as input to diff for comparing the actual syntax tree with the expected one. func toJSON(v interface{}) string { s, _ := json.MarshalIndent(v, "", "\t") s = append(s, '\n') return string(s) } // Compare expected and actual values, failing and outputting a diff of the two values if they are not deeply equal func compare(t *testing.T, actual, expected interface{}) { if !reflect.DeepEqual(expected, actual) { testutils.Tdiff(t, []byte(toJSON(expected)), []byte(toJSON(actual))) } } // Small tests checking that the parser returns exactly the right syntax tree. // If out is nil, we only check that the parser accepts the file. var parseTests = []struct { in string out *File }{ // Reified types containing ellipsis are valid value arguments too. { in: `cast(value, struct[{"name": str}, ...])`, }, { in: `isinstance(struct[{"name": str}, ...], value)`, }, { in: `go_binary(name = "x" ) `, out: &File{ Path: "BUILD", Type: TypeBuild, Stmt: []Expr{ &CallExpr{ X: &Ident{ NamePos: Position{1, 1, 0}, Name: "go_binary", }, ListStart: Position{1, 10, 9}, List: []Expr{ &AssignExpr{ LHS: &Ident{ NamePos: Position{1, 11, 10}, Name: "name", }, OpPos: Position{1, 16, 15}, Op: "=", RHS: &StringExpr{ Start: Position{1, 18, 17}, Value: "x", End: Position{1, 21, 20}, Token: `"x"`, }, }, }, End: End{Pos: Position{2, 1, 21}}, ForceMultiLine: true, }, }, }, }, { in: `foo.bar.baz(name = "x")`, out: &File{ Path: "test", Type: TypeDefault, Stmt: []Expr{ &CallExpr{ X: &DotExpr{ X: &DotExpr{ X: &Ident{ NamePos: Position{1, 1, 0}, Name: "foo", }, Dot: Position{1, 4, 3}, NamePos: Position{1, 5, 4}, Name: "bar", }, Dot: Position{1, 8, 7}, NamePos: Position{1, 9, 8}, Name: "baz", }, ListStart: Position{1, 12, 11}, List: []Expr{ &AssignExpr{ LHS: &Ident{ NamePos: Position{1, 13, 12}, Name: "name", }, OpPos: Position{1, 18, 17}, Op: "=", RHS: &StringExpr{ Start: Position{1, 20, 19}, Value: "x", End: Position{1, 23, 22}, Token: `"x"`, }, }, }, End: End{Pos: Position{1, 23, 22}}, }, }, }, }, { in: `package(default_visibility = ["//visibility:legacy_public"]) `, }, { in: `__unused__ = [ foo_binary( name = "signed_release_%sdpi" % dpi, srcs = [ ":aps_release_%s" % dpi, # all of Maps, obfuscated, w/o NLP ":qlp_release_%s" % dpi, # the NLP ":check_binmode_release", ":check_remote_strings_release", ], debug_key = "//foo:bar.baz", resources = ":R_src_release_%sdpi" % dpi) for dpi in dpis ] `, }, { in: `load(":foo.bzl", "foo", """bar""", baz="foo", foo="""baz""") `, out: &File{ Path: "BUILD", Type: TypeBuild, Stmt: []Expr{ &LoadStmt{ Load: Position{1, 1, 0}, Module: &StringExpr{ Value: ":foo.bzl", Token: "\":foo.bzl\"", Start: Position{1, 6, 5}, End: Position{1, 16, 15}, }, From: []*Ident{ { Name: "foo", NamePos: Position{1, 19, 18}, }, { Name: "bar", NamePos: Position{1, 28, 27}, }, { Name: "foo", NamePos: Position{1, 41, 40}, }, { Name: "baz", NamePos: Position{1, 54, 53}, }, }, To: []*Ident{ { Name: "foo", NamePos: Position{1, 19, 18}, }, { Name: "bar", NamePos: Position{1, 28, 27}, }, { Name: "baz", NamePos: Position{1, 36, 35}, }, { Name: "foo", NamePos: Position{1, 47, 46}, }, }, Rparen: End{Pos: Position{1, 60, 59}}, ForceCompact: true, }, }, }, }, { in: `type OptionalDict[T, U] = dict[T, U] | None`, out: &File{ Path: "test", Type: TypeDefault, Stmt: []Expr{ &TypeAliasStmt{ TypePos: Position{Line: 1, LineRune: 1, Byte: 0}, Name: &Ident{ Name: "OptionalDict", NamePos: Position{Line: 1, LineRune: 6, Byte: 5}, }, TypeParams: &ListExpr{ Start: Position{Line: 1, LineRune: 18, Byte: 17}, List: []Expr{ &Ident{ Name: "T", NamePos: Position{Line: 1, LineRune: 19, Byte: 18}, }, &Ident{ Name: "U", NamePos: Position{Line: 1, LineRune: 22, Byte: 21}, }, }, End: End{Pos: Position{Line: 1, LineRune: 23, Byte: 22}}, }, EqualPos: Position{Line: 1, LineRune: 25, Byte: 24}, Type: &BinaryExpr{ X: &TypeAppExpr{ Type: &Ident{ Name: "dict", NamePos: Position{Line: 1, LineRune: 27, Byte: 26}, }, ArgsStart: Position{Line: 1, LineRune: 31, Byte: 30}, Args: []Expr{ &Ident{ Name: "T", NamePos: Position{Line: 1, LineRune: 32, Byte: 31}, }, &Ident{ Name: "U", NamePos: Position{Line: 1, LineRune: 35, Byte: 34}, }, }, End: End{Pos: Position{Line: 1, LineRune: 36, Byte: 35}}, ForceCompact: true, }, Op: "|", OpStart: Position{Line: 1, LineRune: 38, Byte: 37}, Y: &Ident{ Name: "None", NamePos: Position{Line: 1, LineRune: 40, Byte: 39}, }, }, }, }, }, }, { in: `type T = typing.Sequence[int]`, out: &File{ Path: "test", Type: TypeDefault, Stmt: []Expr{ &TypeAliasStmt{ TypePos: Position{Line: 1, LineRune: 1, Byte: 0}, Name: &Ident{ Name: "T", NamePos: Position{Line: 1, LineRune: 6, Byte: 5}, }, EqualPos: Position{Line: 1, LineRune: 8, Byte: 7}, Type: &TypeAppExpr{ Type: &DotExpr{ X: &Ident{ Name: "typing", NamePos: Position{Line: 1, LineRune: 10, Byte: 9}, }, Dot: Position{Line: 1, LineRune: 16, Byte: 15}, NamePos: Position{Line: 1, LineRune: 17, Byte: 16}, Name: "Sequence", }, ArgsStart: Position{Line: 1, LineRune: 25, Byte: 24}, Args: []Expr{ &Ident{ Name: "int", NamePos: Position{Line: 1, LineRune: 26, Byte: 25}, }, }, End: End{Pos: Position{Line: 1, LineRune: 29, Byte: 28}}, }, }, }, }, }, } func TestParseError(t *testing.T) { tests := []struct { name string in string want string }{ { name: "no trailing commas in type applications", in: "x: dict[str, int,]", want: "test:1:19: syntax error near ]", }, { name: "no unparenthesized tuples in type syntax", in: "x: str, int", want: "test:1:8: syntax error near ,", }, { name: "no unparenthesized tuple type syntax", in: "x: str,", want: "test:1:8: syntax error near ,", }, // type alias errors { name: "type alias not at top level", in: "def f():\n type T = int\n", want: "test:2:3: syntax error: type alias not at top level", }, { name: "misspelled type soft keyword", in: "tpye MyInt = int\n", want: "test:1:11: syntax error near MyInt", }, { // Check that 'type' soft keyword support doesn't break syntax error handling for // other occurrences of one identifier followed by another. name: "ident followed by ident syntax error", in: "foo bar\n", want: "test:1:8: syntax error near bar", }, // wrong order of args to cast; expecting cast(type, value) { name: "wrong order of arguments to cast", in: `cast(foo(bar), type)`, want: "test:1:10: syntax error near (", }, // wrong order of args to isinstance; expecting isinstance(value, type) { name: "wrong order of arguments to isinstance", in: `isinstance(type, foo(bar))`, want: "test:1:22: syntax error near (", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := Parse("test", []byte(tt.in)) if err == nil { t.Fatalf("Parse(%q) succeeded, want error %q", tt.in, tt.want) } if got := err.Error(); got != tt.want { t.Errorf("Parse(%q) error = %q, want %q", tt.in, got, tt.want) } }) } } buildtools-10.1.0/build/utils.go0000664000175000017500000000600115254573013016127 0ustar kenhyskenhys/* Copyright 2021 Google LLC 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 https://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 build // GetParamName extracts the param name from an item of function params. func GetParamName(param Expr) (name, op string) { ident, op := GetParamIdent(param) if ident == nil { return "", "" } return ident.Name, op } // GetParamIdent extracts the param identifier from an item of function params. func GetParamIdent(param Expr) (ident *Ident, op string) { switch param := param.(type) { case *Ident: return param, "" case *TypedIdent: ident, _ := GetParamIdent(param.Ident) return ident, "" case *AssignExpr: // keyword parameter return GetParamIdent(param.LHS) case *UnaryExpr: // *args, **kwargs, or * if param.X == nil { // An asterisk separating position and keyword-only arguments break } ident, _ := GetParamIdent(param.X) return ident, param.Op } return nil, "" } // GetTypes returns the list of types defined by the a given expression. // Examples: // // List[tuple[bool, int]] should return [List, Tuple, bool, int] // str should return str func GetTypes(t Expr) []string { switch t := t.(type) { case *TypeAliasStmt: return GetTypes(t.Type) case *TypedIdent: return GetTypes(t.Type) case *TypeAppExpr: ret := GetTypes(t.Type) for _, x := range t.Args { ret = append(ret, GetTypes(x)...) } return ret case *EllipsisExpr: return []string{} case *Ident: return []string{t.Name} case *DefStmt: ret := GetTypes(t.Type) params := make([]string, 0) for _, p := range t.Params { params = append(params, GetTypes(p)...) } return append(ret, params...) case *IndexExpr: left := GetTypes(t.X) right := GetTypes(t.Y) return append(left, right...) case *DotExpr: return GetTypes(t.X) case *BinaryExpr: left := GetTypes(t.X) right := GetTypes(t.Y) return append(left, right...) case *ListExpr: var ret []string for _, x := range t.List { ret = append(ret, GetTypes(x)...) } return ret case *DictExpr: var ret []string for _, kv := range t.List { ret = append(ret, GetTypes(kv.Value)...) } return ret case *TupleExpr: var ret []string for _, x := range t.List { ret = append(ret, GetTypes(x)...) } return ret default: return []string{} } } // IsMultiLine returns whether an Expr is multiline or not. func IsMultiLine(param Expr) bool { start, end := param.Span() if end.Line == 0 && end.Byte == 0 && end.LineRune == 0 { // If the end position is unknown, we assume it's single line (for backwards compatibility). return false } return start.Line != end.Line } buildtools-10.1.0/build/rewrite_test.go0000664000175000017500000000672715254573013017526 0ustar kenhyskenhys/* Copyright 2022 Google LLC 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 https://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 build import ( "bytes" "os" "path" "testing" ) var workingDir string = path.Join(os.Getenv("TEST_SRCDIR"), os.Getenv("TEST_WORKSPACE"), "build") var originalFilePath = workingDir + "/rewrite_test_files/original.star" var formattedFilePath = workingDir + "/rewrite_test_files/original_formatted.star" var originalBytes, _ = os.ReadFile(originalFilePath) var originalFile, _ = ParseDefault(originalFilePath, originalBytes) var formattedBytes, _ = os.ReadFile(formattedFilePath) var formattedFile, _ = ParseDefault(formattedFilePath, formattedBytes) var name map[string]int = map[string]int{"name": -99} var rewriteSet []string = []string{"callsort"} var rewriter = Rewriter{ RewriteSet: rewriteSet, NamePriority: name, } func TestRewriterRegular(t *testing.T) { // Load the original file var modifyBytes, _ = os.ReadFile(originalFilePath) var modifiedFile, _ = ParseDefault(originalFilePath, modifyBytes) // Perform rewrite on loaded file, rewrite should do nothing here Rewrite(modifiedFile) // Initialize printers to obtain bytes later for different types of printers // We will check bytes from printers because that is our source of truth before writing to new file formattedPrinter := &printer{fileType: formattedFile.Type} originalPrinter := &printer{fileType: originalFile.Type} modifiedPrinter := &printer{fileType: modifiedFile.Type} formattedPrinter.file(formattedFile) originalPrinter.file(originalFile) modifiedPrinter.file(modifiedFile) // Assert that bytes to be writter are same as original bytes and different from formatted if !bytes.Equal(originalPrinter.Bytes(), modifiedPrinter.Bytes()) { t.Error("Original Printer should equal Modified Printer") } if bytes.Equal(formattedPrinter.Bytes(), modifiedPrinter.Bytes()) { t.Error("Formmated Printer should not equal Modified Printer") } } func TestRewriterWithRewriter(t *testing.T) { // Load the original file var modifyBytes, _ = os.ReadFile(originalFilePath) var modifiedFile, _ = ParseDefault(originalFilePath, modifyBytes) // Perform rewrite with rewriter on loaded file, should proceed to reorder rewriter.Rewrite(modifiedFile) // Initialize printers to obtain bytes later for different types of printers // We will check bytes from printers because that is our source of truth before writing to new file formattedPrinter := &printer{fileType: formattedFile.Type} originalPrinter := &printer{fileType: originalFile.Type} modifiedPrinter := &printer{fileType: modifiedFile.Type} formattedPrinter.file(formattedFile) originalPrinter.file(originalFile) modifiedPrinter.file(modifiedFile) // Assert that bytes to be written is same as formmatted bytes and different from original if !bytes.Equal(formattedPrinter.Bytes(), modifiedPrinter.Bytes()) { t.Error("Formatted Printer should equal Modified Printer") } if bytes.Equal(originalPrinter.Bytes(), modifiedPrinter.Bytes()) { t.Error("Original Printer should not equal Modified Printer") } } buildtools-10.1.0/build/checkfile_test.go0000664000175000017500000000273115254573013017751 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 build import ( "bytes" "os" "path" "testing" "github.com/bazel-contrib/buildtools/v10/testutils" ) func TestFilesMatch(t *testing.T) { testdata := path.Join(os.Getenv("TEST_SRCDIR"), os.Getenv("TEST_WORKSPACE"), "build") generated, err := os.ReadFile(path.Join(testdata, "parse.y.baz.go")) if err != nil { t.Fatalf("ReadFile(%q) = %v", "parse.y.baz.go", err) } // The first line contains a path to the generator. We want to skip it // (as well as the next character, another '\n') to make the test more // robust. generated = generated[bytes.IndexByte(generated, '\n')+2:] checkedIn, err := os.ReadFile(path.Join(testdata, "parse.y.go")) if err != nil { t.Fatalf("ReadFile(%q) = %v", "parse.y.go", err) } d, err := testutils.Diff(generated, checkedIn) if err != nil { t.Fatalf("diff(generated, checkedIn) = %v", err) } if len(d) != 0 { t.Errorf("diff(generated, checkedIn) = %v", string(d)) } } buildtools-10.1.0/build/BUILD.bazel0000664000175000017500000000267615254573013016334 0ustar kenhyskenhys# gazelle:exclude parse.y.go load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") load(":build_defs.bzl", "go_yacc") go_yacc( src = "parse.y", out = "parse.y.baz.go", ) go_library( name = "build", srcs = [ "lex.go", "parse.y.baz.go", # keep "print.go", "quote.go", "rewrite.go", "rule.go", "syntax.go", "utils.go", "walk.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/build", visibility = ["//visibility:public"], deps = [ "//labels", "//tables", ], ) go_test( name = "build_test", size = "small", srcs = [ "checkfile_test.go", "lex_test.go", "parse_test.go", "print_test.go", "quote_test.go", "rewrite_test.go", "rule_test.go", "utils_test.go", "walk_test.go", ], data = glob(["testdata/*"]) + [ # parse.y.go is checked in to satisfy the Go community # https://github.com/bazel-contrib/buildtools/issues/14 # this test ensures it doesn't get stale. "parse.y.baz.go", "parse.y.go", "rewrite_test_files/original_formatted.star", "rewrite_test_files/original.star", ], embed = [":build"], deps = [ "//tables", "//testutils", ], ) alias( name = "go_default_library", actual = ":build", visibility = ["//visibility:public"], ) buildtools-10.1.0/edit/0000775000175000017500000000000015254573013014271 5ustar kenhyskenhysbuildtools-10.1.0/edit/default_buildifier.go0000664000175000017500000000236215254573013020445 0ustar kenhyskenhyspackage edit import ( "bytes" "fmt" "os" "os/exec" "github.com/bazel-contrib/buildtools/v10/build" ) type defaultBuildifier struct{} // buildify 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 (b *defaultBuildifier) Buildify(opts *Options, f *build.File) ([]byte, error) { if opts.Buildifier == "" { // Current AST may be not entirely correct, e.g. it may contain Ident which // value is a chunk of code, like "f(x)". The AST should be printed and // re-read to parse such expressions correctly. contents := build.Format(f) newF, err := build.Parse(f.Path, contents) if err != nil { return nil, err } return build.Format(newF), nil } cmd := exec.Command(opts.Buildifier, "--type=build") data := build.Format(f) cmd.Stdin = bytes.NewBuffer(data) stdout := bytes.NewBuffer(nil) stderr := bytes.NewBuffer(nil) cmd.Stdout = stdout cmd.Stderr = stderr cmd.Env = append( os.Environ(), // Custom environment variables ) err := cmd.Run() if stderr.Len() > 0 { return nil, fmt.Errorf("%s", stderr.Bytes()) } if err != nil { return nil, err } return stdout.Bytes(), nil } buildtools-10.1.0/edit/bzlmod/0000775000175000017500000000000015254573013015560 5ustar kenhyskenhysbuildtools-10.1.0/edit/bzlmod/bzlmod.go0000664000175000017500000003176315254573013017410 0ustar kenhyskenhys/* Copyright 2023 Google LLC 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 https://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 bzlmod contains functions for working with MODULE.bazel files. package bzlmod import ( "path" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/labels" ) // Proxies returns the names of extension proxies (i.e. the names of variables to which the result // of a use_extension call is assigned) for the given extension with the given value of the // dev_dependency attribute. // Extension proxies created with "isolate = True" are ignored. func Proxies(f *build.File, rawExtBzlFile string, extName string, dev bool) []string { apparentModuleName := getApparentModuleName(f) extBzlFile := normalizeLabelString(rawExtBzlFile, apparentModuleName) var proxies []string for _, stmt := range f.Stmt { proxy, rawBzlFile, name, isDev, isIsolated := parseUseExtension(stmt) if proxy == "" || isDev != dev || isIsolated { continue } bzlFile := normalizeLabelString(rawBzlFile, apparentModuleName) if bzlFile == extBzlFile && name == extName { proxies = append(proxies, proxy) } } return proxies } // AllProxies returns the names of all extension proxies (i.e. the names of variables to which the // result of a use_extension call is assigned) corresponding to the same extension usage as the // given proxy. // For an isolated extension usage, a list containing only the given proxy is returned. // For a non-isolated extension usage, the proxies of all non-isolated extension usages of the same // extension with the same value for the dev_dependency parameter are returned. // If the given proxy is not an extension proxy, nil is returned. func AllProxies(f *build.File, proxy string) []string { for _, stmt := range f.Stmt { proxyCandidate, rawBzlFile, name, isDev, isIsolated := parseUseExtension(stmt) if proxyCandidate == proxy { if isIsolated { return []string{proxy} } return Proxies(f, rawBzlFile, name, isDev) } } return nil } // UseRepos returns the use_repo calls that use the given proxies. func UseRepos(f *build.File, proxies []string) []*build.CallExpr { proxiesSet := make(map[string]struct{}) for _, p := range proxies { proxiesSet[p] = struct{}{} } var useRepos []*build.CallExpr for _, stmt := range f.Stmt { if _, ok := stmt.(*build.CallExpr); !ok { continue } call := stmt.(*build.CallExpr) if _, ok := call.X.(*build.Ident); !ok { continue } if call.X.(*build.Ident).Name != "use_repo" || len(call.List) < 1 { continue } proxy, ok := call.List[0].(*build.Ident) if !ok { continue } if _, ok := proxiesSet[proxy.Name]; !ok { continue } useRepos = append(useRepos, call) } return useRepos } // NewUseRepo inserts and returns a new use_repo call after the last usage of any of the given // proxies, where a usage is either a use_extension call or a tag definition. func NewUseRepo(f *build.File, proxies []string) (*build.File, *build.CallExpr) { lastUsage, proxy := lastProxyUsage(f, proxies) if lastUsage == -1 { return f, nil } useRepo := &build.CallExpr{ X: &build.Ident{Name: "use_repo"}, List: []build.Expr{ &build.Ident{Name: proxy}, }, } stmt := append(f.Stmt[:lastUsage+1], append([]build.Expr{useRepo}, f.Stmt[lastUsage+1:]...)...) return &build.File{Path: f.Path, Comments: f.Comments, Stmt: stmt, Type: build.TypeModule}, useRepo } // AddRepoUsages adds the given repos to the given use_repo calls without introducing duplicate // arguments. // useRepos must not be empty. // Keyword arguments are preserved but adding them is currently not supported. func AddRepoUsages(useRepos []*build.CallExpr, repos ...string) { if len(repos) == 0 { return } if len(useRepos) == 0 { panic("useRepos must not be empty") } seen := make(map[string]struct{}) for _, useRepo := range useRepos { if len(useRepo.List) == 0 { // Invalid use_repo call, skip. continue } for _, arg := range useRepo.List[1:] { seen[repoFromUseRepoArg(arg)] = struct{}{} } } lastUseRepo := getLastUseRepo(useRepos) for _, repo := range repos { if _, ok := seen[repo]; ok { continue } // Sorting of use_repo arguments is handled by Buildify. // TODO: Add a keyword argument instead if repo is of the form "key=value". lastUseRepo.List = append(lastUseRepo.List, &build.StringExpr{Value: repo}) } } // RemoveRepoUsages removes the given repos from the given use_repo calls. // Repositories are identified via their names as exported by the module extension (i.e. the value // rather than the key in the case of keyword arguments). func RemoveRepoUsages(useRepos []*build.CallExpr, repos ...string) { if len(useRepos) == 0 || len(repos) == 0 { return } toRemove := make(map[string]struct{}) for _, repo := range repos { toRemove[repo] = struct{}{} } for _, useRepo := range useRepos { if len(useRepo.List) == 0 { // Invalid use_repo call, skip. continue } var args []build.Expr // Skip over ext in use_repo(ext, ...). for _, arg := range useRepo.List[1:] { repo := repoFromUseRepoArg(arg) if _, remove := toRemove[repo]; !remove { args = append(args, arg) } } useRepo.List = append(useRepo.List[:1], args...) } } func getLastUseRepo(useRepos []*build.CallExpr) *build.CallExpr { var lastUseRepo *build.CallExpr for _, useRepo := range useRepos { if lastUseRepo == nil || useRepo.Pos.Byte > lastUseRepo.Pos.Byte { lastUseRepo = useRepo } } return lastUseRepo } // repoFromUseRepoArg returns the repository name used by the module extension itself from a // use_repo argument. func repoFromUseRepoArg(arg build.Expr) string { switch arg := arg.(type) { case *build.StringExpr: // use_repo(ext, "repo") --> repo return arg.Value case *build.AssignExpr: // use_repo(ext, my_repo = "repo") --> repo if repo, ok := arg.RHS.(*build.StringExpr); ok { return repo.Value } } return "" } // getApparentModuleName returns the apparent name used for the repository of the module defined // in the given MODULE.bazel file. func getApparentModuleName(f *build.File) string { apparentName := "" for _, module := range f.Rules("module") { if repoName := module.AttrString("repo_name"); repoName != "" { apparentName = repoName } else if name := module.AttrString("name"); name != "" { apparentName = name } } return apparentName } // normalizeLabelString converts a label string into the form @apparent_name//path/to:target. func normalizeLabelString(rawLabel, apparentModuleName string) string { label := labels.ParseRelative(rawLabel, "") if label.Repository == "" { // This branch is taken in two different cases: // 1. The label is relative. In this case, labels.ParseRelative populates the Package field // but not the Repository field. // 2. The label is of the form "@//pkg:extension.bzl". Normalize to spelling out the // apparent name of the root module. Note that this syntax is only allowed in the root // module, but since we are inspecting its module file as a tool, we can assume that the // current module is the root module. label.Repository = apparentModuleName } return label.Format() } func parseUseExtension(stmt build.Expr) (proxy string, bzlFile string, name string, dev bool, isolate bool) { assign, ok := stmt.(*build.AssignExpr) if !ok { return } lhsIdent, ok := assign.LHSIdent() if !ok { return } if _, ok = assign.RHS.(*build.CallExpr); !ok { return } call := assign.RHS.(*build.CallExpr) if _, ok = call.X.(*build.Ident); !ok { return } if call.X.(*build.Ident).Name != "use_extension" { return } if len(call.List) < 2 { // Missing required positional arguments. return } bzlFileExpr, ok := call.List[0].(*build.StringExpr) if !ok { return } nameExpr, ok := call.List[1].(*build.StringExpr) if !ok { return } // Check for the optional dev_dependency keyword argument. if len(call.List) > 2 { for _, arg := range call.List[2:] { dev = dev || parseBooleanKeywordArg(arg, "dev_dependency") isolate = isolate || parseBooleanKeywordArg(arg, "isolate") } } return lhsIdent.Name, bzlFileExpr.Value, nameExpr.Value, dev, isolate } // parseBooleanKeywordArg parses a keyword argument of type bool that is assumed to default to // False. func parseBooleanKeywordArg(arg build.Expr, name string) bool { keywordArg, ok := arg.(*build.AssignExpr) if !ok { return false } argName, ok := keywordArg.LHS.(*build.Ident) if !ok || argName.Name != name { return false } argValue, ok := keywordArg.RHS.(*build.Ident) // We assume that any expression other than "False" evaluates to True as otherwise there would // be no reason to specify the argument - MODULE.bazel files are entirely static with no // external inputs, so every expression always evaluates to the same value. if ok && argValue.Name == "False" { return false } return true } func parseTag(stmt build.Expr) string { call, ok := stmt.(*build.CallExpr) if !ok { return "" } if _, ok := call.X.(*build.DotExpr); !ok { return "" } dot := call.X.(*build.DotExpr) if _, ok := dot.X.(*build.Ident); !ok { return "" } return dot.X.(*build.Ident).Name } // lastProxyUsage returns the index of the last statement in the given file that uses one of the // given extension proxies (either in a use_extension assignment or tag call). If no such statement // exists, -1 is returned. func lastProxyUsage(f *build.File, proxies []string) (lastUsage int, lastProxy string) { proxiesSet := make(map[string]struct{}) for _, p := range proxies { proxiesSet[p] = struct{}{} } lastUsage = -1 for i, stmt := range f.Stmt { proxy, _, _, _, _ := parseUseExtension(stmt) if proxy != "" { _, isUsage := proxiesSet[proxy] if isUsage { lastUsage = i lastProxy = proxy continue } } proxy = parseTag(stmt) if proxy != "" { _, isUsage := proxiesSet[proxy] if isUsage { lastUsage = i lastProxy = proxy continue } } } return lastUsage, lastProxy } // ExtractModuleToApparentNameMapping collects the mapping of module names (e.g. "rules_go") to // user-configured apparent names (e.g. "my_rules_go") from the repo's MODULE.bazel, if it exists. // The given function is called with a repo-relative, slash-separated path and should return the // content of the MODULE.bazel or *.MODULE.bazel file at that path, or nil if the file does not // exist. // See https://bazel.build/external/module#repository_names_and_strict_deps for more information on // apparent names. func ExtractModuleToApparentNameMapping(fileReader func(relPath string) *build.File) func(string) string { moduleToApparentName := collectApparentNames(fileReader, "MODULE.bazel") return func(moduleName string) string { return moduleToApparentName[moduleName] } } // Collects the mapping of module names (e.g. "rules_go") to user-configured apparent names (e.g. // "my_rules_go"). See https://bazel.build/external/module#repository_names_and_strict_deps for more // information on apparent names. func collectApparentNames(fileReader func(relPath string) *build.File, relPath string) map[string]string { apparentNames := make(map[string]string) seenFiles := make(map[string]struct{}) filesToProcess := []string{relPath} for len(filesToProcess) > 0 { f := filesToProcess[0] filesToProcess = filesToProcess[1:] if _, seen := seenFiles[f]; seen { continue } seenFiles[f] = struct{}{} bf := fileReader(f) if bf == nil { return nil } names, includeLabels := collectApparentNamesAndIncludes(bf) for name, apparentName := range names { apparentNames[name] = apparentName } for _, includeLabel := range includeLabels { l := labels.Parse(includeLabel) p := path.Join(l.Package, l.Target) filesToProcess = append(filesToProcess, p) } } return apparentNames } func collectApparentNamesAndIncludes(f *build.File) (map[string]string, []string) { apparentNames := make(map[string]string) var includeLabels []string for _, dep := range f.Rules("") { if dep.ExplicitName() == "" { if ident, ok := dep.Call.X.(*build.Ident); !ok || ident.Name != "include" { continue } if len(dep.Call.List) != 1 { continue } if str, ok := dep.Call.List[0].(*build.StringExpr); ok { includeLabels = append(includeLabels, str.Value) } continue } if dep.Kind() != "module" && dep.Kind() != "bazel_dep" { continue } // We support module in addition to bazel_dep to handle language repos that use Gazelle to // manage their own BUILD files. if name := dep.AttrString("name"); name != "" { if repoName := dep.AttrString("repo_name"); repoName != "" { apparentNames[name] = repoName } else { apparentNames[name] = name } } } return apparentNames, includeLabels } buildtools-10.1.0/edit/bzlmod/bzlmod_test.go0000664000175000017500000003111615254573013020437 0ustar kenhyskenhys/* Copyright 2023 Google LLC 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 https://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 bzlmod import ( "reflect" "strconv" "testing" "github.com/bazel-contrib/buildtools/v10/build" ) const proxiesNoModuleHeader = `` const proxiesModuleNameHeader = ` module(name = "name") ` const proxiesModuleRepoNameHeader = ` module(name = "name", repo_name = "repo_name") ` const proxiesBody = ` prox1 = use_extension("@name//bzl:extensions.bzl", "ext") prox1.use(name = "foo") isolated_prox1 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = False, isolate = True) isolated_prox1.use(name = "foo") prox2 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True) prox2.use(list = ["foo", "bar"]) # some comment prox3 = use_extension("@repo_name//bzl:extensions.bzl", "ext") prox3.use(label = "@dep//:bar") isolated_prox2 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True, isolate = True) isolated_prox2.use(name = "foo") prox4 = use_extension("@repo_name//bzl:extensions.bzl", "ext", dev_dependency = True) prox4.use(dict = {"foo": "bar"}) isolated_prox3 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True, isolate = True) isolated_prox3.use(name = "foo") prox5 = use_extension("@//bzl:extensions.bzl", "ext") prox5.use(name = "foo") prox6 = use_extension("@//bzl:extensions.bzl", "ext", dev_dependency = True) prox6.use(list = ["foo", "bar"]) prox7 = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = False) prox7.use(label = "@foo//:bar") isolated_prox4 = use_extension("@name//bzl:extensions.bzl", "ext", isolate = True) isolated_prox4.use(name = "foo") prox8 = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = True) unused = prox8.use(dict = {"foo": "bar"}) prox9 = use_extension( # comment "@dep//:extensions.bzl", "other_ext") prox9.use(label = "@name//:bar") prox10 = use_extension("@dep//:extensions.bzl", "other_ext", dev_dependency = bool(1)) prox10.use(dict = {"foo": "bar"}) prox11 = use_extension("extension.bzl", "ext") prox12 = use_extension(":extension.bzl", "ext") prox13 = use_extension("//:extension.bzl", "ext") prox14 = use_extension("@name//:extension.bzl", "ext") prox15 = use_extension("@repo_name//:extension.bzl", "ext") ` func TestProxies(t *testing.T) { for i, tc := range []struct { content string extBzlFiles []string extName string dev bool expectedProxies []string }{ { proxiesNoModuleHeader + proxiesBody, []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl"}, "ext", false, []string{"prox5", "prox7"}, }, { proxiesNoModuleHeader + proxiesBody, []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl"}, "ext", true, []string{"prox6", "prox8"}, }, { proxiesModuleNameHeader + proxiesBody, []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@name//bzl:extensions.bzl"}, "ext", false, []string{"prox1", "prox5", "prox7"}, }, { proxiesModuleNameHeader + proxiesBody, []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@name//bzl:extensions.bzl"}, "ext", true, []string{"prox2", "prox6", "prox8"}, }, { proxiesModuleRepoNameHeader + proxiesBody, []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@repo_name//bzl:extensions.bzl"}, "ext", false, []string{"prox3", "prox5", "prox7"}, }, { proxiesModuleRepoNameHeader + proxiesBody, []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@repo_name//bzl:extensions.bzl"}, "ext", true, []string{"prox4", "prox6", "prox8"}, }, { proxiesModuleRepoNameHeader + proxiesBody, []string{"@name//bzl:extensions.bzl"}, "ext", false, []string{"prox1"}, }, { proxiesModuleRepoNameHeader + proxiesBody, []string{"@dep//:extensions.bzl"}, "other_ext", false, []string{"prox9"}, }, { proxiesModuleRepoNameHeader + proxiesBody, []string{"@dep//:extensions.bzl"}, "other_ext", true, []string{"prox10"}, }, { proxiesModuleNameHeader + proxiesBody, []string{"//:extension.bzl", "@//:extension.bzl"}, "ext", false, []string{"prox11", "prox12", "prox13", "prox14"}, }, { proxiesModuleRepoNameHeader + proxiesBody, []string{"//:extension.bzl", "@//:extension.bzl"}, "ext", false, []string{"prox11", "prox12", "prox13", "prox15"}, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { for _, extBzlFile := range tc.extBzlFiles { t.Run("label_"+extBzlFile, func(t *testing.T) { f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) if err != nil { t.Fatal(err) } actualProxies := Proxies(f, extBzlFile, tc.extName, tc.dev) if !reflect.DeepEqual(actualProxies, tc.expectedProxies) { t.Error("want: ", tc.expectedProxies, ", got: ", actualProxies) } }) } }) } } func TestAllProxies(t *testing.T) { for i, tc := range []struct { proxy string expectedProxies []string }{ { "invalid_proxy", nil, }, { "isolated_prox1", []string{"isolated_prox1"}, }, { "isolated_prox2", []string{"isolated_prox2"}, }, { "isolated_prox3", []string{"isolated_prox3"}, }, { "isolated_prox4", []string{"isolated_prox4"}, }, { "prox1", []string{"prox1", "prox5", "prox7"}, }, { "prox2", []string{"prox2", "prox6", "prox8"}, }, { "prox9", []string{"prox9"}, }, { "prox10", []string{"prox10"}, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { f, err := build.ParseModule("MODULE.bazel", []byte(proxiesModuleNameHeader+proxiesBody)) if err != nil { t.Fatal(err) } proxies := AllProxies(f, tc.proxy) if !reflect.DeepEqual(proxies, tc.expectedProxies) { t.Error("want: ", tc.expectedProxies, ", got: ", proxies) } }) } } const useReposFile = ` prox1 = use_extension("@mod//bzl:extensions.bzl", "ext") prox1.use() prox1.config() prox2 = use_extension("//bzl:extensions.bzl", "ext") use_repo(prox2) use_repo(prox1, "repo5") prox3 = use_extension("@dep//bzl:extensions.bzl", "ext") prox2_dev = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = True) use_repo(prox1, "repo1") use_repo(prox3, "repo2") use_repo(prox2, "repo3", "repo4") ` func TestUseRepos(t *testing.T) { for i, tc := range []struct { content string proxies []string expectedStmts []int }{ { useReposFile, []string{"prox2"}, []int{4, 10}, }, { useReposFile, []string{"prox1", "prox3"}, []int{5, 8, 9}, }, { useReposFile, []string{"prox2_dev"}, []int{}, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) if err != nil { t.Fatal(err) } var expectedUseRepos []*build.CallExpr for _, stmt := range tc.expectedStmts { expectedUseRepos = append(expectedUseRepos, f.Stmt[stmt].(*build.CallExpr)) } actualUseRepos := UseRepos(f, tc.proxies) if !reflect.DeepEqual(actualUseRepos, expectedUseRepos) { t.Error("want: ", expectedUseRepos, ", got: ", actualUseRepos) } }) } } func TestNewUseRepos(t *testing.T) { for i, tc := range []struct { content string proxies []string expectedContent string expectedUseRepo int }{ { ``, []string{"prox1"}, ``, -1, }, { `prox1 = use_extension("@module//:lib.bzl", "ext")`, []string{"prox2"}, `prox1 = use_extension("@module//:lib.bzl", "ext") `, -1, }, { `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext", dev_dependency = True)`, []string{"prox1"}, `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext", dev_dependency = True) use_repo(prox1) `, 1, }, { `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext") prox1.config() prox1.download(name = "foo")`, []string{"prox1"}, `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext") prox1.config() prox1.download(name = "foo") use_repo(prox1) `, 3, }, { `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") pull.oci_pull(name = "distroless_base") `, []string{"go_deps"}, `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") use_repo(go_deps) pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") pull.oci_pull(name = "distroless_base") `, 2, }, { `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") `, []string{"go_deps"}, `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") use_repo(go_deps) pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") `, 2, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) if err != nil { t.Fatal(err) } f, actualNewUseRepo := NewUseRepo(f, tc.proxies) actualContent := string(build.Format(f)) if actualNewUseRepo != nil { if !reflect.DeepEqual(actualNewUseRepo, f.Stmt[tc.expectedUseRepo]) { t.Error("want: ", f.Stmt[tc.expectedUseRepo], ", got: ", actualNewUseRepo) } } else { if tc.expectedUseRepo != -1 { t.Error("wanted a nil new use_repo") } } if !reflect.DeepEqual(actualContent, tc.expectedContent) { t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent) } }) } } func TestAddRepoUsages(t *testing.T) { for i, tc := range []struct { content string repos []string expectedContent string }{ { ``, []string{}, ``, }, { `use_repo(prox)`, []string{"repo2", "repo1"}, `use_repo(prox, "repo1", "repo2") `, }, { `use_repo( prox, )`, []string{"repo2", "repo1"}, `use_repo( prox, "repo1", "repo2", ) `, }, { `use_repo(prox, "repo2")`, []string{"repo2", "repo1"}, `use_repo(prox, "repo1", "repo2") `, }, { `use_repo( prox, "repo2", )`, []string{"repo2", "repo1"}, `use_repo( prox, "repo1", "repo2", ) `, }, { `use_repo(prox, "repo1") use_repo( prox2, my_repo = "repo2", "repo5", ) use_repo(prox, "repo3")`, []string{"repo3", "repo2", "repo4"}, `use_repo(prox, "repo1") use_repo( prox2, "repo5", my_repo = "repo2", ) use_repo(prox, "repo3", "repo4") `, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) if err != nil { t.Fatal(err) } var useRepos []*build.CallExpr for _, stmt := range f.Stmt { useRepos = append(useRepos, stmt.(*build.CallExpr)) } AddRepoUsages(useRepos, tc.repos...) actualContent := string(build.Format(f)) if !reflect.DeepEqual(actualContent, tc.expectedContent) { t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent) } }) } } func TestRemoveRepoUsages(t *testing.T) { for i, tc := range []struct { content string repos []string expectedContent string }{ { ``, []string{"repo1"}, ``, }, { `use_repo(prox)`, []string{"repo2", "repo1"}, `use_repo(prox) `, }, { `use_repo( prox, )`, []string{"repo2", "repo1"}, `use_repo( prox, ) `, }, { `use_repo(prox, "repo2")`, []string{"repo2", "repo1"}, `use_repo(prox) `, }, { `use_repo( prox, "repo2", )`, []string{"repo2", "repo1"}, `use_repo(prox) `, }, { `use_repo(prox, "repo1") use_repo( prox2, my_repo = "repo2", "repo5", ) use_repo(prox, "repo3")`, []string{"repo3", "repo2", "repo4"}, `use_repo(prox, "repo1") use_repo( prox2, "repo5", ) use_repo(prox) `, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) if err != nil { t.Fatal(err) } var useRepos []*build.CallExpr for _, stmt := range f.Stmt { useRepos = append(useRepos, stmt.(*build.CallExpr)) } RemoveRepoUsages(useRepos, tc.repos...) actualContent := string(build.Format(f)) if !reflect.DeepEqual(actualContent, tc.expectedContent) { t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent) } }) } } buildtools-10.1.0/edit/bzlmod/BUILD.bazel0000664000175000017500000000101615254573013017434 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "bzlmod", srcs = ["bzlmod.go"], importpath = "github.com/bazel-contrib/buildtools/v10/edit/bzlmod", visibility = ["//visibility:public"], deps = [ "//build", "//labels", ], ) go_test( name = "bzlmod_test", srcs = ["bzlmod_test.go"], embed = [":bzlmod"], deps = ["//build"], ) alias( name = "go_default_library", actual = ":bzlmod", visibility = ["//visibility:public"], ) buildtools-10.1.0/edit/safe/0000775000175000017500000000000015254573013015207 5ustar kenhyskenhysbuildtools-10.1.0/edit/safe/buildifier.go0000664000175000017500000000175615254573013017665 0ustar kenhyskenhys// Package buildifier provides a Buildifier which doesn't call os.exec. // // There are some programming frameworks which consider os.exec dangerous. // // The package is imported just for its side-effects. For example: // // import _ "github.com/bazel-contrib/buildtools/v10/edit/safe/buildifier" package buildifier import ( "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/edit" ) func init() { edit.RegisterBuildifier(&buildifier{}) } type buildifier struct{} // runBuildifier formats the build file f using the built-in formatter. func (b *buildifier) Buildify(_ *edit.Options, f *build.File) ([]byte, error) { // Current AST may be not entirely correct, e.g. it may contain Ident which // value is a chunk of code, like "f(x)". The AST should be printed and // re-read to parse such expressions correctly. contents := build.Format(f) newF, err := build.Parse(f.Path, contents) if err != nil { return nil, err } return build.Format(newF), nil } buildtools-10.1.0/edit/safe/BUILD.bazel0000664000175000017500000000061115254573013017063 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "safe", srcs = ["buildifier.go"], importpath = "github.com/bazel-contrib/buildtools/v10/edit/safe", visibility = ["//visibility:public"], deps = [ "//build", "//edit", ], ) alias( name = "go_default_library", actual = ":safe", visibility = ["//visibility:public"], ) buildtools-10.1.0/edit/types.go0000664000175000017500000000463615254573013015775 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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/bazel-contrib/buildtools/v10/build_proto" "github.com/bazel-contrib/buildtools/v10/lang" "github.com/bazel-contrib/buildtools/v10/tables" ) var typeOf = lang.TypeOf // IsList returns true for all attributes whose type is a list. func IsList(attr string) bool { overrideValue, isOverridden := tables.IsListArg[attr] if isOverridden { return overrideValue } // It stands to reason that a sortable list must be a list. isSortableList := tables.IsSortableListArg[attr] if isSortableList { return true } 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(kind, attr string) bool { if kind == "package_group" && attr == "packages" { // "package_group" is a special rule and its "packages" attribute is not a list of labels. return false } ty := typeOf[attr] return ty == buildpb.Attribute_LABEL_LIST || ty == buildpb.Attribute_LABEL } buildtools-10.1.0/edit/edit.go0000664000175000017500000011155015254573013015550 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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" "path" "path/filepath" "regexp" "strconv" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/labels" "github.com/bazel-contrib/buildtools/v10/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 ) // 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, target string) (buildFile, repo, pkg, rule string) { label := labels.Parse(target) repo = label.Repository pkg = label.Package rule = label.Target rootDir, relativePath := wspace.FindWorkspaceRoot(root) if repo != "" { files, err := wspace.FindRepoBuildFiles(rootDir) if err == nil { if buildFile, ok := files[repo]; ok { return buildFile, repo, pkg, rule } } return "", repo, pkg, rule } defaultBuildFileName := "BUILD" if strings.HasPrefix(target, "//") { pkgPath := filepath.Join(rootDir, filepath.FromSlash(pkg)) if wspace.IsRegularFile(pkgPath) { // allow operation on other files like WORKSPACE buildFile = pkgPath pkg = path.Dir(pkg) return } for _, buildFileName := range BuildFileNames { buildFile = filepath.Join(pkgPath, buildFileName) if wspace.IsRegularFile(buildFile) { return } } buildFile = filepath.Join(pkgPath, defaultBuildFileName) return } if wspace.IsRegularFile(filepath.FromSlash(pkg)) { // allow operation on other files like WORKSPACE buildFile = pkg pkg = filepath.Join(relativePath, filepath.FromSlash(path.Dir(pkg))) return } found := false for _, buildFileName := range BuildFileNames { buildFile = filepath.Join(pkg, buildFileName) if wspace.IsRegularFile(buildFile) { found = true break } } if !found { buildFile = filepath.Join(pkg, defaultBuildFileName) } pkg = filepath.Join(relativePath, filepath.FromSlash(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) { buildFile, _, pkg, rule = InterpretLabelForWorkspaceLocation("", target) return buildFile, pkg, rule } // InterpretLabelWithRepo returns the name of the BUILD file to edit, repo name, // the full package name, and the rule. It uses the pwd for resolving workspace // file paths. func InterpretLabelWithRepo(target string) (buildFile string, repo 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.Ident) if !ok || k.Name != kind { return nil, false } return &build.Rule{Call: call, ImplicitName: ""}, 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 optional // docstring, comments, and load statements. func PackageDeclaration(f *build.File) *build.Rule { if pkg := ExistingPackageDeclaration(f); pkg != nil { return pkg } insertAfter := -1 call := &build.CallExpr{X: &build.Ident{Name: "package"}} for i, stmt := range f.Stmt { switch stmt.(type) { case *build.CommentBlock, *build.LoadStmt, *build.StringExpr: // Skip docstring, comments, and load statements to // find a place to insert the package declaration. insertAfter = i continue case *build.CallExpr: // Skip `workspace()` calls which have to be the very first statements // of workspace files if isWorkspaceCall(stmt) { insertAfter = i continue } default: } break } var all []build.Expr all = append(all, f.Stmt[:insertAfter+1]...) all = append(all, call) all = append(all, f.Stmt[insertAfter+1:]...) f.Stmt = all return &build.Rule{Call: call, ImplicitName: ""} } // 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 isEmptyPackage(stmt) { continue } all = append(all, stmt) } return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: f.Type} } func isEmptyPackage(expr build.Expr) bool { if call, ok := expr.(*build.CallExpr); ok { functionName, ok := call.X.(*build.Ident) if ok && functionName.Name == "package" && len(call.List) == 0 { return true } } return false } func isWorkspaceCall(expr build.Expr) bool { if call, ok := expr.(*build.CallExpr); ok { if ident, ok := call.X.(*build.Ident); ok && ident.Name == "workspace" { return true } } return false } // RemoveEmptyUseRepoCalls removes empty use repo declarations from the file. func RemoveEmptyUseRepoCalls(f *build.File) *build.File { if f.Type != build.TypeModule { return f } var all []build.Expr for _, stmt := range f.Stmt { if isEmptyUseRepoCall(stmt) { continue } all = append(all, stmt) } return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: build.TypeModule} } func isEmptyUseRepoCall(expr build.Expr) bool { if call, ok := expr.(*build.CallExpr); ok { functionName, ok := call.X.(*build.Ident) if ok && functionName.Name == "use_repo" && len(call.List) == 1 { return true } } return false } // 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.Ident) if ok && literal.Name == 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.Ident).Name) 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) } _, rule := IndexOfRuleByName(f, name) return rule } // 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, *build.Rule) { linenum := -1 if strings.HasPrefix(name, "%") { // "%" 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 { // first check if call is a CallExpr, else check if it's an AssignExpr // with a CallExpr on the RHS call, ok := stmt.(*build.CallExpr) if !ok { if as, ok := stmt.(*build.AssignExpr); ok { if c, ok := as.RHS.(*build.CallExpr); ok { call = c } } } if call == nil { continue } r := f.Rule(call) start, _ := call.X.Span() if r.Name() == name || start.Line == linenum { return i, r } // Allow for precisely targeting the package declaration. This // helps adding new load() and license() rules if name == "__pkg__" { if rule, ok := ExprToRule(stmt, "package"); ok { return i, rule } } } return -1, nil } // 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, Type: f.Type} } // 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 := f.Rule(call) if r.Name() != name { all = append(all, stmt) } } return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: f.Type} } // 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.Ident) if !ok || k.Name != kind { all = append(all, stmt) } } return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: f.Type} } // 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 } // AllSelects returns all the selects concatenated in an expression. func AllSelects(e build.Expr) []*build.CallExpr { switch e := e.(type) { case *build.BinaryExpr: if e.Op == "+" { return append(AllSelects(e.X), AllSelects(e.Y)...) } case *build.CallExpr: if x, ok := e.X.(*build.Ident); ok && x.Name == "select" { return []*build.CallExpr{e} } } return nil } // allListsFromSelects returns all ListExpr nodes from all select statements // in an expression func allListsFromSelects(e build.Expr) []*build.ListExpr { var lists []*build.ListExpr for _, s := range AllSelects(e) { if len(s.List) != 1 { return nil } dict, ok := s.List[0].(*build.DictExpr) if !ok { return nil } for _, kv := range dict.List { list, ok := kv.Value.(*build.ListExpr) if !ok { continue } lists = append(lists, list) } } return lists } // allListsIncludingSelects returns all the lists concatenated in an expression // including lists inside select statements. // For example, in: glob(["*.go"]) + [":rule"] + select({"foo": [":bar"]}) // the function will return [[":rule", ":bar"]]. func allListsIncludingSelects(e build.Expr) []*build.ListExpr { return append(AllLists(e), allListsFromSelects(e)...) } // 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 } // listsFind looks for a string in list expressions func listsFind(lists []*build.ListExpr, item string, pkg string) *build.StringExpr { for _, list := range lists { for _, elem := range list.List { str, ok := elem.(*build.StringExpr) if ok && labels.Equal(str.Value, item, pkg) { return str } } } 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 { return listsFind(AllLists(e), item, pkg) } // listOrSelectFind looks for a string in the list expression (which may be a // concatenation of lists and select statements). It returns the element // if it is found. nil otherwise. func listOrSelectFind(e build.Expr, item string, pkg string) *build.StringExpr { return listsFind(allListsIncludingSelects(e), item, pkg) } // 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 } // commentDisables checks whether token contains prefix + ": disable=" followed by, // at some positition, target. func commentDisables(token string, prefix string, target string) bool { search := prefix + ": disable=" for { idx := strings.Index(token, search) if idx < 0 { return false } rest := token[idx+len(search):] if strings.Contains(rest, target) { return true } token = rest } } // 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 } // ContainsDisableComment checks whether the expr has a comment that disables // a warning, including when multiple warnings are disabled on the same line. func ContainsDisableComment(expr build.Expr, warning string) bool { warning = strings.ToLower(warning) com := expr.Comment() comments := append(com.Before, com.Suffix...) comments = append(comments, com.After...) tools := []string{"buildifier", "buildozer"} for _, tool := range tools { for _, c := range comments { if commentDisables(strings.ToLower(c.Token), tool, warning) { return true } } } return false } // RemoveEmptySelectsAndConcatLists iterates the tree in order to turn // empty selects into empty lists and adjacent lists are concatenated func RemoveEmptySelectsAndConcatLists(e build.Expr) build.Expr { switch e := e.(type) { case *build.BinaryExpr: if e.Op == "+" { e.X = RemoveEmptySelectsAndConcatLists(e.X) e.Y = RemoveEmptySelectsAndConcatLists(e.Y) x, xIsList := e.X.(*build.ListExpr) y, yIsList := e.Y.(*build.ListExpr) if xIsList && yIsList { return &build.ListExpr{List: append(x.List, y.List...)} } if xIsList && len(x.List) == 0 { return e.Y } if yIsList && len(y.List) == 0 { return e.X } } case *build.CallExpr: if x, ok := e.X.(*build.Ident); ok && x.Name == "select" { if len(e.List) == 0 { return &build.ListExpr{List: []build.Expr{}} } if dict, ok := e.List[0].(*build.DictExpr); ok { for _, keyVal := range dict.List { val, ok := keyVal.Value.(*build.ListExpr) if !ok || len(val.List) > 0 { return e } } return &build.ListExpr{List: []build.Expr{}} } } } return e } // ComputeIntersection returns the intersection of the two lists given as parameters; // if the containing elements are not build.StringExpr, the result will be nil. func ComputeIntersection(list1, list2 []build.Expr) []build.Expr { if list1 == nil || list2 == nil { return nil } if len(list2) == 0 { return []build.Expr{} } i := 0 for j, common := range list1 { if common, ok := common.(*build.StringExpr); ok { found := false for _, elem := range list2 { if str, ok := elem.(*build.StringExpr); ok { if str.Value == common.Value { found = true break } } else { return nil } } if found { list1[i] = list1[j] i++ } } else { return nil } } return list1[:i] } // SelectListsIntersection returns the intersection of the lists of strings inside // the dictionary argument of the select expression given as a parameter func SelectListsIntersection(sel *build.CallExpr, pkg string) (intersection []build.Expr) { if len(sel.List) == 0 || len(sel.List) > 1 { return nil } dict, ok := sel.List[0].(*build.DictExpr) if !ok || len(dict.List) == 0 { return nil } if val, ok := dict.List[0].Value.(*build.ListExpr); ok { intersection = make([]build.Expr, len(val.List)) copy(intersection, val.List) } for _, keyVal := range dict.List[1:] { val, ok := keyVal.Value.(*build.ListExpr) if !ok { return nil } intersection = ComputeIntersection(intersection, val.List) if len(intersection) == 0 { return intersection } } return intersection } // ResolveAttr extracts common elements of the lists inside select dictionaries // and adds them at attribute level rather than select level, as well as turns // empty selects into empty lists and concatenates adjacent lists func ResolveAttr(r *build.Rule, attr, pkg string) { var toExtract []build.Expr e := r.Attr(attr) if e == nil { return } for _, sel := range AllSelects(e) { intersection := SelectListsIntersection(sel, pkg) if intersection != nil { toExtract = append(toExtract, intersection...) } } for _, common := range toExtract { e = AddValueToList(e, pkg, common, false) // this will also remove them from selects } r.SetAttr(attr, RemoveEmptySelectsAndConcatLists(e)) } // SelectDelete removes the item from all the lists which are values // in the dictionary of every select func SelectDelete(e build.Expr, item, pkg string, deleted **build.StringExpr) { for _, sel := range AllSelects(e) { if len(sel.List) == 0 { continue } if dict, ok := sel.List[0].(*build.DictExpr); ok { for _, keyVal := range dict.List { if val, ok := keyVal.Value.(*build.ListExpr); ok { RemoveFromList(val, item, pkg, deleted) } } } } } // RemoveFromList removes one element from a ListExpr and stores // the deleted StringExpr at the address pointed by the last parameter func RemoveFromList(li *build.ListExpr, item, pkg string, deleted **build.StringExpr) { var all []build.Expr for _, elem := range li.List { if str, ok := elem.(*build.StringExpr); ok { if labels.Equal(str.Value, item, pkg) && (DeleteWithComments || !hasComments(str)) { if deleted != nil { *deleted = str } continue } } all = append(all, elem) } li.List = all } // 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) { if unquoted, _, err := build.Unquote(item); err == nil { item = unquoted } deleted = nil for _, li := range AllLists(e) { RemoveFromList(li, item, pkg, &deleted) } SelectDelete(e, item, pkg, &deleted) 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 for _, li := range allListsIncludingSelects(e) { for k, elem := range li.List { str, ok := elem.(*build.StringExpr) if !ok || !labels.Equal(str.Value, old, pkg) { continue } li.List[k] = &build.StringExpr{Value: ShortenLabel(value, pkg), Comments: *elem.Comment()} replaced = true } } return replaced } // ListSubstitute replaces strings matching a regular expression in all lists // in e and returns a Boolean to indicate whether the replacement was // successful. func ListSubstitute(e build.Expr, oldRegexp *regexp.Regexp, newTemplate string) bool { substituted := false for _, li := range allListsIncludingSelects(e) { for k, elem := range li.List { str, ok := elem.(*build.StringExpr) if !ok { continue } newValue, ok := stringSubstitute(str.Value, oldRegexp, newTemplate) if ok { li.List[k] = &build.StringExpr{Value: newValue, Comments: *elem.Comment()} substituted = true } } } return substituted } func stringSubstitute(oldValue string, oldRegexp *regexp.Regexp, newTemplate string) (string, bool) { match := oldRegexp.FindStringSubmatchIndex(oldValue) if match == nil { return oldValue, false } newValue := string(oldRegexp.ExpandString(nil, newTemplate, oldValue, match)) if match[0] > 0 { newValue = oldValue[:match[0]] + newValue } if match[1] < len(oldValue) { newValue = newValue + oldValue[match[1]:] } return newValue, true } // 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.AssignExpr) (varAssignment *build.AssignExpr) { if vars == nil { return nil } if literal, ok := expr.(*build.Ident); ok { if varAssignment = (*vars)[literal.Name]; 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 { if ListFind(oldList, str.Value, pkg) != nil { // The value is already in the list. return oldList } SelectDelete(oldList, str.Value, pkg, nil) } 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.AssignExpr) { old := r.Attr(name) sorted := !attributeMustNotBeSorted(r.Kind(), name) if varAssignment := getVariable(old, vars); varAssignment != nil { varAssignment.RHS = AddValueToList(varAssignment.RHS, 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.AssignExpr) 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 _, kv := range dict.List { 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 } // DictionaryGet looks for the key in the dictionary expression, and returns the // current value. If it is unset, it returns nil. func DictionaryGet(dict *build.DictExpr, key string) build.Expr { for _, kv := range dict.List { if k, ok := kv.Key.(*build.StringExpr); ok && k.Value == key { return kv.Value } } return nil } // DictionaryDelete looks for the key in the dictionary expression. If the key exists, // it removes the key-value pair and returns it. Otherwise it returns nil. func DictionaryDelete(dict *build.DictExpr, key string) (deleted build.Expr) { if unquoted, _, err := build.Unquote(key); err == nil { key = unquoted } deleted = nil var all []*build.KeyValueExpr for _, kv := range dict.List { if k, ok := kv.Key.(*build.StringExpr); ok { if k.Value == key { deleted = kv } else { all = append(all, kv) } } } dict.List = all return deleted } // 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.AssignExpr) if !ok { continue } k, ok := as.LHS.(*build.Ident) if !ok || k.Name != oldName { continue } k.Name = 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.Ident) if !ok || fct.Name != name { return nil } return f(call, stk) }) } // UsedSymbols returns the set of symbols used in the BUILD file (variables, function names). func UsedSymbols(stmt build.Expr) map[string]bool { symbols := make(map[string]bool) build.Walk(stmt, func(expr build.Expr, stack []build.Expr) { // Don't traverse inside load statements if len(stack) > 0 { if _, ok := stack[len(stack)-1].(*build.LoadStmt); ok { return } } literal, ok := expr.(*build.Ident) if !ok { return } // If we are on the left-side of an assignment or are declared by a var statement // or type alias, or are a type parameter, it doesn't count as a use. for _, e := range stack { switch e := e.(type) { case *build.AssignExpr: // TODO: should we be checking all CollectLValues here? if ident, ok := e.LHSIdent(); ok && ident == expr { return } case *build.DefStmt: if contains(e.TypeParams, expr) { return } case *build.TypedIdent: if expr == e.GetIdent() { return } case *build.TypeAliasStmt: if expr == e.GetIdent() { return } if contains(e.TypeParams, expr) { return } } } symbols[literal.Name] = true }) return symbols } func contains(haystack build.Expr, needle build.Expr) bool { if haystack == nil { return false } found := false build.WalkInterruptable(haystack, func(expr build.Expr, stack []build.Expr) error { if expr == needle { found = true return &build.StopTraversalError{} } return nil }) return found } // UsedTypes returns the set of types used in the BUILD file (variables, function names). func UsedTypes(stmt build.Expr) map[string]bool { symbols := make(map[string]bool) build.Walk(stmt, func(expr build.Expr, stack []build.Expr) { // Don't traverse inside load statements if len(stack) > 0 { if _, ok := stack[len(stack)-1].(*build.LoadStmt); ok { return } } // Types can only be found in method declarations, var statements, and type aliases. switch expr := expr.(type) { case *build.TypeAliasStmt: for _, t := range build.GetTypes(expr) { symbols[t] = true } case *build.TypedIdent: for _, t := range build.GetTypes(expr) { symbols[t] = true } case *build.DefStmt: for _, t := range build.GetTypes(expr) { symbols[t] = true } } }) return symbols } // NewLoad creates a new LoadStmt node func NewLoad(location string, from, to []string) *build.LoadStmt { load := &build.LoadStmt{ Module: &build.StringExpr{ Value: location, }, ForceCompact: true, } for i := range from { load.From = append(load.From, &build.Ident{Name: from[i]}) load.To = append(load.To, &build.Ident{Name: to[i]}) } return load } // AppendToLoad appends symbols to an existing load statement // Returns true if the statement was acually edited (if the required symbols haven't been // loaded yet) func AppendToLoad(load *build.LoadStmt, from, to []string) bool { symbolsToLoad := make(map[string]string) for i, s := range to { symbolsToLoad[s] = from[i] } for _, ident := range load.To { delete(symbolsToLoad, ident.Name) // Already loaded. } if len(symbolsToLoad) == 0 { return false } // Append the remaining loads to the load statement. for s := range symbolsToLoad { load.From = append(load.From, &build.Ident{Name: symbolsToLoad[s]}) load.To = append(load.To, &build.Ident{Name: s}) } return true } // appendLoad tries to find an existing load location and append symbols to it. func appendLoad(stmts []build.Expr, location string, from, to []string) bool { symbolsToLoad := make(map[string]string) for i, s := range to { symbolsToLoad[s] = from[i] } var lastLoad *build.LoadStmt for _, s := range stmts { load, ok := s.(*build.LoadStmt) if !ok { continue } if load.Module.Value != location { continue // Loads a different file. } for _, ident := range load.To { delete(symbolsToLoad, ident.Name) // Already loaded. } // Remember the last insert location, but potentially remove more symbols // that are already loaded in other subsequent calls. lastLoad = load } if lastLoad == nil { return false } // Append the remaining loads to the last load location. from = []string{} to = []string{} for t, f := range symbolsToLoad { from = append(from, f) to = append(to, t) } AppendToLoad(lastLoad, from, to) return true } // InsertLoad inserts a load statement at the top of the list of statements. // The load statement is constructed using a string location and two slices of from- and to-symbols. // The function panics if the slices aren't of the same length. 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, location string, from, to []string) []build.Expr { if len(from) != len(to) { panic(fmt.Errorf("length mismatch: %v (from) and %v (to)", len(from), len(to))) } if appendLoad(stmts, location, from, to) { return stmts } load := NewLoad(location, from, to) insertAfter := -1 for i, stmt := range stmts { _, isComment := stmt.(*build.CommentBlock) _, isString := stmt.(*build.StringExpr) isDocString := isString && i == 0 // We add synthetic package() calls when called with :__pkg__ labels. // We strip them out later when saving the fixed file because they're // not valid. // Pretend they're not there and skip past them while we look for // possible workspace calls. // isSyntheticPackage := isEmptyPackage(stmt) // If we're editing a WORKSPACE file, bazel requires that the workspace // declaration must be the very first expression in the WORKSPACE file, // before any loads. isWorkspaceCallStmt := isWorkspaceCall(stmt) if isComment || isDocString || isWorkspaceCallStmt { insertAfter = i continue } break } var all []build.Expr all = append(all, stmts[:insertAfter+1]...) all = append(all, load) all = append(all, stmts[insertAfter+1:]...) return all } // ReplaceLoad removes load statements for passed to-symbols and replaces them with a new // load at the top of the list of statements. The new load statement is constructed using // a string location and two slices of from- and to-symbols. If stmts already contains a // load for the location in arguments, appends the symbols to load to it. // The function panics if the slices aren't of the same lentgh. func ReplaceLoad(stmts []build.Expr, location string, from, to []string) []build.Expr { if len(from) != len(to) { panic(fmt.Errorf("length mismatch: %v (from) and %v (to)", len(from), len(to))) } toSymbols := make(map[string]bool, len(to)) for _, name := range to { toSymbols[name] = true } // 1. Remove loads that will be replaced. var all []build.Expr for _, stmt := range stmts { load, ok := stmt.(*build.LoadStmt) if !ok { all = append(all, stmt) continue } var loadTo, loadFrom []*build.Ident for i, to := range load.To { // Only add the load to the statement if it will NOT be replaced by a new load. if !toSymbols[to.Name] { loadTo = append(loadTo, load.To[i]) loadFrom = append(loadFrom, load.From[i]) } } load.To = loadTo load.From = loadFrom if len(load.To) > 0 { all = append(all, load) } } // 2. Insert new loads. return InsertLoad(all, location, from, to) } // 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"]). // Deprecated; use `labels.Parse` instead func ParseLabel(target string) (string, string, string) { label := labels.Parse(target) return label.Repository, label.Package, label.Target } // ShortenLabel rewrites labels to use the canonical form (the form // recommended by build-style). // Doesn't do anything if `--shorten_label=false` flag is provided. // Use `labels.Shorten` to shorten labels unconditionally. func ShortenLabel(label, pkg string) string { if !ShortenLabelsFlag { return label } return labels.Shorten(label, pkg) } // LabelsEqual returns true if label1 and label2 are equal. // Deprecated; use `labels.Equal` instead func LabelsEqual(label1, label2, pkg string) bool { return labels.Equal(label1, label2, pkg) } buildtools-10.1.0/edit/buildozer.go0000664000175000017500000015662415254573013016635 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 programmatically editing BUILD files. package edit import ( "bufio" "bytes" "errors" "fmt" "io" "log" "os" "path" "path/filepath" "regexp" "runtime" "strconv" "strings" apipb "github.com/bazel-contrib/buildtools/v10/api_proto" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/edit/bzlmod" "github.com/bazel-contrib/buildtools/v10/file" "github.com/bazel-contrib/buildtools/v10/labels" "github.com/bazel-contrib/buildtools/v10/wspace" "github.com/golang/protobuf/jsonpb" "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 CommandsFiles []string // file names 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 IsPrintingJSON bool // output serialized devtools.buildozer.Output json instead of human-readable strings OutWriter io.Writer // where to write normal output (`os.Stdout` will be used if not specified) ErrWriter io.Writer // where to write error output (`os.Stderr` will be used if not specified) RespectBazelignore bool // whether to use .bazelignore file for ignoring paths DisableSymlinkSafety bool // whether to disable symlink safety checks } // NewOpts returns a new Options struct with some defaults set. func NewOpts() *Options { return &Options{NumIO: 200, PreferEOLComments: true, RespectBazelignore: true} } // Usage is a user-overridden func to print the program usage. var Usage = func() {} 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.AssignExpr // 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 } // AttrType is an enum representing a type of the attribute, e.g. expr or not // provided type type AttrType int const ( // notProvidedTypeAttr represents an attr type that's not provided explicitly and // needs to be inferred by buildozer from bazel's build language proto notProvidedTypeAttr = iota // rawAttr is a type for idents, ints or other types that buildozer should // take as is, without quoting or wrapping rawAttr ) // parseAttr parses the attr name and optional type, e.g. "foo" or "bar:expr" // The only supported type now is `expr` func parseAttr(attrName string) (attr string, attrType AttrType, err error) { chunks := strings.SplitN(attrName, ":", 2) if len(chunks) == 1 { return attrName, notProvidedTypeAttr, nil } attr, typeName := chunks[0], chunks[1] switch typeName { case "expr": return attr, rawAttr, nil default: return attr, notProvidedTypeAttr, fmt.Errorf("unknown attr type: %q", typeName) } } // The cmdXXX functions implement the various commands. func cmdAdd(opts *Options, env CmdEnvironment) (*build.File, error) { attr, attrType, err := parseAttr(env.Args[0]) if err != nil { return nil, err } for _, val := range env.Args[1:] { if attrType == rawAttr { AddValueToListAttribute(env.Rule, attr, env.Pkg, &build.Ident{Name: val}, &env.Vars) continue } if attrType == notProvidedTypeAttr && IsIntList(attr) { AddValueToListAttribute(env.Rule, attr, env.Pkg, &build.LiteralExpr{Token: val}, &env.Vars) continue } var strVal build.Expr strVal = getLabelStringExpr(val, env.Pkg) AddValueToListAttribute(env.Rule, attr, env.Pkg, strVal, &env.Vars) } ResolveAttr(env.Rule, attr, env.Pkg) return env.File, nil } func cmdComment(opts *Options, 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") comment := []build.Comment{} for _, line := range strings.Split(str, "\n") { comment = append(comment, build.Comment{Token: "# " + line}) } // 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 || build.IsMultiLine(attr) { attr.LHS.Comment().Before = comment } else { attr.RHS.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 := listOrSelectFind(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(opts *Options, 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{ {Value: &apipb.Output_Record_Field_Text{Text: commentsText(env.Rule.Call.Comments.Before)}}, } if text := commentsText(env.Rule.Call.Comments.Suffix); text != "" { env.output.Fields = append(env.output.Fields, &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{Text: text}, }) } if text := commentsText(env.Rule.Call.Comments.After); text != "" { env.output.Fields = append(env.output.Fields, &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{Text: text}, }) } case 1: // Print attribute comment. attr := env.Rule.AttrDefn(env.Args[0]) if attr == nil { env.output.Fields = []*apipb.Output_Record_Field{ {Value: &apipb.Output_Record_Field_Error{Error: apipb.Output_Record_Field_MISSING}}, } return nil, attrError() } comments := append(attr.Before, attr.Suffix...) env.output.Fields = []*apipb.Output_Record_Field{ {Value: &apipb.Output_Record_Field_Text{Text: commentsText(comments)}}, } case 2: // Print comment of a specific value in a list. attr := env.Rule.Attr(env.Args[0]) if attr == nil { env.output.Fields = []*apipb.Output_Record_Field{ {Value: &apipb.Output_Record_Field_Error{Error: apipb.Output_Record_Field_MISSING}}, } return nil, attrError() } value := env.Args[1] expr := listOrSelectFind(attr, value, env.Pkg) if expr == nil { env.output.Fields = []*apipb.Output_Record_Field{ {Value: &apipb.Output_Record_Field_Error{Error: apipb.Output_Record_Field_MISSING_LIST_ITEM}}, } 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{ {Value: &apipb.Output_Record_Field_Text{Text: commentsText(comments)}}, } default: panic("cmdPrintComment") } return nil, nil } func cmdDelete(opts *Options, env CmdEnvironment) (*build.File, error) { return DeleteRule(env.File, env.Rule), nil } func cmdMove(opts *Options, 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(opts *Options, 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.Ident{Name: kind}} rule := &build.Rule{Call: call, ImplicitName: ""} 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]) } } // splitLoadArgs splits arguments of form <[to=]from> // into a slice of froms and a slice of tos. func splitLoadArgs(args []string) ([]string, []string) { from := args to := append([]string{}, args...) for i := range from { if s := strings.SplitN(from[i], "=", 2); len(s) == 2 { to[i] = s[0] from[i] = s[1] } } return from, to } func cmdNewLoad(opts *Options, env CmdEnvironment) (*build.File, error) { from, to := splitLoadArgs(env.Args[1:]) env.File.Stmt = InsertLoad(env.File.Stmt, env.Args[0], from, to) return env.File, nil } func cmdReplaceLoad(opts *Options, env CmdEnvironment) (*build.File, error) { from, to := splitLoadArgs(env.Args[1:]) env.File.Stmt = ReplaceLoad(env.File.Stmt, env.Args[0], from, to) return env.File, nil } func cmdSubstituteLoad(opts *Options, env CmdEnvironment) (*build.File, error) { oldRegexp, err := regexp.Compile(env.Args[0]) if err != nil { return nil, err } newTemplate := env.Args[1] for _, stmt := range env.File.Stmt { load, ok := stmt.(*build.LoadStmt) if !ok { continue } if newValue, ok := stringSubstitute(load.Module.Value, oldRegexp, newTemplate); ok { load.Module.Value = newValue } } return env.File, nil } func cmdPrint(opts *Options, 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{Text: env.Rule.Kind()}, } } else if str == "name" { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{Text: env.Rule.Name()}, } } else if str == "label" { if env.Rule.Name() != "" { label := labels.Label{Package: env.Pkg, Target: env.Rule.Name()} fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{Text: label.Format()}, } } else { return nil, nil } } else if str == "rule" { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{Text: build.FormatString(env.Rule.Call)}, } } else if str == "startline" { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Number{Number: int32(env.Rule.Call.ListStart.Line)}, } } else if str == "endline" { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Number{Number: int32(env.Rule.Call.End.Pos.Line)}, } } else if str == "path" { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{Text: env.File.Path}, } } else if str == "attrs" { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_List{List: &apipb.RepeatedString{Strings: env.Rule.AttrKeys()}}, } } else if value == nil { fmt.Fprintf(opts.ErrWriter, "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{Text: lit.Token}, } } else if lit, ok := value.(*build.Ident); ok { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{Text: lit.Name}, } } else if string, ok := value.(*build.StringExpr); ok { fields[i] = &apipb.Output_Record_Field{ Value: &apipb.Output_Record_Field_Text{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{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(opts *Options, env CmdEnvironment) (*build.File, error) { if len(env.Args) == 1 { // Remove the attribute if env.Args[0] == "*" { didDelete := false for _, attr := range env.Rule.AttrKeys() { if attr == "name" { continue } if env.Rule.DelAttr(attr) != nil { didDelete = true } } if didDelete { return env.File, nil } } else { 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 } ResolveAttr(env.Rule, key, env.Pkg) // Remove the attribute if's an empty list if listExpr, ok := env.Rule.Attr(key).(*build.ListExpr); ok && len(listExpr.List) == 0 { env.Rule.DelAttr(key) } } if fixed { return env.File, nil } } return nil, nil } func cmdRemoveIfEqual(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] val := env.Args[1] var equal bool switch input := env.Rule.Attr(attr).(type) { case *build.StringExpr: equal = labels.Equal(input.Value, val, env.Pkg) case *build.Ident: equal = input.Name == val default: return nil, nil } if !equal { return nil, nil } env.Rule.DelAttr(attr) return env.File, nil } func cmdRemoveComment(opts *Options, env CmdEnvironment) (*build.File, error) { switch len(env.Args) { case 0: // Remove comment attached to rule env.Rule.Call.Comments.Before = nil env.Rule.Call.Comments.Suffix = nil env.Rule.Call.Comments.After = nil case 1: // Remove comment attached to attr if attr := env.Rule.AttrDefn(env.Args[0]); attr != nil { attr.Comments.Before = nil attr.Comments.Suffix = nil attr.Comments.After = nil attr.LHS.Comment().Before = nil attr.LHS.Comment().Suffix = nil attr.LHS.Comment().After = nil attr.RHS.Comment().Before = nil attr.RHS.Comment().Suffix = nil attr.RHS.Comment().After = nil } case 2: // Remove comment attached to value if attr := env.Rule.Attr(env.Args[0]); attr != nil { if expr := listOrSelectFind(attr, env.Args[1], env.Pkg); expr != nil { expr.Comments.Before = nil expr.Comments.Suffix = nil expr.Comments.After = nil } } default: panic("cmdRemoveComment") } return env.File, nil } func cmdRename(opts *Options, 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(opts *Options, env CmdEnvironment) (*build.File, error) { oldV := getStringValue(env.Args[1]) newV := getStringValue(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 labels.Equal(e.Value, oldV, env.Pkg) { env.Rule.SetAttr(key, getAttrValueExpr(key, notProvidedTypeAttr, []string{newV}, env)) } } else { ListReplace(attr, oldV, newV, env.Pkg) } } return env.File, nil } func cmdSubstitute(opts *Options, env CmdEnvironment) (*build.File, error) { oldRegexp, err := regexp.Compile(env.Args[1]) if err != nil { return nil, err } newTemplate := env.Args[2] for _, key := range attrKeysForPattern(env.Rule, env.Args[0]) { attr := env.Rule.Attr(key) e, ok := attr.(*build.StringExpr) if !ok { ListSubstitute(attr, oldRegexp, newTemplate) continue } if newValue, ok := stringSubstitute(e.Value, oldRegexp, newTemplate); ok { env.Rule.SetAttr(key, getLabelStringExpr(newValue, env.Pkg)) } } return env.File, nil } func cmdSet(opts *Options, env CmdEnvironment) (*build.File, error) { attr, attrType, err := parseAttr(env.Args[0]) if err != nil { return nil, err } args := env.Args[1:] if attr == "kind" { env.Rule.SetKind(args[0]) } else { env.Rule.SetAttr(attr, getAttrValueExpr(attr, attrType, args, env)) } return env.File, nil } func cmdSetIfAbsent(opts *Options, env CmdEnvironment) (*build.File, error) { attr, attrType, err := parseAttr(env.Args[0]) if err != nil { return nil, err } 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, attrType, args, env)) } return env.File, nil } func getAttrValueExpr(attr string, attrType AttrType, args []string, env CmdEnvironment) build.Expr { switch { case attr == "kind": return nil case attrType == rawAttr: // treat as an ident (it could be an arbitrary expression though, e.g. an // already quoted string or a function call, will be re-parsed and // formatted properly by the subsequent call of buildifier on the // resulting file) return &build.Ident{Name: args[0]} case IsIntList(attr): // list of raw objects (e.g. ints or idents) 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(")): // list of labels var list []build.Expr for _, arg := range args { list = append(list, getLabelStringExpr(arg, env.Pkg)) } return &build.ListExpr{List: list} case len(args) == 0: // Expected a non-list argument, nothing provided return &build.Ident{Name: "None"} case IsString(attr): // single label return getLabelStringExpr(args[0], env.Pkg) default: return &build.Ident{Name: args[0]} } } // getStringValue extracts a string value, which can be either quoted or not, from an input argument func getStringValue(value string) string { if unquoted, _, err := build.Unquote(value); err == nil { return unquoted } return value } // getStringExprLabel creates a StringExpr from an input argument, which can be either quoted or not, // and shortens the label value if possible. func getLabelStringExpr(value, pkg string) build.Expr { if unquoted, triple, err := build.Unquote(value); err == nil { return &build.StringExpr{Value: ShortenLabel(unquoted, pkg), TripleQuote: triple} } return &build.StringExpr{Value: ShortenLabel(value, pkg)} } // getListExpr creates a ListExpr from a list of input arguments func getListExpr(exprs ...string) *build.ListExpr { listVal := &build.ListExpr{} for _, expr := range exprs { listVal.List = append(listVal.List, &build.Ident{Name: expr}) } return listVal } func cmdCopy(opts *Options, env CmdEnvironment) (*build.File, error) { attrName := env.Args[0] from := env.Args[1] return copyAttributeBetweenRules(env, attrName, from) } func cmdCopyNoOverwrite(opts *Options, 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) } // cmdDictAdd adds a key to a dict, if that key does _not_ exit already. func cmdDictAdd(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] args := env.Args[1:] dict := &build.DictExpr{} currDict, ok := env.Rule.Attr(attr).(*build.DictExpr) if ok { dict = currDict } for _, x := range args { kv := splitOnNonEscaped(x, ':', 2) if len(kv) != 2 { return nil, fmt.Errorf("no colon in dict_add argument %q found", x) } kv = removeEscapes(kv, ':') expr := getLabelStringExpr(kv[1], env.Pkg) prev := DictionaryGet(dict, kv[0]) if prev == nil { // Only set the value if the value is currently unset. DictionarySet(dict, kv[0], expr) } } env.Rule.SetAttr(attr, dict) return env.File, nil } func cmdSetSelect(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] args := env.Args[1:] dict := &build.DictExpr{} if len(args)%2 != 0 { return nil, fmt.Errorf("no value passed for last key: %s", args[len(args)-1]) } for i := 0; i < len(args); i += 2 { key := args[i] value := args[i+1] var expr build.Expr if IsList(attr) { list := &build.ListExpr{} if cur := DictionaryGet(dict, key); cur != nil { list = cur.(*build.ListExpr) } AddValueToList(list, env.Pkg, getLabelStringExpr(value, env.Pkg), !attributeMustNotBeSorted(env.Rule.Name(), attr)) expr = list } else { expr = getLabelStringExpr(value, env.Pkg) } // Set overwrites previous values. DictionarySet(dict, key, expr) } call := &build.CallExpr{List: []build.Expr{dict}} call.X = &build.Ident{Name: "select"} env.Rule.SetAttr(attr, call) return env.File, nil } // cmdDictSet adds a key to a dict, overwriting any previous values. func cmdDictSet(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] args := env.Args[1:] dict := &build.DictExpr{} currDict, ok := env.Rule.Attr(attr).(*build.DictExpr) if ok { dict = currDict } for _, x := range args { kv := splitOnNonEscaped(x, ':', 2) if len(kv) != 2 { return nil, fmt.Errorf("no colon in dict_set argument %q found", x) } kv = removeEscapes(kv, ':') expr := getLabelStringExpr(kv[1], env.Pkg) // Set overwrites previous values. DictionarySet(dict, kv[0], expr) } env.Rule.SetAttr(attr, dict) return env.File, nil } // cmdDictRemove removes a key from a dict. func cmdDictRemove(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] args := env.Args[1:] thing := env.Rule.Attr(attr) dictAttr, ok := thing.(*build.DictExpr) if !ok { return env.File, nil } // Removes "\:" escapes for consistency with dict_add and dict_set. args = removeEscapes(args, ':') for _, x := range args { // should errors here be flagged? DictionaryDelete(dictAttr, x) env.Rule.SetAttr(attr, dictAttr) } // If the removal results in the dict having no contents, delete the attribute (stay clean!) if dictAttr == nil || len(dictAttr.List) == 0 { env.Rule.DelAttr(attr) } return env.File, nil } // cmdDictReplaceIfEqual updates a value in a dict if it is equal to a given value. func cmdDictReplaceIfEqual(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] key := env.Args[1] oldV := getStringValue(env.Args[2]) newV := getStringValue(env.Args[3]) thing := env.Rule.Attr(attr) dictAttr, ok := thing.(*build.DictExpr) if !ok { return env.File, nil } // If not using wildcard, missing key is considered an error. if prev := DictionaryGet(dictAttr, key); key != "*" && prev == nil { return nil, fmt.Errorf("key '%s' not found in dict", key) } for _, kv := range dictAttr.List { if k, ok := kv.Key.(*build.StringExpr); key == "*" || (ok && k.Value == key) { // Key-Value Pair matches key. if val, ok := kv.Value.(*build.StringExpr); ok { if labels.Equal(val.Value, oldV, env.Pkg) { kv.Value = getLabelStringExpr(newV, env.Pkg) } } else if val, ok := kv.Value.(*build.Ident); ok { if val.Name == oldV { kv.Value = getLabelStringExpr(newV, env.Pkg) } } } } return env.File, nil } // cmdDictListAdd adds an item to a list in a dict. func cmdDictListAdd(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] key := env.Args[1] args := env.Args[2:] dict := &build.DictExpr{} if currDict, ok := env.Rule.Attr(attr).(*build.DictExpr); ok { dict = currDict } prev := DictionaryGet(dict, key) if prev == nil { prev = &build.ListExpr{} } for _, val := range args { expr := getLabelStringExpr(val, env.Pkg) prev = AddValueToList(prev, env.Pkg, expr, true) } DictionarySet(dict, key, prev) env.Rule.SetAttr(attr, dict) return env.File, nil } 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.ParseBuild("" /* 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 cmdUseRepoAdd(opts *Options, env CmdEnvironment) (*build.File, error) { return cmdImplUseRepo(env, "use_repo_add") } func cmdUseRepoRemove(opts *Options, env CmdEnvironment) (*build.File, error) { return cmdImplUseRepo(env, "use_repo_remove") } func cmdImplUseRepo(env CmdEnvironment, mode string) (*build.File, error) { if env.File.Type != build.TypeModule { return nil, fmt.Errorf("%s: only applies to MODULE.bazel files", mode) } dev := false args := env.Args if env.Args[0] == "dev" && isExtensionLabel(env.Args[1]) { dev = true args = env.Args[1:] } var proxies []string var repos []string if isExtensionLabel(args[0]) { extBzlFile := args[0] extName := args[1] proxies = bzlmod.Proxies(env.File, extBzlFile, extName, dev) if len(proxies) == 0 { return nil, fmt.Errorf("%s: no use_extension assignment found for extension %q defined in %q", mode, extName, extBzlFile) } repos = args[2:] } else { proxy := args[0] proxies = bzlmod.AllProxies(env.File, proxy) if len(proxies) == 0 { return nil, fmt.Errorf("%s: no use_extension assignment to variable %q found", mode, proxy) } repos = args[1:] } useRepos := bzlmod.UseRepos(env.File, proxies) if len(useRepos) == 0 { var newUseRepo *build.CallExpr env.File, newUseRepo = bzlmod.NewUseRepo(env.File, proxies) useRepos = []*build.CallExpr{newUseRepo} } if mode == "use_repo_add" { bzlmod.AddRepoUsages(useRepos, repos...) } else { bzlmod.RemoveRepoUsages(useRepos, repos...) } return env.File, nil } func cmdFormat(opts *Options, env CmdEnvironment) (*build.File, error) { // Force formatting by not returning a nil *build.File. return env.File, nil } func isExtensionLabel(arg string) bool { // Labels referencing extensions are either absolute or repo-absolute. Repository names are not // allowed to contain "@" or "/". return strings.HasPrefix(arg, "@") || strings.HasSuffix(arg, "//") } func cmdFix(opts *Options, 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(*Options, CmdEnvironment) (*build.File, error) PerRule bool 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, true, 2, -1, " "}, "new_load": {cmdNewLoad, false, 1, -1, " <[to=]from(s)>"}, "replace_load": {cmdReplaceLoad, false, 1, -1, " <[to=]symbol(s)>"}, "substitute_load": {cmdSubstituteLoad, false, 2, 2, " "}, "comment": {cmdComment, true, 1, 3, "? ? "}, "print_comment": {cmdPrintComment, true, 0, 2, "? ?"}, "delete": {cmdDelete, true, 0, 0, ""}, "fix": {cmdFix, true, 0, -1, "?"}, "move": {cmdMove, true, 3, -1, " "}, "new": {cmdNew, false, 2, 4, " [(before|after) ]"}, "print": {cmdPrint, true, 0, -1, ""}, "remove": {cmdRemove, true, 1, -1, " "}, "remove_comment": {cmdRemoveComment, true, 0, 2, "? ?"}, "remove_if_equal": {cmdRemoveIfEqual, true, 2, 2, " "}, "rename": {cmdRename, true, 2, 2, " "}, "replace": {cmdReplace, true, 3, 3, " "}, "substitute": {cmdSubstitute, true, 3, 3, " "}, "set": {cmdSet, true, 1, -1, " "}, "set_if_absent": {cmdSetIfAbsent, true, 1, -1, " "}, "set_select": {cmdSetSelect, true, 1, -1, " "}, "copy": {cmdCopy, true, 2, 2, " "}, "copy_no_overwrite": {cmdCopyNoOverwrite, true, 2, 2, " "}, "dict_add": {cmdDictAdd, true, 2, -1, " <(key:value)(s)>"}, "dict_set": {cmdDictSet, true, 2, -1, " <(key:value)(s)>"}, "dict_remove": {cmdDictRemove, true, 2, -1, " "}, "dict_replace_if_equal": {cmdDictReplaceIfEqual, true, 4, 4, " "}, "dict_list_add": {cmdDictListAdd, true, 3, -1, " "}, "use_repo_add": {cmdUseRepoAdd, false, 2, -1, "([dev] |) "}, "use_repo_remove": {cmdUseRepoRemove, false, 2, -1, "([dev] |) "}, "format": {cmdFormat, false, 0, 0, ""}, } var readonlyCommands = map[string]bool{ "print": true, "print_comment": true, } 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 // "%" 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(opts *Options, rules []*build.Rule) (result []*build.Rule) { if len(opts.FilterRuleTypes) == 0 { return rules } for _, rule := range rules { for _, filterType := range opts.FilterRuleTypes { if rule.Kind() == filterType { result = append(result, rule) break } } } 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(opts *Options, name string, cmd CommandInfo, count int) { if count >= cmd.MinArg && (cmd.MaxArg == -1 || count <= cmd.MaxArg) { return } if count < cmd.MinArg { fmt.Fprintf(opts.ErrWriter, "Too few arguments for command '%s', expected at least %d.\n", name, cmd.MinArg) } else { fmt.Fprintf(opts.ErrWriter, "Too many arguments for command '%s', expected at most %d.\n", name, cmd.MaxArg) } Usage() os.Exit(1) } // Match text that only contains spaces or line breaks if they're escaped with '\'. var spaceRegex = regexp.MustCompile(`(\\ |\\\n|[^ \n])+`) // SplitOnSpaces behaves like strings.Fields, except that spaces can be escaped. // Also splits on linebreaks unless they are escaped too. // " some dummy\\ string" -> ["some", "dummy string"] func SplitOnSpaces(input string) []string { result := spaceRegex.FindAllString(input, -1) for i, s := range result { s = strings.Replace(s, `\ `, " ", -1) s = strings.Replace(s, "\\\n", "\n", -1) result[i] = s } return result } var splitRegexes = map[byte]*regexp.Regexp{ ':': regexp.MustCompile(`[^\\]:`), '|': regexp.MustCompile(`[^\\]\|`), } func splitOnNonEscaped(input string, sep byte, n int) []string { re, ok := splitRegexes[sep] if !ok { panic(fmt.Sprintf("splitOnNonEscaped not implemented for %c", sep)) } inputBytes := []byte(input) var splits []string previousIndex := 0 // Split n-1 times since it's limiting number of divisions, not the number of segments. for _, find := range re.FindAllIndex(inputBytes, n-1) { splitVal := string(inputBytes[previousIndex:find[1]]) splitVal = strings.TrimSuffix(splitVal, string(sep)) splits = append(splits, splitVal) previousIndex = find[1] } splits = append(splits, string(inputBytes[previousIndex:])) return splits } func removeEscapes(values []string, sep byte) []string { for i, val := range values { values[i] = strings.ReplaceAll(val, `\`+string(sep), string(sep)) } return values } // 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(opts *Options, args []string) (commands []command, targets []string, err error) { for _, arg := range args { commandTokens := SplitOnSpaces(arg) if len(commandTokens) == 0 { return nil, nil, fmt.Errorf("empty command list") } cmd, found := AllCommands[commandTokens[0]] if found { checkCommandUsage(opts, 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 AssignExpr whose RHS value is the assignment "a = v". func getGlobalVariables(exprs []build.Expr) (vars map[string]*build.AssignExpr) { vars = make(map[string]*build.AssignExpr) for _, expr := range exprs { if as, ok := expr.(*build.AssignExpr); ok { if lhs, ok := as.LHSIdent(); ok { vars[lhs.Name] = as } } } 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". // BuildFileNames is exported so that users that want to override it // in scripts are free to do so. var BuildFileNames = [...]string{"BUILD.bazel", "BUILD", "BUCK"} // Buildifier formats the build file using the buildifier logic. type Buildifier interface { // Buildify formats the build file and returns the formatted contents. Buildify(*Options, *build.File) ([]byte, error) } var ( buildifier Buildifier = &defaultBuildifier{} buildifierRegistered = false ) // RegisterBuildifier replaces the default buildifier with an // alternative implementation. // // It may only be called once. func RegisterBuildifier(b Buildifier) { if buildifierRegistered { panic("Only one call to RegisterBuildifier is allowed.") } buildifier = b buildifierRegistered = 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(opts *Options, 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 = io.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}} } if f.Type == build.TypeDefault { // Buildozer is unable to infer the file type, fall back to BUILD by default. f.Type = build.TypeBuild } f.WorkspaceRoot, f.Pkg, f.Label = wspace.SplitFilePath(name) vars := map[string]*build.AssignExpr{} if opts.EditVariables { vars = getGlobalVariables(f.Stmt) } var errs []error changed := false for _, cft := range commandsForFile.commands { _, _, absPkg, rule := InterpretLabelForWorkspaceLocation(opts.RootDir, cft.target) if label := labels.Parse(cft.target); label.Package == stdinPackageName { // Special-case: This is already absolute absPkg = stdinPackageName } if strings.HasSuffix(absPkg, "...") { // Special case: the provided target contains an ellipsis, use the file package absPkg = f.Pkg } targets, err := expandTargets(f, rule) if err != nil { cerr := commandError(cft.commands, cft.target, err) errs = append(errs, cerr) if !opts.KeepGoing { return &rewriteResult{file: name, errs: errs, records: records} } } targets = filterRules(opts, targets) newf, err := executeCommandsInFile(opts, f, cft, targets, &records, vars, absPkg, &errs) if err != nil { return &rewriteResult{file: name, errs: []error{err}, records: records} } if newf != nil { changed = true f = newf } } if !changed { return &rewriteResult{file: name, errs: errs, records: records} } ndata, err := cleanAndBuildify(opts, f) if err != nil { return &rewriteResult{file: name, errs: []error{fmt.Errorf("running buildifier: %v", err)}, records: records} } if opts.Stdout || name == stdinPackageName { opts.OutWriter.Write(ndata) return &rewriteResult{file: name, errs: errs, modified: !bytes.Equal(data, ndata), 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} } return &rewriteResult{file: name, errs: errs, modified: true, records: records} } // executeCommandsInFile executes the provided commandsForTarget in the provided build.File. func executeCommandsInFile( opts *Options, f *build.File, cft commandsForTarget, rules []*build.Rule, records *[]*apipb.Output_Record, vars map[string]*build.AssignExpr, absPkg string, errs *[]error, ) (*build.File, error) { changed := false for _, cmd := range cft.commands { cmdInfo := AllCommands[cmd.tokens[0]] // Depending on whether a transformation is rule-specific or not, it should be applied to // every rule that satisfies the filter or just once to the file. cmdTargets := rules if !cmdInfo.PerRule { cmdTargets = []*build.Rule{nil} } for _, r := range cmdTargets { record := &apipb.Output_Record{} newf, err := cmdInfo.Fn(opts, 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}, cft.target, err) if opts.KeepGoing { *errs = append(*errs, cerr) } else { return nil, cerr } } if newf != nil { f = newf changed = true } } } if changed { return f, nil } return nil, nil } func cleanAndBuildify(opts *Options, f *build.File) ([]byte, error) { f = RemoveEmptyPackage(f) f = RemoveEmptyUseRepoCalls(f) return buildifier.Buildify(opts, f) } // 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 } // Given a target, whose package may contain a trailing "/...", returns all // existing BUILD file paths which match the package. func targetExpressionToBuildFiles(rootDir string, target string, respectBazelignore bool) []string { file, _, _, _ := InterpretLabelForWorkspaceLocation(rootDir, target) if 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) } } suffix := filepath.Join("", "...", "BUILD") // /.../BUILD if !strings.HasSuffix(file, suffix) { return []string{file} } var ignoredPrefixes []string if respectBazelignore { ignoredPrefixes = getIgnoredPrefixes(rootDir) } return findBuildFiles(strings.TrimSuffix(file, suffix), ignoredPrefixes) } // Given a root directory, returns all "BUILD" files in that subtree recursively. // ignoredPrefixes are path prefixes to ignore (if a path matches any of these prefixes, // it will be skipped along with its subdirectories). func findBuildFiles(rootDir string, ignoredPrefixes []string) []string { var buildFiles []string searchDirs := []string{rootDir} for len(searchDirs) != 0 { lastIndex := len(searchDirs) - 1 dir := searchDirs[lastIndex] searchDirs = searchDirs[:lastIndex] dirFiles, err := os.ReadDir(dir) if err != nil { continue } for _, dirFile := range dirFiles { fullPath := filepath.Join(dir, dirFile.Name()) if shouldIgnorePath(fullPath, rootDir, ignoredPrefixes) { continue } if dirFile.IsDir() { searchDirs = append(searchDirs, fullPath) } else { for _, buildFileName := range BuildFileNames { if dirFile.Name() == buildFileName { buildFiles = append(buildFiles, fullPath) } } } } } return buildFiles } // getIgnoredPrefixes returns a list of ignored prefixes from the .bazelignore file in the root directory. // It returns an empty list if the file does not exist. func getIgnoredPrefixes(rootDir string) []string { bazelignorePath := filepath.Join(rootDir, ".bazelignore") ignoredPaths := []string{} data, err := os.ReadFile(bazelignorePath) if err != nil { return ignoredPaths } scanner := bufio.NewScanner(bytes.NewReader(data)) lineNum := 0 for scanner.Scan() { lineNum++ line := strings.TrimSpace(scanner.Text()) // Skip empty lines, comments, and absolute paths // Bazel will error out if there are any absolute paths in the .bazelignore file. if line == "" || strings.HasPrefix(line, "#") || path.IsAbs(line) { continue } ignoredPaths = append(ignoredPaths, path.Clean(line)) } return ignoredPaths } // shouldIgnorePath returns true if the path should be ignored based on the list of ignored prefixes. func shouldIgnorePath(path string, rootDir string, ignoredPrefixes []string) bool { if len(ignoredPrefixes) == 0 { return false } rel, err := filepath.Rel(rootDir, path) if err != nil { return false } // Normalize path separators to forward slashes rel = filepath.ToSlash(rel) for _, prefix := range ignoredPrefixes { // Check if the path exactly matches the prefix or if it's a subdirectory of the prefix. if rel == prefix || strings.HasPrefix(rel, prefix+"/") { return true } } return false } // appendCommands adds the given commands to be applied to each of the given targets // via the commandMap. func appendCommands(opts *Options, commandMap map[string][]commandsForTarget, args []string) error { commands, targets, err := parseCommands(opts, args) if err != nil { return err } for _, target := range targets { for _, buildFileName := range BuildFileNames { if strings.HasSuffix(target, filepath.FromSlash("/"+buildFileName)) { target = strings.TrimSuffix(target, filepath.FromSlash("/"+buildFileName)) + ":__pkg__" } else if strings.HasSuffix(target, "/"+buildFileName) { target = strings.TrimSuffix(target, "/"+buildFileName) + ":__pkg__" } } var buildFiles []string if label := labels.Parse(target); label.Package == stdinPackageName { buildFiles = []string{stdinPackageName} } else { buildFiles = targetExpressionToBuildFiles(opts.RootDir, target, opts.RespectBazelignore) } for _, file := range buildFiles { commandMap[file] = append(commandMap[file], commandsForTarget{target, commands}) } } return nil } func appendCommandsFromFiles(opts *Options, commandsByFile map[string][]commandsForTarget, labels []string) error { for _, fileName := range opts.CommandsFiles { var reader io.Reader if fileName == stdinPackageName { reader = os.Stdin } else { rc := file.OpenReadFile(fileName) reader = rc defer rc.Close() } if err := appendCommandsFromReader(opts, reader, commandsByFile, labels); err != nil { return err } } return nil } func appendCommandsFromReader(opts *Options, reader io.Reader, commandsByFile map[string][]commandsForTarget, labels []string) error { r := bufio.NewReader(reader) atEOF := false for !atEOF { line, err := r.ReadString('\n') if err == io.EOF { atEOF = true err = nil } if err != nil { return fmt.Errorf("error while reading commands file: %v", err) } line = strings.TrimSpace(line) if line == "" || line[0] == '#' { continue } args := splitOnNonEscaped(line, '|', -1) args = removeEscapes(args, '|') if len(args) > 1 && args[1] == "*" { cmd := append([]string{args[0]}, labels...) if err := appendCommands(opts, commandsByFile, cmd); err != nil { return err } } else { if err := appendCommands(opts, commandsByFile, args); err != nil { return err } } } return nil } 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 } case *apipb.Output_Record_Field_Number: line[i] = strconv.Itoa(int(value.Number)) case *apipb.Output_Record_Field_Error: switch value.Error { case apipb.Output_Record_Field_UNKNOWN: line[i] = "(unknown)" case apipb.Output_Record_Field_MISSING: line[i] = "(missing)" } case *apipb.Output_Record_Field_List: line[i] = fmt.Sprintf("[%s]", strings.Join(value.List.Strings, " ")) } } fmt.Fprint(writer, strings.Join(line, " ")+"\n") } // Buildozer loops over all arguments on the command line fixing BUILD files. func Buildozer(opts *Options, args []string) int { file.DisableSymlinkSafety = opts.DisableSymlinkSafety if opts.OutWriter == nil { opts.OutWriter = os.Stdout } if opts.ErrWriter == nil { opts.ErrWriter = os.Stderr } commandsByFile := make(map[string][]commandsForTarget) if len(opts.CommandsFiles) > 0 { if err := appendCommandsFromFiles(opts, commandsByFile, args); err != nil { fmt.Fprintf(opts.ErrWriter, "error: %s\n", err) return 1 } } else { if len(args) == 0 { Usage() } if err := appendCommands(opts, commandsByFile, args); err != nil { fmt.Fprintf(opts.ErrWriter, "error: %s\n", err) return 1 } } numFiles := len(commandsByFile) if opts.Parallelism > 0 { runtime.GOMAXPROCS(opts.Parallelism) } results := make(chan *rewriteResult, numFiles) data := make(chan commandsForFile) if opts.NumIO < 1 { fmt.Fprintf(opts.ErrWriter, "NumIO must be at least 1; got %d (are you using `NewOpts`?)\n", opts.NumIO) return 1 } for i := 0; i < opts.NumIO; i++ { go func(results chan *rewriteResult, data chan commandsForFile) { for commandsForFile := range data { results <- rewrite(opts, commandsForFile) } }(results, data) } for file, commands := range commandsByFile { data <- commandsForFile{file, commands} } close(data) records := []*apipb.Output_Record{} var hasErrors bool var fileModified bool for i := 0; i < numFiles; i++ { fileResults := <-results if fileResults == nil { continue } hasErrors = hasErrors || len(fileResults.errs) > 0 fileModified = fileModified || fileResults.modified for _, err := range fileResults.errs { fmt.Fprintf(opts.ErrWriter, "%s: %s\n", fileResults.file, err) } if fileResults.modified && !opts.Quiet { fmt.Fprintf(opts.ErrWriter, "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(opts.OutWriter, "%s", data) } else if opts.IsPrintingJSON { marshaler := jsonpb.Marshaler{} if err := marshaler.Marshal(opts.OutWriter, &apipb.Output{Records: records}); err != nil { log.Fatal("json marshaling error: ", err) } fmt.Fprintln(opts.OutWriter) } else { for _, record := range records { printRecord(opts.OutWriter, record) } } if hasErrors { return 2 } if fileModified || opts.Stdout { return 0 } // The file is not modified, check if there were any non-readonly commands nonReadonlyCommands := false for _, commandsByTarget := range commandsByFile { for _, commands := range commandsByTarget { for _, command := range commands.commands { if _, ok := readonlyCommands[command.tokens[0]]; !ok { nonReadonlyCommands = true break } } } } if nonReadonlyCommands { return 3 } return 0 } // ExecuteCommandsOnInlineFile executes the given commands on the given file content. // Returns the new file content after applying the commands. func ExecuteCommandsOnInlineFile(fileContent []byte, commands []string) ([]byte, error) { opts := Options{} commandsByTargetName, filename, err := groupCommandsForInlineFile(commands, opts) if err != nil { return nil, err } f, err := build.Parse(*filename, fileContent) if err != nil { return nil, err } if f.Type == build.TypeDefault { // Buildozer is unable to infer the file type, fall back to BUILD by default. f.Type = build.TypeBuild } for _, cft := range commandsByTargetName { rules, err := expandTargets(f, cft.target) if err != nil { return nil, err } newf, err := executeCommandsInFile( &opts, f, cft, rules, // Output records are ignored in inline file execution. nil, // Global variables not supported in inline file execution. nil, f.Pkg, // Errors-list is ignored since opts.keepGoing is always false. nil, ) if err != nil { return nil, err } if newf != nil { f = newf } } outputFileContent, err := cleanAndBuildify(&opts, f) if err != nil { return nil, err } return outputFileContent, nil } // groupCommandsForInlineFile groups the given commands by file and returns the commands for a // single file. Returns an error if the commands modify multiple files or if commands are invalid. func groupCommandsForInlineFile(commands []string, opts Options) ([]commandsForTarget, *string, error) { commandsByFile := make(map[string][]commandsForTarget) commandReader := strings.NewReader(strings.Join(commands, "\n")) err := appendCommandsFromReader(&opts, commandReader, commandsByFile, nil) if err != nil { return nil, nil, fmt.Errorf("error parsing commands %s", err) } if len(commandsByFile) != 1 { return nil, nil, fmt.Errorf("invalid input commands, expected all commands to reference a single file") } for filepath, commandsForTargets := range commandsByFile { for i := range commandsForTargets { cft := &commandsForTargets[i] splitTarget := strings.Split(cft.target, ":") switch len(splitTarget) { case 1: // No-op case 2: // Only keeps target (what is after the ":" character) since path is redundant. cft.target = splitTarget[1] default: return nil, nil, fmt.Errorf("invalid target name %q", cft.target) } } _, filename := path.Split(filepath) return commandsForTargets, &filename, nil } panic("unreachable") } buildtools-10.1.0/edit/buildozer_command_file_test.go0000664000175000017500000001430215254573013022353 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 import ( "reflect" "strconv" "strings" "testing" ) func TestEmptyCommandFileContainsNoCommands(t *testing.T) { reader := strings.NewReader("") commandsByBuildFile := make(map[string][]commandsForTarget) appendCommandsFromReader(NewOpts(), reader, commandsByBuildFile, []string{}) t.Logf("Read commands:\n%s", prettyFormat(commandsByBuildFile)) if len(commandsByBuildFile) != 0 { t.Error("No commands should be read") } } func TestCommandFileWithOneSetAttributeLineContainsOneCommand(t *testing.T) { commands := parseCommandFile("set srcs mytarget.go|//test-project:mytarget\n", t) if len(commands) != 1 { t.Error("Exactly one command should be read") return } if commands[0].target != "//test-project:mytarget" { t.Error("Read command should be for the correct target") } if !reflect.DeepEqual(commands[0].command, command{[]string{"set", "srcs", "mytarget.go"}}) { t.Error("Read command should contain the correct command tokens") } } func TestCommandFileWithTwoSetAttributesInOneLineContainsTwoCommands(t *testing.T) { commands := parseCommandFile("set srcs mytarget.go|set deps //other-project:othertarget|//test-project:mytarget\n", t) if len(commands) != 2 { t.Error("Exactly two commands should be read") return } if commands[0].target != "//test-project:mytarget" { t.Error("First read command should be for the correct target") } if !reflect.DeepEqual(commands[0].command, command{[]string{"set", "srcs", "mytarget.go"}}) { t.Error("First read command should contain the correct command tokens") } if commands[1].target != "//test-project:mytarget" { t.Error("Second read command should be for the correct target") } if !reflect.DeepEqual(commands[1].command, command{[]string{"set", "deps", "//other-project:othertarget"}}) { t.Error("Second read command should contain the correct command tokens") } } func TestCommandFileWithTwoSetAttributesInSeparateLinesContainsTwoCommands(t *testing.T) { commands := parseCommandFile("set srcs mytarget.go|//test-project:mytarget\nset deps //other-project:othertarget|//test-project:mytarget\n", t) if len(commands) != 2 { t.Error("Exactly two commands should be read") return } if commands[0].target != "//test-project:mytarget" { t.Error("First read command should be for the correct target") } if !reflect.DeepEqual(commands[0].command, command{[]string{"set", "srcs", "mytarget.go"}}) { t.Error("First read command should contain the correct command tokens") } if commands[1].target != "//test-project:mytarget" { t.Error("Second read command should be for the correct target") } if !reflect.DeepEqual(commands[1].command, command{[]string{"set", "deps", "//other-project:othertarget"}}) { t.Error("Second read command should contain the correct command tokens") } } func TestCommandFileWithoutTrailingNewlineContainsCommand(t *testing.T) { commands := parseCommandFile("set srcs mytarget.go|//test-project:mytarget", t) if len(commands) != 1 { t.Error("Exactly one command should be read") return } if commands[0].target != "//test-project:mytarget" { t.Error("Read command should be for the correct target") } } func TestBlankLinesInCommandFileAreIgnored(t *testing.T) { commands := parseCommandFile("set srcs mytarget.go|//test-project:mytarget\n\n\n\n\nset srcs othertarget.go|//test-project:othertarget\n", t) if len(commands) != 2 { t.Error("Exactly two commands should be read") } } func TestLongLineInCommandFileParsesAsOneCommand(t *testing.T) { srcsLength := 10000 expectedCommandTokens := make([]string, srcsLength+2) expectedCommandTokens[0] = "set" expectedCommandTokens[1] = "srcs" srcs := make([]string, srcsLength) for i := 0; i < srcsLength; i++ { src := "source_" + strconv.Itoa(i) + ".go" srcs[i] = src expectedCommandTokens[i+2] = src } commands := parseCommandFile("set srcs "+strings.Join(srcs, " ")+"|//test-project:mytarget\n", t) if len(commands) != 1 { t.Error("Exactly one command should be read") return } if !reflect.DeepEqual(commands[0].command, command{expectedCommandTokens}) { t.Errorf("First read command should contain the correct command tokens") } } func TestEscapedPipesInCommandFileArentSplit(t *testing.T) { commands := parseCommandFile("set srcs mytarget.go|//test-project:mytarget(\\|\\|)\n", t) if len(commands) != 1 { t.Error("Exactly one command should be read") } if commands[0].target != "//test-project:mytarget(||)" { t.Error("Read command target should be for the correct target") } } func parseCommandFile(fileContent string, t *testing.T) []parsedCommand { reader := strings.NewReader(fileContent) commandsByBuildFile := make(map[string][]commandsForTarget) appendCommandsFromReader(NewOpts(), reader, commandsByBuildFile, []string{}) t.Logf("Read commands:\n%s", prettyFormat(commandsByBuildFile)) return extractCommands(commandsByBuildFile) } type parsedCommand struct { command command target string buildFile string } func extractCommands(commandsByBuildFile map[string][]commandsForTarget) []parsedCommand { out := make([]parsedCommand, 0) for buildFile, targets := range commandsByBuildFile { for _, target := range targets { for _, command := range target.commands { out = append(out, parsedCommand{command, target.target, buildFile}) } } } return out } func prettyFormat(commandsByBuildFile map[string][]commandsForTarget) string { out := "" for buildFile, targets := range commandsByBuildFile { out += buildFile + "\n" for _, target := range targets { out += " target: " + target.target + "\n" for _, command := range target.commands { out += " -" for _, token := range command.tokens { out += " " + token } out += "\n" } } } return out } buildtools-10.1.0/edit/fix_test.go0000664000175000017500000000754215254573013016455 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 import ( "fmt" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/build" "github.com/google/go-cmp/cmp" ) func TestMovePackageDeclarationToTheTop(t *testing.T) { tests := []struct { input, expected string shouldMove bool }{ {`"""Docstring.""" load(":path.bzl", "x") foo() package(attr = "val")`, `"""Docstring.""" load(":path.bzl", "x") package(attr = "val") foo()`, true}, {`"""Docstring.""" load(":path.bzl", "x") package(attr = "val") foo()`, `"""Docstring.""" load(":path.bzl", "x") package(attr = "val") foo()`, false}, {`"""Docstring.""" load(":path.bzl", "x") foo()`, `"""Docstring.""" load(":path.bzl", "x") foo()`, false, }, } for i, tst := range tests { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Fatalf("Failed to parse %s; %v", tst.input, err) } if result := movePackageDeclarationToTheTop(bld); result != tst.shouldMove { t.Errorf("TestMovePackageDeclarationToTheTop: expected %v, got %v", tst.shouldMove, result) } got := strings.TrimSpace(string(build.Format(bld))) want := strings.TrimSpace(tst.expected) if diff := cmp.Diff(want, got); diff != "" { t.Errorf("TestMovePackageDeclarationToTheTop: (-want +got): %s", diff) } }) } } // Test cases for the full fix process. func TestFixAll(t *testing.T) { tests := []struct { name string input string want string }{ { name: "no empty package", input: `load(":path.bzl", "x") x() `, want: `load(":path.bzl", "x") x() `, }, { name: "preserve visibility when default_visibility is not set", input: `cc_library( name = "native_lib", visibility = ["//visibility:private"], ) `, want: `cc_library( name = "native_lib", visibility = ["//visibility:private"], ) `, }, { name: "remove redundant visibility with different slice order", input: `package(default_visibility = [ "//foo", "//bar", ]) cc_library( name = "native_lib", visibility = [ "//bar", "//foo", ], ) `, want: `package(default_visibility = [ "//bar", "//foo", ]) cc_library(name = "native_lib") `, }, { name: "remove redundant visibility matching ident", input: `package(default_visibility = PUBLIC) cc_library( name = "native_lib", visibility = PUBLIC, ) cc_library( name = "other_lib", visibility = PRIVATE, ) `, want: `package(default_visibility = PUBLIC) cc_library(name = "native_lib") cc_library( name = "other_lib", visibility = PRIVATE, ) `, }, { name: "preserve non-matching visibility", input: `package(default_visibility = ["//visibility:public"]) cc_library( name = "native_lib", visibility = ["//visibility:private"], ) `, want: `package(default_visibility = ["//visibility:public"]) cc_library( name = "native_lib", visibility = ["//visibility:private"], ) `, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { bld, err := build.Parse("BUILD", []byte(tc.input)) if err != nil { t.Fatalf("Failed to parse %s; %v", tc.input, err) } FixFile(bld, "//who/cares", []string{}) if diff := cmp.Diff(tc.want, string(build.Format(bld))); diff != "" { t.Errorf("%s: (-want +got): %s", tc.name, diff) } }) } } buildtools-10.1.0/edit/edit_test.go0000664000175000017500000005603115254573013016611 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 import ( "fmt" "os" "path/filepath" "reflect" "regexp" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/build" ) var parseLabelTests = []struct { in string repo string pkg string rule string }{ {"//devtools/buildozer:rule", "", "devtools/buildozer", "rule"}, {"devtools/buildozer:rule", "", "devtools/buildozer", "rule"}, {"//devtools/buildozer", "", "devtools/buildozer", "buildozer"}, {"//base", "", "base", "base"}, {"//base:", "", "base", "base"}, {"@r//devtools/buildozer:rule", "r", "devtools/buildozer", "rule"}, {"@r//devtools/buildozer", "r", "devtools/buildozer", "buildozer"}, {"@r//base", "r", "base", "base"}, {"@r//base:", "r", "base", "base"}, {"@foo", "foo", "", "foo"}, {":label", "", "", "label"}, {"label", "", "", "label"}, {"/abs/path/to/WORKSPACE:rule", "", "/abs/path/to/WORKSPACE", "rule"}, } func TestParseLabel(t *testing.T) { for i, tt := range parseLabelTests { repo, pkg, rule := ParseLabel(tt.in) if repo != tt.repo || pkg != tt.pkg || rule != tt.rule { t.Errorf("%d. ParseLabel(%q) => (%q, %q, %q), want (%q, %q, %q)", i, tt.in, repo, pkg, rule, tt.repo, tt.pkg, tt.rule) } } } var shortenLabelTests = []struct { in string pkg string result string }{ {"//devtools/buildozer:rule", "devtools/buildozer", ":rule"}, {"@//devtools/buildozer:rule", "devtools/buildozer", ":rule"}, {"//devtools/buildozer:rule", "devtools", "//devtools/buildozer:rule"}, {"//base:rule", "devtools", "//base:rule"}, {"//base:base", "devtools", "//base"}, {"//base", "base", ":base"}, {"//devtools/buildozer:buildozer", "", "//devtools/buildozer"}, {"@r//devtools/buildozer:buildozer", "devtools/buildozer", "@r//devtools/buildozer"}, {"@r//devtools/buildozer", "devtools/buildozer", "@r//devtools/buildozer"}, {"@r//devtools", "devtools", "@r//devtools"}, {"@r:rule", "", "@r:rule"}, {"@r", "", "@r"}, {"@foo//:foo", "", "@foo"}, {"@foo//devtools:foo", "", "@foo//devtools:foo"}, {"@foo//devtools:foo", "devtools", "@foo//devtools:foo"}, {"@foo//foo:foo", "", "@foo//foo"}, {":local", "", ":local"}, {"something else", "", "something else"}, {"/path/to/file", "path/to", "/path/to/file"}, } func TestShortenLabel(t *testing.T) { for i, tt := range shortenLabelTests { result := ShortenLabel(tt.in, tt.pkg) if result != tt.result { t.Errorf("%d. ShortenLabel(%q, %q) => %q, want %q", i, tt.in, tt.pkg, result, tt.result) } } } var labelsEqualTests = []struct { label1 string label2 string pkg string expected bool }{ {"//devtools/buildozer:rule", "rule", "devtools/buildozer", true}, {"//devtools/buildozer:rule", "rule:jar", "devtools", false}, } func TestLabelsEqual(t *testing.T) { for i, tt := range labelsEqualTests { if got := LabelsEqual(tt.label1, tt.label2, tt.pkg); got != tt.expected { t.Errorf("%d. LabelsEqual(%q, %q, %q) => %v, want %v", i, tt.label1, tt.label2, tt.pkg, got, tt.expected) } } } var splitOnSpacesTests = []struct { in string out []string }{ {"a", []string{"a"}}, {" abc def ", []string{"abc", "def"}}, {` abc\ def `, []string{"abc def"}}, {" abc def\nghi", []string{"abc", "def", "ghi"}}, } func TestSplitOnSpaces(t *testing.T) { for i, tt := range splitOnSpacesTests { result := SplitOnSpaces(tt.in) if !reflect.DeepEqual(result, tt.out) { t.Errorf("%d. SplitOnSpaces(%q) => %q, want %q", i, tt.in, result, tt.out) } } } func TestInsertLoad(t *testing.T) { tests := []struct{ input, expected string }{ {``, `load("location", "symbol")`}, {`load("location", "symbol")`, `load("location", "symbol")`}, {`load("location", "other", "symbol")`, `load("location", "other", "symbol")`}, {`load("location", "other")`, `load("location", "other", "symbol")`}, { `load("other loc", "symbol")`, `load("location", "symbol") load("other loc", "symbol")`, }, } for _, tst := range tests { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Error(err) continue } bld.Stmt = InsertLoad(bld.Stmt, "location", []string{"symbol"}, []string{"symbol"}) got := strings.TrimSpace(string(build.Format(bld))) if got != tst.expected { t.Errorf("maybeInsertLoad(%s): got %s, expected %s", tst.input, got, tst.expected) } } } func TestReplaceLoad(t *testing.T) { tests := []struct { name string input string location string from []string to []string expected string }{ { name: "add_symbol", input: ``, location: "new_location", from: []string{"symbol"}, to: []string{"symbol"}, expected: `load("new_location", "symbol")`, }, { name: "replace_location", input: `load("location", "symbol")`, location: "new_location", from: []string{"symbol"}, to: []string{"symbol"}, expected: `load("new_location", "symbol")`, }, { name: "replace_location_one_of_multiple", input: `load("location", "other", "symbol")`, location: "new_location", from: []string{"symbol"}, to: []string{"symbol"}, expected: `load("location", "other") load("new_location", "symbol")`, }, { name: "replace_location_alias", input: `load("location", symbol = "other")`, location: "new_location", from: []string{"symbol"}, to: []string{"symbol"}, expected: `load("new_location", "symbol")`, }, { name: "collapse_duplicate_symbol", input: `load("other loc", "symbol") load("location", "symbol")`, location: "new_location", from: []string{"symbol"}, to: []string{"symbol"}, expected: `load("new_location", "symbol")`, }, { name: "replace_multiple_same_location", input: `load("location", "symbol_a", "symbol_b", "symbol_c")`, location: "new_location", from: []string{"symbol_a", "symbol_b", "symbol_c"}, to: []string{"symbol_a", "symbol_b", "symbol_c"}, expected: `load("new_location", "symbol_a", "symbol_b", "symbol_c")`, }, { name: "replace_multiple_same_location_out_of_order", input: `load("location", "symbol_a", "symbol_b", "symbol_c")`, location: "new_location", from: []string{"symbol_c", "symbol_a", "symbol_b"}, to: []string{"symbol_c", "symbol_a", "symbol_b"}, expected: `load("new_location", "symbol_a", "symbol_b", "symbol_c")`, }, { name: "replace_multiple_same_location_partial", input: `load("location", "symbol_a", "symbol_b", "symbol_c")`, location: "new_location", from: []string{"symbol_a", "symbol_b"}, to: []string{"symbol_a", "symbol_b"}, expected: `load("location", "symbol_c") load("new_location", "symbol_a", "symbol_b")`, }, { name: "replace_multiple_same_location_partial_out_of_order", input: `load("location", "symbol_a", "symbol_b", "symbol_c")`, location: "new_location", from: []string{"symbol_b", "symbol_a"}, to: []string{"symbol_b", "symbol_a"}, expected: `load("location", "symbol_c") load("new_location", "symbol_a", "symbol_b")`, }, } for _, tst := range tests { t.Run(tst.name, func(t *testing.T) { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Error(err) return } bld.Stmt = ReplaceLoad(bld.Stmt, tst.location, tst.from, tst.to) got := strings.TrimSpace(string(build.Format(bld))) if got != tst.expected { t.Errorf("ReplaceLoad(%s): got %s, expected %s", tst.input, got, tst.expected) } }) } } func TestAddValueToListAttribute(t *testing.T) { tests := []struct{ input, expected string }{ {`rule(name="rule")`, `rule(name="rule", attr=["foo"])`}, {`rule(name="rule", attr=["foo"])`, `rule(name="rule", attr=["foo"])`}, {`rule(name="rule", attr=IDENT)`, `rule(name="rule", attr=IDENT+["foo"])`}, {`rule(name="rule", attr=["foo"] + IDENT)`, `rule(name="rule", attr=["foo"] + IDENT)`}, {`rule(name="rule", attr=["bar"] + IDENT)`, `rule(name="rule", attr=["bar", "foo"] + IDENT)`}, {`rule(name="rule", attr=IDENT + ["foo"])`, `rule(name="rule", attr=IDENT + ["foo"])`}, {`rule(name="rule", attr=IDENT + ["bar"])`, `rule(name="rule", attr=IDENT + ["bar", "foo"])`}, } for _, tst := range tests { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Error(err) continue } rule := bld.RuleAt(1) AddValueToListAttribute(rule, "attr", "", &build.StringExpr{Value: "foo"}, nil) got := strings.TrimSpace(string(build.Format(bld))) wantBld, err := build.Parse("BUILD", []byte(tst.expected)) if err != nil { t.Error(err) continue } want := strings.TrimSpace(string(build.Format(wantBld))) if got != want { t.Errorf("AddValueToListAttribute(%s): got %s, expected %s", tst.input, got, want) } } } func TestSelectListsIntersection(t *testing.T) { tests := []struct { input string expected []build.Expr }{ {`rule( name = "rule", attr = select() )`, nil}, {`rule( name = "rule", attr = select({}) )`, nil}, {`rule( name = "rule", attr = select(CONFIGS) )`, nil}, {`rule( name = "rule", attr = select({ "config": "string", "DEFAULT": "default" }) )`, nil}, {`rule( name = "rule", attr = select({ "config": LIST, "DEFAULT": DEFAULT }) )`, nil}, {`rule( name = "rule", attr = select({ "config": ":1 :2 :3".split(" "), "DEFAULT": ":2 :3".split(" ") }) )`, nil}, {`rule( name = "rule", attr = select({ "config1": [":1"], "config2": [":2"], "DEFAULT": [] }) )`, []build.Expr{}}, {`rule( name = "rule", attr = select({ "config1": [], "config2": [":1"], "DEFAULT": [":1"] }) )`, []build.Expr{}}, {`rule( name = "rule", attr = select({ "config1": [":1", ":2", ":3"], "config2": [":2"], "config3": [":2", ":3"], "DEFAULT": [":1", ":2"] }) )`, []build.Expr{&build.StringExpr{Value: ":2"}}}, {`rule( name = "rule", attr = select({ "config1": [":4", ":3", ":1", ":5", ":2", ":6"], "config2": [":5", ":2", ":6", ":1"], "config3": [":1", ":2", ":3", ":4", ":5", ":6"], "config4": [":2", ":1"], "DEFAULT": [":3", ":4", ":1", ":2"] }) )`, []build.Expr{&build.StringExpr{Value: ":1"}, &build.StringExpr{Value: ":2"}}}, } for _, tst := range tests { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Error(err) continue } rule := bld.RuleAt(1) got := SelectListsIntersection(rule.Attr("attr").(*build.CallExpr), "") errStr := fmt.Sprintf("TestSelectListsIntersection(%s): got %s, expected %s", tst.input, got, tst.expected) if len(got) != len(tst.expected) { t.Error(errStr) } for i := range got { if got[i].(*build.StringExpr).Value != tst.expected[i].(*build.StringExpr).Value { t.Error(errStr) } } } } func TestRemoveEmptySelectsAndConcatLists(t *testing.T) { tests := []struct{ input, expected string }{ {`rule( name = "rule", attr = select({ "config1": [], "config2": [], "DEFAULT": [] }) )`, `rule( name = "rule", attr = [] )`}, {`rule( name = "rule", attr = select({}) + select() + select({ "config1": [], "config2": [], "DEFAULT": [] }) )`, `rule( name = "rule", attr = [] )`}, {`rule( name = "rule", attr = select({ "config1": [], "config2": [], "DEFAULT": [] }) + select(CONFIGS) )`, `rule( name = "rule", attr = select(CONFIGS) )`}, {`rule( name = "rule", attr = [":1"] + select({ "config1": [], "config2": [], "DEFAULT": [] }) + [":2"] )`, `rule( name = "rule", attr = [":1", ":2"] )`}, {`rule( name = "rule", attr = [":1"] + select({ "config1": [], "config2": [], "DEFAULT": [] }) + LIST + [":2"] )`, `rule( name = "rule", attr = [":1"] + LIST + [":2"] )`}, {`rule( name = "rule", attr = [":1"] + [":2", ":3"] + select({ "config1": [":4"], "config2": [], "DEFAULT": [] }) + [] )`, `rule( name = "rule", attr = [":1", ":2", ":3"] + select({ "config1": [":4"], "config2": [], "DEFAULT": [] }) )`}, {`rule( name = "rule", attr = [":1"] + [":2", ":3"] + select({ "config1": [":4"], "config2": [], "DEFAULT": [] }) + [] + select({ "config": LIST, "DEFAULT": DEFAULT, }) )`, `rule( name = "rule", attr = [":1", ":2", ":3"] + select({ "config1": [":4"], "config2": [], "DEFAULT": [] }) + select({ "config": LIST, "DEFAULT": DEFAULT, }) )`}, } for _, tst := range tests { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Error(err) continue } rule := bld.RuleAt(1) rule.SetAttr("attr", RemoveEmptySelectsAndConcatLists(rule.Attr("attr"))) got := strings.TrimSpace(string(build.Format(bld))) wantBld, err := build.Parse("BUILD", []byte(tst.expected)) if err != nil { t.Error(err) continue } want := strings.TrimSpace(string(build.Format(wantBld))) if got != want { t.Errorf("RemoveEmptySelectsAndConcatLists(%s):\n got: %s,\n expected: %s", tst.input, got, want) } } } func TestResolveAttr(t *testing.T) { tests := []struct{ input, expected string }{ {`rule( name = "rule", attr = select({ "config1": [":1"], "config2": [":1"], "DEFAULT": [":1"] }) )`, `rule( name = "rule", attr = [":1"] )`}, {`rule( name = "rule", attr = select({ "config1": [":1"], "config2": [":1"], "DEFAULT": [":1"] }) + select() + select({}) )`, `rule( name = "rule", attr = [":1"] )`}, {`rule( name = "rule", attr = select({ "config1": [":1"], "config2": [":1"], "DEFAULT": [":1"] }) + LIST )`, `rule( name = "rule", attr = LIST + [":1"] )`}, {`rule( name = "rule", attr = select({ "config1": [":1"], "config2": [":1"], "DEFAULT": [":1"] }) + select({ "config": LIST, "DEFAULT": DEFAULT }) + select({ "config": ":2 :3".split(" "), "DEFAULT": ":3".split(" ") }) )`, `rule( name = "rule", attr = select({ "config": LIST, "DEFAULT": DEFAULT }) + select({ "config": ":2 :3".split(" "), "DEFAULT": ":3".split(" ") }) + [":1"] )`}, {`rule( name = "rule", attr = [":1"] + select({ "config1": [":2"], "config2": [":2"], "DEFAULT": [":2"] }) + [":3"] + select({ "config1": [":4", ":2"], "DEFAULT": [":2"] }) )`, `rule( name = "rule", attr = [":1", ":2", ":3"] + select({ "config1": [":4"], "DEFAULT": [] }) )`}, {`rule( name = "rule", attr = [":1"] + select({ "config1": [":2"], "config2": [":2"], "DEFAULT": [":2"] }) + [":3"] + select({ "config1": [":4", ":2"], "DEFAULT": [":4", ":2"] }) )`, `rule( name = "rule", attr = [":1", ":2", ":4", ":3"] )`}, } for _, tst := range tests { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Error(err) continue } rule := bld.RuleAt(1) ResolveAttr(rule, "attr", "") got := strings.TrimSpace(string(build.Format(bld))) wantBld, err := build.Parse("BUILD", []byte(tst.expected)) if err != nil { t.Error(err) continue } want := strings.TrimSpace(string(build.Format(wantBld))) if got != want { t.Errorf("ResolveAttr(%s):\n got: %s\n expected: %s", tst.input, got, want) } } } func TestListSubstitute(t *testing.T) { tests := []struct { desc, input, oldPattern, newTemplate, want string }{ { desc: "no_match", input: `["abc"]`, oldPattern: `!!`, newTemplate: `xx`, want: `["abc"]`, }, { desc: "full_match", input: `["abc"]`, oldPattern: `.*`, newTemplate: `xx`, want: `["xx"]`, }, { desc: "partial_match", input: `["abcde"]`, oldPattern: `bcd`, newTemplate: `xyz`, want: `["axyze"]`, }, { desc: "number_group", input: `["abcde"]`, oldPattern: `a(bcd)`, newTemplate: `$1 $1`, want: `["bcd bcde"]`, }, { desc: "name_group", input: `["abcde"]`, oldPattern: `a(?Pbcd)`, newTemplate: `$x $x`, want: `["bcd bcde"]`, }, } for _, tst := range tests { t.Run(tst.desc, func(t *testing.T) { f, err := build.ParseBuild("BUILD", []byte(tst.input)) if err != nil { t.Fatalf("parse error: %v", err) } lst := f.Stmt[0] oldRegexp, err := regexp.Compile(tst.oldPattern) if err != nil { t.Fatalf("error compiling regexp %q: %v", tst.oldPattern, err) } ListSubstitute(lst, oldRegexp, tst.newTemplate) if got := build.FormatString(lst); got != tst.want { t.Errorf("ListSubstitute(%q, %q, %q) = %q ; want %q", tst.input, tst.oldPattern, tst.newTemplate, got, tst.want) } }) } } func compareKeyValue(a, b build.Expr) bool { if a == nil && b == nil { return true } if a == nil || b == nil { return false } aKeyVal := a.(*build.KeyValueExpr) bKeyVal := b.(*build.KeyValueExpr) return aKeyVal.Key.(*build.StringExpr).Value == bKeyVal.Key.(*build.StringExpr).Value && aKeyVal.Value.(*build.StringExpr).Value == bKeyVal.Value.(*build.StringExpr).Value } func TestDictionaryDelete(t *testing.T) { tests := []struct { input, expected string expectedReturn build.Expr }{ { `rule(attr = {"deletekey": "value"})`, `rule(attr = {})`, &build.KeyValueExpr{ Key: &build.StringExpr{Value: "deletekey"}, Value: &build.StringExpr{Value: "value"}, }, }, { `rule(attr = {"nodeletekey": "value", "deletekey": "value"})`, `rule(attr = {"nodeletekey": "value"})`, &build.KeyValueExpr{ Key: &build.StringExpr{Value: "deletekey"}, Value: &build.StringExpr{Value: "value"}, }, }, { `rule(attr = {"nodeletekey": "value"})`, `rule(attr = {"nodeletekey": "value"})`, nil, }, } for _, tst := range tests { bld, err := build.ParseBuild("BUILD", []byte(tst.input)) if err != nil { t.Error(err) continue } rule := bld.RuleAt(1) dict := rule.Call.List[0].(*build.AssignExpr).RHS.(*build.DictExpr) returnVal := DictionaryDelete(dict, "deletekey") got := strings.TrimSpace(string(build.Format(bld))) wantBld, err := build.Parse("BUILD", []byte(tst.expected)) if err != nil { t.Error(err) continue } want := strings.TrimSpace(string(build.Format(wantBld))) if got != want { t.Errorf("TestDictionaryDelete(%s): got %s, expected %s", tst.input, got, want) } if !compareKeyValue(returnVal, tst.expectedReturn) { t.Errorf("TestDictionaryDelete(%s): returned %v, expected %v", tst.input, returnVal, tst.expectedReturn) } } } func TestPackageDeclaration(t *testing.T) { tests := []struct{ input, expected string }{ {``, `package(attr = "val")`}, {`"""Docstring.""" load(":path.bzl", "x") # package() comes here x = 2`, `"""Docstring.""" load(":path.bzl", "x") # package() comes here package(attr = "val") x = 2`, }, } for _, tst := range tests { bld, err := build.Parse("BUILD", []byte(tst.input)) if err != nil { t.Error(err) continue } pkg := PackageDeclaration(bld) pkg.SetAttr("attr", &build.StringExpr{Value: "val"}) got := strings.TrimSpace(string(build.Format(bld))) want := strings.TrimSpace(tst.expected) if got != want { t.Errorf("TestPackageDeclaration: got:\n%s\nexpected:\n%s", got, want) } } } type testCase struct { inputRoot, inputTarget string expectedBuildFile, expectedPkg, expectedRule string } func runTestInterpretLabelForWorkspaceLocation(t *testing.T, buildFileName string) { tmp, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) if err := os.MkdirAll(filepath.Join(tmp, "a", "b"), 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "WORKSPACE"), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, buildFileName), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "a", buildFileName), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "a", "b", buildFileName), nil, 0755); err != nil { t.Fatal(err) } for _, tc := range []testCase{ {tmp, "//", filepath.Join(tmp, buildFileName), "", "."}, {tmp, "//a", filepath.Join(tmp, "a", buildFileName), "a", "a"}, {tmp, "//a:a", filepath.Join(tmp, "a", buildFileName), "a", "a"}, {tmp, "//a/b", filepath.Join(tmp, "a", "b", buildFileName), "a/b", "b"}, {tmp, "//a/b:b", filepath.Join(tmp, "a", "b", buildFileName), "a/b", "b"}, } { buildFile, _, pkg, rule := InterpretLabelForWorkspaceLocation(tc.inputRoot, tc.inputTarget) if buildFile != tc.expectedBuildFile || pkg != tc.expectedPkg || rule != tc.expectedRule { t.Errorf("InterpretLabelForWorkspaceLocation(%q, %q) = %q, %q, %q; want %q, %q, %q", tc.inputRoot, tc.inputTarget, buildFile, pkg, rule, tc.expectedBuildFile, tc.expectedPkg, tc.expectedRule) } } } func TestInterpretLabelForWorkspaceLocation(t *testing.T) { runTestInterpretLabelForWorkspaceLocation(t, "BUILD") runTestInterpretLabelForWorkspaceLocation(t, "BUILD.bazel") } func TestFindRuleByName(t *testing.T) { input := `load("foo.bzl", "bar") my_rule( name="r1", srcs=["a.txt"] ) a = my_macro( name="r2", ) ` bld, err := build.Parse("BUILD", []byte(input)) if err != nil { t.Error(err) return } testCases := []struct { name string shouldFind bool }{ {"r1", true}, {"r2", true}, {"r3", false}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { rule := FindRuleByName(bld, tc.name) found := rule != nil if found != tc.shouldFind { t.Errorf("FindRuleByName(%q): found = %v, want %v", tc.name, found, tc.shouldFind) } }) } } func TestContainsDisableComment(t *testing.T) { testCases := []struct { comment string warning string want bool }{ {"# buildifier: disable=warning1", "warning1", true}, {"# buildifier: disable=warning1", "warning2", false}, {"# buildifier: disable=warning1,warning2", "warning1", true}, {"# buildifier: disable=warning1,warning2", "warning2", true}, {"# buildifier: disable=warning1,warning2", "warning3", false}, {"# buildifier: disable=warning1, warning2", "warning2", true}, {"# buildifier: disable=warning1,warning2 (explanation)", "warning1", true}, {"# buildifier: disable=warn", "warning1", false}, {"# buildifier: disable=warning1", "warn", true}, {"# buildifier: disable=warning1,warning2 (warning3)", "warning3", true}, {"# buildozer: disable=warning1,warning2", "warning2", true}, {"# othertool: disable=warning1,warning2", "warning2", false}, } for i, tc := range testCases { expr := &build.Ident{ Comments: build.Comments{ Before: []build.Comment{{Token: tc.comment}}, }, } if got := ContainsDisableComment(expr, tc.warning); got != tc.want { t.Errorf("case %d: ContainsDisableComment(%q, %q) = %v, want %v", i, tc.comment, tc.warning, got, tc.want) } } } buildtools-10.1.0/edit/fix.go0000664000175000017500000004010615254573013015407 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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" "slices" "sort" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/labels" ) // 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, "'\"") && !strings.Contains(str.Value, "$(location") { 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(r.Kind(), attr) { continue } for _, li := range AllLists(e) { for _, elem := range li.List { str, ok := elem.(*build.StringExpr) if ok && str.Value != labels.Shorten(str.Value, pkg) { str.Value = labels.Shorten(str.Value, pkg) fixed = true } } } } return fixed } // removeVisibility removes useless visibility attributes (repeating default_visibility). func removeVisibility(f *build.File, r *build.Rule, pkg string) bool { pkgDecl := ExistingPackageDeclaration(f) if pkgDecl == nil || pkgDecl.Attr("default_visibility") == nil { // If default visibility is not explicit we do not replace other attrs. return false } defaultVis := pkgDecl.Attr("default_visibility") vis := r.Attr("visibility") // Check equality if both are string lists. if dvs := build.Strings(defaultVis); dvs != nil { if vs := build.Strings(vis); vs != nil { if slices.Equal(slices.Sorted(slices.Values(dvs)), slices.Sorted(slices.Values(vs))) { r.DelAttr("visibility") return true } } } // Check Name equality if both are idents. if ai, aok := defaultVis.(*build.Ident); aok { if bi, bok := vis.(*build.Ident); bok { if ai.Name == bi.Name { r.DelAttr("visibility") return true } } } return false } // removeTestOnly removes the useless testonly attributes. func removeTestOnly(f *build.File, r *build.Rule, pkg string) bool { pkgDecl := ExistingPackageDeclaration(f) def := strings.HasSuffix(r.Kind(), "_test") || r.Kind() == "test_suite" if !def { if pkgDecl == nil || 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 } // Regexp comes from LABEL_CHAR_MATCHER in // // java/com/google/devtools/build/lib/analysis/LabelExpander.java var labelCharMatcherRe = regexp.MustCompile("[a-zA-Z0-9:/_.+-]+|[^a-zA-Z0-9:/_.+-]+") // explicitHeuristicLabels adds $(location ...) for each label in the string s. func explicitHeuristicLabels(s string, labels map[string]bool) string { parts := labelCharMatcherRe.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.Ident) if !ok { continue } var fix *build.AssignExpr if dot.Name == "extend" { fix = &build.AssignExpr{LHS: obj, Op: "+=", RHS: call.List[0]} } else if dot.Name == "append" { list := &build.ListExpr{List: []build.Expr{call.List[0]}} fix = &build.AssignExpr{LHS: obj, Op: "+=", RHS: list} } else { continue } fix.Comments = call.Comments // Keep original comments f.Stmt[i] = fix fixed = true } return fixed } // 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 { symbols := UsedSymbols(f) fixed := false // Map of symbol in this file -> modules it's loaded from symbolsToModules := make(map[string][]string) var all []build.Expr for _, stmt := range f.Stmt { load, ok := stmt.(*build.LoadStmt) if !ok || ContainsComments(load, "@unused") { all = append(all, stmt) continue } var fromSymbols, toSymbols []*build.Ident for i := range load.From { fromSymbol := load.From[i] toSymbol := load.To[i] if symbols[toSymbol.Name] { // The symbol is actually used // If the most recent load for this symbol was from the same file, remove it. previousModules := symbolsToModules[toSymbol.Name] if len(previousModules) > 0 { if previousModules[len(previousModules)-1] == load.Module.Value { fixed = true continue } } symbolsToModules[toSymbol.Name] = append(symbolsToModules[toSymbol.Name], load.Module.Value) fromSymbols = append(fromSymbols, fromSymbol) toSymbols = append(toSymbols, toSymbol) } else { fixed = true } } if len(toSymbols) > 0 { // Keep the load statement if it loads at least one symbol. sort.Sort(loadArgs{fromSymbols, toSymbols}) load.From = fromSymbols load.To = toSymbols all = append(all, load) } else { fixed = true // If the load statement contains before- or after-comments, // keep them by re-attaching to a new CommentBlock node. if len(load.Comment().Before) == 0 && len(load.Comment().After) == 0 { continue } cb := &build.CommentBlock{} cb.Comment().After = load.Comment().Before cb.Comment().After = append(cb.Comment().After, load.Comment().After...) all = append(all, cb) } } f.Stmt = all return fixed } // 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) _, isString := stmt.(*build.StringExpr) // typically a docstring _, isAssignExpr := stmt.(*build.AssignExpr) // e.g. variable declaration _, isLoad := stmt.(*build.LoadStmt) if isComment || isString || isAssignExpr || 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 auxiliary function used by moveLicenses. // 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 } // moveLicenses replaces the 'licenses' function with an attribute // in package. // Before: licenses(["notice"]) // After: package(licenses = ["notice"]) func moveLicenses(f *build.File) bool { return moveToPackage(f, "licenses") } // 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 explicitly"}, {"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"}, {"moveLicenses", moveLicenses, "Move licenses 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 } // A wrapper for a LoadStmt's From and To slices for consistent sorting of their contents. // It's assumed that the following slices have the same length, the contents are sorted by // the `To` attribute, the items of `From` are swapped exactly the same way as the items of `To`. type loadArgs struct { From []*build.Ident To []*build.Ident } func (args loadArgs) Len() int { return len(args.From) } func (args loadArgs) Swap(i, j int) { args.From[i], args.From[j] = args.From[j], args.From[i] args.To[i], args.To[j] = args.To[j], args.To[i] } func (args loadArgs) Less(i, j int) bool { return args.To[i].Name < args.To[j].Name } buildtools-10.1.0/edit/buildozer_test.go0000664000175000017500000010143715254573013017664 0ustar kenhyskenhys/* Copyright 2019 Google LLC 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 https://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 import ( "fmt" "os" "path/filepath" "reflect" "runtime" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/build" "github.com/google/go-cmp/cmp" ) var removeCommentTests = []struct { args []string buildFile string expected string }{ {[]string{}, `# comment foo( name = "foo", )`, `foo( name = "foo", )`, }, {[]string{ "name", }, `foo( # comment name = "foo", )`, `foo( name = "foo", )`, }, {[]string{ "name", }, `foo( name = "foo" # comment, )`, `foo( name = "foo", )`, }, {[]string{ "deps", "bar", }, `foo( name = "foo", deps = [ # comment "bar", "baz", ], )`, `foo( name = "foo", deps = [ "bar", "baz", ], )`, }, {[]string{ "deps", "bar", }, `foo( name = "foo", deps = [ "bar", # comment "baz", ], )`, `foo( name = "foo", deps = [ "bar", "baz", ], )`, }, } func TestCmdRemoveComment(t *testing.T) { for i, tt := range removeCommentTests { bld, err := build.Parse("BUILD", []byte(tt.buildFile)) if err != nil { t.Error(err) continue } rl := bld.Rules("foo")[0] env := CmdEnvironment{ File: bld, Rule: rl, Args: tt.args, } bld, _ = cmdRemoveComment(NewOpts(), env) got := strings.TrimSpace(string(build.Format(bld))) if got != tt.expected { t.Errorf("cmdRemoveComment(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected) } } } func TestCmdComment(t *testing.T) { var tests = []struct { name string args []string buildFile string want string }{ { name: "adds_comment_to_rule_call", args: []string{"New Comment"}, buildFile: `foo( name = "foo", )`, want: `# New Comment foo( name = "foo", )`, }, { name: "adds_comment_to_single_line_attribute", args: []string{"deps", "New Comment"}, buildFile: `foo( name = "foo", deps = ["//some/dep"], )`, want: `foo( name = "foo", deps = ["//some/dep"], # New Comment )`, }, { name: "adds_multiline_comment_to_single_line_attribute", args: []string{"deps", "New Comment\nWith Multiple Lines"}, buildFile: `foo( name = "foo", deps = ["//some/dep"], )`, want: `foo( name = "foo", # New Comment # With Multiple Lines deps = ["//some/dep"], )`, }, { name: "adds_comment_to_multiline_attribute", args: []string{"deps", "New Comment"}, buildFile: `foo( name = "foo", deps = [ "//some/dep", "//some/other/dep", ], )`, want: `foo( name = "foo", # New Comment deps = [ "//some/dep", "//some/other/dep", ], )`, }, { name: "adds_comment_to_element_in_list", args: []string{"deps", "//some/other/dep", "New Comment"}, buildFile: `foo( name = "foo", deps = [ "//some/dep", "//some/other/dep", ], )`, want: `foo( name = "foo", deps = [ "//some/dep", "//some/other/dep", # New Comment ], )`, }, { name: "adds_multiline_comment_to_element_in_list", args: []string{"deps", "//some/other/dep", "New Comment\nWith Multiple Lines"}, buildFile: `foo( name = "foo", deps = [ "//some/dep", "//some/other/dep", ], )`, want: `foo( name = "foo", deps = [ "//some/dep", # New Comment # With Multiple Lines "//some/other/dep", ], )`, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { bld, err := build.Parse("BUILD", []byte(tc.buildFile)) if err != nil { t.Error(err) } rl := bld.Rules("foo")[0] env := CmdEnvironment{ File: bld, Rule: rl, Args: tc.args, } bld, _ = cmdComment(NewOpts(), env) got := strings.TrimSpace(string(build.Format(bld))) if diff := cmp.Diff(got, tc.want); diff != "" { t.Fatalf("cmdComment returned unexpected diff %s", diff) } }) } } type targetExpressionToBuildFilesTestCase struct { rootDir, target string buildFiles []string } func setupTestTmpWorkspace(t *testing.T, buildFileName string) (tmp string) { tmp, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } // On MacOS "/tmp" is a symlink to "/private/tmp". Resolve it to make the testing easier tmp, err = filepath.EvalSymlinks(tmp) if err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(tmp, "a", "b"), 0755); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(tmp, "a", "c"), 0755); err != nil { t.Fatal(err) } // Create additional directories that will be ignored if err := os.MkdirAll(filepath.Join(tmp, "ignored"), 0755); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(tmp, "a", "ignored"), 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "WORKSPACE"), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, buildFileName), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "a", buildFileName), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "a", "b", buildFileName), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "a", "c", buildFileName), nil, 0755); err != nil { t.Fatal(err) } // Create BUILD files in ignored directories to verify they're skipped if err := os.WriteFile(filepath.Join(tmp, "ignored", buildFileName), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "a", "ignored", buildFileName), nil, 0755); err != nil { t.Fatal(err) } // Create .bazelignore file with paths to ignore bazelignoreContent := []byte("# Ignore these directories\nignored\na/ignored/\n") if err := os.WriteFile(filepath.Join(tmp, ".bazelignore"), bazelignoreContent, 0644); err != nil { t.Fatal(err) } return } func runTestTargetExpressionToBuildFiles(t *testing.T, buildFileName string) { tmp := setupTestTmpWorkspace(t, buildFileName) defer os.RemoveAll(tmp) for _, tc := range []targetExpressionToBuildFilesTestCase{ {tmp, "//", []string{filepath.Join(tmp, buildFileName)}}, {tmp, "//:foo", []string{filepath.Join(tmp, buildFileName)}}, {tmp, "//a", []string{filepath.Join(tmp, "a", buildFileName)}}, {tmp, "//a:foo", []string{filepath.Join(tmp, "a", buildFileName)}}, {tmp, "//a/b", []string{filepath.Join(tmp, "a", "b", buildFileName)}}, {tmp, "//a/b:foo", []string{filepath.Join(tmp, "a", "b", buildFileName)}}, {tmp, "//...", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}}, {tmp, "//a/...", []string{filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}}, {tmp, "//a/b/...", []string{filepath.Join(tmp, "a", "b", buildFileName)}}, {tmp, "//a/c/...", []string{filepath.Join(tmp, "a", "c", buildFileName)}}, {tmp, "//a/c/...:foo", []string{filepath.Join(tmp, "a", "c", buildFileName)}}, {"", "...:foo", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}}, } { if tc.rootDir == "" { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } // buildozer should be able to find the WORKSPACE file in the current wd if err := os.Chdir(tmp); err != nil { t.Fatal(err) } defer os.Chdir(cwd) } buildFiles := targetExpressionToBuildFiles(tc.rootDir, tc.target, true) expectedBuildFilesMap := make(map[string]bool) buildFilesMap := make(map[string]bool) for _, buildFile := range buildFiles { buildFilesMap[buildFile] = true } for _, buildFile := range tc.buildFiles { expectedBuildFilesMap[buildFile] = true } if !reflect.DeepEqual(expectedBuildFilesMap, buildFilesMap) { t.Errorf("TargetExpressionToBuildFiles(%q, %q) = %q want %q", tc.rootDir, tc.target, buildFiles, tc.buildFiles) } } } func TestTargetExpressionToBuildFiles(t *testing.T) { for _, buildFileName := range BuildFileNames { runTestTargetExpressionToBuildFiles(t, buildFileName) } } func runTestAppendCommands(t *testing.T, buildFileName string) { tmp := setupTestTmpWorkspace(t, buildFileName) defer os.RemoveAll(tmp) for _, tc := range []targetExpressionToBuildFilesTestCase{ {tmp, ".:__pkg__", []string{"./" + buildFileName}}, {tmp, "a" + ":__pkg__", []string{"a/" + buildFileName}}, {"", "a" + ":__pkg__", []string{"a/" + buildFileName}}, } { if tc.rootDir == "" { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } // buildozer should be able to find the WORKSPACE file in the current wd if err := os.Chdir(tmp); err != nil { t.Fatal(err) } defer os.Chdir(cwd) } commandsByFile := make(map[string][]commandsForTarget) opts := NewOpts() opts.RootDir = tc.rootDir appendCommands(opts, commandsByFile, tc.buildFiles) if len(commandsByFile) != 1 { t.Errorf("Expect one target after appendCommands") } for _, value := range commandsByFile { if value[0].target != tc.target { t.Errorf("appendCommands for buildfile %s yielded target %s, expected %s", tc.buildFiles, value[0].target, tc.target) } } } } func TestAppendCommands(t *testing.T) { for _, buildFileName := range BuildFileNames { runTestAppendCommands(t, buildFileName) } } var dictListAddTests = []struct { args []string buildFile string expected string }{ {[]string{ "attr", "key1", "value1", }, `foo( name = "foo", )`, `foo( name = "foo", attr = {"key1": ["value1"]}, )`, }, {[]string{ "attr", "key1", "value2", }, `foo( name = "foo", attr = {"key1": ["value1"]}, )`, `foo( name = "foo", attr = {"key1": [ "value1", "value2", ]}, )`, }, {[]string{ "attr", "key1", "value1", "value2", }, `foo( name = "foo", )`, `foo( name = "foo", attr = {"key1": [ "value1", "value2", ]}, )`, }, {[]string{ "attr", "key2", "value2", }, `foo( name = "foo", attr = {"key1": ["value1"]}, )`, `foo( name = "foo", attr = { "key1": ["value1"], "key2": ["value2"], }, )`, }, {[]string{ "attr", "key1", "value1", }, `foo( name = "foo", attr = {"key1": ["value1"]}, )`, `foo( name = "foo", attr = {"key1": ["value1"]}, )`, }, } func TestCmdDictListAdd(t *testing.T) { for i, tt := range dictListAddTests { bld, err := build.Parse("BUILD", []byte(tt.buildFile)) if err != nil { t.Error(err) continue } rl := bld.Rules("foo")[0] env := CmdEnvironment{ File: bld, Rule: rl, Args: tt.args, } bld, _ = cmdDictListAdd(NewOpts(), env) got := strings.TrimSpace(string(build.Format(bld))) if got != tt.expected { t.Errorf("cmdDictListAdd(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected) } } } var dictReplaceIfEqualTests = []struct { args []string buildFile string expected string }{ {[]string{ "attr", "key1", "value1", "value2", }, `foo( name = "foo", attr = {"key1": "value1"}, )`, `foo( name = "foo", attr = {"key1": "value2"}, )`, }, {[]string{ "attr", "key1", "value1", "value2", }, `foo( name = "foo", attr = {"key1": "x"}, )`, `foo( name = "foo", attr = {"key1": "x"}, )`, }, {[]string{ "attr", "key1", "value1", "value2", }, `foo( name = "foo", attr = { "key1": ["value1"], "key2": ["value2"], }, )`, `foo( name = "foo", attr = { "key1": ["value1"], "key2": ["value2"], }, )`, }, {[]string{ "attr", "key1", "value1", "value2", }, `foo( name = "foo", attr = { "key1": "value1", "key2": "value2", }, )`, `foo( name = "foo", attr = { "key1": "value2", "key2": "value2", }, )`, }, {[]string{ "attr", "key1", "value1", "value2", }, `foo( name = "foo", attr = { "key1": "value1", "key2": "x", }, )`, `foo( name = "foo", attr = { "key1": "value2", "key2": "x", }, )`, }, {[]string{ "attr", "*", "value1", "value2", }, `foo( name = "foo", attr = { "key1": "value1", "key2": "x", }, )`, `foo( name = "foo", attr = { "key1": "value2", "key2": "x", }, )`, }, } func TestCmdDictReplaceIfEqual(t *testing.T) { for i, tt := range dictReplaceIfEqualTests { bld, err := build.Parse("BUILD", []byte(tt.buildFile)) if err != nil { t.Error(err) continue } expectedBld, err := build.Parse("BUILD", []byte(tt.expected)) if err != nil { t.Error(err) continue } rl := bld.Rules("foo")[0] env := CmdEnvironment{ File: bld, Rule: rl, Args: tt.args, } bld, err = cmdDictReplaceIfEqual(NewOpts(), env) if err != nil { t.Errorf("cmdDictReplaceIfEqual(%d):\ngot error:\n%s", i, err) } got := strings.TrimSpace(string(build.Format(bld))) expected := strings.TrimSpace(string(build.Format(expectedBld))) if got != expected { t.Errorf("cmdDictReplaceIfEqual(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected) } } } var substituteLoadsTests = []struct { args []string buildFile string expected string }{ {[]string{ "^(.*)$", "${1}", }, `load("//foo:foo.bzl", "foo")`, `load("//foo:foo.bzl", "foo")`, }, {[]string{ "^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl", }, `load("@rules_bar//bar:defs.bzl", "bar")`, `load("@rules_bar//bar:defs.bzl", "bar")`, }, {[]string{ "^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl", }, `load("@rules_foo//foo:defs.bzl", "foo", "foo2") load("@rules_bar//bar:defs.bzl", "bar")`, `load("@rules_bar//bar:defs.bzl", "bar") load("//build/rules/foo:defs.bzl", "foo", "foo2")`, }, {[]string{ ":foo.bzl$", ":defs.bzl", }, `load("//foo:foo.bzl", "foo")`, `load("//foo:defs.bzl", "foo")`, }, {[]string{ // Keep in sync with the example in `//buildozer:README.md`. "^@([^/]*)//([^:].*)$", "//third_party/build_defs/${1}/${2}", }, `load("@rules_foo//foo:defs.bzl", "foo", "foo2") load("@rules_bar//bar:defs.bzl", "bar") load("@rules_bar//:defs.bzl", legacy_bar = "bar")`, `load("@rules_bar//:defs.bzl", legacy_bar = "bar") load("//third_party/build_defs/rules_bar/bar:defs.bzl", "bar") load("//third_party/build_defs/rules_foo/foo:defs.bzl", "foo", "foo2")`, }, } func TestCmdSubstituteLoad(t *testing.T) { for i, tt := range substituteLoadsTests { bld, err := build.Parse("BUILD", []byte(tt.buildFile)) if err != nil { t.Error(err) continue } env := CmdEnvironment{ File: bld, Args: tt.args, } bld, _ = cmdSubstituteLoad(NewOpts(), env) got := strings.TrimSpace(string(build.Format(bld))) if got != tt.expected { t.Errorf("cmdSubstituteLoad(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected) } } } func TestCmdSubstitute(t *testing.T) { for i, tc := range []struct { name string args []string buildFile string expected string }{ { name: "empty_rule", args: []string{"*", "^$", "x"}, buildFile: `cc_library()`, expected: `cc_library()`, }, { name: "known_attr", args: []string{"*", "^//(.*)$", "//foo/${1}"}, buildFile: `cc_library(deps = ["//bar/baz:quux"])`, expected: `cc_library(deps = ["//foo/bar/baz:quux"])`, }, { name: "custom_attr", args: []string{"*", "^//(.*)$", "//foo/${1}"}, buildFile: `cc_library(my_custom_attr = "//bar/baz:quux")`, expected: `cc_library(my_custom_attr = "//foo/bar/baz:quux")`, }, { name: "specific_rule", args: []string{"deps", "^//(.*)$", "//foo/${1}"}, buildFile: `cc_library(deps = ["//bar"], fancy_deps = ["//bar/baz:quux"])`, expected: `cc_library( fancy_deps = ["//bar/baz:quux"], deps = ["//foo/bar"], )`, }, } { t.Run(tc.name, func(t *testing.T) { bld, err := build.Parse("BUILD", []byte(tc.buildFile)) if err != nil { t.Error(err) return } env := CmdEnvironment{ File: bld, Args: tc.args, Rule: bld.RuleAt(1), } bld, _ = cmdSubstitute(NewOpts(), env) got := strings.TrimSpace(string(build.Format(bld))) if got != tc.expected { t.Errorf("cmdSubstitute(%d):\ngot:\n%s\nexpected:\n%s", i, got, tc.expected) } }) } } func TestCmdDictAddSet_missingColon(t *testing.T) { for _, tc := range []struct { name string fun func(*Options, CmdEnvironment) (*build.File, error) }{ {"dict_add", cmdDictAdd}, {"dict_set", cmdDictSet}, } { t.Run(tc.name, func(t *testing.T) { bld, err := build.Parse("BUILD", []byte("rule()")) if err != nil { t.Fatal(err) } env := CmdEnvironment{ File: bld, Rule: bld.RuleAt(1), Args: []string{"attr", "invalid"}, } _, err = tc.fun(NewOpts(), env) if err == nil { t.Error("succeeded, want error") } }) } } func TestCmdDictOperations(t *testing.T) { tests := []struct { name string dictAddArgs []string dictSetArgs []string dictRemoveArgs []string input string want string }{ { name: "dict_add_set_remove", dictAddArgs: []string{"dict_attr", `added_entry:new_value`}, dictSetArgs: []string{"dict_attr", `entry_to_change:updated_value`}, dictRemoveArgs: []string{"dict_attr", `entry_to_remove`}, input: strings.Join([]string{ `rule(`, ` name = "rule_name",`, ` dict_attr = {`, ` "entry_to_change": "123",`, ` "entry_to_remove": "abc",`, ` },`, `)`, }, "\n"), want: strings.Join([]string{ `rule(`, ` name = "rule_name",`, ` dict_attr = {`, ` "entry_to_change": "updated_value",`, ` "added_entry": "new_value",`, ` },`, `)`, ``, }, "\n"), }, { name: "dict_add_set_remove_with_escaped_colon", dictAddArgs: []string{"dict_attr", `added\:entry:new:value`}, dictSetArgs: []string{"dict_attr", `entry\:to_change:updated\:value`}, dictRemoveArgs: []string{"dict_attr", `entry\:to_remove`}, input: strings.Join([]string{ `rule(`, ` name = "rule_name",`, ` dict_attr = {`, ` "entry:to_change": "123",`, ` "entry:to_remove": "abc",`, ` },`, `)`, }, "\n"), want: strings.Join([]string{ `rule(`, ` name = "rule_name",`, ` dict_attr = {`, ` "entry:to_change": "updated:value",`, ` "added:entry": "new:value",`, ` },`, `)`, ``, }, "\n"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { file, err := build.Parse("BUILD", []byte(tc.input)) if err != nil { t.Fatalf("build.Parse returned err: %s", err) } rule := file.RuleNamed("rule_name") file, err = cmdDictAdd(NewOpts(), CmdEnvironment{File: file, Rule: rule, Args: tc.dictAddArgs}) if err != nil { t.Fatalf("cmdDictAdd returned err: %s", err) } file, err = cmdDictSet(NewOpts(), CmdEnvironment{File: file, Rule: rule, Args: tc.dictSetArgs}) if err != nil { t.Fatalf("cmdDictSet returned err: %s", err) } file, err = cmdDictRemove(NewOpts(), CmdEnvironment{File: file, Rule: rule, Args: tc.dictRemoveArgs}) if err != nil { t.Fatalf("cmdDictRemove returned err: %s", err) } got := string(build.Format(file)) if diff := cmp.Diff(tc.want, got); diff != "" { t.Fatalf("dict operations returned diff -want +got %v", diff) } }) } } func TestCmdSetSelect(t *testing.T) { for i, tc := range []struct { name string args []string buildFile string expected string }{ { name: "select_statement_doesn't_exist", args: []string{ "args", /* attr */ ":use_ci_timeouts", "-test.timeout=123s", /* key, value */ ":use_ci_timeouts", "-test.anotherFlag=flagValue", /* key, value */ "//conditions:default", "-test.timeout=789s", /* key, value */ }, buildFile: `foo( name = "foo", )`, expected: `foo( name = "foo", args = select({ ":use_ci_timeouts": [ "-test.timeout=123s", "-test.anotherFlag=flagValue", ], "//conditions:default": ["-test.timeout=789s"], }), )`}, { name: "select_statement_exists", args: []string{ "args", /* attr */ ":use_ci_timeouts", "-test.timeout=543s", /* key, value */ "//conditions:default", "-test.timeout=876s", /* key, value */ }, buildFile: `foo( name = "foo", args = select({ ":use_ci_timeouts": [ "-test.timeout=123s", "-test.anotherFlag=flagValue", ], "//conditions:default": ["-test.timeout=789s"], }), )`, expected: `foo( name = "foo", args = select({ ":use_ci_timeouts": ["-test.timeout=543s"], "//conditions:default": ["-test.timeout=876s"], }), )`}, { name: "attr_exists_but_not_select", args: []string{ "args", /* attr */ ":use_ci_timeouts", "-test.timeout=543s", /* key, value */ "//conditions:default", "-test.timeout=876s", /* key, value */ }, buildFile: `foo( name = "foo", args = ["-test.timeout=123s"], )`, expected: `foo( name = "foo", args = select({ ":use_ci_timeouts": ["-test.timeout=543s"], "//conditions:default": ["-test.timeout=876s"], }), )`}, } { t.Run(tc.name, func(t *testing.T) { bld, err := build.Parse("BUILD", []byte(tc.buildFile)) if err != nil { t.Error(err) } rl := bld.Rules("foo")[0] env := CmdEnvironment{ File: bld, Rule: rl, Args: tc.args, } bld, _ = cmdSetSelect(NewOpts(), env) got := strings.TrimSpace(string(build.Format(bld))) if got != tc.expected { t.Errorf("cmdSetSelect(%d):\ngot:\n%s\nexpected:\n%s", i, got, tc.expected) } }) } } func TestExecuteCommandsOnInlineFile(t *testing.T) { tests := []struct { name string fileContent []byte commands []string wantOutput []byte }{ { name: "creating_new_target_and_adding_deps", fileContent: nil, commands: []string{ "new java_library foo|//package/path/BUILD", "add deps :bar|//package/path:foo", }, wantOutput: []byte(strings.Join([]string{ `java_library(`, ` name = "foo",`, ` deps = [":bar"],`, `)`, ``}, "\n")), }, { name: "adding_deps_to_existing_targets", fileContent: []byte(strings.Join([]string{ `java_library(`, ` name = "foo",`, `)`, ``, `java_library(`, ` name = "fruits",`, ` deps = ["//package/fruits:apples"],`, `)`, ``}, "\n")), commands: []string{ "add deps :bar|//package/path:foo", "add deps //package/fruits:oranges|//package/path:fruits", }, wantOutput: []byte(strings.Join([]string{ `java_library(`, ` name = "foo",`, ` deps = [":bar"],`, `)`, ``, `java_library(`, ` name = "fruits",`, ` deps = [`, ` "//package/fruits:apples",`, ` "//package/fruits:oranges",`, ` ],`, `)`, ``}, "\n")), }, { name: "substituting_a_target", fileContent: []byte(strings.Join([]string{ `java_library(`, ` name = "fruits",`, ` deps = ["//package/fruits:apples"],`, `)`, ``}, "\n")), commands: []string{ "replace deps //package/fruits:apples //package/fruits:oranges|//whatever/package/path:fruits", }, wantOutput: []byte(strings.Join([]string{ `java_library(`, ` name = "fruits",`, ` deps = ["//package/fruits:oranges"],`, `)`, ``}, "\n")), }, { name: "no_changes_does_not_return_any_diff", fileContent: []byte(strings.Join([]string{ `java_library(`, ` name = "foo",`, ` deps = [":bar"],`, `)`, ``}, "\n")), commands: []string{ "add deps :bar |//whatever/package/path:foo", }, wantOutput: []byte(strings.Join([]string{ `java_library(`, ` name = "foo",`, ` deps = [":bar"],`, `)`, ``}, "\n")), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { output, err := ExecuteCommandsOnInlineFile(tc.fileContent, tc.commands) if err != nil { t.Fatalf("Error, got error %v", err) } if diff := cmp.Diff(tc.wantOutput, output); diff != "" { t.Errorf("%s: (-want +got): %s", tc.name, diff) } }) } } func TestTestExecuteCommandsOnInlineFileFailed(t *testing.T) { tests := []struct { name string fileContent []byte commands []string wantErr error }{ { name: "target_does_not_exist", commands: []string{ "add deps :foo|//package/path:bar", }, wantErr: fmt.Errorf("rule 'bar' not found"), }, { name: "invalid_input", commands: []string{ "completely invalid command", }, wantErr: fmt.Errorf("rule 'completely invalid command' not found"), }, { name: "missing_implementation", commands: []string{ "extrapolate packages :foo|//package/path:bar", }, wantErr: fmt.Errorf("invalid input commands, expected all commands to reference a single file"), }, { name: "commands_for_multiple_files", commands: []string{ "add deps :foo|//package/path:bar", "add deps :foo|//package2/path:bar", }, wantErr: fmt.Errorf("invalid input commands, expected all commands to reference a single file"), }, { name: "command_with_unexpected_target_semicolons", commands: []string{ "add deps :foo|//package:path:bar", }, wantErr: fmt.Errorf("invalid target name \"//package:path:bar\""), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { output, gotErr := ExecuteCommandsOnInlineFile(tc.fileContent, tc.commands) if output != nil { t.Fatalf("Error, got response for invalid input %v, expected error", output) } if diff := cmp.Diff(tc.wantErr.Error(), gotErr.Error()); diff != "" { t.Errorf("%s: (-want +got): %s", tc.name, diff) } }) } } func TestGetIgnoredPrefixes(t *testing.T) { tmp, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) tests := []struct { name string bazelignore string expected []string expectError bool errorContains string }{ { name: "valid paths", bazelignore: `# Ignore these directories ignored a/ignored b/c/d`, expected: []string{"ignored", "a/ignored", "b/c/d"}, }, { name: "empty file", bazelignore: ` `, expected: []string{}, }, { name: "only comments", bazelignore: `# This is a comment # Another comment`, expected: []string{}, }, { name: "empty lines", bazelignore: `ignored a/ignored # comment b/c/d`, expected: []string{"ignored", "a/ignored", "b/c/d"}, }, { name: "absolute paths", bazelignore: `/absolute/path ignored /another/absolute/path`, expected: []string{"ignored"}, }, { name: "no file", bazelignore: "", expected: []string{}, }, { name: "trailing slash should be normalized", bazelignore: `ignored/`, expected: []string{"ignored"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create a new temporary directory for each test case testDir, err := os.MkdirTemp(tmp, "") if err != nil { t.Fatal(err) } if tt.bazelignore != "" { if err := os.WriteFile(filepath.Join(testDir, ".bazelignore"), []byte(tt.bazelignore), 0644); err != nil { t.Fatal(err) } } got := getIgnoredPrefixes(testDir) if !reflect.DeepEqual(got, tt.expected) { t.Errorf("getIgnoredPrefixes() = %v, want %v", got, tt.expected) } }) } } func TestShouldIgnorePath(t *testing.T) { tmp, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) tests := []struct { name string path string ignoredPrefixes []string want bool }{ { name: "exact match", path: filepath.Join(tmp, "foo"), ignoredPrefixes: []string{"foo"}, want: true, }, { name: "subdirectory", path: filepath.Join(tmp, "foo", "bar"), ignoredPrefixes: []string{"foo"}, want: true, }, { name: "similar prefix but not directory", path: filepath.Join(tmp, "foobar"), ignoredPrefixes: []string{"foo"}, want: false, // Should not ignore "foobar" when only "foo" is ignored }, { name: "matched with multiple prefixes", path: filepath.Join(tmp, "foobar"), ignoredPrefixes: []string{"foo2", "foobar", "baz"}, want: true, }, { name: "no match", path: filepath.Join(tmp, "bar"), ignoredPrefixes: []string{"foo", "baz"}, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := shouldIgnorePath(tt.path, tmp, tt.ignoredPrefixes) if got != tt.want { t.Errorf("shouldIgnorePath(%q, %q, %v) = %v, want %v", tt.path, tmp, tt.ignoredPrefixes, got, tt.want) } }) } } func TestSplitOnNonEscaped(t *testing.T) { tests := []struct { name string input string sep byte lim int want []string }{ { name: "no_split", input: "one:two", sep: '|', lim: -1, want: []string{"one:two"}, }, { name: "split_to_two", input: "one:two", sep: ':', lim: 2, want: []string{"one", "two"}, }, { name: "split_with_limit", input: "one:two:three:four", sep: ':', lim: 2, want: []string{"one", "two:three:four"}, }, { name: "split_without_limit", input: "one:two:three:four", sep: ':', lim: -1, want: []string{"one", "two", "three", "four"}, }, { name: "does_not_split_on_escaped", input: `one\:two:three`, sep: ':', lim: 2, want: []string{`one\:two`, "three"}, }, { name: "split_on_pipe", input: `one|two|three`, sep: '|', lim: -1, want: []string{"one", "two", "three"}, }, { name: "skip_escaped_pipes", input: `one\|two|three`, sep: '|', lim: -1, want: []string{`one\|two`, "three"}, }, { name: "split_with_unicode", input: `😉|â�¤ï¸�|ðŸ˜�`, sep: '|', lim: -1, want: []string{"😉", "â�¤ï¸�", "ðŸ˜�"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := splitOnNonEscaped(tc.input, tc.sep, tc.lim) if diff := cmp.Diff(tc.want, got); diff != "" { t.Fatalf("splitOnNonEscaped returned diff -want +got %v", diff) } }) } } func TestBuildozerDisableSymlinkSafety(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("Symlink safety checks are only supported on Linux") } wsDir := t.TempDir() outsideDir := t.TempDir() if err := os.WriteFile(filepath.Join(wsDir, "WORKSPACE"), nil, 0755); err != nil { t.Fatal(err) } outsideBuildFile := filepath.Join(outsideDir, "BUILD") initialContent := `cc_library( name = "lib", ) ` if err := os.WriteFile(outsideBuildFile, []byte(initialContent), 0644); err != nil { t.Fatal(err) } symlinkBuildFile := filepath.Join(wsDir, "BUILD") if err := os.Symlink(outsideBuildFile, symlinkBuildFile); err != nil { t.Fatal(err) } // 1. With DisableSymlinkSafety = false, Buildozer editing the file should fail (return 1 or error) opts := NewOpts() opts.RootDir = wsDir opts.DisableSymlinkSafety = false exitCode := Buildozer(opts, []string{"add deps //foo:bar", "//:lib"}) if exitCode == 0 { t.Errorf("Buildozer with DisableSymlinkSafety=false succeeded on symlink pointing outside workspace, want error (non-zero exit code)") } // 2. With DisableSymlinkSafety = true, Buildozer should succeed and edit outsideBuildFile opts = NewOpts() opts.RootDir = wsDir opts.DisableSymlinkSafety = true exitCode = Buildozer(opts, []string{"add deps //foo:bar", "//:lib"}) if exitCode != 0 { t.Fatalf("Buildozer with DisableSymlinkSafety=true failed with exit code %d", exitCode) } content, err := os.ReadFile(outsideBuildFile) if err != nil { t.Fatal(err) } if !strings.Contains(string(content), "//foo:bar") { t.Errorf("outside BUILD file did not contain added dependency, content:\n%s", string(content)) } } func TestBuildozerWithTypeAliases(t *testing.T) { buildFile := `type Numeric = int | float type OptionalDict[T, U] = dict[T, U] | None _: Any # Enable type checking foo( name = "foo", deps = ["//a"], ) ` bld, err := build.Parse("test.bzl", []byte(buildFile)) if err != nil { t.Fatalf("Parse error: %v", err) } rl := bld.Rules("foo")[0] env := CmdEnvironment{ File: bld, Rule: rl, Args: []string{"deps", "//b"}, } bld, _ = cmdAdd(NewOpts(), env) got := string(build.Format(bld)) expected := `type Numeric = int | float type OptionalDict[T, U] = dict[T, U] | None _: Any # Enable type checking foo( name = "foo", deps = ["//a", "//b"], ) ` if got != expected { t.Errorf("got %q, want %q", got, expected) } } buildtools-10.1.0/edit/BUILD.bazel0000664000175000017500000000207715254573013016155 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "edit", srcs = [ "buildozer.go", "default_buildifier.go", "edit.go", "fix.go", "types.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/edit", visibility = ["//visibility:public"], deps = [ "//api_proto", "//build", "//build_proto", "//bzlenv", "//edit/bzlmod", "//file", "//labels", "//lang", "//tables", "//wspace", "@com_github_golang_protobuf//jsonpb:go_default_library_gen", "@com_github_golang_protobuf//proto:go_default_library", ], ) go_test( name = "edit_test", srcs = [ "buildozer_command_file_test.go", "buildozer_test.go", "edit_test.go", "fix_test.go", ], embed = [":edit"], deps = [ "//build", "@com_github_google_go_cmp//cmp", ], ) alias( name = "go_default_library", actual = ":edit", visibility = ["//visibility:public"], ) buildtools-10.1.0/convertast/0000775000175000017500000000000015254573013015534 5ustar kenhyskenhysbuildtools-10.1.0/convertast/convert_ast.go0000664000175000017500000001665415254573013020426 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // This file contains functions to convert from one AST to the other. // Input: AST from go.starlark.net/syntax // Output: AST from github.com/bazel-contrib/buildtools/v10/build package convertast import ( "fmt" "strconv" "strings" "github.com/bazel-contrib/buildtools/v10/build" "go.starlark.net/syntax" ) func ConvFile(f *syntax.File) *build.File { stmts := []build.Expr{} for _, stmt := range f.Stmts { stmts = append(stmts, convStmt(stmt)) } return &build.File{ Type: build.TypeDefault, Stmt: stmts, Comments: convComments(f.Comments()), } } func convStmt(stmt syntax.Stmt) build.Expr { switch stmt := stmt.(type) { case *syntax.ExprStmt: s := convExpr(stmt.X) *s.Comment() = convComments(stmt.Comments()) return s case *syntax.BranchStmt: return &build.BranchStmt{ Token: stmt.Token.String(), Comments: convComments(stmt.Comments()), } case *syntax.LoadStmt: load := &build.LoadStmt{ Module: convExpr(stmt.Module).(*build.StringExpr), ForceCompact: singleLine(stmt), } for _, ident := range stmt.From { load.From = append(load.From, convExpr(ident).(*build.Ident)) } for _, ident := range stmt.To { load.To = append(load.To, convExpr(ident).(*build.Ident)) } return load case *syntax.AssignStmt: return &build.AssignExpr{ Op: stmt.Op.String(), LHS: convExpr(stmt.LHS), RHS: convExpr(stmt.RHS), Comments: convComments(stmt.Comments()), } case *syntax.IfStmt: return &build.IfStmt{ Cond: convExpr(stmt.Cond), True: convStmts(stmt.True), False: convStmts(stmt.False), Comments: convComments(stmt.Comments()), } case *syntax.DefStmt: return &build.DefStmt{ Name: stmt.Name.Name, Comments: convComments(stmt.Comments()), Function: build.Function{ Params: convExprs(stmt.Params), Body: convStmts(stmt.Body), }, } case *syntax.ForStmt: return &build.ForStmt{ Vars: convExpr(stmt.Vars), X: convExpr(stmt.X), Comments: convComments(stmt.Comments()), Body: convStmts(stmt.Body), } case *syntax.ReturnStmt: return &build.ReturnStmt{ Comments: convComments(stmt.Comments()), Result: convExpr(stmt.Result), } } panic("unreachable") } func convStmts(list []syntax.Stmt) []build.Expr { res := []build.Expr{} for _, i := range list { res = append(res, convStmt(i)) } return res } func convExprs(list []syntax.Expr) []build.Expr { res := []build.Expr{} for _, i := range list { res = append(res, convExpr(i)) } return res } func convCommentList(list []syntax.Comment, txt string) []build.Comment { res := []build.Comment{} for _, c := range list { res = append(res, build.Comment{Token: c.Text}) } return res } func convComments(c *syntax.Comments) build.Comments { if c == nil { return build.Comments{} } return build.Comments{ Before: convCommentList(c.Before, "before"), Suffix: convCommentList(c.Suffix, "suffix"), After: convCommentList(c.After, "after"), } } // singleLine returns true if the node fits on a single line. func singleLine(n syntax.Node) bool { start, end := n.Span() return start.Line == end.Line } func convClauses(list []syntax.Node) []build.Expr { res := []build.Expr{} for _, c := range list { switch stmt := c.(type) { case *syntax.ForClause: res = append(res, &build.ForClause{ Vars: convExpr(stmt.Vars), X: convExpr(stmt.X), }) case *syntax.IfClause: res = append(res, &build.IfClause{ Cond: convExpr(stmt.Cond), }) } } return res } func convExpr(e syntax.Expr) build.Expr { if e == nil { return nil } switch e := e.(type) { case *syntax.Literal: switch e.Token { case syntax.INT: return &build.LiteralExpr{ Token: strconv.FormatInt(e.Value.(int64), 10), Comments: convComments(e.Comments())} case syntax.FLOAT: return &build.LiteralExpr{ Token: e.Raw, Comments: convComments(e.Comments())} case syntax.STRING: return &build.StringExpr{ Value: e.Value.(string), TripleQuote: strings.HasPrefix(e.Raw, "\"\"\""), Comments: convComments(e.Comments())} } case *syntax.Ident: return &build.Ident{Name: e.Name, Comments: convComments(e.Comments())} case *syntax.BinaryExpr: _, lhsEnd := e.X.Span() rhsBegin, _ := e.Y.Span() if e.Op.String() == "=" { return &build.AssignExpr{ LHS: convExpr(e.X), RHS: convExpr(e.Y), Op: e.Op.String(), LineBreak: lhsEnd.Line != rhsBegin.Line, Comments: convComments(e.Comments())} } return &build.BinaryExpr{ X: convExpr(e.X), Y: convExpr(e.Y), Op: e.Op.String(), LineBreak: lhsEnd.Line != rhsBegin.Line, Comments: convComments(e.Comments())} case *syntax.UnaryExpr: return &build.UnaryExpr{Op: e.Op.String(), X: convExpr(e.X)} case *syntax.SliceExpr: return &build.SliceExpr{X: convExpr(e.X), From: convExpr(e.Lo), To: convExpr(e.Hi), Step: convExpr(e.Step)} case *syntax.DotExpr: return &build.DotExpr{X: convExpr(e.X), Name: e.Name.Name} case *syntax.CallExpr: args := []build.Expr{} for _, a := range e.Args { args = append(args, convExpr(a)) } return &build.CallExpr{ X: convExpr(e.Fn), List: args, ForceCompact: singleLine(e), } case *syntax.ListExpr: list := []build.Expr{} for _, i := range e.List { list = append(list, convExpr(i)) } return &build.ListExpr{List: list, Comments: convComments(e.Comments())} case *syntax.DictExpr: list := []*build.KeyValueExpr{} for i := range e.List { entry := e.List[i].(*syntax.DictEntry) list = append(list, &build.KeyValueExpr{ Key: convExpr(entry.Key), Value: convExpr(entry.Value), Comments: convComments(entry.Comments()), }) } return &build.DictExpr{List: list, Comments: convComments(e.Comments())} case *syntax.CondExpr: return &build.ConditionalExpr{ Then: convExpr(e.True), Test: convExpr(e.Cond), Else: convExpr(e.False), Comments: convComments(e.Comments()), } case *syntax.Comprehension: return &build.Comprehension{ Body: convExpr(e.Body), Clauses: convClauses(e.Clauses), Comments: convComments(e.Comments()), Curly: e.Curly, } case *syntax.ParenExpr: return &build.ParenExpr{ X: convExpr(e.X), Comments: convComments(e.Comments()), } case *syntax.TupleExpr: return &build.TupleExpr{ List: convExprs(e.List), NoBrackets: !e.Lparen.IsValid(), Comments: convComments(e.Comments()), ForceCompact: singleLine(e), } case *syntax.IndexExpr: return &build.IndexExpr{ X: convExpr(e.X), Y: convExpr(e.Y), Comments: convComments(e.Comments()), } case *syntax.LambdaExpr: return &build.LambdaExpr{ Comments: convComments(e.Comments()), Function: build.Function{ Params: convExprs(e.Params), Body: []build.Expr{convExpr(e.Body)}, }, } default: panic(fmt.Sprintf("other expr: %T %+v", e, e)) } panic("unreachable") } buildtools-10.1.0/convertast/BUILD.bazel0000664000175000017500000000065115254573013017414 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "convertast", srcs = ["convert_ast.go"], importpath = "github.com/bazel-contrib/buildtools/v10/convertast", visibility = ["//visibility:public"], deps = [ "//build", "@net_starlark_go//syntax", ], ) alias( name = "go_default_library", actual = ":convertast", visibility = ["//visibility:public"], ) buildtools-10.1.0/WORKSPACE0000664000175000017500000000672115254573013014633 0ustar kenhyskenhysworkspace(name = "buildtools") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_go", sha256 = "6734a719993b1ba4ebe9806e853864395a8d3968ad27f9dd759c196b3eb3abe8", urls = [ "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.45.1/rules_go-v0.45.1.zip", "https://github.com/bazelbuild/rules_go/releases/download/v0.45.1/rules_go-v0.45.1.zip", ], ) load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") go_rules_dependencies() go_register_toolchains(version = "1.20.3") http_archive( name = "bazel_gazelle", sha256 = "727f3e4edd96ea20c29e8c2ca9e8d2af724d8c7778e7923a854b2c80952bc405", urls = [ "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.30.0/bazel-gazelle-v0.30.0.tar.gz", "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.30.0/bazel-gazelle-v0.30.0.tar.gz", ], ) load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") gazelle_dependencies() go_repository( name = "net_starlark_go", importpath = "go.starlark.net", sum = "h1:Qoe+9POtDT51UBQ8XEnS9QKeHDQzEl2QRh3eok9R4aw=", version = "v0.0.0-20200203144150-6677ee5c7211", ) http_archive( name = "com_google_protobuf", sha256 = "616bb3536ac1fff3fb1a141450fa28b875e985712170ea7f1bfe5e5fc41e2cd8", strip_prefix = "protobuf-24.4", urls = [ "https://github.com/protocolbuffers/protobuf/archive/v24.4.tar.gz", ], ) go_repository( name = "com_github_google_go_cmp", importpath = "github.com/google/go-cmp", tag = "v0.5.9", ) load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() http_archive( name = "rules_proto", sha256 = "8e7d59a5b12b233be5652e3d29f42fba01c7cbab09f6b3a8d0a57ed6d1e9a0da", strip_prefix = "rules_proto-7e4afce6fe62dbff0a4a03450143146f9f2d7488", urls = [ "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/7e4afce6fe62dbff0a4a03450143146f9f2d7488.tar.gz", "https://github.com/bazelbuild/rules_proto/archive/7e4afce6fe62dbff0a4a03450143146f9f2d7488.tar.gz", ], ) load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") rules_proto_dependencies() rules_proto_toolchains() load("//buildifier:deps.bzl", "buildifier_dependencies") buildifier_dependencies() http_archive( name = "rules_nodejs", sha256 = "162f4adfd719ba42b8a6f16030a20f434dc110c65dc608660ef7b3411c9873f9", strip_prefix = "rules_nodejs-6.0.2", url = "https://github.com/bazelbuild/rules_nodejs/releases/download/v6.0.2/rules_nodejs-v6.0.2.tar.gz", ) load("@rules_nodejs//nodejs:repositories.bzl", "nodejs_register_toolchains") nodejs_register_toolchains( name = "nodejs", node_version = "16.7.0", ) http_archive( name = "aspect_rules_js", sha256 = "7ab9776bcca823af361577a1a2ebb9a30d2eb5b94ecc964b8be360f443f714b2", strip_prefix = "rules_js-1.32.6", url = "https://github.com/aspect-build/rules_js/releases/download/v1.32.6/rules_js-v1.32.6.tar.gz", ) load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies") rules_js_dependencies() load("@bazel_features//:deps.bzl", "bazel_features_deps") bazel_features_deps() load("@aspect_bazel_lib//lib:repositories.bzl", "register_copy_to_directory_toolchains", "register_jq_toolchains") register_jq_toolchains() register_copy_to_directory_toolchains() buildtools-10.1.0/tables/0000775000175000017500000000000015254573013014616 5ustar kenhyskenhysbuildtools-10.1.0/tables/jsonparser_test.go0000664000175000017500000000300215254573013020365 0ustar kenhyskenhys/* Copyright 2017 Google LLC 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 https://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 tables import ( "os" "reflect" "testing" ) func TestParseJSONDefinitions(t *testing.T) { testdata := os.Getenv("TEST_SRCDIR") + "/" + os.Getenv("TEST_WORKSPACE") + "/tables/testdata" definitions, err := ParseJSONDefinitions(testdata + "/simple_tables.json") if err != nil { t.Error(err) } expected := Definitions{ IsLabelArg: map[string]bool{"srcs": true}, LabelDenylist: map[string]bool{}, IsSortableListArg: map[string]bool{"srcs": true, "visibility": true}, SortableDenylist: map[string]bool{"genrule.srcs": true}, SortableAllowlist: map[string]bool{}, NamePriority: map[string]int{"name": -1}, StripLabelLeadingSlashes: true, AllowedSymbolLoadLocations: map[string][]string{"genrule": {"//tools/bazel:genrule.bzl"}}, } if !reflect.DeepEqual(expected, definitions) { t.Errorf("ParseJSONDefinitions(simple_tables.json) = %v; want %v", definitions, expected) } } buildtools-10.1.0/tables/testdata/0000775000175000017500000000000015254573013016427 5ustar kenhyskenhysbuildtools-10.1.0/tables/testdata/simple_tables.json0000664000175000017500000000066115254573013022150 0ustar kenhyskenhys{ "i-am-an-unrelated-field": true, "IsLabelArg": { "srcs": true }, "LabelDenylist": { }, "IsSortableListArg": { "srcs": true, "visibility": true }, "SortableDenylist": { "genrule.srcs": true }, "SortableAllowlist": { }, "NamePriority": { "name": -1 }, "StripLabelLeadingSlashes": true, "AllowedSymbolLoadLocations": { "genrule": [ "//tools/bazel:genrule.bzl" ] } } buildtools-10.1.0/tables/tables.go0000664000175000017500000002634215254573013016426 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // Tables about what Buildifier can and cannot edit. // Perhaps eventually this will be // derived from the BUILD encyclopedia. package tables // IsLabelArg contains a list of named arguments to a rule call that are // considered to have a value that can be treated as a label or list of labels. // There is a separate denylist for rule-specific exceptions. var IsLabelArg = map[string]bool{ "app_target": true, "appdir": true, "base_package": true, "build_deps": true, "cc_deps": true, "ccdeps": true, "common_deps": true, "compile_deps": true, "compiler": true, "data": true, "default_visibility": true, "dep": true, "deps": true, "deps_java": true, "dont_depend_on": true, "env_deps": true, "envscripts": true, "exported_deps": true, "exports": true, "externs_list": true, "files": true, "globals": true, "implementation": true, "implementation_deps": true, "implements": true, "includes": true, "interface": true, "jar": true, "jars": true, "javadeps": true, "lib_deps": true, "library": true, "malloc": true, "model": true, "mods": true, "module_deps": true, "module_target": true, "of": true, "plugins": true, "private_deps": true, "proto_deps": true, "proto_target": true, "protos": true, "resource": true, "resources": true, "runtime_deps": true, "scope": true, "shared_deps": true, "similar_deps": true, "source_jar": true, "src": true, "srcs": true, "stripped_targets": true, "suites": true, "swigdeps": true, "target": true, "target_devices": true, "target_platforms": true, "template": true, "test": true, "test_data": true, "test_deps": true, "test_srcs": true, "tests": true, "tests_deps": true, "tool": true, "tools": true, "visibility": true, } // LabelDenylist is the list of call arguments that cannot be // shortened, because they are not interpreted using the same // rules as for other labels. var LabelDenylist = map[string]bool{ // Shortening this can cause visibility checks to fail. "package_group.includes": true, } // By default, edit.types.IsList consults lang.TypeOf to determine if an arg is a list. // You may override this using IsListArg. Specifying a name here overrides any value // in lang.TypeOf. var IsListArg = map[string]bool{} // IsSortableListArg contains a list of named arguments to a rule call that are // considered to be a sortable list . There is a separate denylist for // rule-specific exceptions. var IsSortableListArg = map[string]bool{ "cc_deps": true, "common_deps": true, "compile_deps": true, "configs": true, "constraints": true, "data": true, "default_visibility": true, "deps": true, "deps_java": true, "exported_deps": true, "exports": true, "filegroups": true, "files": true, "hdrs": true, "implementation_deps": true, "imports": true, "includes": true, "inherits": true, "javadeps": true, "lib_deps": true, "module_deps": true, "outs": true, "packages": true, "plugin_modules": true, "private_deps": true, "proto_deps": true, "protos": true, "pubs": true, "resources": true, "runtime_deps": true, "shared_deps": true, "similar_deps": true, "srcs": true, "swigdeps": true, "swig_includes": true, "tags": true, "test_data": true, "test_deps": true, "test_srcs": true, "test_tags": true, "tests": true, "tools": true, "to_start_extensions": true, "visibility": true, } // SortableDenylist records specific rule arguments that must not be reordered. var SortableDenylist = map[string]bool{ "genrule.outs": true, "genrule.srcs": true, "cc_embed_data.srcs": true, } // SortableAllowlist records specific rule arguments that are guaranteed to be reorderable // (format: "rule_name.attribute_name"). var SortableAllowlist = map[string]bool{} // NamePriority maps an argument name to its sorting priority. // // NOTE(bazel-team): These are the old buildifier rules. It is likely that this table // will change, perhaps swapping in a separate table for each call, // derived from the order used in the Build Encyclopedia. var NamePriority = map[string]int{ "name": -99, "archive_override.module_name": -99, // for MODULE.bazel "git_override.module_name": -99, // for MODULE.bazel "local_path_override.module_name": -99, // for MODULE.bazel "multiple_version_override.module_name": -99, // for MODULE.bazel "single_version_override.module_name": -99, // for MODULE.bazel "bazel_dep.version": -98, // for MODULE.bazel "module.version": -98, // for MODULE.bazel "gwt_name": -98, "package_name": -97, "visible_node_name": -96, // for boq_initial_css_modules and boq_jswire_test_suite "size": -95, "timeout": -94, "testonly": -93, "src": -92, "srcdir": -91, "srcs": -90, "out": -89, "outs": -88, "hdrs": -87, "has_services": -86, // before api versions, for proto "include": -85, // before exclude, for glob "of": -84, // for check_dependencies "baseline": -83, // for searchbox_library // All others sort here, at 0. "destdir": 1, "exports": 2, "runtime_deps": 3, "deps": 4, "implementation": 5, "implements": 6, "alwayslink": 7, } var StripLabelLeadingSlashes = false var ShortenAbsoluteLabelsToRelative = false // AndroidNativeRules lists all Android rules that are being migrated from Native to Starlark. var AndroidNativeRules = []string{ "aar_import", "android_binary", "android_device", "android_instrumentation_test", "android_library", "android_local_test", "android_ndk_respository", "android_sdk_repository", } // AndroidLoadPath is the load path for the Starlark Android Rules. var AndroidLoadPath = "@rules_android//android:rules.bzl" // CcLoadPathPrefix is the load path for the Starlark C++ Rules. var CcLoadPathPrefix = "@rules_cc//cc" // JavaLoadPathPrefix is the load package for the Starlark Java Rules. var JavaLoadPathPrefix = "@rules_java//java" // ShellLoadPathPrefix is the load package for the Starlark Shell Rules. var ShellLoadPathPrefix = "@rules_shell//shell" // PyNativeRules lists all Python rules that are being migrated from Native to Starlark. var PyNativeRules = []string{ "py_library", "py_binary", "py_test", "py_runtime", } // PyLoadPath is the load path for the Starlark Python Rules. var PyLoadPath = "@rules_python//python:defs.bzl" // ProtoLoadPathPrefix is the load path prefix for the Starlark Proto Rules. var ProtoLoadPathPrefix = "@protobuf//bazel" // ModuleToLegacyRepoName contains the mapping from module name to WORKSPACE repository name // for modules with load fixes if those names are different. var ModuleToLegacyRepoName = map[string]string{ "protobuf": "com_google_protobuf", } // IsModuleOverride contains the names of all Bzlmod module overrides available in MODULE.bazel. var IsModuleOverride = map[string]bool{ "archive_override": true, "git_override": true, "local_path_override": true, "multiple_version_override": true, "single_version_override": true, } // AllowedSymbolLoadLocations contains locations for loading rules that are allowed to be used. var AllowedSymbolLoadLocations = map[string]map[string]bool{} // OverrideTables allows a user of the build package to override the special-case rules. The user-provided tables replace the built-in tables. func OverrideTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool, symbolLoadLocation map[string][]string) { IsLabelArg = labelArg LabelDenylist = denylist IsListArg = listArg IsSortableListArg = sortableListArg SortableDenylist = sortDenylist SortableAllowlist = sortAllowlist NamePriority = namePriority StripLabelLeadingSlashes = stripLabelLeadingSlashes ShortenAbsoluteLabelsToRelative = shortenAbsoluteLabelsToRelative AllowedSymbolLoadLocations = map[string]map[string]bool{} for k, v := range symbolLoadLocation { locations := map[string]bool{} for _, l := range v { locations[l] = true } AllowedSymbolLoadLocations[k] = locations } } // MergeTables allows a user of the build package to override the special-case rules. The user-provided tables are merged into the built-in tables. func MergeTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool, symbolLoadLocation map[string][]string) { for k, v := range labelArg { IsLabelArg[k] = v } for k, v := range denylist { LabelDenylist[k] = v } for k, v := range listArg { IsListArg[k] = v } for k, v := range sortableListArg { IsSortableListArg[k] = v } for k, v := range sortDenylist { SortableDenylist[k] = v } for k, v := range sortAllowlist { SortableAllowlist[k] = v } for k, v := range namePriority { NamePriority[k] = v } StripLabelLeadingSlashes = stripLabelLeadingSlashes || StripLabelLeadingSlashes ShortenAbsoluteLabelsToRelative = shortenAbsoluteLabelsToRelative || ShortenAbsoluteLabelsToRelative for k, v := range symbolLoadLocation { locations := map[string]bool{} for _, l := range v { locations[l] = true } AllowedSymbolLoadLocations[k] = locations } } buildtools-10.1.0/tables/jsonparser.go0000664000175000017500000000452215254573013017336 0ustar kenhyskenhys/* Copyright 2017 Google LLC 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 https://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 tables import ( "encoding/json" "os" ) type Definitions struct { IsLabelArg map[string]bool LabelDenylist map[string]bool IsListArg map[string]bool IsSortableListArg map[string]bool SortableDenylist map[string]bool SortableAllowlist map[string]bool NamePriority map[string]int StripLabelLeadingSlashes bool ShortenAbsoluteLabelsToRelative bool AllowedSymbolLoadLocations map[string][]string } // ParseJSONDefinitions reads and parses JSON table definitions from file. func ParseJSONDefinitions(file string) (Definitions, error) { var definitions Definitions data, err := os.ReadFile(file) if err != nil { return definitions, err } err = json.Unmarshal(data, &definitions) return definitions, err } // ParseAndUpdateJSONDefinitions reads definitions from file and merges or // overrides the values in memory. func ParseAndUpdateJSONDefinitions(file string, merge bool) error { definitions, err := ParseJSONDefinitions(file) if err != nil { return err } if merge { MergeTables(definitions.IsLabelArg, definitions.LabelDenylist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableDenylist, definitions.SortableAllowlist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative, definitions.AllowedSymbolLoadLocations) } else { OverrideTables(definitions.IsLabelArg, definitions.LabelDenylist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableDenylist, definitions.SortableAllowlist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative, definitions.AllowedSymbolLoadLocations) } return nil } buildtools-10.1.0/tables/BUILD.bazel0000664000175000017500000000102715254573013016474 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "tables", srcs = [ "jsonparser.go", "tables.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/tables", visibility = ["//visibility:public"], ) go_test( name = "tables_test", size = "small", srcs = ["jsonparser_test.go"], data = glob(["testdata/*"]), embed = [":tables"], ) alias( name = "go_default_library", actual = ":tables", visibility = ["//visibility:public"], ) buildtools-10.1.0/AGENTS.md0000664000175000017500000000031415254573013014645 0ustar kenhyskenhys# Agent instructions Follow [CONTRIBUTING.md](CONTRIBUTING.md). It describes how to build and test the repository, how to regenerate checked-in generated files, and what a pull request needs to contain. buildtools-10.1.0/.mailmap0000664000175000017500000000167515254573013014776 0ustar kenhyskenhys Advay Mengle Artem Zinnatullin Benjamin Denhartog Benjamin Denhartog <3893293+sudoforge@users.noreply.github.com> Carmi Grushko Kristina Chodorow Kristina Chodorow Kristina Tim Peut <38146286+timpeut@users.noreply.github.com> buildtools-10.1.0/warn/0000775000175000017500000000000015254573013014313 5ustar kenhyskenhysbuildtools-10.1.0/warn/warn_visibility.go0000664000175000017500000000663515254573013020072 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Warnings about visibility of .bzl files package warn import ( "fmt" "regexp" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/labels" ) var internalDirectory = regexp.MustCompile("/(internal|private)[/:]") func hasVisibilityStatement(f *build.File, load *build.LoadStmt, fileReader *FileReader) bool { if fileReader == nil { return false } label := labels.ParseRelative(load.Module.Value, f.Pkg) if label.Repository != "" || label.Target == "" { return false } loadedFile := fileReader.GetFile(label.Package, label.Target) if loadedFile == nil { return false } for _, stmt := range loadedFile.Stmt { call, ok := stmt.(*build.CallExpr) if !ok { continue } // We don't try to be exhaustive here, but rather only catch the most // common cases of visibility declarations. if ident, ok := call.X.(*build.Ident); ok && ident.Name == "visibility" { return true } } return false } func bzlVisibilityWarning(f *build.File, fileReader *FileReader) []*LinterFinding { var findings []*LinterFinding if f.WorkspaceRoot == "" { // Empty workspace root means buildifier doesn't know the location of // the file relative to the workspace directory and can't warn about .bzl // file visibility correctly. return findings } for _, stmt := range f.Stmt { load, ok := stmt.(*build.LoadStmt) if !ok || load.Module == nil { continue } // A load statement may use a fully qualified module name (including a // repository name). Buildifier should check if the repository name refers // to the current repository, but because it doesn't know the current // repository name it's better to assume that it matches the repository name // in the load statement: this way it may miss some usages of private .bzl // files that aren't supposed to be visible, but won't show false-positive // warnings in case the private file is actually allowed to be used. module := load.Module.Value if strings.HasPrefix(module, "@") { if chunks := strings.SplitN(module, "//", 2); len(chunks) == 2 { module = "//" + chunks[1] } } if hasVisibilityStatement(f, load, fileReader) { // The module has a visibility statement, which is a more explicit // (and strongly-enforced) mechanism to specify visibility. No need // to issue a warning using the older and less explicit mechanism. continue } path := f.CanonicalPath() // Canonical name of the file chunks := internalDirectory.Split(module, 2) if len(chunks) < 2 { continue } if strings.HasPrefix(path, chunks[0]) || strings.HasPrefix(strings.Replace(path, "/javatests/", "/java/", 1), chunks[0]) { continue } findings = append(findings, makeLinterFinding( load.Module, fmt.Sprintf("Module %q can only be loaded from files located inside %q, not from %q.", load.Module.Value, chunks[0], path))) } return findings } buildtools-10.1.0/warn/warn_deprecated_test.go0000664000175000017500000000404615254573013021034 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestDeprecatedFunction(t *testing.T) { defer setUpFileReader(map[string]string{ "test/package/foo.bzl": ` def foo(): """ This is a function foo. Please use it in favor of the deprecated function bar """ pass def bar(): """ This is a function bar. Deprecated: please use foo instead. """ pass `, "test/package/invalid.bzl": ` This is not a valid Starlark file `, })() checkFindings(t, "deprecated-function", ` load(":foo.bzl", "foo", "bar", "baz") load("//test/package:foo.bzl", "foo", "bar", "baz") load(":invalid.bzl", "foo", "bar", "baz") load(":nonexistent.bzl", "foo", "bar", "baz") `, []string{ `1: The function "bar" defined in "//test/package/foo.bzl" is deprecated.`, `2: The function "bar" defined in "//test/package/foo.bzl" is deprecated.`, }, scopeEverywhere) } func TestDeprecatedFunctionAnotherRepo(t *testing.T) { defer setUpFileReader(map[string]string{ "test/package/foo.bzl": ` def foo(): """ This is a function foo. Deprecated: do not use. """ pass `, })() checkFindings(t, "deprecated-function", ` load("@another_repo//test/package:foo.bzl", "foo") `, []string{}, scopeEverywhere) } func TestDeprecatedFunctionNoReader(t *testing.T) { checkFindings(t, "deprecated-function", ` load(":foo.bzl", "foo", "bar", "baz") load("//test/package:foo.bzl", "foo", "bar", "baz") load(":invalid.bzl", "foo", "bar", "baz") load(":nonexistent.bzl", "foo", "bar", "baz") `, []string{}, scopeEverywhere) } buildtools-10.1.0/warn/warn_bazel_operation_test.go0000664000175000017500000001114115254573013022103 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestDepsetIteration(t *testing.T) { checkFindingsAndFix(t, "depset-iteration", ` d = depset([1, 2, 3]) + bar max(d + foo) min(d) all(d) any(d) sorted(d) zip( d, a, b, ) zip( a, d, ) list(d) tuple(d) depset(d) len(d) 1 in d 2 not in d [foo(x) for x in d] for x in d: pass # Non-iteration is ok foobar(d) d == b # The following iterations over a list don't trigger warnings l = list([1, 2, 3]) max(l) zip(l, foo) [foo(x) for x in l] 1 in l for x in l: pass `, ` d = depset([1, 2, 3]) + bar max((d + foo).to_list()) min(d.to_list()) all(d.to_list()) any(d.to_list()) sorted(d.to_list()) zip( d.to_list(), a, b, ) zip( a, d.to_list(), ) d.to_list() tuple(d.to_list()) depset(d.to_list()) len(d.to_list()) 1 in d.to_list() 2 not in d.to_list() [foo(x) for x in d.to_list()] for x in d.to_list(): pass # Non-iteration is ok foobar(d) d == b # The following iterations over a list don't trigger warnings l = list([1, 2, 3]) max(l) zip(l, foo) [foo(x) for x in l] 1 in l for x in l: pass `, []string{ `:3: Depset iteration is deprecated, use the "to_list()" method instead.`, `:4: Depset iteration is deprecated, use the "to_list()" method instead.`, `:5: Depset iteration is deprecated, use the "to_list()" method instead.`, `:6: Depset iteration is deprecated, use the "to_list()" method instead.`, `:7: Depset iteration is deprecated, use the "to_list()" method instead.`, `:9: Depset iteration is deprecated, use the "to_list()" method instead.`, `:15: Depset iteration is deprecated, use the "to_list()" method instead.`, `:17: Depset iteration is deprecated, use the "to_list()" method instead.`, `:18: Depset iteration is deprecated, use the "to_list()" method instead.`, `:19: Depset iteration is deprecated, use the "to_list()" method instead.`, `:20: Depset iteration is deprecated, use the "to_list()" method instead.`, `:21: Depset iteration is deprecated, use the "to_list()" method instead.`, `:22: Depset iteration is deprecated, use the "to_list()" method instead.`, `:24: Depset iteration is deprecated, use the "to_list()" method instead.`, `:26: Depset iteration is deprecated, use the "to_list()" method instead.`, }, scopeEverywhere) } func TestDepsetUnion(t *testing.T) { checkFindings(t, "depset-union", ` d = depset([1, 2, 3]) d + foo foo + d d + foo + bar foo + bar + d d | foo foo | d d | foo | bar foo | bar | d d += foo d |= bar foo += d bar |= d d.union(aaa) bbb.union(d) ccc.union(ddd) eee + fff | ggg `, []string{ `:3: Depsets should be joined using the "depset()" constructor`, `:4: Depsets should be joined using the "depset()" constructor`, `:5: Depsets should be joined using the "depset()" constructor`, `:5: Depsets should be joined using the "depset()" constructor`, `:6: Depsets should be joined using the "depset()" constructor`, `:8: Depsets should be joined using the "depset()" constructor`, `:9: Depsets should be joined using the "depset()" constructor`, `:10: Depsets should be joined using the "depset()" constructor`, `:10: Depsets should be joined using the "depset()" constructor`, `:11: Depsets should be joined using the "depset()" constructor`, `:13: Depsets should be joined using the "depset()" constructor`, `:14: Depsets should be joined using the "depset()" constructor`, `:15: Depsets should be joined using the "depset()" constructor`, `:16: Depsets should be joined using the "depset()" constructor`, `:18: Depsets should be joined using the "depset()" constructor`, `:19: Depsets should be joined using the "depset()" constructor`, }, scopeEverywhere) } func TestOverlyNestedDepset(t *testing.T) { checkFindings(t, "overly-nested-depset", ` x = depset() x = depset(1, transitive=[a, x, b]) # not inside a loop, ok for y in z: if y: x = depset([2], transitive = [a, x, b]) # problem here y = depset([3], transitive = [a, x, b]) # ok x = depset([4]) # ok `, []string{ `:6: Depset "x" is potentially overly nested.`, }, scopeEverywhere) } buildtools-10.1.0/warn/warn_control_flow_test.go0000664000175000017500000007347315254573013021455 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "testing" "github.com/bazel-contrib/buildtools/v10/build" ) func TestMissingReturnValueWarning(t *testing.T) { // empty return checkFindings(t, "return-value", ` def foo(): if x: return x else: return `, []string{ `:5: Some but not all execution paths of "foo" return a value.`, }, scopeEverywhere) // empty return; implicit return in the end checkFindings(t, "return-value", ` def bar(): if x: pass elif y: return y else: for z in t: return `, []string{ `:1: Some but not all execution paths of "bar" return a value. The function may terminate by an implicit return in the end.`, `:8: Some but not all execution paths of "bar" return a value.`, }, scopeEverywhere) // implicit return in the end checkFindings(t, "return-value", ` def foo(): if x: return x else: bar() `, []string{ `:1: Some but not all execution paths of "foo" return a value. The function may terminate by an implicit return in the end.`, }, scopeEverywhere) // implicit return in the end checkFindings(t, "return-value", ` def bar(): if x: return x elif y: return y else: foo if z: return z if foo: return not foo `, []string{ `:1: Some but not all execution paths of "bar" return a value. The function may terminate by an implicit return in the end.`, }, scopeEverywhere) // only returned values and fail() statements, ok checkFindings(t, "return-value", ` def bar(): if x: return x elif y: return y else: foo if z: return z if foo: return not foo else: fail("unreachable") `, []string{}, scopeEverywhere) // implicit return in the end because fail() is not a statement checkFindings(t, "return-value", ` def bar(): if x: return x elif y: return y else: foo if z: return z if foo: return not foo else: foo() or fail("unreachable") `, []string{ `:1: Some but not all execution paths of "bar" return a value. The function may terminate by an implicit return in the end.`, }, scopeEverywhere) // only empty returns, ok checkFindings(t, "return-value", ` def bar(): if x: x() elif y: return else: foo if z: fail() if foo: return `, []string{}, scopeEverywhere) // no returns, ok checkFindings(t, "return-value", ` def foobar(): pass `, []string{}, scopeEverywhere) // only fails, ok checkFindings(t, "return-value", ` def foobar(): if foo: fail() `, []string{}, scopeEverywhere) // nested functions, no errors checkFindings(t, "return-value", ` def foo(): def bar(): pass return bar `, []string{}, scopeEverywhere) // nested functions, missing return value in the outer function checkFindings(t, "return-value", ` def foo(): def bar(): pass if bar(): return 42 `, []string{ `:1: Some but not all execution paths of "foo" return a value. The function may terminate by an implicit return in the end.`, }, scopeEverywhere) // nested functions, missing return value in the inner function checkFindings(t, "return-value", ` def foo(): def bar(): if something: return something if bar(): return 42 return 43 `, []string{ `:2: Some but not all execution paths of "bar" return a value. The function may terminate by an implicit return in the end.`, }, scopeEverywhere) // nested functions, missing return value in both checkFindings(t, "return-value", ` def foo(): def bar(): if something: return something if bar(): return 42 `, []string{ `:1: Some but not all execution paths of "foo" return a value. The function may terminate by an implicit return in the end.`, `:2: Some but not all execution paths of "bar" return a value. The function may terminate by an implicit return in the end.`, }, scopeEverywhere) } func TestUnreachableStatementWarning(t *testing.T) { // after return checkFindings(t, "unreachable", ` def foo(): return bar() baz() `, []string{ `:3: The statement is unreachable.`, }, scopeEverywhere) // two returns checkFindings(t, "unreachable", ` def foo(): return 1 return 2 `, []string{ `:3: The statement is unreachable.`, }, scopeEverywhere) // after fail() checkFindings(t, "unreachable", ` def foo(): fail("die") bar() baz() `, []string{ `:3: The statement is unreachable.`, }, scopeEverywhere) // after break and continue checkFindings(t, "unreachable", ` def foo(): for x in y: if x: break bar() # unreachable if y: continue bar() # unreachable def bar(): for x in y: if x: break elif y: continue else: return x foo() # unreachable foobar() # potentially reachable `, []string{ `:5: The statement is unreachable.`, `:8: The statement is unreachable.`, `:19: The statement is unreachable.`, }, scopeEverywhere) // ok checkFindings(t, "unreachable", ` def foo(): if x: return bar() `, []string{}, scopeEverywhere) // ok checkFindings(t, "unreachable", ` def foo(): x() or fail("maybe") bar() `, []string{}, scopeEverywhere) // unreacheable statement inside a nested function checkFindings(t, "unreachable", ` def foo(): def bar(): fail("die") baz() `, []string{ `:4: The statement is unreachable.`, }, scopeEverywhere) // unreachable comment is ok checkFindings(t, "unreachable", ` def foo(): fail("die") # comment `, []string{}, scopeEverywhere) } func TestNoEffect(t *testing.T) { checkFindings(t, "no-effect", ` """Docstring.""" def bar(): """Other Docstring""" fct() pass return 2 [f() for i in rang(3)] # top-level comprehension is okay `, []string{}, scopeEverywhere) checkFindings(t, "no-effect", ` def foo(): [fct() for i in range(3)] `, []string{":2: Expression result is not used. Use a for-loop instead"}, scopeEverywhere) checkFindings(t, "no-effect", `None`, []string{":1: Expression result is not used."}, scopeEverywhere) checkFindings(t, "no-effect", ` foo # 1 foo() def bar(): [1, 2] # 5 if True: "string" # 7 `, []string{":1:", ":5:", ":7:"}, scopeEverywhere) checkFindings(t, "no-effect", ` # A comment """A docstring""" # Another comment """Not a docstring""" def bar(): """A docstring""" foo """ Not a docstring""" return foo `, []string{ ":7: Expression result is not used. Docstrings should be the first statements of a file or a function (they may follow comment lines).", ":11: Expression result is not used.", ":12: Expression result is not used. Docstrings should be the first statements of a file or a function (they may follow comment lines).", }, scopeEverywhere) checkFindings(t, "no-effect", ` foo == bar foo = bar a + b c // d -e foo != bar foo += bar bar -= bar bar *= bar bar /= bar bar //= bar bar %= bar bar &= bar bar |= bar bar ^= bar bar <<= bar bar >>= bar `, []string{":1:", ":3:", ":4:", ":5:", ":6:"}, scopeEverywhere) checkFindings(t, "no-effect", ` def foo(): """Doc.""" def bar(): """Doc.""" foo == bar `, []string{":5:"}, scopeEverywhere) } func TestWarnUnusedVariable(t *testing.T) { checkFindings(t, "unused-variable", ` load(":f.bzl", "x") x = "unused" y = "also unused" z = "name" t = "unused by design" # @unused _foo, _bar = pair #@unused cc_library(name = z) def f(): pass def g(): pass g() + 3 `, []string{":2: Variable \"x\" is unused.", ":3: Variable \"y\" is unused.", ":9: Function \"f\" is unused."}, scopeDeclarative) checkFindings(t, "unused-variable", ` a = 1 b = 2 c = 3 d = (a if b else c) # only d is unused `, []string{":4: Variable \"d\" is unused."}, scopeDeclarative) checkFindings(t, "unused-variable", ` _a = 1 _a += 2 _b = 3 print(_b) def _f(): pass def _g(): pass _g() `, []string{ ":1: Variable \"_a\" is unused.", ":6: Function \"_f\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` a = 1 def foo( x, y = 0, z = 1): b = 2 c = 3 d = (a if b else c) # only d is unused e = 7 f = 8 # @unused # @unused g = 9 return e + z foo() `, []string{ ":4: Variable \"x\" is unused.", ":5: Variable \"y\" is unused.", ":9: Variable \"d\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` a = 1 def foo(a): b = 2 return a def foo(): pass def bar(c, cc): d = 3 print(c) def baz(): foo() d = 4 return a bar() `, []string{ ":4: Variable \"b\" is unused.", ":10: Variable \"cc\" is unused.", ":11: Variable \"d\" is unused.", ":14: Function \"baz\" is unused.", ":16: Variable \"d\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(): a = 1 b = 2 c = 3 def bar( x = a + baz(c = 4), y = b): pass foo() `, []string{ ":4: Variable \"c\" is unused.", ":6: Function \"bar\" is unused.", ":7: Variable \"x\" is unused.", ":8: Variable \"y\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(): a = 1 b = 2 c = [x for a in aa if a % b for x in a] return c foo() `, []string{ ":2: Variable \"a\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(): a = 1 b = 2 c = [ a + b for a in [b for b in bb if b] if a ] return c foo() `, []string{ ":2: Variable \"a\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(): a = 1 b = 2 def bar(*args): def baz(**kwargs): def foobar(*a, **kw): return b return foobar(**kwargs) return baz(*args) return bar() foo() `, []string{ ":2: Variable \"a\" is unused.", ":7: Variable \"a\" is unused.", ":8: Variable \"kw\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(): a = 1 b = 2 c = 3 d = 4 e, f = 5, 6 for x, yy in xx: for (y, z, _, _t) in yy: print(a + y) if bar: print(c) elif baz: print(d) else: print(e) foo() `, []string{ ":3: Variable \"b\" is unused.", ":6: Variable \"f\" is unused.", ":8: Variable \"x\" is unused.", ":9: Variable \"z\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(): # @unused def bar(): pass def baz(): pass foo() `, []string{ ":7: Function \"baz\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(my_iterable, arg, _some_unused_argument, _also_unused = None, *_args, **_kwargs): a, b, _c = 1, 2, 3 # ok to not use _c print(a) _d, _e = 4, 5 # all are underscored print(_d) for f, g, _h, _ in my_iterable: # ok to not use any underscored print(f) for _i, (_j, _k) in another_iterable: # ok to not use any of them pass [1 for (_y, _z) in bar] foo() `, []string{ ":1: Variable \"arg\" is unused.", ":3: Variable \"b\" is unused.", ":6: Variable \"_e\" is unused.", ":9: Variable \"g\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo( x, _y, z, # @unused t = 42, #@unused *args, # @unused **kwargs, ### also @unused ): pass foo() `, []string{ ":2: Variable \"x\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(x): # @unused pass def bar( x, y): # @unused pass foo() bar() `, []string{ ":5: Variable \"x\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo(x) -> int: # @unused pass def bar( x, y) -> int: # @unused pass foo() bar() `, []string{ ":5: Variable \"x\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo( x, ): # @unused pass def bar( x, y, ) -> int: # @unused pass foo() bar() `, []string{ ":7: Variable \"x\" is unused.", }, scopeEverywhere) checkFindings(t, "unused-variable", ` def foo( name, x): pass def bar( name = "", y = 3): pass foo() bar() `, []string{ ":3: Variable \"x\" is unused.", ":9: Variable \"y\" is unused.", }, scopeEverywhere) // foo is unused in the outer macro, since the symbol is overloaded, and only // the inner_def "foo" is actually used. checkFindings(t, "unused-variable", ` def sample_macro_with_unused_foo(name, foo = "foo"): def inner_def(foo = "bar"): print(foo) inner_def() sample_macro_with_unused_foo() `, []string{ ":1: Variable \"foo\" is unused.", }, scopeEverywhere) // Foo is referenced as the default value for foo in inner_def, hence it is not unused. checkFindings(t, "unused-variable", ` def sample_macro_with_used_foo(name, foo = "foo"): def inner_def(bar = foo): print(bar) inner_def() sample_macro_with_used_foo() `, []string{}, scopeEverywhere) // Unused variables declared by var statements checkFindings(t, "unused-variable", ` _x: int def foo(): y: str foo() `, []string{ ":1: Variable \"_x\" is unused.", ":4: Variable \"y\" is unused.", }, scopeEverywhere) // Unused variables declared by var statements but marked @unused checkFindings(t, "unused-variable", ` # @unused _x: int def foo(): # @unused y: str foo() `, []string{}, scopeEverywhere) // "_" is always allowed to be unused checkFindings(t, "unused-variable", ` _: Any # Enable type checking def foo(): _: None # Enable type checking def callback(_): pass callback(None) foo() `, []string{}, scopeEverywhere) // Type alias used in function return annotation checkFindings(t, "unused-variable", ` type _Numeric = int | float def foo() -> _Numeric: return 42 foo() `, []string{}, scopeEverywhere) // Type alias used in function parameter annotation checkFindings(t, "unused-variable", ` type _Numeric = int | float def foo(x: _Numeric): print(x) foo(42) `, []string{}, scopeEverywhere) // Type alias unused - shadowed by function type parameter checkFindings(t, "unused-variable", ` type _Numeric = int | float def foo[_Numeric](x: _Numeric): print(x) foo(42) `, []string{ ":1: Type \"_Numeric\" is unused.", }, scopeEverywhere) // Type alias used in an assignment LHS checkFindings(t, "unused-variable", ` type _Numeric = int | float # @unused _x: _Numeric = 42 `, []string{}, scopeEverywhere) // Type alias used in a var statement checkFindings(t, "unused-variable", ` type _Numeric = int | float # @unused _x: _Numeric `, []string{}, scopeEverywhere) // Type alias used in another type alias checkFindings(t, "unused-variable", ` type _Numeric = int | float # @unused type _OptionalNumeric = _Numeric | None `, []string{}, scopeEverywhere) // Type alias unused - shadowed by type alias type parameter checkFindings(t, "unused-variable", ` type _Numeric = int | float # @unused type _Opt[_T] = _T | None `, []string{ ":1: Type \"_Numeric\" is unused.", }, scopeEverywhere) // Type alias explicitly marked @unused checkFindings(t, "unused-variable", ` # @unused type _Numeric = int | float `, []string{}, scopeEverywhere) // Unused type alias checkFindings(t, "unused-variable", ` type _Numeric = int | float `, []string{ ":1: Type \"_Numeric\" is unused.", }, scopeEverywhere) // Unused type parameter checkFindings(t, "unused-variable", ` def foo[ T, _U, # @unused V, ](): pass foo() `, []string{ ":2: Type \"T\" is unused.", }, scopeEverywhere) // Type parameter used in parameter annotation checkFindings(t, "unused-variable", ` def foo[T](x: T): print(x) foo() `, []string{}, scopeEverywhere) // Type parameter used in parameter annotation with a default value checkFindings(t, "unused-variable", ` def foo[T](x: T = 42): print(x) foo() `, []string{}, scopeEverywhere) // Type parameter used in varargs/kwargs annotation checkFindings(t, "unused-variable", ` def foo[T, U](*args: T, **kwargs: U): print(*args, **kwargs) foo() `, []string{}, scopeEverywhere) // Type parameter used in an inner function checkFindings(t, "unused-variable", ` def foo[T](): def bar(): x: T = 42 print(x) bar() foo() `, []string{}, scopeEverywhere) } func TestUnusedVariableFunctionParameterCommentForms(t *testing.T) { tests := []struct { name string input string want string commentOnParameter bool }{ { name: "preferred parameter comment", input: `def f( x, y, # @unused ): pass f() `, want: `def f( x, y # @unused ): pass f() `, commentOnParameter: true, }, { name: "legacy header comment", input: `def f( x, y, ): # @unused pass f() `, want: `def f( x, y): # @unused pass f() `, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { file, err := build.ParseBzl("test.bzl", []byte(tc.input)) if err != nil { t.Fatal(err) } formatted := build.Format(file) if string(formatted) != tc.want { t.Fatalf("formatted output:\n%s\nwant:\n%s", formatted, tc.want) } // The preferred and legacy forms intentionally have the same lint // semantics but remain distinguishable in the AST. This lets users // adopt the parameter-attached form without buildifier changing it // back to the legacy header-attached form. reparsed, err := build.ParseBzl("test.bzl", formatted) if err != nil { t.Fatal(err) } def := reparsed.Stmt[0].(*build.DefStmt) parameterComments := def.Params[1].Comment().Suffix headerComments := def.ColonPos.Comment().Suffix if tc.commentOnParameter { if len(parameterComments) != 1 || parameterComments[0].Token != "# @unused" { t.Errorf("parameter suffix comments = %v, want # @unused", parameterComments) } if len(headerComments) != 0 { t.Errorf("unexpected header suffix comments: %v", headerComments) } } else { if len(parameterComments) != 0 { t.Errorf("unexpected parameter suffix comments: %v", parameterComments) } if len(headerComments) != 1 || headerComments[0].Token != "# @unused" { t.Errorf("header suffix comments = %v, want # @unused", headerComments) } } // In both forms, @unused applies only to the final parameter y. checkFindings(t, "unused-variable", string(formatted), []string{ `:2: Variable "x" is unused.`, }, scopeEverywhere) }) } } func TestRedefinedVariable(t *testing.T) { checkFindings(t, "redefined-variable", ` x = "old_value" x = "new_value" x[1] = "new" cc_library(name = x)`, []string{":2: Variable \"x\" has already been defined."}, scopeEverywhere) checkFindings(t, "redefined-variable", ` x = "a" def foo(): x = "b" y = "c" y = "d" def bar(): x = "e" y = "f" y = "g"`, []string{}, scopeEverywhere) checkFindings(t, "redefined-variable", ` x = [1, 2, 3] y = [a for a in b] z = list() n = 43 x += something() y += something() z += something() n += something() x -= something()`, []string{ ":9: Variable \"n\" has already been defined.", ":10: Variable \"x\" has already been defined.", }, scopeEverywhere) checkFindings(t, "redefined-variable", ` x = [1, 2, 3] y = [a for a in b] z = list() a = something() b = something() c = something() d = something() e = something() a += x b += y c += z d += [42] e += foo`, []string{ ":15: Variable \"e\" has already been defined.", }, scopeEverywhere) checkFindings(t, "redefined-variable", ` type T = int T = 42`, []string{ ":2: Variable \"T\" has already been defined as a type.", }, scopeEverywhere) checkFindings(t, "redefined-variable", ` type T = int T += [42]`, []string{ ":2: Variable \"T\" has already been defined as a type.", }, scopeEverywhere) checkFindings(t, "redefined-variable", ` T = 42 type T = int`, []string{ ":2: Type \"T\" has already been defined as a variable.", }, scopeEverywhere) checkFindings(t, "redefined-variable", ` T: int type T = int`, []string{ ":2: Type \"T\" has already been defined as a variable.", }, scopeEverywhere) // Ok to assign a variable after declaring it. checkFindings(t, "redefined-variable", ` T: int T = 42`, []string{}, scopeEverywhere) } func TestWarnUnusedLoad(t *testing.T) { checkFindingsAndFix(t, "load", ` load(":f.bzl", "s1", "s2") load(":bar.bzl", "s1") foo(name = s1)`, ` load(":f.bzl", "s1") load(":bar.bzl", "s1") foo(name = s1)`, []string{ ":1: Loaded symbol \"s2\" is unused.", ":2: A different symbol \"s1\" has already been loaded on line 1.", }, scopeEverywhere) checkFindingsAndFix(t, "load", ` load("foo", "b", "a", "c") load("foo", "a", "d", "e") z = a + b + d`, ` load("foo", "a", "b") load("foo", "d") z = a + b + d`, []string{ ":1: Loaded symbol \"c\" is unused.", ":2: Symbol \"a\" has already been loaded on line 1.", ":2: Loaded symbol \"e\" is unused.", }, scopeEverywhere) checkFindingsAndFix(t, "load", ` load("foo", "a") a(1) load("bar", "a") a(2) load("bar", a = "a") a(3) load("bar", a = "b") a(4) load("foo", "a") a(5) load("foo", "a") a(6) load("foo", a = "a") a(7)`, ` load("foo", "a") a(1) load("bar", "a") a(2) a(3) load("bar", a = "b") a(4) load("foo", "a") a(5) a(6) a(7)`, []string{ ":3: A different symbol \"a\" has already been loaded on line 1.", ":5: Symbol \"a\" has already been loaded on line 3.", ":7: A different symbol \"a\" has already been loaded on line 5.", ":9: A different symbol \"a\" has already been loaded on line 7.", ":11: Symbol \"a\" has already been loaded on line 9.", ":13: Symbol \"a\" has already been loaded on line 11.", }, scopeEverywhere) checkFindingsAndFix(t, "load", ` load( ":f.bzl", "s1", "s2", # @unused (s2) ) # @unused - both s3 and s4 load( ":f.bzl", "s3", "s4", )`, ` load( ":f.bzl", "s2", # @unused (s2) ) # @unused - both s3 and s4 load( ":f.bzl", "s3", "s4", )`, []string{":3: Loaded symbol \"s1\" is unused."}, scopeEverywhere) checkFindingsAndFix(t, "load", ` load(":f.bzl", "x") x = "unused"`, ` x = "unused"`, []string{":1: Loaded symbol \"x\" is unused."}, scopeEverywhere) checkFindings(t, "load", ` load( ":f.bzl", "s1", ) def test(x: s1): pass `, []string{}, scopeEverywhere) checkFindings(t, "load", ` load( ":f.bzl", "s1", "s2", ) def test(x: s1) -> List[s2]: pass `, []string{}, scopeEverywhere) checkFindingsAndFix(t, "load", ` load( ":f.bzl", "s1", "s2", ) load( ":s.bzl", "s3", ) def test(x: s1) -> List[s2]: pass `, ` load( ":f.bzl", "s1", "s2", ) def test(x: s1) -> List[s2]: pass `, []string{ ":9: Loaded symbol \"s3\" is unused.", }, scopeEverywhere) checkFindingsAndFix(t, "load", ` load(":f.bzl", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8") s1: s2 s3: s4 = s5 type s6[s7] = s8 `, ` load(":f.bzl", "s2", "s4", "s5", "s8") s1: s2 s3: s4 = s5 type s6[s7] = s8 `, []string{ "1: Loaded symbol \"s1\" is unused.", "1: Loaded symbol \"s3\" is unused.", "1: Loaded symbol \"s6\" is unused.", "1: Loaded symbol \"s7\" is unused.", }, scopeEverywhere) } func TestUninitializedVariable(t *testing.T) { checkFindings(t, "uninitialized", ` def foo(x): if bar: x = 1 y = 2 bar = True print(x + y) `, []string{ ":2: Variable \"bar\" may not have been initialized.", ":7: Variable \"y\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x): for t in s: x = 1 y = 2 print(x + y) `, []string{ ":6: Variable \"y\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): if bar: x = 1 y = 2 else: if foobar: x = 3 y = 4 else: x = 5 print(x + y) `, []string{ ":12: Variable \"y\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x): if bar: t = 1 else: for t in maybe_empty: pass print(t) `, []string{ ":8: Variable \"t\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x): if bar: t = 1 else: for y in maybe_empty: return print(t) `, []string{ ":8: Variable \"t\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x): if bar: for t in [2, 3]: pass print(t) `, []string{ ":6: Variable \"t\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x): print(t) # maybe global or loaded `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x): if bar: y = 1 print(y) x, y = y, x print(y) `, []string{ ":5: Variable \"y\" may not have been initialized.", ":6: Variable \"y\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): if a: x = 1 y = 1 z = 1 elif b: x = 2 z = 2 t = 2 else: x = 3 y = 3 t = 3 print(x + y + z + t) `, []string{ ":15: Variable \"y\" may not have been initialized.", ":15: Variable \"z\" may not have been initialized.", ":15: Variable \"t\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(y): if y < 0: x = -1 elif y > 0: x = 1 else: fail() print(x) `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(y): if y < 0: x = -1 elif y > 0: x = 1 else: if z: fail("z") else: fail("not z") print(x) `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(y): if y < 0: return elif y > 0: x = 1 else: return x # not initialized print(x) `, []string{ ":7: Variable \"x\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(y): if y < 0: return elif y > 0: x = 1 else: pass print(x) # not initialized `, []string{ ":9: Variable \"x\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): for x in y: print(x) print(x.attr) print(x) print(x.attr) `, []string{ ":6: Variable \"x\" may not have been initialized.", ":7: Variable \"x\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): for x in y: a = x print(a) print(a) `, []string{ ":6: Variable \"a\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(): if foo: x = foo f(x = y) `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(): if foo: x = foo f(x + y) `, []string{ ":5: Variable \"x\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(): if foo: x = foo x, y = 1, x `, []string{ ":5: Variable \"x\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(): if foo: x = foo x, y = 1, 2 `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(y): return [x for x in y if x] x = 1 `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(): if foo: f(x = foo) else: x = 3 print(x) `, []string{ ":7: Variable \"x\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x): for y in x: if foo: break elif bar: continue elif baz: return else: z = 3 print(z) `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x: int, y: int = 2): if bar: x = 1 y = 2 z = 3 print(x + y + z) `, []string{ ":7: Variable \"z\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x: int, y: int = 2): def bar(y=x): if baz: x = 1 y = 2 z = 3 print(x + y + z) if something: x = bar() return x `, []string{ ":8: Variable \"x\" may not have been initialized.", ":8: Variable \"z\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(x: int, y: int = 2): if bar: y, z, t = 2, 3, 4 w, s = 5, 6 r = 7 [t for t in range(5)] [a for a in range(z + y)] {b: c + s for b, c in [ d * 2 for d in range(t) if d != baz(r=w) ]} `, []string{ ":8: Variable \"z\" may not have been initialized.", ":9: Variable \"s\" may not have been initialized.", ":10: Variable \"t\" may not have been initialized.", ":11: Variable \"w\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): x = 1 for y, z in t: print(y, z) def bar(x, y, s = z): pass `, []string{ ":6: Variable \"z\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): for bar in baz: pass y = [baz.get(bar) for bar in bars] x = lambda bar: baz.get(bar) `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): def bar(x): print(x) for x, y in z: bar(x) `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def foo(): [x, y] = [1, 2] x = 3 print(x) `, []string{}, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(): x: int return x `, []string{ ":3: Variable \"x\" may not have been initialized.", }, scopeEverywhere) checkFindings(t, "uninitialized", ` def f(): x: int x = 1 return x `, []string{}, scopeEverywhere) } buildtools-10.1.0/warn/warn_bazel_api_test.go0000664000175000017500000010270115254573013020657 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "fmt" "testing" "github.com/bazel-contrib/buildtools/v10/tables" ) func TestAttrDataConfigurationWarning(t *testing.T) { checkFindingsAndFix(t, "attr-cfg", ` rule( attrs = { "foo": attr.label_list(mandatory = True, cfg = "data"), } ) attr.label_list(mandatory = True, cfg = "exec")`, ` rule( attrs = { "foo": attr.label_list(mandatory = True), } ) attr.label_list(mandatory = True, cfg = "exec")`, []string{`:3: cfg = "data" for attr definitions has no effect and should be removed.`}, scopeBzl) } func TestAttrHostConfigurationWarning(t *testing.T) { checkFindingsAndFix(t, "attr-cfg", ` rule( attrs = { "foo": attr.label_list(mandatory = True, cfg = "host"), } ) attr.label_list(mandatory = True, cfg = "exec")`, ` rule( attrs = { "foo": attr.label_list(mandatory = True, cfg = "exec"), } ) attr.label_list(mandatory = True, cfg = "exec")`, []string{`:3: cfg = "host" for attr definitions should be replaced by cfg = "exec".`}, scopeBzl) } func TestDepsetItemsWarning(t *testing.T) { checkFindings(t, "depset-items", ` def f(): depset(items=foo) a = depset() depset(a) `, []string{ `:2: Parameter "items" is deprecated, use "direct" and/or "transitive" instead.`, `:4: Giving a depset as first unnamed parameter to depset() is deprecated, use the "transitive" parameter instead.`, }, scopeEverywhere) } func TestAttrNonEmptyWarning(t *testing.T) { checkFindingsAndFix(t, "attr-non-empty", ` rule( attrs = { "foo": attr.label_list(mandatory = True, non_empty = True), "bar": attr.label_list(mandatory = True, non_empty = False), "baz": attr.label_list(mandatory = True, non_empty = foo.bar()), "qux": attr.label_list(mandatory = True, non_empty = not foo.bar()), "aaa": attr.label_list(mandatory = True, non_empty = (foo.bar())), "bbb": attr.label_list(mandatory = True, non_empty = ( not foo.bar() )), } )`, ` rule( attrs = { "foo": attr.label_list(mandatory = True, allow_empty = False), "bar": attr.label_list(mandatory = True, allow_empty = True), "baz": attr.label_list(mandatory = True, allow_empty = not foo.bar()), "qux": attr.label_list(mandatory = True, allow_empty = foo.bar()), "aaa": attr.label_list(mandatory = True, allow_empty = (not foo.bar())), "bbb": attr.label_list(mandatory = True, allow_empty = ( foo.bar() )), } )`, []string{ ":3: non_empty attributes for attr definitions are deprecated in favor of allow_empty.", ":4: non_empty attributes for attr definitions are deprecated in favor of allow_empty.", ":5: non_empty attributes for attr definitions are deprecated in favor of allow_empty.", ":6: non_empty attributes for attr definitions are deprecated in favor of allow_empty.", ":7: non_empty attributes for attr definitions are deprecated in favor of allow_empty.", ":8: non_empty attributes for attr definitions are deprecated in favor of allow_empty.", }, scopeBzl) } func TestAttrSingleFileWarning(t *testing.T) { checkFindingsAndFix(t, "attr-single-file", ` rule( attrs = { "foo": attr.label_list(single_file = True, allow_files = [".cc"], mandatory = True), "bar": attr.label_list(single_file = True, mandatory = True), "baz": attr.label_list(single_file = False, mandatory = True), } )`, ` rule( attrs = { "foo": attr.label_list(allow_single_file = [".cc"], mandatory = True), "bar": attr.label_list(allow_single_file = True, mandatory = True), "baz": attr.label_list(mandatory = True), } )`, []string{ ":3: single_file is deprecated in favor of allow_single_file.", ":4: single_file is deprecated in favor of allow_single_file.", ":5: single_file is deprecated in favor of allow_single_file.", }, scopeBzl) } func TestCtxActionsWarning(t *testing.T) { checkFindingsAndFix(t, "ctx-actions", ` def impl(ctx): ctx.new_file(foo) ctx.new_file(foo, "foo %s " % bar) ctx.new_file(foo, name = bar) ctx.new_file(foo, bar, baz) ctx.experimental_new_directory(foo, bar) ctx.file_action(foo, bar) ctx.file_action(foo, bar, executable = True) ctx.action(foo, bar, command = "foo") ctx.action(foo, bar, executable = "bar") ctx.empty_action(foo, bar) ctx.template_action(foo, bar) ctx.template_action(foo, bar, executable = True) ctx.foobar(foo, bar) `, ` def impl(ctx): ctx.actions.declare_file(foo) ctx.actions.declare_file("foo %s " % bar, sibling = foo) ctx.actions.declare_file(bar, sibling = foo) ctx.new_file(foo, bar, baz) ctx.actions.declare_directory(foo, bar) ctx.actions.write(foo, bar) ctx.actions.write(foo, bar, is_executable = True) ctx.actions.run_shell(foo, bar, command = "foo") ctx.actions.run(foo, bar, executable = "bar") ctx.actions.do_nothing(foo, bar) ctx.actions.expand_template(foo, bar) ctx.actions.expand_template(foo, bar, is_executable = True) ctx.foobar(foo, bar) `, []string{ `:2: "ctx.new_file" is deprecated in favor of "ctx.actions.declare_file".`, `:3: "ctx.new_file" is deprecated in favor of "ctx.actions.declare_file".`, `:4: "ctx.new_file" is deprecated in favor of "ctx.actions.declare_file".`, `:5: "ctx.new_file" is deprecated in favor of "ctx.actions.declare_file".`, `:6: "ctx.experimental_new_directory" is deprecated in favor of "ctx.actions.declare_directory".`, `:7: "ctx.file_action" is deprecated in favor of "ctx.actions.write".`, `:8: "ctx.file_action" is deprecated in favor of "ctx.actions.write".`, `:9: "ctx.action" is deprecated in favor of "ctx.actions.run_shell".`, `:10: "ctx.action" is deprecated in favor of "ctx.actions.run".`, `:11: "ctx.empty_action" is deprecated in favor of "ctx.actions.do_nothing".`, `:12: "ctx.template_action" is deprecated in favor of "ctx.actions.expand_template".`, `:13: "ctx.template_action" is deprecated in favor of "ctx.actions.expand_template".`, }, scopeBzl) checkFindings(t, "ctx-actions", ` def impl(ctx): ctx.new_file(foo, bar, baz) `, []string{ `:2: "ctx.new_file" is deprecated in favor of "ctx.actions.declare_file".`, }, scopeBzl) } func TestPackageNameWarning(t *testing.T) { checkFindingsAndFix(t, "package-name", ` foo(a = PACKAGE_NAME) def f(PACKAGE_NAME): foo(a = PACKAGE_NAME) def g(): foo(a = PACKAGE_NAME) def h(x: PACKAGE_NAME) -> PACKAGE_NAME: return PACKAGE_NAME type MyType = PACKAGE_NAME cast(PACKAGE_NAME, PACKAGE_NAME) isinstance(PACKAGE_NAME, PACKAGE_NAME) `, ` foo(a = native.package_name()) def f(PACKAGE_NAME): foo(a = PACKAGE_NAME) def g(): foo(a = native.package_name()) def h(x: PACKAGE_NAME) -> PACKAGE_NAME: return native.package_name() type MyType = PACKAGE_NAME cast(PACKAGE_NAME, native.package_name()) isinstance(native.package_name(), PACKAGE_NAME) `, []string{ `:1: Global variable "PACKAGE_NAME" is deprecated in favor of "native.package_name()". Please rename it.`, `:7: Global variable "PACKAGE_NAME" is deprecated in favor of "native.package_name()". Please rename it.`, `:10: Global variable "PACKAGE_NAME" is deprecated in favor of "native.package_name()". Please rename it.`, `:14: Global variable "PACKAGE_NAME" is deprecated in favor of "native.package_name()". Please rename it.`, `:16: Global variable "PACKAGE_NAME" is deprecated in favor of "native.package_name()". Please rename it.`, }, scopeBzl) checkFindings(t, "package-name", ` PACKAGE_NAME = "foo" foo(a = PACKAGE_NAME) `, []string{}, scopeBzl) checkFindings(t, "package-name", ` load(":foo.bzl", "PACKAGE_NAME") foo(a = PACKAGE_NAME) `, []string{}, scopeBzl) } func TestRepositoryNameWarning(t *testing.T) { checkFindingsAndFix(t, "repository-name", ` foo(a = REPOSITORY_NAME) def f(REPOSITORY_NAME): foo(a = REPOSITORY_NAME) def g(): foo(a = REPOSITORY_NAME) `, ` foo(a = native.repository_name()) def f(REPOSITORY_NAME): foo(a = REPOSITORY_NAME) def g(): foo(a = native.repository_name()) `, []string{ `:1: Global variable "REPOSITORY_NAME" is deprecated in favor of "native.repository_name()". Please rename it.`, `:7: Global variable "REPOSITORY_NAME" is deprecated in favor of "native.repository_name()". Please rename it.`, }, scopeBzl) checkFindings(t, "repository-name", ` REPOSITORY_NAME = "foo" foo(a = REPOSITORY_NAME) `, []string{}, scopeBzl) checkFindings(t, "repository-name", ` load(":foo.bzl", "REPOSITORY_NAME") foo(a = REPOSITORY_NAME) `, []string{}, scopeBzl) } func TestFileTypeNameWarning(t *testing.T) { checkFindings(t, "filetype", ` rule1(types=FileType([".cc", ".h"])) rule2(types=FileType(types=[".cc", ".h"])) FileType(foobar) def macro1(): a = FileType([".py"]) def macro2(): FileType = foo b = FileType([".java"]) `, []string{ ":1: The FileType function is deprecated.", ":2: The FileType function is deprecated.", ":4: The FileType function is deprecated.", ":7: The FileType function is deprecated.", }, scopeBzl) checkFindings(t, "filetype", ` FileType = foo rule1(types=FileType([".cc", ".h"])) rule2(types=FileType(types=[".cc", ".h"])) `, []string{}, scopeBzl) } func TestOutputGroupWarning(t *testing.T) { checkFindingsAndFix(t, "output-group", ` def _impl(ctx): bin = ctx.attr.my_dep.output_group.bin `, ` def _impl(ctx): bin = ctx.attr.my_dep[OutputGroupInfo].bin `, []string{ `:2: "ctx.attr.dep.output_group" is deprecated in favor of "ctx.attr.dep[OutputGroupInfo]".`, }, scopeBzl) } func TestNativeGitRepositoryWarning(t *testing.T) { defer setUpFileReader(nil)() checkFindingsAndFix(t, "git-repository", ` """My file""" def macro(): git_repository(foo, bar) `, ` """My file""" load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") def macro(): git_repository(foo, bar) `, []string{ `:4: Function "git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`, }, scopeBzl) checkFindingsAndFix(t, "git-repository", ` """My file""" def macro(): git_repository(foo, bar) new_git_repository(foo, bar) `, ` """My file""" load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository") def macro(): git_repository(foo, bar) new_git_repository(foo, bar) `, []string{ `:4: Function "git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`, `:5: Function "new_git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`, }, scopeBzl) checkFindingsAndFix(t, "git-repository", ` """My file""" load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") def macro(): git_repository(foo, bar) new_git_repository(foo, bar) `, ` """My file""" load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository") def macro(): git_repository(foo, bar) new_git_repository(foo, bar) `, []string{ `:7: Function "new_git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`, }, scopeBzl) } func TestNativeHttpArchiveWarning(t *testing.T) { defer setUpFileReader(nil)() checkFindingsAndFix(t, "http-archive", ` """My file""" def macro(): http_archive(foo, bar) `, ` """My file""" load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") def macro(): http_archive(foo, bar) `, []string{ `:4: Function "http_archive" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:http.bzl".`, }, scopeBzl) } func TestContextArgsAPIWarning(t *testing.T) { checkFindingsAndFix(t, "ctx-args", ` def impl(ctx): args = ctx.actions.args() args.add(foo, bar) args.add(foo, bar, before_each = aaa) args.add(foo, bar, join_with = bbb) args.add(foo, bar, before_each = ccc, join_with = ddd) args.add(foo, bar, map_fn = eee) args.add(foo, bar, map_fn = fff, before_each = ggg) args.add(foo, bar, map_fn = hhh, join_with = iii) args.add(foo, bar, map_fn = jjj, before_each = kkk, join_with = lll) `, ` def impl(ctx): args = ctx.actions.args() args.add(foo, bar) args.add_all(foo, bar, before_each = aaa) args.add_joined(foo, bar, join_with = bbb) args.add_joined(foo, bar, format_each = ccc + "%s", join_with = ddd) args.add_all(foo, bar, map_each = eee) args.add_all(foo, bar, map_each = fff, before_each = ggg) args.add_joined(foo, bar, map_each = hhh, join_with = iii) args.add_joined(foo, bar, map_each = jjj, format_each = kkk + "%s", join_with = lll) `, []string{ `:4: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, `:5: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, `:6: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, `:7: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, `:8: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, `:9: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, `:10: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, }, scopeBzl) } func TestAttrOutputDefault(t *testing.T) { checkFindings(t, "attr-output-default", ` rule( attrs = { "foo": attr.output(default="foo"), "bar": attr.output(not_default="foo"), "baz": attr.string(default="foo"), } ) `, []string{ `:3: The "default" parameter for attr.output() is deprecated.`, }, scopeBzl) } func TestAttrLicense(t *testing.T) { checkFindings(t, "attr-license", ` rule( attrs = { "foo": attr.license(foo), "bar": attr.license(), "baz": attr.no_license(), } ) `, []string{ `:3: "attr.license()" is deprecated and shouldn't be used.`, `:4: "attr.license()" is deprecated and shouldn't be used.`, }, scopeBzl) } func TestRuleImplReturn(t *testing.T) { checkFindings(t, "rule-impl-return", ` def _impl(ctx): return struct() rule(implementation=_impl) `, []string{ `:2: Avoid using the legacy provider syntax.`, }, scopeBzl) checkFindings(t, "rule-impl-return", ` def _impl(ctx): if True: return struct() return x = rule(_impl, attrs = {}) `, []string{ `:3: Avoid using the legacy provider syntax.`, }, scopeBzl) checkFindings(t, "rule-impl-return", ` def _impl(ctx): pass # no return statements x = rule(_impl, attrs = {}) `, []string{}, scopeBzl) checkFindings(t, "rule-impl-return", ` def _impl1(): # not used as a rule implementation function return struct() def _impl2(): # no structs returned if x: return [] elif y: return foo() return x = rule( implementation=_impl2, ) rule( _impl3, # not defined here ) rule( _impl1(), # not an identifier ) rule() # no parameters rule(foo = bar) # no matching parameters `, []string{}, scopeBzl) } func TestNativeAndroidWarning(t *testing.T) { defer setUpFileReader(nil)() checkFindingsAndFix(t, "native-android", ` """My file""" def macro(): aar_import() android_library() native.android_library() native.android_local_test() android_binary() `, fmt.Sprintf(` """My file""" load(%q, "aar_import", "android_binary", "android_library", "android_local_test") def macro(): aar_import() android_library() android_library() android_local_test() android_binary() `, tables.AndroidLoadPath), []string{ fmt.Sprintf(`:4: Function "aar_import" is not global anymore and needs to be loaded from "%s".`, tables.AndroidLoadPath), fmt.Sprintf(`:5: Function "android_library" is not global anymore and needs to be loaded from "%s".`, tables.AndroidLoadPath), fmt.Sprintf(`:6: Function "android_library" is not global anymore and needs to be loaded from "%s".`, tables.AndroidLoadPath), fmt.Sprintf(`:7: Function "android_local_test" is not global anymore and needs to be loaded from "%s".`, tables.AndroidLoadPath), fmt.Sprintf(`:9: Function "android_binary" is not global anymore and needs to be loaded from "%s".`, tables.AndroidLoadPath), }, scopeBzl|scopeBuild) } func TestNativeCcWarning(t *testing.T) { defer setUpFileReader(nil)() expectedLoadPrefix := "@rules_cc//cc" checkFindingsAndFix(t, "native-cc-binary,native-cc-import,native-cc-library,native-cc-test,native-cc-fdo-prefetch-hints,native-cc-objc-import,native-cc-objc-library,native-cc-toolchain,native-cc-toolchain-suite,native-cc-fdo-profile", ` """My file""" def macro(): cc_library() native.cc_binary() cc_test() native.fdo_prefetch_hints() native.objc_library() objc_import() cc_toolchain() native.cc_toolchain_suite() fdo_profile() cc_import() `, fmt.Sprintf(` """My file""" load("%[1]s:cc_binary.bzl", "cc_binary") load("%[1]s:cc_import.bzl", "cc_import") load("%[1]s:cc_library.bzl", "cc_library") load("%[1]s:cc_test.bzl", "cc_test") load("%[1]s:objc_import.bzl", "objc_import") load("%[1]s:objc_library.bzl", "objc_library") load("%[1]s/toolchains:cc_toolchain.bzl", "cc_toolchain") load("%[1]s/toolchains:cc_toolchain_suite.bzl", "cc_toolchain_suite") load("%[1]s/toolchains:fdo_prefetch_hints.bzl", "fdo_prefetch_hints") load("%[1]s/toolchains:fdo_profile.bzl", "fdo_profile") def macro(): cc_library() cc_binary() cc_test() fdo_prefetch_hints() objc_library() objc_import() cc_toolchain() cc_toolchain_suite() fdo_profile() cc_import() `, expectedLoadPrefix), []string{ fmt.Sprintf(`:4: Function "cc_library" is not global anymore and needs to be loaded from "%s:cc_library.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:5: Function "cc_binary" is not global anymore and needs to be loaded from "%s:cc_binary.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:6: Function "cc_test" is not global anymore and needs to be loaded from "%s:cc_test.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:7: Function "fdo_prefetch_hints" is not global anymore and needs to be loaded from "%s/toolchains:fdo_prefetch_hints.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:8: Function "objc_library" is not global anymore and needs to be loaded from "%s:objc_library.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:9: Function "objc_import" is not global anymore and needs to be loaded from "%s:objc_import.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:10: Function "cc_toolchain" is not global anymore and needs to be loaded from "%s/toolchains:cc_toolchain.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:11: Function "cc_toolchain_suite" is not global anymore and needs to be loaded from "%s/toolchains:cc_toolchain_suite.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:13: Function "fdo_profile" is not global anymore and needs to be loaded from "%s/toolchains:fdo_profile.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:14: Function "cc_import" is not global anymore and needs to be loaded from "%s:cc_import.bzl".`, expectedLoadPrefix), }, scopeBzl|scopeBuild) } func TestNativeJavaWarning(t *testing.T) { defer setUpFileReader(map[string]string{ "MODULE.bazel": ` include("//my/pkg:java.MODULE.bazel") `, "my/pkg/java.MODULE.bazel": ` bazel_dep(name = "rules_java", version = "1.2.3", repo_name = "my_rules_java") `, })() expectedLoadPrefix := "@my_rules_java//java" checkFindingsAndFix(t, "native-java-binary,native-java-import,native-java-library,native-java-plugin,native-java-test,native-java-package-config,native-java-runtime,native-java-toolchain,native-java-common,native-java-info,native-java-plugin-info", ` """My file""" def macro(): java_import() java_library() native.java_library() native.java_binary() native.java_plugin() native.java_package_configuration() native.java_runtime() native.java_toolchain() JavaInfo JavaPluginInfo java_common java_test() `, fmt.Sprintf(` """My file""" load("%[1]s:java_binary.bzl", "java_binary") load("%[1]s:java_import.bzl", "java_import") load("%[1]s:java_library.bzl", "java_library") load("%[1]s:java_plugin.bzl", "java_plugin") load("%[1]s:java_test.bzl", "java_test") load("%[1]s/common:java_common.bzl", "java_common") load("%[1]s/common:java_info.bzl", "JavaInfo") load("%[1]s/common:java_plugin_info.bzl", "JavaPluginInfo") load("%[1]s/toolchains:java_package_configuration.bzl", "java_package_configuration") load("%[1]s/toolchains:java_runtime.bzl", "java_runtime") load("%[1]s/toolchains:java_toolchain.bzl", "java_toolchain") def macro(): java_import() java_library() java_library() java_binary() java_plugin() java_package_configuration() java_runtime() java_toolchain() JavaInfo JavaPluginInfo java_common java_test() `, expectedLoadPrefix), []string{ fmt.Sprintf(`:4: Function "java_import" is not global anymore and needs to be loaded from "%s:java_import.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:5: Function "java_library" is not global anymore and needs to be loaded from "%s:java_library.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:6: Function "java_library" is not global anymore and needs to be loaded from "%s:java_library.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:7: Function "java_binary" is not global anymore and needs to be loaded from "%s:java_binary.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:8: Function "java_plugin" is not global anymore and needs to be loaded from "%s:java_plugin.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:9: Function "java_package_configuration" is not global anymore and needs to be loaded from "%s/toolchains:java_package_configuration.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:10: Function "java_runtime" is not global anymore and needs to be loaded from "%s/toolchains:java_runtime.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:11: Function "java_toolchain" is not global anymore and needs to be loaded from "%s/toolchains:java_toolchain.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:13: Symbol "JavaInfo" is not global anymore and needs to be loaded from "%s/common:java_info.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:14: Symbol "JavaPluginInfo" is not global anymore and needs to be loaded from "%s/common:java_plugin_info.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:15: Symbol "java_common" is not global anymore and needs to be loaded from "%s/common:java_common.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:17: Function "java_test" is not global anymore and needs to be loaded from "%s:java_test.bzl".`, expectedLoadPrefix), }, scopeBzl|scopeBuild) } func TestNativePyWarning(t *testing.T) { defer setUpFileReader(map[string]string{ "MODULE.bazel": ` bazel_dep(name = "rules_python", repo_name = "my_rules_python") `, })() expectedLoadPath := "@my_rules_python//python:defs.bzl" checkFindingsAndFix(t, "native-py", ` """My file""" def macro(): py_library() py_binary() native.py_test() native.py_runtime() py_test() `, fmt.Sprintf(` """My file""" load(%q, "py_binary", "py_library", "py_runtime", "py_test") def macro(): py_library() py_binary() py_test() py_runtime() py_test() `, expectedLoadPath), []string{ fmt.Sprintf(`:4: Function "py_library" is not global anymore and needs to be loaded from "%s".`, expectedLoadPath), fmt.Sprintf(`:5: Function "py_binary" is not global anymore and needs to be loaded from "%s".`, expectedLoadPath), fmt.Sprintf(`:6: Function "py_test" is not global anymore and needs to be loaded from "%s".`, expectedLoadPath), fmt.Sprintf(`:7: Function "py_runtime" is not global anymore and needs to be loaded from "%s".`, expectedLoadPath), fmt.Sprintf(`:9: Function "py_test" is not global anymore and needs to be loaded from "%s".`, expectedLoadPath), }, scopeBzl|scopeBuild) } func TestNativeProtoWarning(t *testing.T) { // No MODULE.bazel file, so loads should use the legacy protobuf repo name. defer setUpFileReader(nil)() expectedLoadPrefix := "@com_google_protobuf//bazel" checkFindingsAndFix(t, "native-proto,native-proto-lang-toolchain,native-proto-info,native-proto-common", ` """My file""" def macro(): proto_library() proto_lang_toolchain() native.proto_lang_toolchain() native.proto_library() ProtoInfo proto_common `, fmt.Sprintf(` """My file""" load("%[1]s:proto_library.bzl", "proto_library") load("%[1]s/common:proto_common.bzl", "proto_common") load("%[1]s/common:proto_info.bzl", "ProtoInfo") load("%[1]s/toolchains:proto_lang_toolchain.bzl", "proto_lang_toolchain") def macro(): proto_library() proto_lang_toolchain() proto_lang_toolchain() proto_library() ProtoInfo proto_common `, expectedLoadPrefix), []string{ fmt.Sprintf(`:4: Function "proto_library" is not global anymore and needs to be loaded from "%s:proto_library.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:5: Function "proto_lang_toolchain" is not global anymore and needs to be loaded from "%s/toolchains:proto_lang_toolchain.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:6: Function "proto_lang_toolchain" is not global anymore and needs to be loaded from "%s/toolchains:proto_lang_toolchain.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:7: Function "proto_library" is not global anymore and needs to be loaded from "%s:proto_library.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:9: Symbol "ProtoInfo" is not global anymore and needs to be loaded from "%s/common:proto_info.bzl".`, expectedLoadPrefix), fmt.Sprintf(`:10: Symbol "proto_common" is not global anymore and needs to be loaded from "%s/common:proto_common.bzl".`, expectedLoadPrefix), }, scopeBzl|scopeBuild) } func TestNativeShBinaryWarning(t *testing.T) { defer setUpFileReader(nil)() checkFindingsAndFix(t, "native-sh-binary", ` """My file""" def macro(): native.sh_binary() sh_binary() `, ` """My file""" load("@rules_shell//shell:sh_binary.bzl", "sh_binary") def macro(): sh_binary() sh_binary() `, []string{ fmt.Sprintf(`:4: Function "sh_binary" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_binary.bzl".`), fmt.Sprintf(`:6: Function "sh_binary" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_binary.bzl".`), }, scopeBzl|scopeBuild) } func TestNativeShLibraryWarning(t *testing.T) { defer setUpFileReader(nil)() checkFindingsAndFix(t, "native-sh-library", ` """My file""" def macro(): native.sh_library() sh_library() `, ` """My file""" load("@rules_shell//shell:sh_library.bzl", "sh_library") def macro(): sh_library() sh_library() `, []string{ fmt.Sprintf(`:4: Function "sh_library" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_library.bzl".`), fmt.Sprintf(`:6: Function "sh_library" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_library.bzl".`), }, scopeBzl|scopeBuild) } func TestNativeShTestWarning(t *testing.T) { defer setUpFileReader(nil)() checkFindingsAndFix(t, "native-sh-test", ` """My file""" def macro(): native.sh_test() sh_test() `, ` """My file""" load("@rules_shell//shell:sh_test.bzl", "sh_test") def macro(): sh_test() sh_test() `, []string{ fmt.Sprintf(`:4: Function "sh_test" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_test.bzl".`), fmt.Sprintf(`:6: Function "sh_test" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_test.bzl".`), }, scopeBzl|scopeBuild) } func TestNativeWarningLoadPlacedAfterFileDocstringWithComment(t *testing.T) { defer setUpFileReader(nil)() checkFindingsAndFix(t, "native-sh-binary,native-java-binary", ` # Copyright 2020 Google LLC # # 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 # # https://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. """My file""" def macro(): native.sh_binary() native.java_binary() java_binary() sh_binary() `, ` # Copyright 2020 Google LLC # # 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 # # https://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. """My file""" load("@rules_java//java:java_binary.bzl", "java_binary") load("@rules_shell//shell:sh_binary.bzl", "sh_binary") def macro(): sh_binary() java_binary() java_binary() sh_binary() `, []string{ fmt.Sprintf(`:18: Function "sh_binary" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_binary.bzl".`), fmt.Sprintf(`:19: Function "java_binary" is not global anymore and needs to be loaded from "@rules_java//java:java_binary.bzl".`), fmt.Sprintf(`:21: Function "java_binary" is not global anymore and needs to be loaded from "@rules_java//java:java_binary.bzl".`), fmt.Sprintf(`:22: Function "sh_binary" is not global anymore and needs to be loaded from "@rules_shell//shell:sh_binary.bzl".`), }, scopeBzl|scopeBuild) } func TestKeywordParameters(t *testing.T) { checkFindingsAndFix(t, "keyword-positional-params", ` foo(key = value) all(elements = [True, False]) any(elements = [True, False]) tuple(x = [1, 2, 3]) list(x = [1, 2, 3]) len(x = [1, 2, 3]) str(x = foo) repr(x = foo) bool(x = 3) int(x = "3") int(x = "13", base = 8) dir(x = foo) type(x = foo) select(x = {}) `, ` foo(key = value) all([True, False]) any([True, False]) tuple([1, 2, 3]) list([1, 2, 3]) len([1, 2, 3]) str(foo) repr(foo) bool(3) int("3") int("13", base = 8) dir(foo) type(foo) select({}) `, []string{ `:2: Keyword parameter "elements" for "all" should be positional.`, `:3: Keyword parameter "elements" for "any" should be positional.`, `:4: Keyword parameter "x" for "tuple" should be positional.`, `:5: Keyword parameter "x" for "list" should be positional.`, `:6: Keyword parameter "x" for "len" should be positional.`, `:7: Keyword parameter "x" for "str" should be positional.`, `:8: Keyword parameter "x" for "repr" should be positional.`, `:9: Keyword parameter "x" for "bool" should be positional.`, `:10: Keyword parameter "x" for "int" should be positional.`, `:11: Keyword parameter "x" for "int" should be positional.`, `:12: Keyword parameter "x" for "dir" should be positional.`, `:13: Keyword parameter "x" for "type" should be positional.`, `:14: Keyword parameter "x" for "select" should be positional.`, }, scopeEverywhere) checkFindingsAndFix(t, "keyword-positional-params", ` hasattr( x = foo, name = "bar", ) getattr( x = foo, name = "bar", ) getattr( x = foo, name = "bar", default = "baz", ) `, ` hasattr( foo, "bar", ) getattr( foo, "bar", ) getattr( foo, "bar", "baz", ) `, []string{ `:2: Keyword parameter "x" for "hasattr" should be positional.`, `:3: Keyword parameter "name" for "hasattr" should be positional.`, `:6: Keyword parameter "x" for "getattr" should be positional.`, `:7: Keyword parameter "name" for "getattr" should be positional.`, `:10: Keyword parameter "x" for "getattr" should be positional.`, `:11: Keyword parameter "name" for "getattr" should be positional.`, `:12: Keyword parameter "default" for "getattr" should be positional.`, }, scopeEverywhere) } func TestProvider(t *testing.T) { checkFindings(t, "provider-params", `provider(doc = "doc", fields = [])`, []string{}, scopeBzl) checkFindings(t, "provider-params", `provider("doc", fields = [])`, []string{}, scopeBzl) checkFindings(t, "provider-params", `provider(fields = None, doc = "doc")`, []string{}, scopeBzl) checkFindings(t, "provider-params", `provider(fields = [])`, []string{`1: Calls to 'provider' should provide a documentation`}, scopeBzl) checkFindings(t, "provider-params", `provider(doc = "doc")`, []string{`1: Calls to 'provider' should provide a list of fields:`}, scopeBzl) checkFindings(t, "provider-params", `p = provider()`, []string{`1: Calls to 'provider' should provide a list of fields and a documentation:`}, scopeBzl) } func TestAttributeNameWarning(t *testing.T) { checkFindings(t, "attr-licenses", ` def _impl(ctx): pass foo = rule( implementation = _impl, attrs = { "license": attr.string(), "licenses": attr.string(), }, ) `, []string{ ":6: Do not use 'licenses' as an attribute name. It may cause unexpected behavior.", }, scopeBzl) checkFindings(t, "attr-applicable_licenses", ` def _impl(ctx): pass foo = rule( implementation = _impl, attrs = { "applicable_licenses": attr.string(), "package_metadata": attr.string(), }, ) `, []string{ ":6: Do not use 'applicable_licenses' as an attribute name. It may cause unexpected behavior.", ":6: Do not use 'package_metadata' as an attribute name. It may cause unexpected behavior.", }, scopeBzl) } buildtools-10.1.0/warn/multifile.go0000664000175000017500000000404715254573013016641 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "github.com/bazel-contrib/buildtools/v10/build" ) // FileReader is a class that can read an arbitrary Starlark file // from the repository and cache the results. type FileReader struct { cache map[string]*build.File readFile func(string) ([]byte, error) } // NewFileReader creates and initializes a FileReader instance with a // custom readFile function that can read an arbitrary file in the // repository using a path relative to the workspace root // (OS-independent, with forward slashes). func NewFileReader(readFile func(string) ([]byte, error)) *FileReader { return &FileReader{ cache: make(map[string]*build.File), readFile: readFile, } } // retrieveFile reads a Starlark file using only the readFile method // (without using the cache). func (fr *FileReader) retrieveFile(filename string) *build.File { contents, err := fr.readFile(filename) if err != nil { return nil } file, err := build.Parse(filename, contents) if err != nil { return nil } file.Path = filename return file } // GetFile reads a Starlark file from the repository or the cache. // Returns nil if the file is not found or not valid. func (fr *FileReader) GetFile(pkg, label string) *build.File { filename := label if pkg != "" { filename = pkg + "/" + label } // Try to retrieve from the cache if file, ok := fr.cache[filename]; ok { return file } file := fr.retrieveFile(filename) if file != nil { file.Pkg = pkg file.Label = label } fr.cache[filename] = file return file } buildtools-10.1.0/warn/warn_macro_test.go0000664000175000017500000002437415254573013020043 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestUnnamedMacroNoReaderSameFile(t *testing.T) { checkFindings(t, "unnamed-macro", ` load(":foo.bzl", "foo") my_rule = rule() def macro(x): foo() my_rule(name = x) def not_macro(x): foo() native.glob() native.existing_rule() native.existing_rules() native.package_name() native.repository_name() native.exports_files() return my_rule def another_macro(x): foo() [native.cc_library() for i in x] `, []string{ `5: The macro "macro" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "my_rule" on line 7.`, `19: The macro "another_macro" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "native.cc_library" on line 21.`, }, scopeBzl) checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro1(foo, name, bar): my_rule() def macro2(foo, *, name): my_rule() def macro3(foo, *args, **kwargs): my_rule() `, []string{}, scopeBzl) checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro(name): my_rule(name = name) alias = macro def bad_macro(): for x in y: alias(x) `, []string{ `8: The macro "bad_macro" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "alias" on line 10.`, }, scopeBzl) checkFindings(t, "unnamed-macro", ` symbolic_macro = macro() def bad_macro(): symbolic_macro(x) `, []string{ `3: The macro "bad_macro" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "symbolic_macro" on line 4.`, }, scopeBzl) checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro1(): my_rule(name = x) def macro2(): macro1() def macro3(): macro2() def macro4(): my_rule() `, []string{ `3: The macro "macro1" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "my_rule" on line 4.`, `6: The macro "macro2" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "macro1" on line 7`, `9: The macro "macro3" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "macro2" on line 10.`, `12: The macro "macro4" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "my_rule" on line 13.`, }, scopeBzl) } func TestUnnamedMacroRecursion(t *testing.T) { // Recursion is not allowed in Bazel, but shouldn't cause Buildifier to crash checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro(): macro() `, []string{}, scopeBzl) checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro(): macro() `, []string{}, scopeBzl) checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro1(): macro2() def macro2(): macro3() def macro3(): macro1() `, []string{}, scopeBzl) checkFindings(t, "unnamed-macro", ` my_rule = rule() def foo(): bar() def bar(): foo() my_rule() `, []string{ `3: The macro "foo" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "bar" on line 4.`, `6: The macro "bar" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "my_rule" on line 8.`, }, scopeBzl) } func TestUnnamedMacroWithReader(t *testing.T) { defer setUpFileReader(map[string]string{ "test/package/subdir1/foo.bzl": ` def foo(): native.foo_binary() def bar(): foo() my_rule = rule() `, "test/package/subdir2/baz.bzl": ` load(":subdir1/foo.bzl", "bar", your_rule = "my_rule") load("//does/not:exist.bzl", "something") def baz(): if False: bar() def qux(): your_rule() def f(): something() `, })() checkFindings(t, "unnamed-macro", ` load("//test/package:subdir1/foo.bzl", abc = "bar") load(":subdir2/baz.bzl", "baz", "qux", "f") def macro1(surname): abc() def macro2(surname): baz() def macro3(surname): qux() def not_macro(x): f() `, []string{ `4: The macro "macro1" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "abc" on line 5. By convention, every public macro needs a "name" argument (even if it doesn't use it). This is important for tooling and automation. * If this function is a helper function that's not supposed to be used outside of this file, please make it private (e.g. rename it to "_macro1"). * Otherwise, add a "name" argument. If possible, use that name when calling other macros/rules.`, `7: The macro "macro2" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "baz" on line 8. By convention, every public macro needs a "name" argument (even if it doesn't use it). This is important for tooling and automation. * If this function is a helper function that's not supposed to be used outside of this file, please make it private (e.g. rename it to "_macro2"). * Otherwise, add a "name" argument. If possible, use that name when calling other macros/rules.`, `10: The macro "macro3" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "qux" on line 11. By convention, every public macro needs a "name" argument (even if it doesn't use it). This is important for tooling and automation. * If this function is a helper function that's not supposed to be used outside of this file, please make it private (e.g. rename it to "_macro3"). * Otherwise, add a "name" argument. If possible, use that name when calling other macros/rules.`, }, scopeBzl) } func TestUnnamedMacroRecursionWithReader(t *testing.T) { // Recursion is not allowed in Bazel, but shouldn't cause Buildifier to crash defer setUpFileReader(map[string]string{ "test/package/foo.bzl": ` load(":bar.bzl", "bar") def foo(): foo() def baz(): bar() def qux(): native.cc_library() `, "test/package/bar.bzl": ` load(":foo.bzl", "foo", "baz", quuux = "qux") def bar(): baz() def qux(): foo() baz() quuux() `, })() checkFindings(t, "unnamed-macro", ` load(":foo.bzl", "foo", "baz") load(":bar.bzl", quux = "qux") def macro(): foo() baz() quux() `, []string{ `4: The macro "macro" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "quux" on line 7.`, }, scopeBzl) } func TestUnnamedMacroLoadCycle(t *testing.T) { defer setUpFileReader(map[string]string{ "test/package/foo.bzl": ` load(":test_file.bzl", some_rule = "my_rule") def foo(): some_rule() `, })() checkFindings(t, "unnamed-macro", ` load(":foo.bzl", bar = "foo") my_rule = rule() def macro(): bar() `, []string{ `5: The macro "macro" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "bar" on line 6.`, }, scopeBzl) } func TestUnnamedMacroLoadedFiles(t *testing.T) { // Test that not necessary files are not loaded defer setUpFileReader(map[string]string{ "a.bzl": "a = rule()", "b.bzl": "b = rule()", "c.bzl": "c = rule()", "d.bzl": "d = rule()", })() checkFindings(t, "unnamed-macro", ` load("//:a.bzl", "a") load("//:b.bzl", "b") load("//:c.bzl", "c") load("//:d.bzl", "d") def macro1(): a() # has to load a.bzl to analyze def macro2(): b() # can skip b.bzl because there's a native rule native.cc_library() def macro3(): c() # can skip c.bzl because a.bzl has already been loaded a() def macro4(): d() # can skip d.bzl because there's a rule or another macro defined in the same file r() r = rule() `, []string{ `6: The macro "macro1" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "a" on line 7.`, `9: The macro "macro2" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "native.cc_library" on line 11.`, `13: The macro "macro3" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "a" on line 15.`, `17: The macro "macro4" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "r" on line 19.`, }, scopeBzl) if len(fileReaderRequests) == 1 && fileReaderRequests[0] == "a.bzl" { return } t.Errorf("expected to load only a.bzl, instead loaded %d files: %v", len(fileReaderRequests), fileReaderRequests) } func TestUnnamedMacroAliases(t *testing.T) { defer setUpFileReader(map[string]string{ "test/package/foo.bzl": ` load(":bar.bzl", _bar = "bar") bar = _bar`, "test/package/bar.bzl": ` my_rule = rule() bar = my_rule`, })() checkFindings(t, "unnamed-macro", ` load(":bar.bzl", "bar") baz = bar def macro1(): baz() def macro2(name): baz() `, []string{ `5: The macro "macro1" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "baz" on line 6.`, }, scopeBzl) } func TestUnnamedMacroPrivate(t *testing.T) { checkFindings(t, "unnamed-macro", ` my_rule = rule() def _not_macro(x): my_rule(name = x) def macro(x): _not_macro(x) `, []string{ `6: The macro "macro" should have a keyword argument called "name". It is considered a macro because it calls a rule or another macro "_not_macro" on line 7.`, }, scopeBzl) } func TestUnnamedMacroTypeAnnotation(t *testing.T) { checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro(name: string): my_rule(name) `, []string{}, scopeBzl) checkFindings(t, "unnamed-macro", ` my_rule = rule() def macro(name: string = "default"): my_rule(name) `, []string{}, scopeBzl) } buildtools-10.1.0/warn/types.go0000664000175000017500000001217315254573013016012 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "regexp" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/bzlenv" ) // Type describes an expression type in Starlark. type Type int // List of known types const ( Unknown Type = iota Bool Ctx CtxActions CtxActionsArgs Depset Dict Int None String List Float ) func (t Type) String() string { return [...]string{ "unknown", "bool", "ctx", "ctx.actions", "ctx.actions.args", "depset", "dict", "int", "none", "string", "list", "float", }[t] } var intRegexp = regexp.MustCompile(`^([0-9]+|0[Xx][0-9A-Fa-f]+|0[Oo][0-7]+)$`) // DetectTypes tries to infer the type of expressions in the current file, using basic heuristics. // // Warning: the types inferred by the function might change in the future, as we update the // heuristics. func DetectTypes(f *build.File) map[build.Expr]Type { variables := make(map[int]Type) result := make(map[build.Expr]Type) var walk func(e *build.Expr, env *bzlenv.Environment) walk = func(e *build.Expr, env *bzlenv.Environment) { // Postorder: determining types of subnodes may help with this node's type walkOnce(*e, env, walk) nodeType := Unknown defer func() { if nodeType != Unknown { result[*e] = nodeType } }() switch node := (*e).(type) { case *build.StringExpr: nodeType = String case *build.DictExpr: nodeType = Dict case *build.ListExpr: nodeType = List case *build.LiteralExpr: if intRegexp.MatchString(node.Token) { nodeType = Int } else { nodeType = Float } case *build.Comprehension: if node.Curly { nodeType = Dict } else { nodeType = List } case *build.CallExpr: if ident, ok := (node.X).(*build.Ident); ok { switch ident.Name { case "bool": nodeType = Bool case "int": nodeType = Int case "float": nodeType = Float case "str": nodeType = String case "depset": nodeType = Depset case "dict": nodeType = Dict case "list": nodeType = List } } else if dot, ok := (node.X).(*build.DotExpr); ok { if result[dot.X] == CtxActions && dot.Name == "args" { nodeType = CtxActionsArgs } } case *build.ParenExpr: nodeType = result[node.X] case *build.Ident: switch node.Name { case "True", "False": nodeType = Bool return case "None": nodeType = None return case "ctx": binding := env.Get(node.Name) if binding != nil && binding.Kind == bzlenv.Parameter { nodeType = Ctx return } } binding := env.Get(node.Name) if binding != nil { if t, ok := variables[binding.ID]; ok { nodeType = t } } case *build.DotExpr: if result[node.X] == Ctx && node.Name == "actions" { nodeType = CtxActions } case *build.BinaryExpr: switch node.Op { case ">", ">=", "<", "<=", "==", "!=", "in", "not in": // Boolean nodeType = Bool case "+", "-", "*", "/", "//", "%", "|": // We assume these operators can only applied to expressions of the same type and // preserve the type if t, ok := result[node.X]; ok { nodeType = t } else if t, ok := result[node.Y]; ok { if node.Op != "%" || t == String { // The percent operator is special because it can be applied to to arguments of // different types (`"%s\n" % foo`), and we can't assume that the expression has // type X if the right-hand side has the type X. nodeType = t } } } case *build.AssignExpr: t, ok := result[node.RHS] if !ok { return } if node.Op == "%=" && t != String { // If the right hand side is not a string, the left hand side can still be a string return } ident, ok := node.LHSIdent() if !ok { return } binding := env.Get(ident.Name) if binding == nil { return } variables[binding.ID] = t } } var expr build.Expr = f walk(&expr, bzlenv.NewEnvironment()) return result } // walkOnce is a wrapper for bzlint.WalkOnceWithEnvironment which skips the left hand side for // named parameters of functions. E.g. for `foo(x, y = z)` it visits `foo`, `x`, and `z`. // In the following example `x` in the last line shouldn't be recognised as int, but 'y' should: // // x = 3 // y = 5 // foo(x = y) func walkOnce(node build.Expr, env *bzlenv.Environment, fct func(e *build.Expr, env *bzlenv.Environment)) { switch expr := node.(type) { case *build.CallExpr: fct(&expr.X, env) for _, param := range expr.List { if as, ok := param.(*build.AssignExpr); ok { fct(&as.RHS, env) } else { fct(¶m, env) } } default: bzlenv.WalkOnceWithEnvironment(expr, env, fct) } } buildtools-10.1.0/warn/warn_bazel.go0000664000175000017500000001654315254573013016777 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // General Bazel-related warnings package warn import ( "fmt" "regexp" "strings" "github.com/bazel-contrib/buildtools/v10/build" ) // locationMakeVariableRe matches deprecated $(location) and $(locations) make variables. var locationMakeVariableRe = regexp.MustCompile(`\$\(locations?(?:\s[^)]*)?\)`) func constantGlobPatternWarning(patterns *build.ListExpr) []*LinterFinding { findings := []*LinterFinding{} for _, expr := range patterns.List { str, ok := expr.(*build.StringExpr) if !ok { continue } if !strings.Contains(str.Value, "*") { message := fmt.Sprintf( `Glob pattern %q has no wildcard ('*'). Constant patterns can be error-prone, move the file outside the glob.`, str.Value) findings = append(findings, makeLinterFinding(expr, message)) return findings // at most one warning per glob } } return findings } func constantGlobWarning(f *build.File) []*LinterFinding { switch f.Type { case build.TypeBuild, build.TypeWorkspace, build.TypeBzl: default: // Not applicable return nil } findings := []*LinterFinding{} build.Walk(f, func(expr build.Expr, stack []build.Expr) { call, ok := expr.(*build.CallExpr) if !ok || len(call.List) == 0 { return } ident, ok := (call.X).(*build.Ident) if !ok || ident.Name != "glob" { return } patterns, ok := call.List[0].(*build.ListExpr) if ok { // first arg is unnamed and is a list findings = append(findings, constantGlobPatternWarning(patterns)...) return // at most one warning per glob } // look for named args called include for _, arg := range call.List { assignExpr, ok := arg.(*build.AssignExpr) if !ok { continue } str, ok := assignExpr.LHS.(*build.Ident) if !ok || str.Name != "include" { continue } patterns, ok := assignExpr.RHS.(*build.ListExpr) if ok { findings = append(findings, constantGlobPatternWarning(patterns)...) return // at most one warning per glob } } }) return findings } func nativeInBuildFilesWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBuild { return nil } findings := []*LinterFinding{} build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Search for `native.xxx` nodes dot, ok := (*expr).(*build.DotExpr) if !ok { return } ident, ok := dot.X.(*build.Ident) if !ok || ident.Name != "native" { return } findings = append(findings, makeLinterFinding(ident, `The "native" module shouldn't be used in BUILD files, its members are available as global symbols.`, LinterReplacement{expr, &build.Ident{Name: dot.Name}})) }) return findings } func nativePackageWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } findings := []*LinterFinding{} build.Walk(f, func(expr build.Expr, stack []build.Expr) { // Search for `native.package()` nodes call, ok := expr.(*build.CallExpr) if !ok { return } dot, ok := call.X.(*build.DotExpr) if !ok || dot.Name != "package" { return } ident, ok := dot.X.(*build.Ident) if !ok || ident.Name != "native" { return } findings = append(findings, makeLinterFinding(call, `"native.package()" shouldn't be used in .bzl files.`)) }) return findings } func duplicatedNameWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBuild && f.Type != build.TypeWorkspace { // Not applicable to .bzl files. return nil } findings := []*LinterFinding{} names := make(map[string]int) // map from name to line number msg := `A rule with name %q was already found on line %d. ` + `Even if it's valid for Blaze, this may confuse other tools. ` + `Please rename it and use different names.` for _, rule := range f.Rules("") { name := rule.ExplicitName() if name == "" { continue } start, _ := rule.Call.Span() if line, ok := names[name]; ok { finding := makeLinterFinding(rule.Call, fmt.Sprintf(msg, name, line)) if nameNode := rule.Attr("name"); nameNode != nil { finding.Start, finding.End = nameNode.Span() start = finding.Start } findings = append(findings, finding) } else { names[name] = start.Line } } return findings } func positionalArgumentsWarning(f *build.File, fileReader *FileReader) (findings []*LinterFinding) { if f.Type != build.TypeBuild { return nil } macroAnalyzer := newMacroAnalyzer(fileReader) macroAnalyzer.files[f.Pkg+":"+f.Label] = analyzeFile(f) for _, expr := range f.Stmt { build.Walk(expr, func(x build.Expr, _ []build.Expr) { if fnCall, ok := x.(*build.CallExpr); ok { fnIdent, ok := fnCall.X.(*build.Ident) if !ok { return } if macroAnalyzer.IsRuleOrMacro(function{pkg: f.Pkg, filename: f.Label, name: fnIdent.Name}).isRuleOrMacro { for _, arg := range fnCall.List { if _, ok := arg.(*build.AssignExpr); ok || arg == nil { continue } findings = append(findings, makeLinterFinding(fnCall, fmt.Sprintf( `All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax. Found call to rule or macro %q with positional arguments.`, fnIdent.Name))) return } } } }) } return } func argsKwargsInBuildFilesWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBuild { return nil } findings := []*LinterFinding{} build.Walk(f, func(expr build.Expr, stack []build.Expr) { // Search for function call nodes call, ok := expr.(*build.CallExpr) if !ok { return } for _, param := range call.List { unary, ok := param.(*build.UnaryExpr) if !ok { continue } switch unary.Op { case "*": findings = append(findings, makeLinterFinding(param, `*args are not allowed in BUILD files.`)) case "**": findings = append(findings, makeLinterFinding(param, `**kwargs are not allowed in BUILD files.`)) } } }) return findings } func printWarning(f *build.File) []*LinterFinding { if f.Type == build.TypeDefault { // Only applicable to Bazel files return nil } findings := []*LinterFinding{} build.Walk(f, func(expr build.Expr, stack []build.Expr) { call, ok := expr.(*build.CallExpr) if !ok { return } ident, ok := (call.X).(*build.Ident) if !ok || ident.Name != "print" { return } findings = append(findings, makeLinterFinding(expr, `"print()" is a debug function and shouldn't be submitted.`)) }) return findings } func makeLocationVariableWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBuild { return nil } findings := []*LinterFinding{} build.Walk(f, func(expr build.Expr, stack []build.Expr) { stringExpr, ok := expr.(*build.StringExpr) if !ok { return } if locationMakeVariableRe.MatchString(stringExpr.Value) { findings = append(findings, makeLinterFinding(stringExpr, `The "$(location)" and "$(locations)" make variables are deprecated. Use "$(execpath ...)" or "$(rootpath ...)" instead.`)) } }) return findings } buildtools-10.1.0/warn/warn_deprecated.go0000664000175000017500000000417015254573013017773 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Warnings for using deprecated functions package warn import ( "fmt" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/labels" ) func checkDeprecatedFunction(stmt build.Expr, loadedSymbols *map[string]*build.Ident, fullLabel string) *LinterFinding { def, ok := stmt.(*build.DefStmt) if !ok { return nil } node, ok := (*loadedSymbols)[def.Name] if !ok { return nil } docstring, ok := getDocstring(def.Body) if !ok { return nil } str, ok := (*docstring).(*build.StringExpr) if !ok { return nil } docstringInfo := parseFunctionDocstring(str) if !docstringInfo.deprecated { return nil } return makeLinterFinding(node, fmt.Sprintf("The function %q defined in %q is deprecated.", def.Name, fullLabel)) } func deprecatedFunctionWarning(f *build.File, fileReader *FileReader) []*LinterFinding { if fileReader == nil { return nil } findings := []*LinterFinding{} for _, stmt := range f.Stmt { load, ok := stmt.(*build.LoadStmt) if !ok { continue } label := labels.ParseRelative(load.Module.Value, f.Pkg) if label.Repository != "" || label.Target == "" { continue } loadedFile := fileReader.GetFile(label.Package, label.Target) if loadedFile == nil { continue } loadedSymbols := make(map[string]*build.Ident) for _, from := range load.From { loadedSymbols[from.Name] = from } for _, stmt := range loadedFile.Stmt { if finding := checkDeprecatedFunction(stmt, &loadedSymbols, loadedFile.CanonicalPath()); finding != nil { findings = append(findings, finding) } } } return findings } buildtools-10.1.0/warn/warn_operation.go0000664000175000017500000001212215254573013017667 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Warnings about deprecated operations in Starlark package warn import ( "github.com/bazel-contrib/buildtools/v10/build" ) func dictionaryConcatenationWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding var addWarning = func(expr build.Expr) { findings = append(findings, makeLinterFinding(expr, "Dictionary concatenation is deprecated.")) } types := DetectTypes(f) build.Walk(f, func(expr build.Expr, stack []build.Expr) { switch expr := expr.(type) { case *build.BinaryExpr: if expr.Op != "+" { return } if types[expr.X] == Dict || types[expr.Y] == Dict { addWarning(expr) } case *build.AssignExpr: if expr.Op != "+=" { return } if types[expr.LHS] == Dict || types[expr.RHS] == Dict { addWarning(expr) } } }) return findings } // Detect the patterns: // // dict.pop(..., default = ...) // dict.pop(..., default = ...) // dict.setdefault(..., default = ...) // // And recommend removing the `default =` part. These functions take positional argument instead. func dictMethodNamedArgWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding types := DetectTypes(f) build.Walk(f, func(expr build.Expr, stack []build.Expr) { var callexpr *build.CallExpr var ok bool if callexpr, ok = expr.(*build.CallExpr); !ok { return } var dotexpr *build.DotExpr if dotexpr, ok = callexpr.X.(*build.DotExpr); !ok || types[dotexpr.X] != Dict { return } if dotexpr.Name != "pop" && dotexpr.Name != "get" && dotexpr.Name != "setdefault" { return } for _, expr := range callexpr.List { assignExpr, ok := expr.(*build.AssignExpr) if !ok { continue } if left, ok := assignExpr.LHS.(*build.Ident); !ok || left.Name != "default" { continue } findings = append(findings, makeLinterFinding(expr, "Named argument \"default\" not allowed, use a positional (unnamed) argument")) } }) return findings } func stringIterationWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding addWarning := func(expr build.Expr) { findings = append(findings, makeLinterFinding(expr, "String iteration is deprecated.")) } types := DetectTypes(f) build.Walk(f, func(expr build.Expr, stack []build.Expr) { switch expr := expr.(type) { case *build.ForStmt: if types[expr.X] == String { addWarning(expr.X) } case *build.ForClause: if types[expr.X] == String { addWarning(expr.X) } case *build.CallExpr: ident, ok := expr.X.(*build.Ident) if !ok { return } switch ident.Name { case "all", "any", "reversed", "max", "min": if len(expr.List) != 1 { return } if types[expr.List[0]] == String { addWarning(expr.List[0]) } case "zip": for _, arg := range expr.List { if types[arg] == String { addWarning(arg) } } } } }) return findings } func integerDivisionWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding types := DetectTypes(f) build.WalkPointers(f, func(e *build.Expr, stack []build.Expr) { switch expr := (*e).(type) { case *build.BinaryExpr: if expr.Op != "/" { return } if types[expr.X] != Int || types[expr.Y] != Int { return } newBinary := *expr newBinary.Op = "//" findings = append(findings, makeLinterFinding(expr, `The "/" operator for integer division is deprecated in favor of "//".`, LinterReplacement{e, &newBinary})) case *build.AssignExpr: if expr.Op != "/=" { return } if types[expr.LHS] != Int || types[expr.RHS] != Int { return } newAssign := *expr newAssign.Op = "//=" findings = append(findings, makeLinterFinding(expr, `The "/=" operator for integer division is deprecated in favor of "//=".`, LinterReplacement{e, &newAssign})) } }) return findings } func listAppendWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { as, ok := (*expr).(*build.AssignExpr) if !ok || as.Op != "+=" { return } list, ok := as.RHS.(*build.ListExpr) if !ok || len(list.List) != 1 { return } _, end := as.Span() findings = append(findings, makeLinterFinding(as, `Prefer using ".append()" to adding a single element list. NOTE: This does not work if the target is a select, hence this warning can safely be ignored or suppressed.`, LinterReplacement{expr, &build.CallExpr{ Comments: as.Comments, X: &build.DotExpr{ X: as.LHS, Name: "append", }, List: list.List, End: build.End{Pos: end}, }})) }) return findings } buildtools-10.1.0/warn/warn_bazel_operation.go0000664000175000017500000001104415254573013021046 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Warnings about deprecated Bazel-related operations package warn import ( "fmt" "github.com/bazel-contrib/buildtools/v10/build" ) func depsetUnionWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding addWarning := func(expr build.Expr) { findings = append(findings, makeLinterFinding(expr, `Depsets should be joined using the "depset()" constructor.`)) } types := DetectTypes(f) build.Walk(f, func(expr build.Expr, stack []build.Expr) { switch expr := expr.(type) { case *build.BinaryExpr: // `depset1 + depset2` or `depset1 | depset2` if types[expr.X] != Depset && types[expr.Y] != Depset { return } switch expr.Op { case "+", "|": addWarning(expr) } case *build.AssignExpr: // `depset1 += depset2` or `depset1 |= depset2` if types[expr.LHS] != Depset && types[expr.RHS] != Depset { return } switch expr.Op { case "+=", "|=": addWarning(expr) } case *build.CallExpr: // `depset1.union(depset2)` if len(expr.List) == 0 { return } dot, ok := expr.X.(*build.DotExpr) if !ok { return } if dot.Name != "union" { return } if types[dot.X] != Depset && types[expr.List[0]] != Depset { return } addWarning(expr) } }) return findings } func depsetIterationWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding addFinding := func(expr *build.Expr) { _, end := (*expr).Span() newNode := &build.CallExpr{ X: &build.DotExpr{ X: *expr, Name: "to_list", }, End: build.End{Pos: end}, } findings = append(findings, makeLinterFinding(*expr, `Depset iteration is deprecated, use the "to_list()" method instead.`, LinterReplacement{expr, newNode})) } types := DetectTypes(f) build.WalkPointers(f, func(e *build.Expr, stack []build.Expr) { switch expr := (*e).(type) { case *build.ForStmt: if types[expr.X] != Depset { return } addFinding(&expr.X) case *build.ForClause: if types[expr.X] != Depset { return } addFinding(&expr.X) case *build.BinaryExpr: if expr.Op != "in" && expr.Op != "not in" { return } if types[expr.Y] != Depset { return } addFinding(&expr.Y) case *build.CallExpr: ident, ok := expr.X.(*build.Ident) if !ok { return } switch ident.Name { case "all", "any", "depset", "len", "sorted", "max", "min", "list", "tuple": if len(expr.List) != 1 { return } if types[expr.List[0]] != Depset { return } addFinding(&expr.List[0]) if ident.Name == "list" { // `list(d.to_list())` can be simplified to just `d.to_list()` findings[len(findings)-1].Replacement[0].Old = e } case "zip": for i, arg := range expr.List { if types[arg] != Depset { continue } addFinding(&expr.List[i]) } } } return }) return findings } func overlyNestedDepsetWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { // Are we inside a for-loop? isForLoop := false for _, e := range stack { if _, ok := e.(*build.ForStmt); ok { isForLoop = true break } } if !isForLoop { return } // Search for assignment statements assign, ok := expr.(*build.AssignExpr) if !ok { return } // Is the LHS an ident? lhs, ok := assign.LHSIdent() if !ok { return } // Is the RHS a depset constructor? call, ok := assign.RHS.(*build.CallExpr) if !ok { return } if ident, ok := call.X.(*build.Ident); !ok || ident.Name != "depset" { return } _, _, param := getParam(call.List, "transitive") if param == nil { return } transitives, ok := param.RHS.(*build.ListExpr) if !ok { return } for _, transitive := range transitives.List { if ident, ok := transitive.(*build.Ident); ok && ident.Name == lhs.Name { findings = append(findings, makeLinterFinding(assign, fmt.Sprintf("Depset %q is potentially overly nested.", lhs.Name))) return } } return }) return findings } buildtools-10.1.0/warn/warn_naming.go0000664000175000017500000001151415254573013017144 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "fmt" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/bzlenv" ) var ambiguousNames = map[string]bool{ "I": true, "l": true, "O": true, } // ambiguousNameCheck checks for the names of idents and functions func ambiguousNameCheck(expr build.Expr, name string, findings []*LinterFinding) []*LinterFinding { if ambiguousNames[name] { findings = append(findings, makeLinterFinding(expr, `Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').`)) } return findings } func confusingNameWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding // check for global variable names for _, ident := range collectLocalVariables(f.Stmt) { findings = ambiguousNameCheck(ident, ident.Name, findings) } build.Walk(f, func(expr build.Expr, stack []build.Expr) { switch expr := expr.(type) { case *build.DefStmt: findings = ambiguousNameCheck(expr, expr.Name, findings) if expr.TypeParams != nil { for _, ident := range bzlenv.CollectLValues(expr.TypeParams) { findings = ambiguousNameCheck(ident, ident.Name, findings) } } for _, param := range expr.Params { name, _ := build.GetParamName(param) findings = ambiguousNameCheck(param, name, findings) } for _, ident := range collectLocalVariables(expr.Body) { findings = ambiguousNameCheck(ident, ident.Name, findings) } case *build.Comprehension: for _, clause := range expr.Clauses { forClause, ok := clause.(*build.ForClause) if !ok { continue } for _, ident := range bzlenv.CollectLValues(forClause.Vars) { findings = ambiguousNameCheck(ident, ident.Name, findings) } } case *build.TypeAliasStmt: findings = ambiguousNameCheck(expr, expr.GetIdent().Name, findings) if expr.TypeParams != nil { for _, ident := range bzlenv.CollectLValues(expr.TypeParams) { findings = ambiguousNameCheck(ident, ident.Name, findings) } } } }) return findings } func isUpperCamelCase(name string) bool { if strings.HasPrefix(name, "_") { // Private providers are allowed name = name[1:] } return !strings.ContainsRune(name, '_') && name == strings.Title(name) } func isLowerSnakeCase(name string) bool { return name == strings.ToLower(name) } func isUpperSnakeCase(name string) bool { return name == strings.ToUpper(name) } func nameConventionsWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding build.WalkStatements(f, func(stmt build.Expr, stack []build.Expr) (err error) { switch stmt := stmt.(type) { case *build.AssignExpr: // looking for provider declaration statements: `xxx = provider()` // note that the code won't trigger on complex assignments, such as `x, y = foo, provider()` for _, ident := range bzlenv.CollectLValues(stmt.LHS) { findings = appendVariableNameConventionsWarning(ident, findings) } case *build.TypedIdent: ident := stmt.GetIdent() findings = appendVariableNameConventionsWarning(ident, findings) case *build.TypeAliasStmt: ident := stmt.GetIdent() findings = appendTypeNameConventionsWarning(ident, findings) } return }) return findings } func appendVariableNameConventionsWarning(ident *build.Ident, findings []*LinterFinding) []*LinterFinding { if isLowerSnakeCase(ident.Name) || isUpperSnakeCase(ident.Name) { return findings } if isUpperCamelCase(ident.Name) && strings.HasSuffix(ident.Name, "Info") { return findings } return append(findings, makeLinterFinding(ident, fmt.Sprintf(`Variable name "%s" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, ident.Name))) } func appendTypeNameConventionsWarning(ident *build.Ident, findings []*LinterFinding) []*LinterFinding { if isLowerSnakeCase(ident.Name) || isUpperCamelCase(ident.Name) { // Built-in types are either lower_snake (str, dict, list) or UpperCamel (Callable, Sequence). // User-defined types and type params should follow the same pattern. // Note that providers (expected to be UpperCamelCase) are also types. return findings } return append(findings, makeLinterFinding(ident, fmt.Sprintf(`Type name "%s" should be lower_snake_case or UpperCamelCase (possibly prefixed with an underscore).`, ident.Name))) } buildtools-10.1.0/warn/warn_load.go0000664000175000017500000000221615254573013016611 0ustar kenhyskenhyspackage warn import ( "fmt" "slices" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/tables" ) func symbolLoadLocationWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding for stmtIndex := 0; stmtIndex < len(f.Stmt); stmtIndex++ { load, ok := f.Stmt[stmtIndex].(*build.LoadStmt) if !ok { continue } for i := 0; i < len(load.From); i++ { from := load.From[i] expected, ok := tables.AllowedSymbolLoadLocations[from.Name] if !ok || expected[load.Module.Value] { continue } var f *LinterFinding if len(expected) == 1 { var loc string for l := range expected { loc = l break } f = makeLinterFinding(from, fmt.Sprintf("Symbol %q must be loaded from %s.", from.Name, loc)) } else { locs := make([]string, 0, len(expected)) for l := range expected { locs = append(locs, l) } slices.Sort(locs) f = makeLinterFinding(from, fmt.Sprintf("Symbol %q must be loaded from one of the allowed locations: %s.", from.Name, strings.Join(locs, ", "))) } findings = append(findings, f) } } return findings } buildtools-10.1.0/warn/warn_cosmetic.go0000664000175000017500000002313615254573013017504 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Cosmetic warnings (e.g. for improved readability of Starlark files) package warn import ( "regexp" "sort" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/edit" ) // packageOnTopWarning hoists package statements to the top after any comments / docstrings / load statements. // If applied together with loadOnTopWarning and/or outOfOrderLoadWarning, it should be applied after them. // This is currently guaranteed by sorting the warning categories names before applying them: // "load-on-top" < "out-of-order-load" < "package-on-top" func packageOnTopWarning(f *build.File) []*LinterFinding { if f.Type == build.TypeWorkspace || f.Type == build.TypeModule { // Not applicable to WORKSPACE or MODULE files return nil } // Find the misplaced load statements misplacedPackages := make(map[int]*build.CallExpr) firstStmtIndex := -1 // index of the first seen non string, comment or load statement for i := 0; i < len(f.Stmt); i++ { stmt := f.Stmt[i] // Assign statements may define variables that are used by the package statement, // e.g. visibility declarations. To avoid false positive detections and also // for keeping things simple, the warning should be just suppressed if there's // any assignment statement, even if it's not used by the package declaration. if _, ok := stmt.(*build.AssignExpr); ok { break } _, isString := stmt.(*build.StringExpr) // typically a docstring _, isComment := stmt.(*build.CommentBlock) _, isLoad := stmt.(*build.LoadStmt) _, isLicenses := edit.ExprToRule(stmt, "licenses") _, isPackageGroup := edit.ExprToRule(stmt, "package_group") if isString || isComment || isLoad || isLicenses || isPackageGroup || stmt == nil { continue } rule, ok := edit.ExprToRule(stmt, "package") if !ok { if firstStmtIndex == -1 { firstStmtIndex = i } continue } if firstStmtIndex == -1 { continue } misplacedPackages[i] = rule.Call } offset := len(misplacedPackages) if offset == 0 { return nil } // Calculate a fix: if firstStmtIndex == -1 { firstStmtIndex = 0 } var replacements []LinterReplacement for i := range f.Stmt { if i < firstStmtIndex { // Docstring or comment or load in the beginning, skip continue } else if _, ok := misplacedPackages[i]; ok { // A misplaced load statement, should be moved up to the `firstStmtIndex` position replacements = append(replacements, LinterReplacement{&f.Stmt[firstStmtIndex], f.Stmt[i]}) firstStmtIndex++ offset-- if offset == 0 { // No more statements should be moved break } } else { // An actual statement (not a docstring or a comment in the beginning), should be moved // `offset` positions down. replacements = append(replacements, LinterReplacement{&f.Stmt[i+offset], f.Stmt[i]}) } } var findings []*LinterFinding for _, load := range misplacedPackages { findings = append(findings, makeLinterFinding(load, "Package declaration should be at the top of the file, after the load() statements, "+ "but before any call to a rule or a macro. "+ "package_group() and licenses() may be called before package().", replacements...)) } return findings } func unsortedDictItemsWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding compareItems := func(item1, item2 *build.KeyValueExpr) bool { key1 := item1.Key.(*build.StringExpr).Value key2 := item2.Key.(*build.StringExpr).Value // regular keys should precede private ones (start with "_") if strings.HasPrefix(key1, "_") { return strings.HasPrefix(key2, "_") && key1 < key2 } if strings.HasPrefix(key2, "_") { return true } // "//conditions:default" should always be the last const conditionsDefault = "//conditions:default" if key1 == conditionsDefault { return false } else if key2 == conditionsDefault { return true } return key1 < key2 } build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { dict, ok := (*expr).(*build.DictExpr) mustSkipCheck := func(expr build.Expr) bool { return edit.ContainsComments(expr, "@unsorted-dict-items") } if !ok || mustSkipCheck(dict) { return } // do not process dictionaries nested within expressions that do not // want dict items to be sorted for i := len(stack) - 1; i >= 0; i-- { if mustSkipCheck(stack[i]) { return } } var sortedItems []*build.KeyValueExpr for _, item := range dict.List { // include only string literal keys into consideration if _, ok = item.Key.(*build.StringExpr); !ok { continue } sortedItems = append(sortedItems, item) } // Fix comp := func(i, j int) bool { return compareItems(sortedItems[i], sortedItems[j]) } var misplacedItems []*build.KeyValueExpr for i := 1; i < len(sortedItems); i++ { if comp(i, i-1) { misplacedItems = append(misplacedItems, sortedItems[i]) } } if len(misplacedItems) == 0 { // Already sorted return } newDict := *dict newDict.List = append([]*build.KeyValueExpr{}, dict.List...) sort.SliceStable(sortedItems, comp) sortedItemIndex := 0 for originalItemIndex := 0; originalItemIndex < len(dict.List); originalItemIndex++ { item := dict.List[originalItemIndex] if _, ok := item.Key.(*build.StringExpr); !ok { continue } newDict.List[originalItemIndex] = sortedItems[sortedItemIndex] sortedItemIndex++ } for _, item := range misplacedItems { findings = append(findings, makeLinterFinding(item, "Dictionary items are out of their lexicographical order.", LinterReplacement{expr, &newDict})) } }) return findings } // skylarkToStarlark converts a string "skylark" in different cases to "starlark" // trying to preserve the same case style, e.g. capitalized "Skylark" becomes "Starlark". func skylarkToStarlark(s string) string { switch { case s == "SKYLARK": return "STARLARK" case strings.HasPrefix(s, "S"): return "Starlark" default: return "starlark" } } var skylarkRegex = regexp.MustCompile("(?i)skylark") // replaceSkylark replaces a substring "skylark" (case-insensitive) with a // similar cased string "starlark". Doesn't replace it if the previous or the // next symbol is '/', which may indicate it's a part of a URL. // Normally that should be done with look-ahead and look-behind assertions in a // regular expression, but negative look-aheads and look-behinds are not // supported by Go regexp module. func replaceSkylark(s string) (newString string, changed bool) { newString = s for _, r := range skylarkRegex.FindAllStringIndex(s, -1) { if r[0] > 0 && s[r[0]-1] == '/' { continue } if r[1] < len(s)-1 && s[r[1]+1] == '/' { continue } newString = newString[:r[0]] + skylarkToStarlark(newString[r[0]:r[1]]) + newString[r[1]:] } return newString, newString != s } func skylarkCommentWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding msg := `"Skylark" is an outdated name of the language, please use "starlark" instead.` // Check comments build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { comments := (*expr).Comment() newComments := build.Comments{ Before: append([]build.Comment{}, comments.Before...), Suffix: append([]build.Comment{}, comments.Suffix...), After: append([]build.Comment{}, comments.After...), } isModified := false var start, end build.Position for _, block := range []*[]build.Comment{&newComments.Before, &newComments.Suffix, &newComments.After} { for i, comment := range *block { // Don't trigger on disabling comments if strings.Contains(comment.Token, "disable=skylark-docstring") { continue } newValue, changed := replaceSkylark(comment.Token) (*block)[i] = build.Comment{ Start: comment.Start, Token: newValue, } if changed { isModified = true start, end = comment.Span() } } } if !isModified { return } newExpr := (*expr).Copy() newExpr.Comment().Before = newComments.Before newExpr.Comment().Suffix = newComments.Suffix newExpr.Comment().After = newComments.After finding := makeLinterFinding(*expr, msg, LinterReplacement{expr, newExpr}) finding.Start = start finding.End = end findings = append(findings, finding) }) return findings } func checkSkylarkDocstring(stmts []build.Expr) *LinterFinding { msg := `"Skylark" is an outdated name of the language, please use "starlark" instead.` doc, ok := getDocstring(stmts) if !ok { return nil } docString := (*doc).(*build.StringExpr) newValue, updated := replaceSkylark(docString.Value) if !updated { return nil } newDocString := *docString newDocString.Value = newValue return makeLinterFinding(docString, msg, LinterReplacement{doc, &newDocString}) } func skylarkDocstringWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding // File docstring if finding := checkSkylarkDocstring(f.Stmt); finding != nil { findings = append(findings, finding) } // Function docstrings for _, stmt := range f.Stmt { def, ok := stmt.(*build.DefStmt) if !ok { continue } if finding := checkSkylarkDocstring(def.Body); finding != nil { findings = append(findings, finding) } } return findings } buildtools-10.1.0/warn/warn.go0000664000175000017500000004567515254573013015632 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn implements functions that generate warnings for BUILD files. package warn import ( "fmt" "log" "os" "sort" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/edit" ) // LintMode is an enum representing a linter mode. Can be either "warn", "fix", or "suggest" type LintMode int const ( // ModeWarn means only warnings should be returned for each finding. ModeWarn LintMode = iota // ModeFix means that all warnings that can be fixed automatically should be fixed and // no warnings should be returned for them. ModeFix // ModeSuggest means that automatic fixes shouldn't be applied, but instead corresponding // suggestions should be attached to all warnings that can be fixed automatically. ModeSuggest ) // LinterFinding is a low-level warning reported by single linter/fixer functions. type LinterFinding struct { Start build.Position End build.Position Message string URL string Replacement []LinterReplacement } // LinterReplacement is a low-level object returned by single fixer functions. type LinterReplacement struct { Old *build.Expr New build.Expr } // A Finding is a warning reported by the analyzer. It may contain an optional suggested fix. type Finding struct { File *build.File Start build.Position End build.Position Category string Message string URL string Actionable bool AutoFixable bool Replacement *Replacement } // A Replacement is a suggested fix. Text between Start and End should be replaced with Content. type Replacement struct { Description string Start int End int Content string } func docURL(cat string) string { return "https://github.com/bazel-contrib/buildtools/blob/main/WARNINGS.md#" + cat } // makeFinding creates a Finding object func makeFinding(f *build.File, start, end build.Position, cat, url, msg string, actionable bool, autoFixable bool, fix *Replacement) *Finding { if url == "" { url = docURL(cat) } return &Finding{ File: f, Start: start, End: end, Category: cat, URL: url, Message: msg, Actionable: actionable, AutoFixable: autoFixable, Replacement: fix, } } // makeLinterFinding creates a LinterFinding object func makeLinterFinding(node build.Expr, message string, replacement ...LinterReplacement) *LinterFinding { start, end := node.Span() return &LinterFinding{ Start: start, End: end, Message: message, Replacement: replacement, } } // RuleWarningMap lists the warnings that run on a single rule. // These warnings run only on BUILD files (not bzl files). var RuleWarningMap = map[string]func(call *build.CallExpr, pkg string) *LinterFinding{} // FileWarningMap lists the warnings that run on the whole file. var FileWarningMap = map[string]func(f *build.File) []*LinterFinding{ "allowed-symbol-load-locations": symbolLoadLocationWarning, "attr-applicable_licenses": attrApplicableLicensesWarning, "attr-cfg": attrConfigurationWarning, "attr-license": attrLicenseWarning, "attr-licenses": attrLicensesWarning, "attr-non-empty": attrNonEmptyWarning, "attr-output-default": attrOutputDefaultWarning, "attr-single-file": attrSingleFileWarning, "build-args-kwargs": argsKwargsInBuildFilesWarning, "confusing-name": confusingNameWarning, "constant-glob": constantGlobWarning, "ctx-actions": ctxActionsWarning, "ctx-args": contextArgsAPIWarning, "depset-items": depsetItemsWarning, "depset-iteration": depsetIterationWarning, "depset-union": depsetUnionWarning, "dict-method-named-arg": dictMethodNamedArgWarning, "dict-concatenation": dictionaryConcatenationWarning, "duplicated-name": duplicatedNameWarning, "filetype": fileTypeWarning, "function-docstring": functionDocstringWarning, "function-docstring-header": functionDocstringHeaderWarning, "function-docstring-args": functionDocstringArgsWarning, "function-docstring-return": functionDocstringReturnWarning, "integer-division": integerDivisionWarning, "keyword-positional-params": keywordPositionalParametersWarning, "list-append": listAppendWarning, "load": unusedLoadWarning, "make-location": makeLocationVariableWarning, "module-docstring": moduleDocstringWarning, "name-conventions": nameConventionsWarning, "native-build": nativeInBuildFilesWarning, "native-package": nativePackageWarning, "no-effect": noEffectWarning, "output-group": outputGroupWarning, "overly-nested-depset": overlyNestedDepsetWarning, "package-name": packageNameWarning, "package-on-top": packageOnTopWarning, "print": printWarning, "provider-params": providerParamsWarning, "redefined-variable": redefinedVariableWarning, "repository-name": repositoryNameWarning, "rule-impl-return": ruleImplReturnWarning, "return-value": missingReturnValueWarning, "skylark-comment": skylarkCommentWarning, "skylark-docstring": skylarkDocstringWarning, "string-iteration": stringIterationWarning, "uninitialized": uninitializedVariableWarning, "unreachable": unreachableStatementWarning, "unsorted-dict-items": unsortedDictItemsWarning, "unused-variable": unusedVariableWarning, } // MultiFileWarningMap lists the warnings that run on the whole file, but may use other files. var MultiFileWarningMap = map[string]func(f *build.File, fileReader *FileReader) []*LinterFinding{ "bzl-visibility": bzlVisibilityWarning, "deprecated-function": deprecatedFunctionWarning, "git-repository": nativeGitRepositoryWarning, "http-archive": nativeHTTPArchiveWarning, "native-android": nativeAndroidRulesWarning, "native-cc-binary": NativeCcRulesWarning("cc_binary"), "native-cc-import": NativeCcRulesWarning("cc_import"), "native-cc-library": NativeCcRulesWarning("cc_library"), "native-cc-objc-import": NativeCcRulesWarning("objc_import"), "native-cc-objc-library": NativeCcRulesWarning("objc_library"), "native-cc-shared-library": NativeCcRulesWarning("cc_shared_library"), "native-cc-test": NativeCcRulesWarning("cc_test"), "native-cc-toolchain": NativeCcToolchainRulesWarning("cc_toolchain"), "native-cc-toolchain-suite": NativeCcToolchainRulesWarning("cc_toolchain_suite"), "native-cc-fdo-prefetch-hints": NativeCcToolchainRulesWarning("fdo_prefetch_hints"), "native-cc-fdo-profile": NativeCcToolchainRulesWarning("fdo_profile"), "native-cc-memprof-profile": NativeCcToolchainRulesWarning("memprof_profile"), "native-cc-propeller-optimize": NativeCcToolchainRulesWarning("propeller_optimize"), "native-cc-common": NativeCcSymbolsWarning("cc_common", "cc_common"), "native-cc-debug-package-info": NativeCcSymbolsWarning("DebugPackageInfo", "debug_package_info"), "native-cc-info": NativeCcSymbolsWarning("CcInfo", "cc_info"), "native-cc-shared-library-info": NativeCcSymbolsWarning("CcSharedLibraryInfo", "cc_shared_library_info"), "native-cc-shared-library-hint-info": NativeCcSymbolsWarning("CcSharedLibraryHintInfo", "cc_shared_library_hint_info"), "native-java-binary": NativeJavaRulesWarning("java_binary"), "native-java-import": NativeJavaRulesWarning("java_import"), "native-java-library": NativeJavaRulesWarning("java_library"), "native-java-plugin": NativeJavaRulesWarning("java_plugin"), "native-java-test": NativeJavaRulesWarning("java_test"), "native-java-package-config": NativeJavaToolchainRulesWarning("java_package_configuration"), "native-java-runtime": NativeJavaToolchainRulesWarning("java_runtime"), "native-java-toolchain": NativeJavaToolchainRulesWarning("java_toolchain"), "native-java-common": NativeJavaSymbolsWarning("java_common", "java_common"), "native-java-info": NativeJavaSymbolsWarning("JavaInfo", "java_info"), "native-java-plugin-info": NativeJavaSymbolsWarning("JavaPluginInfo", "java_plugin_info"), "native-proto": NativeProtoRulesWarning("proto_library"), "native-java-proto": NativeProtoRulesWarning("java_proto_library"), "native-java-lite-proto": NativeProtoRulesWarning("java_lite_proto_library"), "native-cc-proto": NativeProtoRulesWarning("cc_proto_library"), "native-proto-lang-toolchain": nativeProtoLangToolchainWarning, "native-proto-info": nativeProtoSymbolsWarning("ProtoInfo", "proto_info.bzl"), "native-proto-common": nativeProtoSymbolsWarning("proto_common", "proto_common.bzl"), "native-proto-lang-toolchain-info": nativeProtoSymbolsWarning("ProtoLangToolchainInfo", "proto_lang_toolchain_info.bzl"), "native-py": nativePyRulesWarning, "native-sh-binary": NativeShellRulesWarning("sh_binary"), "native-sh-library": NativeShellRulesWarning("sh_library"), "native-sh-test": NativeShellRulesWarning("sh_test"), "positional-args": positionalArgumentsWarning, "unnamed-macro": unnamedMacroWarning, } // nonDefaultWarnings contains warnings that are enabled by default because they're not applicable // for all files and cause too much diff noise when applied. var nonDefaultWarnings = map[string]bool{ "unsorted-dict-items": true, // dict items should be sorted } // fileWarningWrapper is a wrapper that converts a file warning function to a generic function. // A generic function takes a `pkg string` and a `*ReadFile` arguments which are not used for file warnings, // so they are just removed. func fileWarningWrapper(fct func(f *build.File) []*LinterFinding) func(*build.File, string, *FileReader) []*LinterFinding { return func(f *build.File, _ string, _ *FileReader) []*LinterFinding { return fct(f) } } // multiFileWarningWrapper is a wrapper that converts a multifile warning function to a generic function. // A generic function takes a `pkg string` argument which is not used for file warnings, so it's just removed. func multiFileWarningWrapper(fct func(f *build.File, fileReader *FileReader) []*LinterFinding) func(*build.File, string, *FileReader) []*LinterFinding { return func(f *build.File, _ string, fileReader *FileReader) []*LinterFinding { return fct(f, fileReader) } } // ruleWarningWrapper is a wrapper that converts a per-rule function to a per-file function. // It also doesn't run on .bzl or default files, only on BUILD and WORKSPACE files. func ruleWarningWrapper(ruleWarning func(call *build.CallExpr, pkg string) *LinterFinding) func(*build.File, string, *FileReader) []*LinterFinding { return func(f *build.File, pkg string, _ *FileReader) []*LinterFinding { if f.Type != build.TypeBuild { return nil } var findings []*LinterFinding for _, stmt := range f.Stmt { switch stmt := stmt.(type) { case *build.CallExpr: finding := ruleWarning(stmt, pkg) if finding != nil { findings = append(findings, finding) } case *build.Comprehension: // Rules are often called within list comprehensions, e.g. [my_rule(foo) for foo in bar] if call, ok := stmt.Body.(*build.CallExpr); ok { finding := ruleWarning(call, pkg) if finding != nil { findings = append(findings, finding) } } } } return findings } } // runWarningsFunction runs a linter/fixer function over a file and applies the fixes conditionally func runWarningsFunction(category string, f *build.File, fct func(f *build.File, pkg string, fileReader *FileReader) []*LinterFinding, formatted *[]byte, mode LintMode, fileReader *FileReader) []*Finding { findings := []*Finding{} for _, w := range fct(f, f.Pkg, fileReader) { if !DisabledWarning(f, w.Start.Line, category) { finding := makeFinding(f, w.Start, w.End, category, w.URL, w.Message, true, len(w.Replacement) > 0, nil) if len(w.Replacement) > 0 { // An automatic fix exists switch mode { case ModeFix: // Apply the fix and discard the finding for _, r := range w.Replacement { *r.Old = r.New } finding = nil case ModeSuggest: // Apply the fix, calculate the diff and roll back the fix newContents := formatWithFix(f, &w.Replacement) start, end, replacement := calculateDifference(formatted, &newContents) finding.Replacement = &Replacement{ Description: w.Message, Start: start, End: end, Content: replacement, } } } if finding != nil { findings = append(findings, finding) } } } return findings } // HasDisablingComment checks if a node has a comment that disables a certain warning func HasDisablingComment(expr build.Expr, warning string) bool { return edit.ContainsDisableComment(expr, warning) } // DisabledWarning checks if the warning was disabled by a comment. // The comment format is buildozer: disable= func DisabledWarning(f *build.File, findingLine int, warning string) bool { disabled := false build.Walk(f, func(expr build.Expr, stack []build.Expr) { if expr == nil { return } start, end := expr.Span() comments := expr.Comment() if len(comments.Before) > 0 { start, _ = comments.Before[0].Span() } if len(comments.After) > 0 { _, end = comments.After[len(comments.After)-1].Span() } if findingLine < start.Line || findingLine > end.Line { return } if HasDisablingComment(expr, warning) { disabled = true return } }) return disabled } // FileWarnings returns a list of all warnings found in the file. func FileWarnings(f *build.File, enabledWarnings []string, formatted *[]byte, mode LintMode, fileReader *FileReader) []*Finding { findings := []*Finding{} // Sort the warnings to make sure they're applied in the same determined order // Make a local copy first to avoid race conditions warnings := append([]string{}, enabledWarnings...) sort.Strings(warnings) // If suggestions are requested and formatted file is not provided, format it to compare modified versions with if mode == ModeSuggest && formatted == nil { contents := build.Format(f) formatted = &contents } for _, warn := range warnings { if fct, ok := FileWarningMap[warn]; ok { findings = append(findings, runWarningsFunction(warn, f, fileWarningWrapper(fct), formatted, mode, fileReader)...) } else if fct, ok := MultiFileWarningMap[warn]; ok { findings = append(findings, runWarningsFunction(warn, f, multiFileWarningWrapper(fct), formatted, mode, fileReader)...) } else if fct, ok := RuleWarningMap[warn]; ok { findings = append(findings, runWarningsFunction(warn, f, ruleWarningWrapper(fct), formatted, mode, fileReader)...) } else { log.Printf("unexpected warning %q", warn) os.Exit(1) } } sort.Slice(findings, func(i, j int) bool { if findings[i].Start.Line != findings[j].Start.Line { return findings[i].Start.Line < findings[j].Start.Line } if findings[i].Start.LineRune != findings[j].Start.LineRune { return findings[i].Start.LineRune < findings[j].Start.LineRune } if findings[i].Category != findings[j].Category { return findings[i].Category < findings[j].Category } return findings[i].Message < findings[j].Message }) return findings } // formatWithFix applies a fix, formats a file, and rolls back the fix func formatWithFix(f *build.File, replacements *[]LinterReplacement) []byte { for i := range *replacements { r := (*replacements)[i] old := *r.Old *r.Old = r.New defer func() { *r.Old = old }() } return build.Format(f) } // calculateDifference compares two file contents and returns a replacement in the form of // a 3-tuple (byte from, byte to (non inclusive), a string to replace with). func calculateDifference(old, new *[]byte) (start, end int, replacement string) { commonPrefix := 0 // length of the common prefix for i, b := range *old { if i >= len(*new) || b != (*new)[i] { break } commonPrefix++ } commonSuffix := 0 // length of the common suffix for i := range *old { b := (*old)[len(*old)-1-i] if i >= len(*new) || b != (*new)[len(*new)-1-i] { break } commonSuffix++ } // In some cases common suffix and prefix can overlap. E.g. consider the following case: // old = "abc" // new = "abdbc" // In this case the common prefix is "ab" and the common suffix is "bc". // If they overlap, just shorten the suffix so that they don't. // The new suffix will be just "c". if commonPrefix+commonSuffix > len(*old) { commonSuffix = len(*old) - commonPrefix } if commonPrefix+commonSuffix > len(*new) { commonSuffix = len(*new) - commonPrefix } return commonPrefix, len(*old) - commonSuffix, string((*new)[commonPrefix:(len(*new) - commonSuffix)]) } // FixWarnings fixes all warnings that can be fixed automatically. func FixWarnings(f *build.File, enabledWarnings []string, verbose bool, fileReader *FileReader) { warnings := FileWarnings(f, enabledWarnings, nil, ModeFix, fileReader) if verbose { fmt.Fprintf(os.Stderr, "%s: applied fixes, %d warnings left\n", f.DisplayPath(), len(warnings)) } } func collectAllWarnings() []string { var result []string // Collect list of all warnings. for k := range FileWarningMap { result = append(result, k) } for k := range MultiFileWarningMap { result = append(result, k) } for k := range RuleWarningMap { result = append(result, k) } sort.Strings(result) return result } // AllWarnings is the list of all available warnings. var AllWarnings = collectAllWarnings() func collectDefaultWarnings() []string { warnings := []string{} for _, warning := range AllWarnings { if !nonDefaultWarnings[warning] { warnings = append(warnings, warning) } } return warnings } // DefaultWarnings is the list of all warnings that should be used inside google3 var DefaultWarnings = collectDefaultWarnings() buildtools-10.1.0/warn/warn_visibility_test.go0000664000175000017500000001041115254573013021114 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestBzlVisibility(t *testing.T) { checkFindings(t, "bzl-visibility", ` load("//foo/bar/internal/baz:module.bzl", "foo") load("//foo/bar/private/baz:module.bzl", "bar") load("//foo/bar/external/baz:module.bzl", "baz") foo() bar() baz() `, []string{ `:1: Module "//foo/bar/internal/baz:module.bzl" can only be loaded from files located inside "//foo/bar", not from "//test/package/`, `:2: Module "//foo/bar/private/baz:module.bzl" can only be loaded from files located inside "//foo/bar", not from "//test/package/`, }, scopeEverywhere) checkFindings(t, "bzl-visibility", ` load("//foo/bar/internal:module.bzl", "foo") load("//foo/bar/private:module.bzl", "bar") load("//foo/bar/external:module.bzl", "baz") foo() bar() baz() `, []string{ `:1: Module "//foo/bar/internal:module.bzl" can only be loaded from files located inside "//foo/bar", not from "//test/package/`, `:2: Module "//foo/bar/private:module.bzl" can only be loaded from files located inside "//foo/bar", not from "//test/package/`, }, scopeEverywhere) checkFindings(t, "bzl-visibility", ` load("@repo//foo/bar/internal:module.bzl", "foo") load("@repo//foo/bar/private:module.bzl", "bar") load("@repo//foo/bar/external:module.bzl", "baz") load("@repo/internal:module.bzl", "qux") # incorrect, but shouldn't cause buildifier crashes foo() bar() baz() qux() `, []string{ `:1: Module "@repo//foo/bar/internal:module.bzl" can only be loaded from files located inside "//foo/bar", not from "//test/package/`, `:2: Module "@repo//foo/bar/private:module.bzl" can only be loaded from files located inside "//foo/bar", not from "//test/package/`, `:4: Module "@repo/internal:module.bzl" can only be loaded from files located inside "@repo", not from "//test/package/`, }, scopeEverywhere) checkFindings(t, "bzl-visibility", ` load("//test/internal/foo:module.bzl", "foo") load("//test/private/foo:module.bzl", "bar") load("//test/external/foo:module.bzl", "baz") load("//test/internal:module.bzl", "foo") load("//test/private:module.bzl", "bar") load("//test/external:module.bzl", "baz") `, []string{}, scopeEverywhere) checkFindings(t, "bzl-visibility", ` load("@repo//test/internal/foo:module.bzl", "foo") load("@repo//test/private/foo:module.bzl", "bar") load("@repo//test/external/foo:module.bzl", "baz") load("@repo//test/internal:module.bzl", "foo") load("@repo//test/private:module.bzl", "bar") load("@repo//test/external:module.bzl", "baz") `, []string{}, scopeEverywhere) } func TestBzlVisibilityJavatest(t *testing.T) { defer setUpTestPackage("foo/javatests/bar")() checkFindings(t, "bzl-visibility", ` load("//foo/java/bar/internal/baz:module.bzl", "foo") load("//foo/java/bar/private/baz:module.bzl", "bar") load("//foo/javatests/bar/internal/baz:module.bzl", "foo1") load("//foo/javatests/bar/private/baz:module.bzl", "bar1") foo() bar() foo1() bar1() `, []string{}, scopeEverywhere) } // Test that the warning is not issued if the module has a visibility statement. func TestBzlVisibilityWithVisibilityStatement(t *testing.T) { defer setUpFileReader(map[string]string{ "foo/private/bar.bzl": ` visibility([ "//test/package:__subpackages__", ]) abc = 1 `, })() checkFindings(t, "bzl-visibility", ` load("//foo/private:bar.bzl", "abc") `, []string{}, scopeEverywhere) } // Test that the warning *is* issued if the module does *not* have a visibility statement. func TestBzlVisibilityWithoutVisibilityStatement(t *testing.T) { defer setUpFileReader(map[string]string{ "foo/private/bar.bzl": ` abc = 1 `, })() checkFindings(t, "bzl-visibility", ` load("//foo/private:bar.bzl", "abc") `, []string{ `Module "//foo/private:bar.bzl" can only be loaded from files located inside`, }, scopeEverywhere) } buildtools-10.1.0/warn/warn_bazel_api.go0000664000175000017500000011503215254573013017621 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Warnings for incompatible changes in the Bazel API package warn import ( "fmt" "sort" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/bzlenv" "github.com/bazel-contrib/buildtools/v10/edit" "github.com/bazel-contrib/buildtools/v10/edit/bzlmod" "github.com/bazel-contrib/buildtools/v10/labels" "github.com/bazel-contrib/buildtools/v10/tables" ) // Bazel API-specific warnings // negateExpression returns an expression which is a negation of the input. // If it's a boolean literal (true or false), just return the opposite literal. // If it's a unary expression with a unary `not` operator, just remove it. // Otherwise, insert a `not` operator. // It's assumed that input is no longer needed as it may be mutated or reused by the function. func negateExpression(expr build.Expr) build.Expr { paren, ok := expr.(*build.ParenExpr) if ok { newParen := *paren newParen.X = negateExpression(paren.X) return &newParen } unary, ok := expr.(*build.UnaryExpr) if ok && unary.Op == "not" { return unary.X } boolean, ok := expr.(*build.Ident) if ok { newBoolean := *boolean if boolean.Name == "True" { newBoolean.Name = "False" } else { newBoolean.Name = "True" } return &newBoolean } return &build.UnaryExpr{ Op: "not", X: expr, } } // getParam search for a param with a given name in a given list of function arguments // and returns it with its index func getParam(attrs []build.Expr, paramName string) (int, *build.Ident, *build.AssignExpr) { for i, attr := range attrs { as, ok := attr.(*build.AssignExpr) if !ok { continue } name, ok := (as.LHS).(*build.Ident) if !ok || name.Name != paramName { continue } return i, name, as } return -1, nil, nil } // isFunctionCall checks whether expr is a call of a function with a given name func isFunctionCall(expr build.Expr, name string) (*build.CallExpr, bool) { call, ok := expr.(*build.CallExpr) if !ok { return nil, false } if ident, ok := call.X.(*build.Ident); ok && ident.Name == name { return call, true } return nil, false } // globalVariableUsageCheck checks whether there's a usage of a given global variable in the file. // It's ok to shadow the name with a local variable and use it. // Type expressions are checked only if isType is true. func globalVariableUsageCheck(f *build.File, global string, isType bool, alternative string) []*LinterFinding { var findings []*LinterFinding if f.Type != build.TypeBzl { return findings } // Nodes that will be ignored skipNodes := make(map[build.Expr]bool) var walk func(e *build.Expr, env *bzlenv.Environment) walk = func(e *build.Expr, env *bzlenv.Environment) { if _, ok := skipNodes[*e]; ok { return } var ident *build.Ident // Recurse into type syntax only if isType is set. switch node := (*e).(type) { case *build.Ident: ident = node case *build.TypedIdent: // Either recurse or process the ident now without recursing if isType { defer bzlenv.WalkOnceWithEnvironment(node, env, walk) } else { ident = node.GetIdent() } case *build.TypeAliasStmt: if isType { defer bzlenv.WalkOnceWithEnvironment(node, env, walk) } case *build.DefStmt: if node.Type != nil && !isType { skipNodes[node.Type] = true } defer bzlenv.WalkOnceWithEnvironment(node, env, walk) case *build.CallExpr: switch node.CallExprKind { case build.CallExprCast: if !isType && len(node.List) > 0 { skipNodes[node.List[0]] = true } case build.CallExprIsInstance: if !isType && len(node.List) > 1 { skipNodes[node.List[1]] = true } } defer bzlenv.WalkOnceWithEnvironment(node, env, walk) default: defer bzlenv.WalkOnceWithEnvironment(node, env, walk) } if ident == nil { return } if ident.Name != global { return } if binding := env.Get(ident.Name); binding != nil { return } // Fix newIdent := *ident newIdent.Name = alternative findings = append(findings, makeLinterFinding(ident, fmt.Sprintf(`Global variable %q is deprecated in favor of %q. Please rename it.`, global, alternative), LinterReplacement{e, &newIdent})) } var expr build.Expr = f walk(&expr, bzlenv.NewEnvironment()) return findings } // insertLoad returns a *LinterReplacement object representing a replacement required for inserting // an additional load statement. Returns nil if nothing needs to be changed. func insertLoad(f *build.File, module string, symbols []string) *LinterReplacement { // Try to find an existing load statement for i, stmt := range f.Stmt { load, ok := stmt.(*build.LoadStmt) if !ok || load.Module.Value != module { continue } // Modify an existing load statement newLoad := *load if !edit.AppendToLoad(&newLoad, symbols, symbols) { return nil } return &LinterReplacement{&(f.Stmt[i]), &newLoad} } // Need to insert a new load statement. Can't modify the tree here, so just insert a placeholder // nil statement and return a replacement for it. i := 0 for i = range f.Stmt { stmt := f.Stmt[i] if _, isComment := stmt.(*build.CommentBlock); isComment { continue } if _, isDocString := stmt.(*build.StringExpr); !isDocString { // Insert a nil statement here break } } stmts := append([]build.Expr{}, f.Stmt[:i]...) stmts = append(stmts, nil) stmts = append(stmts, f.Stmt[i:]...) f.Stmt = stmts return &LinterReplacement{&(f.Stmt[i]), edit.NewLoad(module, symbols, symbols)} } // Caches the result of bzlmod.ExtractModuleToApparentNameMapping. var moduleToApparentRepoName func(string) string // useApparentRepoNameIfExternal replaces the module name in a load statement with the apparent repository // name used by the root Bazel module (if any). func useApparentRepoNameIfExternal(load string, fileReader *FileReader) string { if !strings.HasPrefix(load, "@") || fileReader == nil { // Not a load from an external repository or we can't load external files. return load } if moduleToApparentRepoName == nil { moduleToApparentRepoName = bzlmod.ExtractModuleToApparentNameMapping(func(relPath string) *build.File { return fileReader.GetFile("", relPath) }) } l := labels.Parse(load) apparentName := moduleToApparentRepoName(l.Repository) if apparentName == "" { // The module that hosts the load is not a bazel_dep of the root module. We assume that's // because it is a WORKSPACE repo, which uses the legacy name. apparentName = tables.ModuleToLegacyRepoName[l.Repository] if apparentName == "" { apparentName = l.Repository } } l.Repository = apparentName return l.Format() } func notLoadedFunctionUsageCheckInternal(expr *build.Expr, env *bzlenv.Environment, globals []string, loadFrom string, fileReader *FileReader) ([]string, []*LinterFinding) { var loads []string var findings []*LinterFinding call, ok := (*expr).(*build.CallExpr) if !ok { return loads, findings } var name string var replacements []LinterReplacement switch node := call.X.(type) { case *build.DotExpr: // Maybe native.`global`? ident, ok := node.X.(*build.Ident) if !ok || ident.Name != "native" { return loads, findings } name = node.Name // Replace `native.foo()` with `foo()` newCall := *call newCall.X = &build.Ident{Name: node.Name} replacements = append(replacements, LinterReplacement{expr, &newCall}) case *build.Ident: // Maybe `global`()? if binding := env.Get(node.Name); binding != nil { return loads, findings } name = node.Name default: return loads, findings } for _, global := range globals { if name == global { loads = append(loads, name) findings = append(findings, makeLinterFinding(call.X, fmt.Sprintf(`Function %q is not global anymore and needs to be loaded from %q.`, global, useApparentRepoNameIfExternal(loadFrom, fileReader)), replacements...)) break } } return loads, findings } func notLoadedSymbolUsageCheckInternal(expr *build.Expr, env *bzlenv.Environment, globals []string, loadFrom string, fileReader *FileReader) ([]string, []*LinterFinding) { var loads []string var findings []*LinterFinding ident, ok := (*expr).(*build.Ident) if !ok { return loads, findings } if binding := env.Get(ident.Name); binding != nil { return loads, findings } for _, global := range globals { if ident.Name == global { loads = append(loads, ident.Name) findings = append(findings, makeLinterFinding(ident, fmt.Sprintf(`Symbol %q is not global anymore and needs to be loaded from %q.`, global, useApparentRepoNameIfExternal(loadFrom, fileReader)))) break } } return loads, findings } // notLoadedUsageCheck checks whether there's a usage of a given not imported function or symbol in the file // and adds a load statement if necessary. func notLoadedUsageCheck(f *build.File, fileReader *FileReader, functions, symbols []string, loadFrom string) []*LinterFinding { toLoad := make(map[string]bool) var findings []*LinterFinding var walk func(expr *build.Expr, env *bzlenv.Environment) walk = func(expr *build.Expr, env *bzlenv.Environment) { defer bzlenv.WalkOnceWithEnvironment(*expr, env, walk) functionLoads, functionFindings := notLoadedFunctionUsageCheckInternal(expr, env, functions, loadFrom, fileReader) findings = append(findings, functionFindings...) for _, load := range functionLoads { toLoad[load] = true } symbolLoads, symbolFindings := notLoadedSymbolUsageCheckInternal(expr, env, symbols, loadFrom, fileReader) findings = append(findings, symbolFindings...) for _, load := range symbolLoads { toLoad[load] = true } } var expr build.Expr = f walk(&expr, bzlenv.NewEnvironment()) if len(toLoad) == 0 { return nil } loadFrom = useApparentRepoNameIfExternal(loadFrom, fileReader) loads := []string{} for l := range toLoad { loads = append(loads, l) } sort.Strings(loads) replacement := insertLoad(f, loadFrom, loads) if replacement != nil { // Add the same replacement to all relevant findings. for _, f := range findings { f.Replacement = append(f.Replacement, *replacement) } } return findings } // NotLoadedFunctionUsageCheck checks whether there's a usage of a given not imported function in the file // and adds a load statement if necessary. func NotLoadedFunctionUsageCheck(f *build.File, fileReader *FileReader, globals []string, loadFrom string) []*LinterFinding { return notLoadedUsageCheck(f, fileReader, globals, []string{}, loadFrom) } // NotLoadedSymbolUsageCheck checks whether there's a usage of a given not imported function in the file // and adds a load statement if necessary. func NotLoadedSymbolUsageCheck(f *build.File, fileReader *FileReader, globals []string, loadFrom string) []*LinterFinding { return notLoadedUsageCheck(f, fileReader, []string{}, globals, loadFrom) } // makePositional makes the function argument positional (removes the keyword if it exists) func makePositional(argument build.Expr) build.Expr { if binary, ok := argument.(*build.AssignExpr); ok { return binary.RHS } return argument } // makeKeyword makes the function argument keyword (adds or edits the keyword name) func makeKeyword(argument build.Expr, name string) build.Expr { assign, ok := argument.(*build.AssignExpr) if !ok { return &build.AssignExpr{ LHS: &build.Ident{Name: name}, Op: "=", RHS: argument, } } ident, ok := assign.LHS.(*build.Ident) if ok && ident.Name == name { // Nothing to change return argument } // Technically it's possible that the LHS is not an ident, but that is a syntax error anyway. newAssign := *assign newAssign.LHS = &build.Ident{Name: name} return &newAssign } func attrConfigurationWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Find nodes that match the following pattern: attr.xxxx(..., cfg = "data", ...) and attr.xxxx(..., cfg = "host", ...) call, ok := (*expr).(*build.CallExpr) if !ok { return } dot, ok := (call.X).(*build.DotExpr) if !ok { return } base, ok := dot.X.(*build.Ident) if !ok || base.Name != "attr" { return } i, _, param := getParam(call.List, "cfg") if param == nil { return } value, ok := (param.RHS).(*build.StringExpr) if !ok { return } newCall := *call switch value.Value { case "data": newCall.List = append(newCall.List[:i], newCall.List[i+1:]...) findings = append(findings, makeLinterFinding(param, `cfg = "data" for attr definitions has no effect and should be removed.`, LinterReplacement{expr, &newCall})) case "host": { newCall.List = append([]build.Expr{}, newCall.List...) newParam := newCall.List[i].Copy().(*build.AssignExpr) newRHS := newParam.RHS.Copy().(*build.StringExpr) newRHS.Value = "exec" newParam.RHS = newRHS newCall.List[i] = newParam findings = append(findings, makeLinterFinding(param, `cfg = "host" for attr definitions should be replaced by cfg = "exec".`, LinterReplacement{expr, &newCall})) } default: // value not matched. return } }) return findings } func depsetItemsWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding types := DetectTypes(f) build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { call, ok := (*expr).(*build.CallExpr) if !ok { return } base, ok := call.X.(*build.Ident) if !ok || base.Name != "depset" { return } if len(call.List) == 0 { return } _, _, param := getParam(call.List, "items") if param != nil { findings = append(findings, makeLinterFinding(param, `Parameter "items" is deprecated, use "direct" and/or "transitive" instead.`)) return } if _, ok := call.List[0].(*build.AssignExpr); ok { return } // We have an unnamed first parameter. Check the type. if types[call.List[0]] == Depset { findings = append(findings, makeLinterFinding(call.List[0], `Giving a depset as first unnamed parameter to depset() is deprecated, use the "transitive" parameter instead.`)) } }) return findings } func attrNonEmptyWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Find nodes that match the following pattern: attr.xxxx(..., non_empty = ..., ...) call, ok := (*expr).(*build.CallExpr) if !ok { return } dot, ok := (call.X).(*build.DotExpr) if !ok { return } base, ok := dot.X.(*build.Ident) if !ok || base.Name != "attr" { return } _, name, param := getParam(call.List, "non_empty") if param == nil { return } // Fix newName := *name newName.Name = "allow_empty" negatedRHS := negateExpression(param.RHS) findings = append(findings, makeLinterFinding(param, "non_empty attributes for attr definitions are deprecated in favor of allow_empty.", LinterReplacement{¶m.LHS, &newName}, LinterReplacement{¶m.RHS, negatedRHS}, )) }) return findings } func attrSingleFileWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Find nodes that match the following pattern: attr.xxxx(..., single_file = ..., ...) call, ok := (*expr).(*build.CallExpr) if !ok { return } dot, ok := (call.X).(*build.DotExpr) if !ok { return } base, ok := dot.X.(*build.Ident) if !ok || base.Name != "attr" { return } singleFileIndex, singleFileKw, singleFileParam := getParam(call.List, "single_file") if singleFileParam == nil { return } // Fix newCall := *call newCall.List = append([]build.Expr{}, call.List...) newSingleFileKw := *singleFileKw newSingleFileKw.Name = "allow_single_file" singleFileValue := singleFileParam.RHS if boolean, ok := singleFileValue.(*build.Ident); ok && boolean.Name == "False" { // if the value is `False`, just remove the whole parameter newCall.List = append(newCall.List[:singleFileIndex], newCall.List[singleFileIndex+1:]...) } else { // search for `allow_files` parameter in the same attr definition and remove it allowFileIndex, _, allowFilesParam := getParam(call.List, "allow_files") if allowFilesParam != nil { singleFileValue = allowFilesParam.RHS newCall.List = append(newCall.List[:allowFileIndex], newCall.List[allowFileIndex+1:]...) if singleFileIndex > allowFileIndex { singleFileIndex-- } } } findings = append(findings, makeLinterFinding(singleFileParam, "single_file is deprecated in favor of allow_single_file.", LinterReplacement{expr, &newCall}, LinterReplacement{&singleFileParam.LHS, &newSingleFileKw}, LinterReplacement{&singleFileParam.RHS, singleFileValue}, )) }) return findings } func ctxActionsWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Find nodes that match the following pattern: ctx.xxxx(...) call, ok := (*expr).(*build.CallExpr) if !ok { return } dot, ok := (call.X).(*build.DotExpr) if !ok { return } base, ok := dot.X.(*build.Ident) if !ok || base.Name != "ctx" { return } switch dot.Name { case "new_file", "experimental_new_directory", "file_action", "action", "empty_action", "template_action": // fix default: return } // Fix newCall := *call newCall.List = append([]build.Expr{}, call.List...) newDot := *dot newCall.X = &newDot switch dot.Name { case "new_file": if len(call.List) > 2 { // Can't fix automatically because the new API doesn't support the 3 arguments signature findings = append(findings, makeLinterFinding(dot, fmt.Sprintf(`"ctx.new_file" is deprecated in favor of "ctx.actions.declare_file".`))) return } newDot.Name = "actions.declare_file" if len(call.List) == 2 { // swap arguments: // ctx.new_file(sibling, name) -> ctx.actions.declare_file(name, sibling=sibling) newCall.List[0], newCall.List[1] = makePositional(call.List[1]), makeKeyword(call.List[0], "sibling") } case "experimental_new_directory": newDot.Name = "actions.declare_directory" case "file_action": newDot.Name = "actions.write" i, ident, param := getParam(newCall.List, "executable") if ident != nil { newIdent := *ident newIdent.Name = "is_executable" newParam := *param newParam.LHS = &newIdent newCall.List[i] = &newParam } case "action": newDot.Name = "actions.run" if _, _, command := getParam(call.List, "command"); command != nil { newDot.Name = "actions.run_shell" } case "empty_action": newDot.Name = "actions.do_nothing" case "template_action": newDot.Name = "actions.expand_template" if i, ident, param := getParam(call.List, "executable"); ident != nil { newIdent := *ident newIdent.Name = "is_executable" newParam := *param newParam.LHS = &newIdent newCall.List[i] = &newParam } } findings = append(findings, makeLinterFinding(dot, fmt.Sprintf(`"ctx.%s" is deprecated in favor of "ctx.%s".`, dot.Name, newDot.Name), LinterReplacement{expr, &newCall})) }) return findings } func fileTypeWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding var walk func(e *build.Expr, env *bzlenv.Environment) walk = func(e *build.Expr, env *bzlenv.Environment) { defer bzlenv.WalkOnceWithEnvironment(*e, env, walk) call, ok := isFunctionCall(*e, "FileType") if !ok { return } if binding := env.Get("FileType"); binding == nil { findings = append(findings, makeLinterFinding(call, "The FileType function is deprecated.")) } } var expr build.Expr = f walk(&expr, bzlenv.NewEnvironment()) return findings } func packageNameWarning(f *build.File) []*LinterFinding { return globalVariableUsageCheck(f, "PACKAGE_NAME", false, "native.package_name()") } func repositoryNameWarning(f *build.File) []*LinterFinding { return globalVariableUsageCheck(f, "REPOSITORY_NAME", false, "native.repository_name()") } func outputGroupWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Find nodes that match the following pattern: ctx.attr.xxx.output_group outputGroup, ok := (*expr).(*build.DotExpr) if !ok || outputGroup.Name != "output_group" { return } dep, ok := (outputGroup.X).(*build.DotExpr) if !ok { return } attr, ok := (dep.X).(*build.DotExpr) if !ok || attr.Name != "attr" { return } ctx, ok := (attr.X).(*build.Ident) if !ok || ctx.Name != "ctx" { return } // Replace `xxx.output_group` with `xxx[OutputGroupInfo]` findings = append(findings, makeLinterFinding(outputGroup, `"ctx.attr.dep.output_group" is deprecated in favor of "ctx.attr.dep[OutputGroupInfo]".`, LinterReplacement{expr, &build.IndexExpr{ X: dep, Y: &build.Ident{Name: "OutputGroupInfo"}, }, })) }) return findings } func nativeGitRepositoryWarning(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{"git_repository", "new_git_repository"}, "@bazel_tools//tools/build_defs/repo:git.bzl") } func nativeHTTPArchiveWarning(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{"http_archive"}, "@bazel_tools//tools/build_defs/repo:http.bzl") } func nativeAndroidRulesWarning(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, tables.AndroidNativeRules, tables.AndroidLoadPath) } // NativeCcRulesWarning produces a warning for missing loads of cc rules func NativeCcRulesWarning(rule string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{rule}, tables.CcLoadPathPrefix+":"+rule+".bzl") } } // NativeCcToolchainRulesWarning produces a warning for missing loads of cc toolchain rules func NativeCcToolchainRulesWarning(rule string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{rule}, tables.CcLoadPathPrefix+"/toolchains:"+rule+".bzl") } } // NativeCcSymbolsWarning produces a warning for missing loads of cc top-level symbols func NativeCcSymbolsWarning(symbol string, bzlfile string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedSymbolUsageCheck(f, fileReader, []string{symbol}, tables.CcLoadPathPrefix+"/common:"+bzlfile+".bzl") } } // NativeJavaRulesWarning produces a warning for missing loads of java rules func NativeJavaRulesWarning(rule string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{rule}, tables.JavaLoadPathPrefix+":"+rule+".bzl") } } // NativeJavaToolchainRulesWarning produces a warning for missing loads of java toolchain rules func NativeJavaToolchainRulesWarning(rule string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{rule}, tables.JavaLoadPathPrefix+"/toolchains:"+rule+".bzl") } } // NativeJavaSymbolsWarning produces a warning for missing loads of java top-level symbols func NativeJavaSymbolsWarning(symbol string, bzlfile string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedSymbolUsageCheck(f, fileReader, []string{symbol}, tables.JavaLoadPathPrefix+"/common:"+bzlfile+".bzl") } } func nativePyRulesWarning(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, tables.PyNativeRules, tables.PyLoadPath) } // NativeProtoRulesWarning produces a warning for missing loads of proto rules func NativeProtoRulesWarning(rule string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{rule}, tables.ProtoLoadPathPrefix+":"+rule+".bzl") } } func nativeProtoLangToolchainWarning(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{"proto_lang_toolchain"}, tables.ProtoLoadPathPrefix+"/toolchains:proto_lang_toolchain.bzl") } func nativeProtoSymbolsWarning(symbol string, bzlfile string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedSymbolUsageCheck(f, fileReader, []string{symbol}, tables.ProtoLoadPathPrefix+"/common:"+bzlfile) } } // NativeShellRulesWarning produces a warning for missing loads of shell rules func NativeShellRulesWarning(rule string) func(f *build.File, fileReader *FileReader) []*LinterFinding { return func(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl && f.Type != build.TypeBuild { return nil } return NotLoadedFunctionUsageCheck(f, fileReader, []string{rule}, tables.ShellLoadPathPrefix+":"+rule+".bzl") } } func contextArgsAPIWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding types := DetectTypes(f) build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Search for `.add()` nodes call, ok := (*expr).(*build.CallExpr) if !ok { return } dot, ok := call.X.(*build.DotExpr) if !ok || dot.Name != "add" || types[dot.X] != CtxActionsArgs { return } // If neither before_each nor join_with nor map_fn is specified, the node is ok. // Otherwise if join_with is specified, use `.add_joined` instead. // Otherwise use `.add_all` instead. _, beforeEachKw, beforeEach := getParam(call.List, "before_each") _, _, joinWith := getParam(call.List, "join_with") _, mapFnKw, mapFn := getParam(call.List, "map_fn") if beforeEach == nil && joinWith == nil && mapFn == nil { // No deprecated API detected return } // Fix var replacements []LinterReplacement newDot := *dot newDot.Name = "add_all" replacements = append(replacements, LinterReplacement{&call.X, &newDot}) if joinWith != nil { newDot.Name = "add_joined" if beforeEach != nil { // `add_joined` doesn't have a `before_each` parameter, replace it with `format_each`: // `before_each = foo` -> `format_each = foo + "%s"` newBeforeEachKw := *beforeEachKw newBeforeEachKw.Name = "format_each" replacements = append(replacements, LinterReplacement{&beforeEach.LHS, &newBeforeEachKw}) replacements = append(replacements, LinterReplacement{&beforeEach.RHS, &build.BinaryExpr{ X: beforeEach.RHS, Op: "+", Y: &build.StringExpr{Value: "%s"}, }}) } } if mapFnKw != nil { // Replace `map_fn = ...` with `map_each = ...` newMapFnKw := *mapFnKw newMapFnKw.Name = "map_each" replacements = append(replacements, LinterReplacement{&mapFn.LHS, &newMapFnKw}) } findings = append(findings, makeLinterFinding(call, `"ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`, replacements...)) }) return findings } func attrOutputDefaultWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.Walk(f, func(expr build.Expr, stack []build.Expr) { // Find nodes that match the following pattern: attr.output(..., default = ...) call, ok := expr.(*build.CallExpr) if !ok { return } dot, ok := (call.X).(*build.DotExpr) if !ok || dot.Name != "output" { return } base, ok := dot.X.(*build.Ident) if !ok || base.Name != "attr" { return } _, _, param := getParam(call.List, "default") if param == nil { return } findings = append(findings, makeLinterFinding(param, `The "default" parameter for attr.output() is deprecated.`)) }) return findings } func attrLicenseWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.Walk(f, func(expr build.Expr, stack []build.Expr) { // Find nodes that match the following pattern: attr.license(...) call, ok := expr.(*build.CallExpr) if !ok { return } dot, ok := (call.X).(*build.DotExpr) if !ok || dot.Name != "license" { return } base, ok := dot.X.(*build.Ident) if !ok || base.Name != "attr" { return } findings = append(findings, makeLinterFinding(expr, `"attr.license()" is deprecated and shouldn't be used.`)) }) return findings } // ruleImplReturnWarning checks whether a rule implementation function returns an old-style struct func ruleImplReturnWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding // iterate over rules and collect rule implementation function names implNames := make(map[string]bool) build.Walk(f, func(expr build.Expr, stack []build.Expr) { call, ok := isFunctionCall(expr, "rule") if !ok { return } // Try to get the implementaton parameter either by name or as the first argument var impl build.Expr _, _, param := getParam(call.List, "implementation") if param != nil { impl = param.RHS } else if len(call.List) > 0 { impl = call.List[0] } if name, ok := impl.(*build.Ident); ok { implNames[name.Name] = true } }) // iterate over functions for _, stmt := range f.Stmt { def, ok := stmt.(*build.DefStmt) if !ok || !implNames[def.Name] { // either not a function or not used in the file as a rule implementation function continue } // traverse the function and find all of its return statements build.Walk(def, func(expr build.Expr, stack []build.Expr) { ret, ok := expr.(*build.ReturnStmt) if !ok { return } // check whether it returns a struct if _, ok := isFunctionCall(ret.Result, "struct"); ok { findings = append(findings, makeLinterFinding(ret, `Avoid using the legacy provider syntax.`)) } }) } return findings } type signature struct { Positional []string // These parameters are typePositional-only Keyword []string // These parameters are typeKeyword-only } var signatures = map[string]signature{ "all": {[]string{"elements"}, []string{}}, "any": {[]string{"elements"}, []string{}}, "tuple": {[]string{"x"}, []string{}}, "list": {[]string{"x"}, []string{}}, "len": {[]string{"x"}, []string{}}, "str": {[]string{"x"}, []string{}}, "repr": {[]string{"x"}, []string{}}, "bool": {[]string{"x"}, []string{}}, "int": {[]string{"x"}, []string{}}, "dir": {[]string{"x"}, []string{}}, "type": {[]string{"x"}, []string{}}, "hasattr": {[]string{"x", "name"}, []string{}}, "getattr": {[]string{"x", "name", "default"}, []string{}}, "select": {[]string{"x"}, []string{}}, } // functionName returns the name of the given function if it's a direct function call (e.g. // `foo(...)` or `native.foo(...)`, but not `foo.bar(...)` or `x[3](...)` func functionName(call *build.CallExpr) (string, bool) { if ident, ok := call.X.(*build.Ident); ok { return ident.Name, true } // Also check for `native.` dot, ok := call.X.(*build.DotExpr) if !ok { return "", false } if ident, ok := dot.X.(*build.Ident); !ok || ident.Name != "native" { return "", false } return dot.Name, true } const ( typePositional int = iota typeKeyword typeArgs typeKwargs ) // paramType returns the type of the param. If it's a typeKeyword param, also returns its name func paramType(param build.Expr) (int, string) { switch param := param.(type) { case *build.AssignExpr: if param.Op == "=" { ident, ok := param.LHSIdent() if ok { return typeKeyword, ident.Name } return typeKeyword, "" } case *build.UnaryExpr: switch param.Op { case "*": return typeArgs, "" case "**": return typeKwargs, "" } } return typePositional, "" } // keywordPositionalParametersWarning checks for deprecated typeKeyword parameters of builtins func keywordPositionalParametersWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding // Check for legacy typeKeyword parameters build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { call, ok := (*expr).(*build.CallExpr) if !ok || len(call.List) == 0 { return } function, ok := functionName(call) if !ok { return } // Findings and replacements for the current call expression var callFindings []*LinterFinding var callReplacements []LinterReplacement signature, ok := signatures[function] if !ok { return } var paramTypes []int // types of the parameters (typeKeyword or not) after the replacements has been applied. for i, parameter := range call.List { pType, name := paramType(parameter) paramTypes = append(paramTypes, pType) if pType == typeKeyword && i < len(signature.Positional) && signature.Positional[i] == name { // The parameter should be typePositional callFindings = append(callFindings, makeLinterFinding( parameter, fmt.Sprintf(`Keyword parameter %q for %q should be positional.`, signature.Positional[i], function), )) callReplacements = append(callReplacements, LinterReplacement{&call.List[i], makePositional(parameter)}) paramTypes[i] = typePositional } if pType == typePositional && i >= len(signature.Positional) && i < len(signature.Positional)+len(signature.Keyword) { // The parameter should be typeKeyword keyword := signature.Keyword[i-len(signature.Positional)] callFindings = append(callFindings, makeLinterFinding( parameter, fmt.Sprintf(`Parameter at the position %d for %q should be keyword (%s = ...).`, i+1, function, keyword), )) callReplacements = append(callReplacements, LinterReplacement{&call.List[i], makeKeyword(parameter, keyword)}) paramTypes[i] = typeKeyword } } if len(callFindings) == 0 { return } // Only apply the replacements if the signature is correct after they have been applied // (i.e. the order of the parameters is typePositional, typeKeyword, typeArgs, typeKwargs) // Otherwise the signature will be not correct, probably it was incorrect initially. // All the replacements should be applied to the first finding for the current node. if sort.IntsAreSorted(paramTypes) { // It's possible that the parameter list had `ForceCompact` set to true because it only contained // positional arguments, and now it has keyword arguments as well. Reset the flag to let the // printer decide how the function call should be formatted. for _, t := range paramTypes { if t == typeKeyword { // There's at least one keyword argument newCall := *call newCall.ForceCompact = false callFindings[0].Replacement = append(callFindings[0].Replacement, LinterReplacement{expr, &newCall}) break } } // Attach all the parameter replacements to the first finding callFindings[0].Replacement = append(callFindings[0].Replacement, callReplacements...) } findings = append(findings, callFindings...) }) return findings } func providerParamsWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.Walk(f, func(expr build.Expr, stack []build.Expr) { call, ok := isFunctionCall(expr, "provider") if !ok { return } _, _, fields := getParam(call.List, "fields") _, _, doc := getParam(call.List, "doc") // doc can also be the first positional argument hasPositional := false if len(call.List) > 0 { if _, ok := call.List[0].(*build.AssignExpr); !ok { hasPositional = true } } msg := "" if fields == nil { msg = "a list of fields" } if doc == nil && !hasPositional { if msg != "" { msg += " and " } msg += "a documentation" } if msg != "" { findings = append(findings, makeLinterFinding(call, `Calls to 'provider' should provide `+msg+`:\n`+ ` provider("description", fields = [...])`)) } }) return findings } func attrNameWarning(f *build.File, names []string) []*LinterFinding { if f.Type != build.TypeBzl { return nil } var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { // Find nodes that match "attrs = {..., "license", ...}" dict, ok := (*expr).(*build.DictExpr) if !ok { return } for _, item := range dict.List { // include only string literal keys into consideration value, ok := item.Key.(*build.StringExpr) if !ok { continue } for _, name := range names { if value.Value == name { findings = append(findings, makeLinterFinding(dict, fmt.Sprintf(`Do not use '%s' as an attribute name.`+ ` It may cause unexpected behavior.`, value.Value))) } } } }) return findings } func attrLicensesWarning(f *build.File) []*LinterFinding { return attrNameWarning(f, []string{"licenses"}) } func attrApplicableLicensesWarning(f *build.File) []*LinterFinding { return attrNameWarning(f, []string{"applicable_licenses", "package_metadata"}) } buildtools-10.1.0/warn/warn_test.go0000664000175000017500000002775115254573013016664 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "bytes" "fmt" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/testutils" ) const ( scopeBuild = build.TypeBuild scopeBzl = build.TypeBzl scopeWorkspace = build.TypeWorkspace scopeDefault = build.TypeDefault scopeModule = build.TypeModule scopeRepo = build.TypeRepo scopeVendor = build.TypeVendor scopeEverywhere = scopeBuild | scopeBzl | scopeWorkspace | scopeDefault | scopeModule | scopeRepo | scopeVendor scopeBazel = scopeBuild | scopeBzl | scopeWorkspace | scopeModule | scopeRepo | scopeVendor scopeDeclarative = scopeBuild | scopeWorkspace | scopeModule | scopeRepo | scopeVendor ) // A global FileReader object that can be used by tests. If a test redefines it it must // reset it when it finishes. var testFileReader *FileReader // A global variable containing package name for test cases. Can be overwritten // but must be reset when the test finishes. var testPackage string = "test/package" // fileReaderRequests is used by tests to check which files have actually been requested by testFileReader var fileReaderRequests []string func setUpFileReader(data map[string]string) (cleanup func()) { readFile := func(filename string) ([]byte, error) { fileReaderRequests = append(fileReaderRequests, filename) if contents, ok := data[filename]; ok { return []byte(contents), nil } return nil, fmt.Errorf("file not found") } testFileReader = NewFileReader(readFile) fileReaderRequests = nil // The cached mapping depends on the file reader, so we need to reset it as well. moduleToApparentRepoName = nil return func() { // Tear down testFileReader = nil fileReaderRequests = nil } } func setUpTestPackage(name string) (cleanup func()) { oldName := testPackage testPackage = name return func() { // Tear down testPackage = oldName } } func getFilename(fileType build.FileType) string { switch fileType { case build.TypeBuild: return "BUILD" case build.TypeWorkspace: return "WORKSPACE" case build.TypeBzl: return "test_file.bzl" case build.TypeModule: return "MODULE.bazel" case build.TypeRepo: return "REPO.bazel" case build.TypeVendor: return "VENDOR.bazel" default: return "test_file.strlrk" } } func getFileForTest(input string, fileType build.FileType) *build.File { input = strings.TrimLeft(input, "\n") filename := getFilename(fileType) file, err := build.Parse(testPackage+"/"+filename, []byte(input)) if err != nil { panic(fmt.Sprintf("%v", err)) } file.Pkg = testPackage file.Label = filename file.WorkspaceRoot = "/home/users/foo/bar" return file } func getFindings(categories, input string, fileType build.FileType) []*Finding { file := getFileForTest(input, fileType) return FileWarnings(file, strings.Split(categories, ","), nil, ModeWarn, testFileReader) } func compareFindings(t *testing.T, categories, input string, expected []string, scope, fileType build.FileType) { // If scope doesn't match the file type, no warnings are expected if scope&fileType == 0 { expected = []string{} } findings := getFindings(categories, input, fileType) // We ensure that there is the expected number of warnings. // At the moment, we check only the line numbers. if len(expected) != len(findings) { t.Errorf("Input: %s", input) t.Errorf("number of matches: %d, want %d", len(findings), len(expected)) for _, e := range expected { t.Errorf("expected: %s", e) } for _, f := range findings { t.Errorf("got: %d: %s", f.Start.Line, f.Message) } return } for i := range findings { msg := fmt.Sprintf(":%d: %s", findings[i].Start.Line, findings[i].Message) if !strings.Contains(msg, expected[i]) { t.Errorf("Input: %s", input) t.Errorf("got: `%s`,\nwant: `%s`", msg, expected[i]) } } } // checkFix makes sure that fixed file contents match the expected output func checkFix(t *testing.T, categories, input, expected string, scope, fileType build.FileType) { // If scope doesn't match the file type, no changes are expected if scope&fileType == 0 { expected = input } file := getFileForTest(input, fileType) goldenFile := getFileForTest(expected, fileType) FixWarnings(file, strings.Split(categories, ","), false, testFileReader) have := build.Format(file) want := build.Format(goldenFile) if !bytes.Equal(have, want) { t.Errorf("fixed a test (type %s) incorrectly:\ninput:\n%s\ndiff (-expected, +ours)\n", fileType, input) testutils.Tdiff(t, want, have) } } // checkFix makes sure that the file contents don't change if a fix is not requested // (i.e. the warning functions have no side effects modifying the AST) func checkNoFix(t *testing.T, categories, input string, fileType build.FileType) { file := getFileForTest(input, fileType) formatted := build.Format(file) // No fixes expected FileWarnings(file, strings.Split(categories, ","), nil, ModeWarn, testFileReader) fixed := build.FormatWithoutRewriting(file) if !bytes.Equal(formatted, fixed) { t.Errorf("Modified a file (type %s) while getting warnings:\ninput:\n%s\ndiff (-before, +after)\n", fileType, input) testutils.Tdiff(t, formatted, fixed) } } func checkFindings(t *testing.T, category, input string, expected []string, scope build.FileType) { // The same as checkFindingsAndFix but ensure that fixes don't change the file (except for formatting) checkFindingsAndFix(t, category, input, input, expected, scope) } func checkFindingsAndFix(t *testing.T, categories, input, output string, expected []string, scope build.FileType) { fileTypes := []build.FileType{ build.TypeDefault, build.TypeBuild, build.TypeWorkspace, build.TypeBzl, build.TypeModule, } for _, fileType := range fileTypes { compareFindings(t, categories, input, expected, scope, fileType) checkFix(t, categories, input, output, scope, fileType) checkFix(t, categories, output, output, scope, fileType) checkNoFix(t, categories, input, fileType) } } func TestCalculateDifference(t *testing.T) { tests := []struct { before string after string start int end int replacement string }{ { before: "asdf", after: "asxydf", start: 2, end: 2, replacement: "xy", }, { before: "asxydf", after: "asdf", start: 2, end: 4, replacement: "", }, { before: "asxydf", after: "asztdf", start: 2, end: 4, replacement: "zt", }, { before: "", after: "foobar", start: 0, end: 0, replacement: "foobar", }, { before: "foobar", after: "", start: 0, end: 6, replacement: "", }, { before: "qwerty", after: "asdfgh", start: 0, end: 6, replacement: "asdfgh", }, { before: "aa", after: "aaaa", start: 2, end: 2, replacement: "aa", }, { before: "aaaa", after: "aa", start: 2, end: 4, replacement: "", }, { before: "abc", after: "abdbc", start: 2, end: 2, replacement: "db", }, { before: "abdbc", after: "abc", start: 2, end: 4, replacement: "", }, } for _, tc := range tests { before := []byte(tc.before) after := []byte(tc.after) start, end, replacement := calculateDifference(&before, &after) if start != tc.start || end != tc.end || replacement != tc.replacement { t.Errorf("Wrong difference for %q and %q: want %d, %d, %q, got %d, %d, %q", tc.before, tc.after, tc.start, tc.end, tc.replacement, start, end, replacement) } } } func TestSuggestions(t *testing.T) { // Suggestions are not generated by individual warning functions but by the warnings framework. contents := `foo() attr.bar(name = "bar", cfg = "data") attr.baz("baz", cfg = "data") ` f, err := build.ParseBzl("file.bzl", []byte(contents)) if err != nil { t.Fatalf("Parse error: %v", err) } findings := FileWarnings(f, []string{"attr-cfg"}, nil, ModeSuggest, testFileReader) want := []struct { start int end int replacement string }{ { start: 28, end: 42, replacement: "", }, { start: 59, end: 73, replacement: "", }, } if len(findings) != len(want) { t.Errorf("Expected %d findings, got %d", len(want), len(findings)) } for i, f := range findings { w := want[i] if f.Replacement == nil { t.Errorf("No replacement for finding %d", i) } r := f.Replacement if r.Start != w.start || r.End != w.end || r.Content != w.replacement { t.Errorf("Wrong replacement #%d, want %d, %d, %q, got %d, %d, %q", i, w.start, w.end, w.replacement, r.Start, r.End, r.Content) } } } func TestDisabledWarning(t *testing.T) { contents := `foo() # buildifier: disable=depset-iteration for x in depset([1, 2, 3]): print(x) # buildozer: disable=print for y in "foobar": # buildozer: disable=string-iteration # buildifier: disable=no-effect y # buildifier: disable=duplicated-name-2 cc_library( name = "foo", # buildifier: disable=duplicated-name-1 ) # buildifier: disable=skylark-comment # some comment mentioning skylark def foo(x, y): # buildifier: disable=unused-variable pass ` f, err := build.ParseBzl("file.bzl", []byte(contents)) if err != nil { t.Fatalf("Parse error: %v", err) } tests := []struct { start int end int category string }{ { start: 3, end: 5, category: "depset-iteration", }, { start: 5, end: 5, category: "print", }, { start: 7, end: 7, category: "string-iteration", }, { start: 8, end: 9, category: "no-effect", }, { start: 13, end: 13, category: "duplicated-name-1", }, { start: 11, end: 14, category: "duplicated-name-2", }, { start: 16, end: 17, category: "skylark-comment", }, { start: 19, end: 19, category: "unused-variable", }, } linesCount := strings.Count(contents, "\n") for _, tc := range tests { for line := 1; line <= linesCount; line++ { disabled := DisabledWarning(f, line, tc.category) shouldBeDisabled := line >= tc.start && line <= tc.end if disabled != shouldBeDisabled { t.Errorf("Wrong disabled status for the category %q, want %t, got %t", tc.category, shouldBeDisabled, disabled) } } } } func TestDisabledWarningMultiple(t *testing.T) { contents := `foo() # buildifier: disable=depset-iteration,print for x in depset([1, 2, 3]): print(x) # buildozer: disable=string-iteration, no-effect (with explanation) for y in "foobar": y ` f, err := build.ParseBzl("file.bzl", []byte(contents)) if err != nil { t.Fatalf("Parse error: %v", err) } tests := []struct { start int end int category string }{ { start: 3, end: 5, category: "depset-iteration", }, { start: 3, end: 5, category: "print", }, { start: 7, end: 9, category: "string-iteration", }, { start: 7, end: 9, category: "no-effect", }, } linesCount := strings.Count(contents, "\n") for _, tc := range tests { for line := 1; line <= linesCount; line++ { disabled := DisabledWarning(f, line, tc.category) shouldBeDisabled := line >= tc.start && line <= tc.end if disabled != shouldBeDisabled { t.Errorf("Wrong disabled status for category %q at line %d: want %t, got %t", tc.category, line, shouldBeDisabled, disabled) } } } } buildtools-10.1.0/warn/warn_docstring_test.go0000664000175000017500000002515615254573013020735 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestModuleDocstring(t *testing.T) { checkFindings(t, "module-docstring", ``, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` # empty file`, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` """This is the module""" load("foo", "bar") bar()`, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` load("foo", "bar") """This is the module""" bar()`, []string{":1: The file has no module docstring."}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` # comment # comment """This is the module""" load("foo", "bar") bar()`, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` # comment load("foo", "bar") # comment """This is the module""" bar()`, []string{":3: The file has no module docstring."}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` def foo(bar): if bar: f() return g()`, []string{":1: The file has no module docstring."}, scopeBzl|scopeDefault) } func TestFunctionDocstringExists(t *testing.T) { checkFindings(t, "function-docstring", ` def f(x): # short function return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring", ` def f(x): """Short function with a docstring""" return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring", ` def f(x): # long function x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{":1: The function \"f\" has no docstring."}, scopeEverywhere) checkFindings(t, "function-docstring", ` def f(x): def g(x): # long function x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x return g `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring", ` def _f(x): # long private function x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{}, scopeEverywhere) } func TestFunctionDocstringHeader(t *testing.T) { checkFindings(t, "function-docstring-header", ` def f(): """This is a function. this is the description """ pass pass pass pass pass `, []string{`2: The docstring for the function "f" should start with a one-line summary.`}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(): def g(): """This is a function. this is the description """ pass pass pass pass pass return f `, []string{`3: The docstring for the function "g" should start with a one-line summary.`}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def _f(x): """Long private function with a docstring""" x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{ `:2: The docstring for the function "_f" should start with a one-line summary.`, }, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(x): """Long function with a docstring Docstring body """ x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(): """ This is a function. This is a multiline description""" pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(): """\r Header in a CRLF formatted file.\r \r This is a\r multiline description""" `, []string{}, scopeEverywhere) } func TestFunctionDocstringArgs(t *testing.T) { checkFindings(t, "function-docstring-args", ` def f(x): """This is a function. Documented here: http://example.com Args: x: something, as described at http://example.com Returns: something, as described at https://example.com """ pass pass pass pass pass return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x): """This is a function. Args: x: something """ passf pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, y): """Short function with a docstring Arguments: x: smth """ return x + y `, []string{ `2: Argument "y" is not documented.`, `4: Prefer "Args:" to "Arguments:" when documenting function arguments.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(): def g(x, y): """Short function with a docstring Arguments: x: smth """ return x + y return g `, []string{ `3: Argument "y" is not documented.`, `5: Prefer "Args:" to "Arguments:" when documenting function arguments.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(): def g(x, y): """Short function with a docstring """ return x + y return g `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def _f(x, y): """Long private function Args: x: something z: something """ x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{ `:2: Argument "y" is not documented.`, `:6: Argument "z" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, y): """This is a function. Arguments: x: something y: something (this is in fact the description of x continued) z: something else Returns: None """ pass pass pass pass pass `, []string{ `2: Argument "y" is not documented.`, `4: Prefer "Args:" to "Arguments:" when documenting function arguments.`, `7: Argument "z" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def my_function(x, y, z = None, *args, **kwargs): """This is a function. """ pass pass pass pass pass `, []string{ `2: Arguments "x", "y", "z", "*args", "**kwargs" are not documented. If the documentation for the arguments exists but is not recognized by Buildifier make sure it follows the line "Args:" which has the same indentation as the opening """, and the argument description starts with ":" and indented with at least one (preferably two) space more than "Args:", for example: def my_function(x): """Function description. Args: x: argument description, can be multiline with additional indentation. """`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, y, z = None, *args, **kwargs): """This is a function. Args: x (Map[string, int]): x y (deprecated, mutable): y z: z *args (List): the args **kwargs: the kwargs """ pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, *, y, z = None): """This is a function. Args: x: x y: y z: z """ pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, *, y, z = None): """This is a function. Args: x: x *: a separator y: y : argument without a name z: z """ pass `, []string{ `6: Argument "*" is documented but doesn't exist in the function signature.`, `8: Argument "" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x): """ This is a function. Args: The function signature is extremely complicated x: something Returns: nothing """ pass pass pass pass pass return None `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(foobar, *bar, **baz): """Some function Args: foobar: something foo: something bar: something baz: something """ pass `, []string{ `:2: Arguments "*bar", "**baz" are not documented.`, `:6: Argument "foo" is documented but doesn't exist in the function signature.`, `:7: Argument "bar" is documented but doesn't exist in the function signature. Do you mean "*bar"?`, `:8: Argument "baz" is documented but doesn't exist in the function signature. Do you mean "**baz"?`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x: int, y: str, z: bool = False, *, *bar: List[int], **baz: Mapping[str, bool]): """Some function Args: x: something t: something """ pass `, []string{ `:2: Arguments "y", "z", "*bar", "**baz" are not documented.`, `:6: Argument "t" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) } func TestFunctionDocstringReturn(t *testing.T) { checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something Returns: something """ pass pass pass pass pass return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ def g(y): return y pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ pass pass pass pass pass return x `, []string{`2: Return value of "f" is not documented.`}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(): def g(x): """This is a function. Args: x: something """ pass pass pass pass pass return x return g `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ pass pass pass pass pass if foo: return else: return x `, []string{`2: Return value of "f" is not documented.`}, scopeEverywhere) } buildtools-10.1.0/warn/warn_load_test.go0000664000175000017500000000244315254573013017652 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "testing" "github.com/bazel-contrib/buildtools/v10/tables" ) func TestWarnLoadLocation(t *testing.T) { tables.AllowedSymbolLoadLocations["s1"] = map[string]bool{":z.bzl": true} tables.AllowedSymbolLoadLocations["s3"] = map[string]bool{":x.bzl": true, ":y.bzl": true} tables.AllowedSymbolLoadLocations["s4"] = map[string]bool{":a.bzl": true} checkFindingsAndFix(t, "allowed-symbol-load-locations", ` load(":f.bzl", "s1", "s2") load(":a.bzl", "s3") load(":a.bzl", "s4") `, ` load(":f.bzl", "s1", "s2") load(":a.bzl", "s3") load(":a.bzl", "s4") `, []string{ ":1: Symbol \"s1\" must be loaded from :z.bzl.", ":2: Symbol \"s3\" must be loaded from one of the allowed locations: :x.bzl, :y.bzl.", }, scopeEverywhere) } buildtools-10.1.0/warn/warn_cosmetic_test.go0000664000175000017500000001674315254573013020551 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestPackageOnTop(t *testing.T) { checkFindingsAndFix(t, "package-on-top", ` my_macro(name = "foo") package()`, ` package() my_macro(name = "foo")`, []string{":2: Package declaration should be at the top of the file, after the load() statements, but before any call to a rule or a macro. package_group() and licenses() may be called before package()."}, scopeDefault|scopeBzl|scopeBuild) checkFindingsAndFix(t, "package-on-top", ` # Some comments """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") package() foo(baz)`, ` # Some comments """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") package() foo(baz)`, []string{}, scopeDefault|scopeBzl|scopeBuild) checkFindingsAndFix(t, "package-on-top", ` # Some comments """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") package_group(name = "my_group") licenses(["my_license"]) foo(baz) package()`, ` # Some comments """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") package_group(name = "my_group") licenses(["my_license"]) package() foo(baz)`, []string{":11: Package declaration should be at the top of the file, after the load() statements, but before any call to a rule or a macro. package_group() and licenses() may be called before package()."}, scopeDefault|scopeBzl|scopeBuild) checkFindingsAndFix(t, "package-on-top", ` """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") VISIBILITY = baz foo() package(default_visibility = VISIBILITY)`, ` """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") VISIBILITY = baz foo() package(default_visibility = VISIBILITY)`, []string{}, scopeDefault|scopeBzl|scopeBuild) checkFindingsAndFix(t, "package-on-top", ` """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") irrelevant = baz foo() package()`, ` """This is a docstring""" load(":foo.bzl", "foo") load(":bar.bzl", baz = "bar") irrelevant = baz foo() package()`, []string{}, scopeDefault|scopeBzl|scopeBuild) } func TestUnsortedDictItems(t *testing.T) { checkFindingsAndFix(t, "unsorted-dict-items", ` d = { "b": "b value", "a": "a value", }`, ` d = { "a": "a value", "b": "b value", }`, []string{":3: Dictionary items are out of their lexicographical order."}, scopeEverywhere) checkFindings(t, "unsorted-dict-items", ` d = { "a": "a value", "a": "a value", }`, []string{}, scopeEverywhere) checkFindingsAndFix(t, "unsorted-dict-items", ` d = { 2: "two", "b": "b value", 1: "one", "a": "a value", 3: "three", }`, ` d = { 2: "two", "a": "a value", 1: "one", "b": "b value", 3: "three", }`, []string{":5: Dictionary items are out of their lexicographical order."}, scopeEverywhere) checkFindings(t, "unsorted-dict-items", ` d = {}`, []string{}, scopeEverywhere) checkFindingsAndFix(t, "unsorted-dict-items", ` d = { # b comment "b": "b value", "a": "a value", }`, ` d = { "a": "a value", # b comment "b": "b value", }`, []string{":4: Dictionary items are out of their lexicographical order."}, scopeEverywhere) checkFindings(t, "unsorted-dict-items", ` # @unsorted-dict-items d = { "b": "b value", "a": "a value", }`, []string{}, scopeEverywhere) checkFindingsAndFix(t, "unsorted-dict-items", ` d = { "key" : { "b": "b value", "a": "a value", } }`, ` d = { "key" : { "a": "a value", "b": "b value", } }`, []string{"4: Dictionary items are out of their lexicographical order."}, scopeEverywhere) checkFindingsAndFix(t, "unsorted-dict-items", ` d = { "srcs": ["foo.go"], "deps": [], }`, ` d = { "deps": [], "srcs": ["foo.go"], }`, []string{"3: Dictionary items are out of their lexicographical order."}, scopeEverywhere) checkFindingsAndFix(t, "unsorted-dict-items", ` d = select({ "//conditions:zzz": ["myrule_b.sh"], "//conditions:default": ["myrule_default.sh"], })`, ` d = select({ "//conditions:zzz": ["myrule_b.sh"], "//conditions:default": ["myrule_default.sh"], })`, []string{}, scopeEverywhere) checkFindingsAndFix(t, "unsorted-dict-items", ` foo_binary = rule( implementation = _foo_binary_impl, attrs = { "_foocc": attr.label( default = Label("//depsets:foocc"), ), "srcs": attr.label_list(allow_files = True), "deps": attr.label_list(), }, outputs = {"out": "%{name}.out"}, )`, ` foo_binary = rule( implementation = _foo_binary_impl, attrs = { "deps": attr.label_list(), "srcs": attr.label_list(allow_files = True), "_foocc": attr.label( default = Label("//depsets:foocc"), ), }, outputs = {"out": "%{name}.out"}, )`, []string{ "7: Dictionary items are out of their lexicographical order.", "8: Dictionary items are out of their lexicographical order.", }, scopeEverywhere) checkFindings(t, "unsorted-dict-items", ` # @unsorted-dict-items d = { "key" : { "b": "b value", "a": "a value", } }`, []string{}, scopeEverywhere) checkFindings(t, "unsorted-dict-items", ` d.update( # @unsorted-dict-items { "b": "value2", "a": "value1", }, )`, []string{}, scopeEverywhere) checkFindings(t, "unsorted-dict-items", ` d.update( { "b": "value2", "a": "value1", }, # @unsorted-dict-items )`, []string{}, scopeEverywhere) } func TestSkylark(t *testing.T) { checkFindingsAndFix(t, "skylark-comment", ` # Skyline foo() # SkyLark # Implemented in skylark # Skylark bar() # SKYLARK # see https://docs.bazel.build/versions/main/skylark/lib/Label.html Label() `, ` # Skyline foo() # Starlark # Implemented in starlark # Starlark bar() # STARLARK # see https://docs.bazel.build/versions/main/skylark/lib/Label.html Label() `, []string{ `:3: "Skylark" is an outdated name of the language, please use "starlark" instead.`, `:7: "Skylark" is an outdated name of the language, please use "starlark" instead.`, }, scopeEverywhere) checkFindingsAndFix(t, "skylark-comment", ` """ Some docstring with skylark """ # buildifier: disable=skylark-docstring def f(): """Some docstring with skylark""" # buildozer: disable=skylark-docstring `, ` """ Some docstring with skylark """ # buildifier: disable=skylark-docstring def f(): """Some docstring with skylark""" # buildozer: disable=skylark-docstring `, []string{}, scopeEverywhere) checkFindingsAndFix(t, "skylark-docstring", ` # Some file """ This is a docstring describing a skylark file """ def f(): """SKYLARK""" def l(): """ Returns https://docs.bazel.build/versions/main/skylark/lib/Label.html """ return Label("skylark") `, ` # Some file """ This is a docstring describing a starlark file """ def f(): """STARLARK""" def l(): """ Returns https://docs.bazel.build/versions/main/skylark/lib/Label.html """ return Label("skylark") `, []string{ `:3: "Skylark" is an outdated name of the language, please use "starlark" instead.`, `:8: "Skylark" is an outdated name of the language, please use "starlark" instead.`, }, scopeEverywhere) } buildtools-10.1.0/warn/types_test.go0000664000175000017500000001040115254573013017041 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "bytes" "fmt" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/testutils" ) func checkTypes(t *testing.T, input, output string) { input = strings.TrimLeft(input, "\n") f, err := build.Parse("test.bzl", []byte(input)) if err != nil { t.Fatalf("%v", err) } types := DetectTypes(f) var edit func(expr build.Expr, stack []build.Expr) build.Expr edit = func(expr build.Expr, stack []build.Expr) build.Expr { t, ok := types[expr] if !ok { return nil } // Traverse the node's children before modifying this node. build.EditChildren(expr, edit) start, _ := expr.Span() return &build.Ident{ Name: fmt.Sprintf("%s:<%s>", t, build.FormatString(expr)), NamePos: start, } } build.Edit(f, edit) want := []byte(strings.TrimLeft(output, "\n")) have := build.FormatWithoutRewriting(f) if !bytes.Equal(have, want) { t.Errorf("detected types incorrectly: diff shows -expected, +ours") testutils.Tdiff(t, want, have) } } func TestTypes(t *testing.T) { checkTypes(t, ` b = True b2 = bool("hello") i = 3 i2 = int(1.2) f = 1.2 f2 = float(3) s = "string" s2 = s s3 = str(42) d = {} d2 = {foo: bar} d3 = dict(**foo) d4 = {k: v for k, v in foo} dep = depset(items=[s, d]) foo = bar `, ` b = bool: b2 = bool:)> i = int:<3> i2 = int:)> f = float:<1.2> f2 = float:)> s = string:<"string"> s2 = string: s3 = string:)> d = dict:<{}> d2 = dict:<{foo: bar}> d3 = dict: d4 = dict:<{k: v for k, v in foo}> dep = depset:, dict:, ]>)> foo = bar `) } func TestScopesFunction(t *testing.T) { checkTypes(t, ` s = "string" def f(): s1 = s def g(): s2 = s1 `, ` s = string:<"string"> def f(): s1 = string: def g(): s2 = s1 `) } func TestScopesParameters(t *testing.T) { checkTypes(t, ` x = 3 y = 4 z = 5 foo(y = "bar") foo(x, y = bar(z = z), t + z) def f(z = "bar"): return z bar(x, y, z) `, ` x = int:<3> y = int:<4> z = int:<5> foo(y = string:<"bar">) foo(int:, y = bar(z = int:), int:>) def f(z = string:<"bar">): return string: bar(int:, int:, int:) `) } func TestBinaryOperators(t *testing.T) { checkTypes(t, ` i = 1 d = {} s = depset() i - foo foo - i d + bar bar + d s | baz baz | s `, ` i = int:<1> d = dict:<{}> s = depset: int: - foo> int:> dict: + bar> dict:> depset: | baz> depset:> `) } func TestPercentOperator(t *testing.T) { checkTypes(t, ` n = 3 s = "foo" foo % n foo % s foo % bar n % foo s % foo s %= foo n %= foo baz = unknown baz %= s baz boq = unknown boq %= n boq `, ` n = int:<3> s = string:<"foo"> foo % int: string:> foo % bar int: % foo> string: % foo> string: %= foo int: %= foo baz = unknown baz %= string: string: boq = unknown boq %= int: boq `) } func TestContext(t *testing.T) { checkTypes(t, ` def foobar(ctx, foo, bar): ctx ctx.actions ctx.actions.args() actions = ctx.actions not_args = actions.args args = actions.args() args `, ` def foobar(ctx:, foo, bar): ctx: ctx.actions:.actions> ctx.actions.args:.actions>.args()> actions = ctx.actions:.actions> not_args = ctx.actions:.args args = ctx.actions.args:.args()> ctx.actions.args: `) } func TestContextFalse(t *testing.T) { checkTypes(t, ` def foobar(foo, bar): ctx ctx.actions ctx.actions.args() `, ` def foobar(foo, bar): ctx ctx.actions ctx.actions.args() `) } buildtools-10.1.0/warn/warn_docstring.go0000664000175000017500000002635215254573013017675 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import ( "fmt" "regexp" "strings" "github.com/bazel-contrib/buildtools/v10/build" ) // FunctionLengthDocstringThreshold is a limit for a function size (in statements), above which // a public function is required to have a docstring. const FunctionLengthDocstringThreshold = 5 // getDocstring returns a docstring of the statements and true if it exists. // Otherwise it returns the first non-comment statement and false. func getDocstring(stmts []build.Expr) (*build.Expr, bool) { for i, stmt := range stmts { if stmt == nil { continue } switch stmt.(type) { case *build.CommentBlock: continue case *build.StringExpr: return &stmts[i], true default: return &stmts[i], false } } return nil, false } func moduleDocstringWarning(f *build.File) []*LinterFinding { if f.Type != build.TypeDefault && f.Type != build.TypeBzl { return nil } if stmt, ok := getDocstring(f.Stmt); stmt != nil && !ok { start, _ := (*stmt).Span() end := build.Position{ Line: start.Line, LineRune: start.LineRune + 1, Byte: start.Byte + 1, } finding := makeLinterFinding(*stmt, `The file has no module docstring. A module docstring is a string literal (not a comment) which should be the first statement of a file (it may follow comment lines).`) finding.End = end return []*LinterFinding{finding} } return nil } func stmtsCount(stmts []build.Expr) int { result := 0 for _, stmt := range stmts { result++ switch stmt := stmt.(type) { case *build.IfStmt: result += stmtsCount(stmt.True) result += stmtsCount(stmt.False) case *build.ForStmt: result += stmtsCount(stmt.Body) } } return result } // docstringInfo contains information about a function docstring type docstringInfo struct { hasHeader bool // whether the docstring has a one-line header args map[string]build.Position // map of documented arguments, the values are line numbers returns bool // whether the return value is documented deprecated bool // whether the function is marked as deprecated argumentsPos build.Position // line of the `Arguments:` block (not `Args:`), if it exists } // countLeadingSpaces returns the number of leading spaces of a string. func countLeadingSpaces(s string) int { spaces := 0 for _, c := range s { if c == ' ' { spaces++ } else { break } } return spaces } var argRegex = regexp.MustCompile(`^ *(\*?\*?\w*)( *\([\w\ ,<>\[\]]+\))?:`) // parseFunctionDocstring parses a function docstring and returns a docstringInfo object containing // the parsed information about the function, its arguments and its return value. func parseFunctionDocstring(doc *build.StringExpr) docstringInfo { start, _ := doc.Span() indent := start.LineRune - 1 prefix := strings.Repeat(" ", indent) lines := strings.Split(doc.Value, "\n") // Trim "/r" in the end of the lines to parse CRLF-formatted files correctly for i, line := range lines { lines[i] = strings.TrimRight(line, "\r") } info := docstringInfo{} info.args = make(map[string]build.Position) isArgumentsDescription := false // Whether the currently parsed block is an 'Args:' section argIndentation := 1000000 // Indentation at which previous arg documentation started for i := range lines { lines[i] = strings.TrimRight(lines[i], " ") } // The first non-empty line should be a single-line header for i, line := range lines { if line == "" { continue } if i == len(lines)-1 || lines[i+1] == "" { info.hasHeader = true } break } // Search for Args: and Returns: sections for i, line := range lines { switch line { case prefix + "Arguments:": info.argumentsPos = build.Position{ Line: start.Line + i, LineRune: indent, } isArgumentsDescription = true continue case prefix + "Args:": isArgumentsDescription = true continue case prefix + "Returns:": isArgumentsDescription = false info.returns = true continue case prefix + "Deprecated:": isArgumentsDescription = false info.deprecated = true continue } if isArgumentsDescription { newIndentation := countLeadingSpaces(line) if line != "" && newIndentation <= indent { // The indented block is over isArgumentsDescription = false continue } else if newIndentation > argIndentation { // Continuation of the previous argument description continue } else { // Maybe a new argument is described here result := argRegex.FindStringSubmatch(line) if len(result) > 1 { argIndentation = newIndentation info.args[result[1]] = build.Position{ Line: start.Line + i, LineRune: indent + argIndentation, } } } } } return info } func hasReturnValues(def *build.DefStmt) bool { result := false build.WalkStatements(def, func(expr build.Expr, stack []build.Expr) (err error) { if _, ok := expr.(*build.DefStmt); ok && len(stack) > 0 { // Don't go into inner function definitions return &build.StopTraversalError{} } ret, ok := expr.(*build.ReturnStmt) if ok && ret.Result != nil { result = true } return }) return result } // isDocstringRequired returns whether a function is required to has a docstring. // A docstring is required for public functions if they are long enough (at least 5 statements) func isDocstringRequired(def *build.DefStmt) bool { if start, _ := def.Span(); start.LineRune > 1 { // Nested functions don't require docstrings return false } return !strings.HasPrefix(def.Name, "_") && stmtsCount(def.Body) >= FunctionLengthDocstringThreshold } func functionDocstringWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding // Docstrings are required only for top-level functions for _, stmt := range f.Stmt { def, ok := stmt.(*build.DefStmt) if !ok { continue } if !isDocstringRequired(def) { continue } if _, ok = getDocstring(def.Body); ok { continue } message := fmt.Sprintf(`The function %q has no docstring. A docstring is a string literal (not a comment) which should be the first statement of a function body (it may follow comment lines).`, def.Name) finding := makeLinterFinding(def, message) _, finding.End = def.HeaderSpan() findings = append(findings, finding) } return findings } func functionDocstringHeaderWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { def, ok := expr.(*build.DefStmt) if !ok { return } doc, ok := getDocstring(def.Body) if !ok { return } info := parseFunctionDocstring((*doc).(*build.StringExpr)) if !info.hasHeader { message := fmt.Sprintf("The docstring for the function %q should start with a one-line summary.", def.Name) findings = append(findings, makeLinterFinding(*doc, message)) } return }) return findings } func functionDocstringArgsWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { def, ok := expr.(*build.DefStmt) if !ok { return } doc, ok := getDocstring(def.Body) if !ok { return } info := parseFunctionDocstring((*doc).(*build.StringExpr)) if info.argumentsPos.LineRune > 0 { argumentsEnd := info.argumentsPos argumentsEnd.LineRune += len("Arguments:") argumentsEnd.Byte += len("Arguments:") finding := makeLinterFinding(*doc, `Prefer "Args:" to "Arguments:" when documenting function arguments.`) finding.Start = info.argumentsPos finding.End = argumentsEnd findings = append(findings, finding) } if !isDocstringRequired(def) && len(info.args) == 0 { return } // If a docstring is required or there are any arguments described, check for their integrity. // Check whether all arguments are documented. notDocumentedArguments := []string{} paramNames := make(map[string]bool) for _, param := range def.Params { name, op := build.GetParamName(param) if name == "" { continue } name = op + name // *args or **kwargs paramNames[name] = true if _, ok := info.args[name]; !ok { notDocumentedArguments = append(notDocumentedArguments, name) } } // Check whether all existing arguments are commented if len(notDocumentedArguments) > 0 { message := fmt.Sprintf("Argument %q is not documented.", notDocumentedArguments[0]) plural := "" if len(notDocumentedArguments) > 1 { message = fmt.Sprintf( `Arguments "%s" are not documented.`, strings.Join(notDocumentedArguments, `", "`), ) plural = "s" } if len(info.args) == 0 { // No arguments are documented maybe the Args: block doesn't exist at all or // formatted improperly. Add extra information to the warning message message += fmt.Sprintf(` If the documentation for the argument%s exists but is not recognized by Buildifier make sure it follows the line "Args:" which has the same indentation as the opening """, and the argument description starts with ":" and indented with at least one (preferably two) space more than "Args:", for example: def %s(%s): """Function description. Args: %s: argument description, can be multiline with additional indentation. """`, plural, def.Name, notDocumentedArguments[0], notDocumentedArguments[0]) } findings = append(findings, makeLinterFinding(*doc, message)) } // Check whether all documented arguments actually exist in the function signature. for name, pos := range info.args { if paramNames[name] { continue } msg := fmt.Sprintf("Argument %q is documented but doesn't exist in the function signature.", name) // *args and **kwargs should be documented with asterisks for _, asterisks := range []string{"*", "**"} { if paramNames[asterisks+name] { msg += fmt.Sprintf(` Do you mean "%s%s"?`, asterisks, name) break } } posEnd := pos posEnd.LineRune += len(name) finding := makeLinterFinding(*doc, msg) finding.Start = pos finding.End = posEnd findings = append(findings, finding) } return }) return findings } func functionDocstringReturnWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { def, ok := expr.(*build.DefStmt) if !ok { return } doc, ok := getDocstring(def.Body) if !ok { return } info := parseFunctionDocstring((*doc).(*build.StringExpr)) // Check whether the return value is documented if isDocstringRequired(def) && hasReturnValues(def) && !info.returns { message := fmt.Sprintf("Return value of %q is not documented.", def.Name) findings = append(findings, makeLinterFinding(*doc, message)) } return }) return findings } buildtools-10.1.0/warn/warn_naming_test.go0000664000175000017500000001423215254573013020203 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestAmbiguousNames(t *testing.T) { checkFindings(t, "confusing-name", ` i = 0 I, x = 1, 2 # here l = 3 # here L = [] L[l] = 4 O = 5 # here x, l = a, b # here a, b = x, l `, []string{ ":2: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":3: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":6: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":8: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", }, scopeEverywhere) checkFindings(t, "confusing-name", ` def l(): # here if True: i = 1 I = 2 # here else: l = 3 # here L = 4 for O in Os: # here pass x, l = a, b # here a, b = x, l `, []string{ ":1: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":4: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":6: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":9: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":12: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", }, scopeEverywhere) checkFindings(t, "confusing-name", ` [l for l in s] cc_library( name = "name-conventions", tags = [I for I in tags if I], ) def f(x): return [O for O in x] `, []string{ ":1: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":5: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":9: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", }, scopeEverywhere) checkFindings(t, "confusing-name", ` [ foo(l, I, O) for l in ls # here if l < m for I, (x, O) in bar # here 2 times for L in Ls if I > l and O == 0 if x == L ] `, []string{ ":3: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":5: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":5: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", }, scopeEverywhere) checkFindings(t, "confusing-name", ` type I = int type J[l] = list[l] def f[O](): pass `, []string{ ":1: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":2: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", ":4: Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').", }, scopeEverywhere) } func TestVariableNames(t *testing.T) { checkFindings(t, "name-conventions", ` _ = 0 foo = 1 FOO = 2 Foo = 3 foo, Bar, BazInfo = 4, 5, 6 foo, (BAR, _) = 7, (8, 9) fOO = 10 FooInfo = 11 _FooInfo = 12 _BAR = 13 _baz = 14 _Foo_bar, foo_Baz = 15, 16 baz: int BAZ: int Baz: int type MyInt = int type _MyInt = int type My_Int = int `, []string{ `:4: Variable name "Foo" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:5: Variable name "Bar" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:7: Variable name "fOO" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:12: Variable name "_Foo_bar" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:12: Variable name "foo_Baz" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:15: Variable name "Baz" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:18: Type name "My_Int" should be lower_snake_case or UpperCamelCase (possibly prefixed with an underscore).`, }, scopeEverywhere) checkFindings(t, "name-conventions", ` def f(x, _, Arg = None): _ = 0 foo = 1 FOO = 2 Foo = 3 foo, Bar, BazInfo = 4, 5, 6 foo, (BAR, _) = 7, (8, 9) fOO = 10 FooInfo = 11 _FooInfo = 12 _BAR = 13 _baz = 14 _Foo_bar, foo_Baz = 15, 16 baz: int BAZ: int Baz: int `, []string{ `:5: Variable name "Foo" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:6: Variable name "Bar" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:8: Variable name "fOO" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:13: Variable name "_Foo_bar" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:13: Variable name "foo_Baz" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, `:16: Variable name "Baz" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, }, scopeEverywhere) checkFindings(t, "name-conventions", ` foo( Bar = 1, ___ = 2, BazInfo = 3, baz = 4, FOO = 5, ) `, []string{}, scopeEverywhere) } buildtools-10.1.0/warn/warn_control_flow.go0000664000175000017500000011310015254573013020374 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Warnings related to the control flow package warn import ( "fmt" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/bzlenv" "github.com/bazel-contrib/buildtools/v10/edit" ) // findReturnsWithoutValue searches for return statements without a value, calls `callback` on // them and returns whether the current list of statements terminates (either by a return or fail() // statements on the current level in all subbranches. func findReturnsWithoutValue(stmts []build.Expr, callback func(*build.ReturnStmt)) bool { if len(stmts) == 0 { // May occur in empty else-clauses return false } terminated := false for _, stmt := range stmts { switch stmt := stmt.(type) { case *build.ReturnStmt: if stmt.Result == nil { callback(stmt) } terminated = true case *build.CallExpr: ident, ok := stmt.X.(*build.Ident) if ok && ident.Name == "fail" { terminated = true } case *build.ForStmt: // Call recursively to find all return statements without a value there. // Even if a for-loop is guaranteed to terminate in each iteration, buildifier still can't // check whether the loop is not empty, so we can't say that the statement after the ForStmt // is unreachable. findReturnsWithoutValue(stmt.Body, callback) case *build.IfStmt: // Save to separate values to avoid short circuit evaluation term1 := findReturnsWithoutValue(stmt.True, callback) term2 := findReturnsWithoutValue(stmt.False, callback) if term1 && term2 { terminated = true } } } return terminated } // missingReturnValueWarning warns if a function returns both explicit and implicit values. func missingReturnValueWarning(f *build.File) []*LinterFinding { findings := []*LinterFinding{} // Collect all def statements in the file defStmts := []*build.DefStmt{} build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { if def, ok := expr.(*build.DefStmt); ok { defStmts = append(defStmts, def) } return }) for _, function := range defStmts { var hasNonEmptyReturns bool build.WalkStatements(function, func(expr build.Expr, stack []build.Expr) (err error) { if _, ok := expr.(*build.DefStmt); ok { if len(stack) > 0 { return &build.StopTraversalError{} } } if ret, ok := expr.(*build.ReturnStmt); ok && ret.Result != nil { hasNonEmptyReturns = true } return err }) if !hasNonEmptyReturns { continue } explicitReturn := findReturnsWithoutValue(function.Body, func(ret *build.ReturnStmt) { findings = append(findings, makeLinterFinding(ret, fmt.Sprintf("Some but not all execution paths of %q return a value.", function.Name))) }) if !explicitReturn { findings = append(findings, makeLinterFinding(function, fmt.Sprintf(`Some but not all execution paths of %q return a value. The function may terminate by an implicit return in the end.`, function.Name))) } } return findings } // findUnreachableStatements searches for unreachable statements (i.e. statements that immediately // follow `return`, `break`, `continue`, and `fail()` statements and calls `callback` on them. // If there are several consequent unreachable statements, it only reports the first of them. // Returns whether the execution is terminated explicitly. func findUnreachableStatements(stmts []build.Expr, callback func(build.Expr)) bool { unreachable := false for _, stmt := range stmts { if unreachable { if _, ok := stmt.(*build.CommentBlock); ok { continue } callback(stmt) return true } switch stmt := stmt.(type) { case *build.ReturnStmt: unreachable = true case *build.CallExpr: ident, ok := stmt.X.(*build.Ident) if ok && ident.Name == "fail" { unreachable = true } case *build.BranchStmt: if stmt.Token != "pass" { // either break or continue unreachable = true } case *build.ForStmt: findUnreachableStatements(stmt.Body, callback) case *build.IfStmt: // Save to separate values to avoid short circuit evaluation term1 := findUnreachableStatements(stmt.True, callback) term2 := findUnreachableStatements(stmt.False, callback) if term1 && term2 { unreachable = true } } } return unreachable } func unreachableStatementWarning(f *build.File) []*LinterFinding { findings := []*LinterFinding{} build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { def, ok := expr.(*build.DefStmt) if !ok { return } findUnreachableStatements(def.Body, func(expr build.Expr) { findings = append(findings, makeLinterFinding(expr, `The statement is unreachable.`)) }) return }) return findings } func noEffectStatementsCheck(body []build.Expr, isTopLevel, isFunc bool, findings []*LinterFinding) []*LinterFinding { seenNonComment := false for _, stmt := range body { if stmt == nil { continue } _, isString := stmt.(*build.StringExpr) if isString { if !seenNonComment && (isTopLevel || isFunc) { // It's a docstring. seenNonComment = true continue } } if _, ok := stmt.(*build.CommentBlock); !ok { seenNonComment = true } switch s := (stmt).(type) { case *build.DefStmt, *build.ForStmt, *build.IfStmt, *build.LoadStmt, *build.ReturnStmt, *build.CallExpr, *build.CommentBlock, *build.BranchStmt, *build.AssignExpr, *build.TypeAliasStmt, *build.TypedIdent: continue case *build.Comprehension: if !isTopLevel || s.Curly { // List comprehensions are allowed on top-level. findings = append(findings, makeLinterFinding(stmt, "Expression result is not used. Use a for-loop instead of a list comprehension.")) } continue } msg := "Expression result is not used." if isString { msg += " Docstrings should be the first statements of a file or a function (they may follow comment lines)." } findings = append(findings, makeLinterFinding(stmt, msg)) } return findings } func noEffectWarning(f *build.File) []*LinterFinding { findings := []*LinterFinding{} findings = noEffectStatementsCheck(f.Stmt, true, false, findings) build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { // Docstrings are valid statements without effects. To detect them we need to // analyze blocks of statements rather than single statements. switch expr := expr.(type) { case *build.ForStmt: findings = noEffectStatementsCheck(expr.Body, false, false, findings) case *build.DefStmt: findings = noEffectStatementsCheck(expr.Function.Body, false, true, findings) case *build.IfStmt: findings = noEffectStatementsCheck(expr.True, false, false, findings) findings = noEffectStatementsCheck(expr.False, false, false, findings) } return }) return findings } // extractIdentsFromStmt returns all idents from an AST node representing a // single statement that are either defined outside the node and used inside, // or defined inside the node and can be used outside. // Examples of idents that don't fall into either of the categories: // - Named arguments of function calls: `foo` in `f(foo = "bar")` // - Iterators of comprehension nodes and its usages: `x` in `[f(x) for x in y]` // - Lambda arguments: `x` in `lambda x: x + 1` // // Statements that contain other statements (for-loops, if-else blocks) are not // traversed inside. func extractIdentsFromStmt(stmt build.Expr) (defined, used map[*build.Ident]bool) { // The values for `defined` are `true` if the warning for the variable should // be suppressed, and `false` otherwise. // It's still important to know that the variable has been assigned in the // current scope because it could shadow a variable with the same name from an // outer scope. defined = make(map[*build.Ident]bool) used = make(map[*build.Ident]bool) // Local scopes for comprehensions scopes := make(map[build.Expr]map[string]bool) // Nodes that are excluded from traversal blockedNodes := make(map[build.Expr]bool) build.WalkInterruptable(stmt, func(node build.Expr, stack []build.Expr) (err error) { // Check if the current node has been blocked if _, ok := blockedNodes[node]; ok { return &build.StopTraversalError{} } switch expr := node.(type) { case *build.AssignExpr: // If it's a top-level assign expression, extract LValues from LHS // Otherwise just ignore the LHS, it must be a keyword argument of a // function call. if node != stmt { // The assignment expression is not the statement itself but its child, // that means it's a keyword argument for a function call. Its LHS // should be ignored. blockedNodes[expr.LHS] = true return } hasUnusedComment := edit.ContainsComments(expr, "@unused") // LHS may contain both variables that are being used and variables that // are being assigned to, e.g. in the following example: // x[i][f(name='foo')], y = 1, 2 // `x`, `i`, `f` are used, `y` is assigned, `name` should be ignored. // Further traversal will ignore `name` but won't know that it's in an LHS // of an assign expression, so it'll erroneously collect `y` as used. // After the traversal it'll need to be removed from `used`. // If some (but not all) variables assigned to by the statements are // prefixed with an underscore, suppress the warning on them (i.e. allow // them to be unused). That's a common use case for partial unpacking of // tuples: // // foo, _bar = my_function() # only `foo` is needed // // However if all variables are underscored and unused, they should still // be reported: // // _foo, _bar = my_function() # LHS can just be removed lValues := bzlenv.CollectLValues(expr.LHS) allLValuesUnderscored := true for _, lValue := range lValues { if !strings.HasPrefix(lValue.Name, "_") { allLValuesUnderscored = false break } } for _, lValue := range bzlenv.CollectLValues(expr.LHS) { defined[lValue] = hasUnusedComment || (!allLValuesUnderscored && strings.HasPrefix(lValue.Name, "_")) } case *build.ForStmt: // Like AssignExpr, ForStmt too has an analogue of LHS and RHS. // Unlike AssignExpr, in this function they may appear only in the root of // traversal and shouldn't be traversed inside (the caller of // `extractIdentsFromStmt` should be responsible for checking all // statements including those that are inside for-loops. // It's common to not use all variables (or even not use any of them) // after unpacking tuples, suppress the warning an all of them that are // prefixed with an underscore: // // for _, (_b, c) in iterable: // print(c) for _, lValue := range bzlenv.CollectLValues(expr.Vars) { defined[lValue] = strings.HasPrefix(lValue.Name, "_") } // Don't traverse inside the inner statements (but still traverse into // the expressions in `Vars` an `X`). for _, substmt := range expr.Body { blockedNodes[substmt] = true } case *build.IfStmt: // Nothing special, just don't traverse the inner statements (like with // ForStmt nodes). for _, substmt := range expr.True { blockedNodes[substmt] = true } for _, substmt := range expr.False { blockedNodes[substmt] = true } case *build.Comprehension: // Comprehensions introduce their own visibility scope that shadows the // outer scope. Iterators that are defined and used there don't affect // the usage of variables with the same name outside the comprehension // scope. scope := make(map[string]bool) for _, clause := range expr.Clauses { forClause, ok := clause.(*build.ForClause) if !ok { // if-clause continue } for _, lValue := range bzlenv.CollectLValues(forClause.Vars) { scope[lValue.Name] = true } } scopes[expr] = scope case *build.LambdaExpr: // Similar to Comprehension nodes scope := make(map[string]bool) for _, param := range expr.Params { for _, lValue := range bzlenv.CollectLValues(param) { scope[lValue.Name] = true } } scopes[expr] = scope case *build.Ident: // If the identifier is defined in an intermediate scope, ignore it. for _, node := range stack { if scope, ok := scopes[node]; ok { if _, ok := scope[expr.Name]; ok { return } } } used[expr] = true case *build.TypedIdent: ident := expr.GetIdent() if _, ok := defined[ident]; ok { // If the same ident (not the same variable but the same AST node) // is already registered as defined, it means that our TypedIdent // isn't a var statemet, but the LHS of an AssignExpr that already // processed its lvalues. Nothing left to do here. return } // Var statements declare only 1 variable, so unlike for AssignExpr, // we don't need to check for allLValuesUnderscored. hasUnusedComment := edit.ContainsComments(expr, "@unused") defined[ident] = hasUnusedComment default: // Do nothing, just traverse further } return }) for ident := range defined { // If the same ident (not the same variable but the same AST node) is // registered as both "assigned" and "used", it means it was in fact just // assigned, remove it from "used". delete(used, ident) } return defined, used } // unusedVariableCheck checks for unused variables inside a given node `stmt` (either *build.File or // *build.DefStmt) and variables that are used in the current scope or subscopes, // but not defined here. func unusedVariableCheck(f *build.File, root build.Expr) (map[string]bool, []*LinterFinding) { findings := []*LinterFinding{} // Symbols that are defined in the current scope definedSymbols := make(map[string]build.Expr) // Functions that are defined in the current scope definedFunctions := make(map[string]build.Expr) // For a file top-level, this is the map of type aliases. // For a generic function, this is the scope (logically outer to the function's // own scope) that holds the function's type parameters. definedTypes := make(map[string]build.Expr) // Symbols that are used in the current and inner scopes usedSymbols := make(map[string]bool) // Symbols for which the warning should be suppressed suppressedWarnings := make(map[string]bool) // Symbols from outer scopes that are used in the current scope usedSymbolsFromOuterScope := make(map[string]bool) build.WalkStatements(root, func(expr build.Expr, stack []build.Expr) (err error) { switch expr := expr.(type) { case *build.File: // File nodes don't have anything meaningful, just traverse its subnodes. case *build.DefStmt: if len(stack) > 0 { // Nested def statement. Don't traverse inside, instead call // unusedVariableCheck recursively to handle nested scopes properly. // The function name is defined in the current scope if _, ok := definedFunctions[expr.Name]; !ok { definedFunctions[expr.Name] = expr } if edit.ContainsComments(expr, "@unused") { suppressedWarnings[expr.Name] = true } usedSymbolsInFunction, findingsInFunction := unusedVariableCheck(f, expr) findings = append(findings, findingsInFunction...) for symbol := range usedSymbolsInFunction { usedSymbols[symbol] = true } return &build.StopTraversalError{} } // The function is a root for the current scope. // Collect its type parameters. if expr.TypeParams != nil { for _, typeParam := range bzlenv.CollectLValues(expr.TypeParams) { definedTypes[typeParam.Name] = typeParam if strings.HasPrefix(typeParam.Name, "_") || edit.ContainsComments(typeParam, "@unused") { suppressedWarnings[typeParam.Name] = true } } } // Collect its (normal) parameters as defined in the current scope. finalParameterMarkedUnused := expr.ColonPos != nil && edit.ContainsComments(expr.ColonPos, "@unused") for i, param := range expr.Params { // Function parameters are defined in the current scope. if ident, _ := build.GetParamIdent(param); ident != nil { definedSymbols[ident.Name] = ident if ident.Name == "name" || strings.HasPrefix(ident.Name, "_") || edit.ContainsComments(param, "@unused") || i == len(expr.Params)-1 && finalParameterMarkedUnused { // Don't warn about function arguments if they start with "_" // or explicitly marked with @unused. An @unused comment at the // end of the function header applies to its final argument. // Also don't warn about unused "name" arguments, it could be a // macro where such argument is encouraged (by `unnamed-macro`) // even if not used. suppressedWarnings[ident.Name] = true } // Parameters' types are defined in type params or in the outer scope. paramType := getParamType(param) if paramType != nil { collectUsedTypeSymbols(paramType, definedTypes, usedSymbols, usedSymbolsFromOuterScope) } } // The default variables for the parameters are defined in the outer // scope but used here. assign, ok := param.(*build.AssignExpr) if !ok { continue } // RHS is not a statement, but similar traversal rules should be applied // to it. E.g. it may have a comprehension node with its inner scope or // a function call with a keyword parameter. _, used := extractIdentsFromStmt(assign.RHS) for ident := range used { // RHS idents in the def statement contains direct references to the outer scope. usedSymbolsFromOuterScope[ident.Name] = true } } // Return type may be defined in type params or in the outer scope. if expr.Type != nil { collectUsedTypeSymbols(expr.Type, definedTypes, usedSymbols, usedSymbolsFromOuterScope) } case *build.TypeAliasStmt: if len(stack) > 0 { // Same as for def statements, call unusedVariableCheck recursively to handle // type parameter scope properly. // The type alias name is defined in the current scope typeName := expr.GetIdent().Name if _, ok := definedTypes[typeName]; !ok { definedTypes[typeName] = expr.Name } if edit.ContainsComments(expr, "@unused") { suppressedWarnings[typeName] = true } // TODO: if Bazel/Buck2 ever support recursive type aliases, we'll want to // check for type aliases that are only used in their own definition. usedSymbolsInTypeAlias, findingsInTypeAlias := unusedVariableCheck(f, expr) findings = append(findings, findingsInTypeAlias...) for symbol := range usedSymbolsInTypeAlias { usedSymbols[symbol] = true } return &build.StopTraversalError{} } if expr.TypeParams != nil { for _, typeParam := range bzlenv.CollectLValues(expr.TypeParams) { definedTypes[typeParam.Name] = typeParam if strings.HasPrefix(typeParam.Name, "_") || edit.ContainsComments(typeParam, "@unused") { suppressedWarnings[typeParam.Name] = true } } } collectUsedTypeSymbols(expr.Type, definedTypes, usedSymbols, usedSymbolsFromOuterScope) case *build.LoadStmt: // LoadStmt nodes store the loaded symbols as idents, even though in the // source code they are strings. These idents may confuse the check, // they also shouldn't affect the warnings at all, unused loads are taken // care of by another check. It's safe to just ignore them here. return default: defined, used := extractIdentsFromStmt(expr) for symbol := range used { usedSymbols[symbol.Name] = true } for symbol, isSuppressed := range defined { if _, ok := definedSymbols[symbol.Name]; !ok { definedSymbols[symbol.Name] = symbol if isSuppressed { suppressedWarnings[symbol.Name] = true } } } } return }) // If a variable is defined in an outer scope but also in this scope, it // shadows the outer variable. If it's used in the current scope, it doesn't // make the variable with the same name from an outer scope also used. // Collect variables that are used in the current or inner scopes but are not // defined in the current scope. for symbol := range usedSymbols { if _, ok := definedTypes[symbol]; ok { continue } if _, ok := definedSymbols[symbol]; ok { continue } if _, ok := definedFunctions[symbol]; ok { continue } usedSymbolsFromOuterScope[symbol] = true } // Top-level variables defined in .bzl or generic Starlark files // can be imported from elsewhere, even if not used in the current file. // Do not warn on exportable variables. ignoreTopLevel := (f.Type == build.TypeBzl || f.Type == build.TypeDefault) && root == f findings = appendUnusedVariableLinterFindings(findings, definedTypes, "Type", usedSymbols, suppressedWarnings, ignoreTopLevel) findings = appendUnusedVariableLinterFindings(findings, definedSymbols, "Variable", usedSymbols, suppressedWarnings, ignoreTopLevel) findings = appendUnusedVariableLinterFindings(findings, definedFunctions, "Function", usedSymbols, suppressedWarnings, ignoreTopLevel) return usedSymbolsFromOuterScope, findings } // Return the type annotation of a function parameter, or nil if there is none. func getParamType(param build.Expr) build.Expr { switch param := param.(type) { case *build.TypedIdent: return param.Type case *build.AssignExpr: return getParamType(param.LHS) case *build.UnaryExpr: if param.Op == "*" || param.Op == "**" { return getParamType(param.X) } return nil default: return nil } } // Collect identifiers into a type expression into appropriate used-symbol maps, depending on whether // the identifier was defined as a type parameter in the given type param scope or not. // // Helper function for unusedVariableCheck. func collectUsedTypeSymbols(expr build.Expr, typeParamScope map[string]build.Expr, usedSymbolsFromTypeParams map[string]bool, usedSymbolsFromOuterScope map[string]bool) { _, used := extractIdentsFromStmt(expr) for ident := range used { _, ok := typeParamScope[ident.Name] if ok { usedSymbolsFromTypeParams[ident.Name] = true } else { usedSymbolsFromOuterScope[ident.Name] = true } } } // Format and append linter findings for unused variables to the findings slice. // // Helper function for unusedVariableCheck. func appendUnusedVariableLinterFindings(findings []*LinterFinding, definedSymbols map[string]build.Expr, symbolKind string, usedSymbols map[string]bool, suppressedWarnings map[string]bool, ignoreTopLevel bool) []*LinterFinding { for name, expr := range definedSymbols { if _, ok := usedSymbols[name]; ok { // The variable is used either in this scope or in a nested scope continue } if _, ok := suppressedWarnings[name]; ok { // The variable is explicitly marked with @unused, ignore continue } if ignoreTopLevel && !strings.HasPrefix(name, "_") { continue } if name == "_" { // The "_" name is a strong signal that the variable is intentionally unused; // it's also part of the `_: Any # Enable type checking` idiom. continue } findings = append(findings, makeLinterFinding(expr, fmt.Sprintf(`%s %q is unused. Please remove it.`, symbolKind, name))) } return findings } func unusedVariableWarning(f *build.File) []*LinterFinding { _, findings := unusedVariableCheck(f, f) return findings } func redefinedVariableWarning(f *build.File) []*LinterFinding { findings := []*LinterFinding{} const ( DeclaredVariable = iota AssignedVariable TypeAlias ) definedSymbols := make(map[string]int) types := DetectTypes(f) for _, s := range f.Stmt { switch s := s.(type) { case *build.AssignExpr: left, ok := s.LHSIdent() if !ok { continue } if kind, ok := definedSymbols[left.Name]; !ok || kind == DeclaredVariable { definedSymbols[left.Name] = AssignedVariable continue } if s.Op == "+=" && (types[s.LHS] == List || types[s.RHS] == List) && definedSymbols[left.Name] == AssignedVariable { // Not a reassignment, just appending to a list continue } suffix := "" if definedSymbols[left.Name] == TypeAlias { suffix = " as a type. Redefining a type is incompatible with static type checking." } else { suffix = ". Redefining a global value is discouraged and will be forbidden in the future. Consider using a new variable instead." } findings = append(findings, makeLinterFinding(s.LHS, fmt.Sprintf("Variable %q has already been defined%s", left.Name, suffix))) case *build.TypedIdent: ident := s.GetIdent() if _, ok := definedSymbols[ident.Name]; !ok { definedSymbols[ident.Name] = DeclaredVariable continue } // TODO(#1500): don't report a warning for a var statement that redeclares a global variable because // 1. this cannot be a warning that could be ignored - it is a static fatal error; and // 2. it must be a fatal error for local variables too (not just globals). // To fix, we need a proper resolver stage after parsing. See https://github.com/bazel-contrib/buildtools/issues/1500 case *build.TypeAliasStmt: typeName := s.GetIdent().Name if _, ok := definedSymbols[typeName]; !ok { definedSymbols[typeName] = TypeAlias continue } detail := "" if definedSymbols[typeName] != TypeAlias { detail = " as a variable" } findings = append(findings, makeLinterFinding(s.Name, fmt.Sprintf("Type %q has already been defined%s. Redefining a type is incompatible with static type checking.", typeName, detail))) default: continue } } return findings } func unusedLoadWarning(f *build.File) []*LinterFinding { findings := []*LinterFinding{} loaded := make(map[string]struct { label, from string line int }) symbols := edit.UsedSymbols(f) types := edit.UsedTypes(f) for stmtIndex := 0; stmtIndex < len(f.Stmt); stmtIndex++ { originalLoad, ok := f.Stmt[stmtIndex].(*build.LoadStmt) if !ok { continue } // Findings related to the current load statement loadFindings := []*LinterFinding{} // Copy the `load` object to provide a replacement if needed load := *originalLoad load.From = append([]*build.Ident{}, load.From...) load.To = append([]*build.Ident{}, load.To...) for i := 0; i < len(load.To); i++ { from := load.From[i] to := load.To[i] // Check if the symbol was already loaded origin, alreadyLoaded := loaded[to.Name] start, _ := from.Span() loaded[to.Name] = struct { label, from string line int }{load.Module.Token, from.Name, start.Line} if alreadyLoaded { // The same symbol has already been loaded earlier if origin.label == load.Module.Token && origin.from == from.Name { // Only fix if it's loaded from the same label and variable load.To = append(load.To[:i], load.To[i+1:]...) load.From = append(load.From[:i], load.From[i+1:]...) i-- loadFindings = append(loadFindings, makeLinterFinding(to, fmt.Sprintf("Symbol %q has already been loaded on line %d. Please remove it.", to.Name, origin.line))) continue } loadFindings = append(loadFindings, makeLinterFinding(to, fmt.Sprintf("A different symbol %q has already been loaded on line %d. Please use a different local name.", to.Name, origin.line))) continue } _, ok := symbols[to.Name] if !ok { // Fallback to verify if the symbol is used as a type. _, ok = types[to.Name] } if !ok && !edit.ContainsComments(originalLoad, "@unused") && !edit.ContainsComments(to, "@unused") && !edit.ContainsComments(from, "@unused") { // The loaded symbol is not used and is not protected by a special "@unused" comment load.To = append(load.To[:i], load.To[i+1:]...) load.From = append(load.From[:i], load.From[i+1:]...) i-- loadFindings = append(loadFindings, makeLinterFinding(to, fmt.Sprintf("Loaded symbol %q is unused. Please remove it.\nTo disable the warning, add '@unused' in a comment.", to.Name))) if f.Type == build.TypeDefault || f.Type == build.TypeBzl { loadFindings[len(loadFindings)-1].Message += fmt.Sprintf(` If you want to re-export a symbol, use the following pattern: load(..., _%s = %q, ...) %s = _%s `, to.Name, from.Name, to.Name, to.Name) } } } if len(loadFindings) == 0 { // No problems with the current load statement continue } build.SortLoadArgs(&load) var newStmt build.Expr = &load if len(load.To) == 0 { // If there are no loaded symbols left remove the entire load statement newStmt = nil } replacement := LinterReplacement{&f.Stmt[stmtIndex], newStmt} // Individual replacements can't be combined together: assume we need to remove both loaded // symbols from // // load(":foo.bzl", "a", "b") // // Individual replacements are just to remove each of the symbols, but if these replacements // are applied together, the result will be incorrect and a syntax error in Bazel: // // load(":foo.bzl") // // A workaround is to attach the full replacement to the first finding. loadFindings[0].Replacement = []LinterReplacement{replacement} findings = append(findings, loadFindings...) } return findings } // collectLocalVariables traverses statements (e.g. of a function definition) and returns a list // of idents for variables defined anywhere inside the function. func collectLocalVariables(stmts []build.Expr) []*build.Ident { variables := []*build.Ident{} for _, stmt := range stmts { switch stmt := stmt.(type) { case *build.DefStmt: // Don't traverse nested functions case *build.ForStmt: variables = append(variables, bzlenv.CollectLValues(stmt.Vars)...) variables = append(variables, collectLocalVariables(stmt.Body)...) case *build.IfStmt: variables = append(variables, collectLocalVariables(stmt.True)...) variables = append(variables, collectLocalVariables(stmt.False)...) case *build.AssignExpr: variables = append(variables, bzlenv.CollectLValues(stmt.LHS)...) case *build.TypedIdent: variables = append(variables, bzlenv.CollectLValues(stmt)...) } // Don't need to handle build.TypeAliasStmt - type alias statements must be top-level. } return variables } // findUninitializedVariables takes a list of statements (e.g. body of a block statement) // and a map of previously initialized statements, and calls `callback` on all idents that are not // initialized. An ident is considered initialized if it's initialized by every possible execution // path (before or by `stmts`). // Returns a boolean indicating whether the current list of statements is guaranteed to be // terminated explicitly (by return or fail() statements) and a map of variables that are guaranteed // to be defined by `stmts`. func findUninitializedVariables(stmts []build.Expr, previouslyInitialized map[string]bool, callback func(*build.Ident)) (bool, map[string]bool) { // Variables that are guaranteed to be initialized locallyInitialized := make(map[string]bool) // in the local block of `stmts` initialized := make(map[string]bool) // anywhere before the current line for key := range previouslyInitialized { initialized[key] = true } // findUninitializedIdents traverses an expression (simple statement or a part of it), and calls // `callback` on every *build.Ident that's not mentioned in the map of initialized variables findUninitializedIdents := func(expr build.Expr, callback func(ident *build.Ident)) { // Collect lValues, they shouldn't be taken into account // For example, if the expression is `a = foo(b = c)`, only `c` can be an uninitialized variable here. lValues := make(map[*build.Ident]bool) build.WalkInterruptable(expr, func(expr build.Expr, stack []build.Expr) (err error) { switch expr := expr.(type) { case *build.DefStmt: // Function arguments can't be uninitialized, even if they share the same // name with a variable that's not initialized for some execution path // in an outer scope. for _, param := range expr.Params { if ident, _ := build.GetParamIdent(param); ident != nil { lValues[ident] = true } } // Don't traverse into nested def statements return &build.StopTraversalError{} case *build.AssignExpr: for _, ident := range bzlenv.CollectLValues(expr.LHS) { lValues[ident] = true } } return }) // Check if the ident is really not initialized and call the callback on it. callbackIfNeeded := func(ident *build.Ident) { if !initialized[ident.Name] && !lValues[ident] { callback(ident) } } walkBlockList := map[build.Expr]bool{} build.WalkInterruptable(expr, func(expr build.Expr, stack []build.Expr) (err error) { if walkBlockList[expr] { return &build.StopTraversalError{} } switch expr := expr.(type) { case *build.TypedIdent: // Don't traverse var statements: they declare variables, but don't assign any values. return &build.StopTraversalError{} case *build.DefStmt: // The header of the DefStmt may contain uninitialized variables (e.g. // default values of parameters) and should be traversed. // Its body shouldn't be traversed because it has another scope and will // be analyzed by another call of `findUninitializedVariables`. for _, stmt := range expr.Body { walkBlockList[stmt] = true } case *build.Comprehension, *build.LambdaExpr: // Comprehension and Lambda nodes are special, they have their own scope // with variables that are only defined inside. // Instead of traversing inside stop the traversal and call a special // function to retrieve idents from the outer scope that are used inside // the comprehension. _, used := extractIdentsFromStmt(expr) for ident := range used { callbackIfNeeded(ident) } return &build.StopTraversalError{} case *build.Ident: callbackIfNeeded(expr) default: // Just traverse further } return }) } for _, stmt := range stmts { newlyDefinedVariables := make(map[string]bool) switch stmt := stmt.(type) { case *build.DefStmt: // Don't traverse nested functions case *build.CallExpr: if _, ok := isFunctionCall(stmt, "fail"); ok { return true, locallyInitialized } case *build.ReturnStmt: findUninitializedIdents(stmt, callback) return true, locallyInitialized case *build.BranchStmt: if stmt.Token == "break" || stmt.Token == "continue" { return true, locallyInitialized } case *build.ForStmt: // Although loop variables are defined as local variables, buildifier doesn't know whether // the collection will be empty or not. // Traverse but ignore the result. Even if something is defined inside a for-loop, the loop // may be empty and the variable initialization may not happen. findUninitializedIdents(stmt.X, callback) // The loop can access the variables defined above, and also the for-loop variables. initializedInLoop := make(map[string]bool) for name := range initialized { initializedInLoop[name] = true } for _, ident := range bzlenv.CollectLValues(stmt.Vars) { initializedInLoop[ident.Name] = true } findUninitializedVariables(stmt.Body, initializedInLoop, callback) continue case *build.IfStmt: findUninitializedIdents(stmt.Cond, callback) // Check the variables defined in the if- and else-clauses. terminatedTrue, definedInTrue := findUninitializedVariables(stmt.True, initialized, callback) terminatedFalse, definedInFalse := findUninitializedVariables(stmt.False, initialized, callback) if terminatedTrue && terminatedFalse { return true, locallyInitialized } else if terminatedTrue { // Only take definedInFalse into account for key := range definedInFalse { locallyInitialized[key] = true initialized[key] = true } } else if terminatedFalse { // Only take definedInTrue into account for key := range definedInTrue { locallyInitialized[key] = true initialized[key] = true } } else { // If a variable is defined in both if- and else-clauses, it's considered as defined for key := range definedInTrue { if definedInFalse[key] { locallyInitialized[key] = true initialized[key] = true } } } continue case *build.AssignExpr: // Assignment expression. Collect all definitions from the lhs for _, ident := range bzlenv.CollectLValues(stmt.LHS) { newlyDefinedVariables[ident.Name] = true } } findUninitializedIdents(stmt, callback) for name := range newlyDefinedVariables { locallyInitialized[name] = true initialized[name] = true } } return false, locallyInitialized } // uninitializedVariableWarning warns about usages of values that may not have been initialized. func uninitializedVariableWarning(f *build.File) []*LinterFinding { findings := []*LinterFinding{} build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { def, ok := expr.(*build.DefStmt) if !ok { return } // Get all variables defined in the function body. // If a variable is not defined there, it can be builtin, global, or loaded. localVars := make(map[string]bool) for _, ident := range collectLocalVariables(def.Body) { localVars[ident.Name] = true } // Function parameters are guaranteed to be defined everywhere in the function, even if they // are redefined inside the function body. They shouldn't be taken into consideration. for _, param := range def.Params { if name, _ := build.GetParamName(param); name != "" { delete(localVars, name) } } // Search for all potentially initialized variables in the function body findUninitializedVariables(def.Body, make(map[string]bool), func(ident *build.Ident) { // Check that the found ident represents a local variable if localVars[ident.Name] { findings = append(findings, makeLinterFinding(ident, fmt.Sprintf(`Variable "%s" may not have been initialized.`, ident.Name))) } }) return }) return findings } buildtools-10.1.0/warn/docs/0000775000175000017500000000000015254573013015243 5ustar kenhyskenhysbuildtools-10.1.0/warn/docs/proto/0000775000175000017500000000000015254573013016406 5ustar kenhyskenhysbuildtools-10.1.0/warn/docs/proto/docs.gen.pb.go0000664000175000017500000001510315254573013021035 0ustar kenhyskenhys// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.10 // protoc v7.34.0 // source: warn/docs/proto/docs.proto package proto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Warnings struct { state protoimpl.MessageState `protogen:"open.v1"` Warnings []*Warnings_Warning `protobuf:"bytes,1,rep,name=warnings,proto3" json:"warnings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Warnings) Reset() { *x = Warnings{} mi := &file_warn_docs_proto_docs_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Warnings) String() string { return protoimpl.X.MessageStringOf(x) } func (*Warnings) ProtoMessage() {} func (x *Warnings) ProtoReflect() protoreflect.Message { mi := &file_warn_docs_proto_docs_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Warnings.ProtoReflect.Descriptor instead. func (*Warnings) Descriptor() ([]byte, []int) { return file_warn_docs_proto_docs_proto_rawDescGZIP(), []int{0} } func (x *Warnings) GetWarnings() []*Warnings_Warning { if x != nil { return x.Warnings } return nil } type Warnings_Warning struct { state protoimpl.MessageState `protogen:"open.v1"` Name []string `protobuf:"bytes,1,rep,name=name,proto3" json:"name,omitempty"` Header string `protobuf:"bytes,2,opt,name=header,proto3" json:"header,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` Autofix bool `protobuf:"varint,4,opt,name=autofix,proto3" json:"autofix,omitempty"` BazelFlag string `protobuf:"bytes,5,opt,name=bazel_flag,json=bazelFlag,proto3" json:"bazel_flag,omitempty"` BazelFlagLink string `protobuf:"bytes,6,opt,name=bazel_flag_link,json=bazelFlagLink,proto3" json:"bazel_flag_link,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Warnings_Warning) Reset() { *x = Warnings_Warning{} mi := &file_warn_docs_proto_docs_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Warnings_Warning) String() string { return protoimpl.X.MessageStringOf(x) } func (*Warnings_Warning) ProtoMessage() {} func (x *Warnings_Warning) ProtoReflect() protoreflect.Message { mi := &file_warn_docs_proto_docs_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Warnings_Warning.ProtoReflect.Descriptor instead. func (*Warnings_Warning) Descriptor() ([]byte, []int) { return file_warn_docs_proto_docs_proto_rawDescGZIP(), []int{0, 0} } func (x *Warnings_Warning) GetName() []string { if x != nil { return x.Name } return nil } func (x *Warnings_Warning) GetHeader() string { if x != nil { return x.Header } return "" } func (x *Warnings_Warning) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Warnings_Warning) GetAutofix() bool { if x != nil { return x.Autofix } return false } func (x *Warnings_Warning) GetBazelFlag() string { if x != nil { return x.BazelFlag } return "" } func (x *Warnings_Warning) GetBazelFlagLink() string { if x != nil { return x.BazelFlagLink } return "" } var File_warn_docs_proto_docs_proto protoreflect.FileDescriptor const file_warn_docs_proto_docs_proto_rawDesc = "" + "\n" + "\x1awarn/docs/proto/docs.proto\x12\x04docs\"\xf9\x01\n" + "\bWarnings\x122\n" + "\bwarnings\x18\x01 \x03(\v2\x16.docs.Warnings.WarningR\bwarnings\x1a\xb8\x01\n" + "\aWarning\x12\x12\n" + "\x04name\x18\x01 \x03(\tR\x04name\x12\x16\n" + "\x06header\x18\x02 \x01(\tR\x06header\x12 \n" + "\vdescription\x18\x03 \x01(\tR\vdescription\x12\x18\n" + "\aautofix\x18\x04 \x01(\bR\aautofix\x12\x1d\n" + "\n" + "bazel_flag\x18\x05 \x01(\tR\tbazelFlag\x12&\n" + "\x0fbazel_flag_link\x18\x06 \x01(\tR\rbazelFlagLinkB9Z7github.com/bazel-contrib/buildtools/v10/warn/docs/protob\x06proto3" var ( file_warn_docs_proto_docs_proto_rawDescOnce sync.Once file_warn_docs_proto_docs_proto_rawDescData []byte ) func file_warn_docs_proto_docs_proto_rawDescGZIP() []byte { file_warn_docs_proto_docs_proto_rawDescOnce.Do(func() { file_warn_docs_proto_docs_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_warn_docs_proto_docs_proto_rawDesc), len(file_warn_docs_proto_docs_proto_rawDesc))) }) return file_warn_docs_proto_docs_proto_rawDescData } var file_warn_docs_proto_docs_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_warn_docs_proto_docs_proto_goTypes = []any{ (*Warnings)(nil), // 0: docs.Warnings (*Warnings_Warning)(nil), // 1: docs.Warnings.Warning } var file_warn_docs_proto_docs_proto_depIdxs = []int32{ 1, // 0: docs.Warnings.warnings:type_name -> docs.Warnings.Warning 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_warn_docs_proto_docs_proto_init() } func file_warn_docs_proto_docs_proto_init() { if File_warn_docs_proto_docs_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_warn_docs_proto_docs_proto_rawDesc), len(file_warn_docs_proto_docs_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_warn_docs_proto_docs_proto_goTypes, DependencyIndexes: file_warn_docs_proto_docs_proto_depIdxs, MessageInfos: file_warn_docs_proto_docs_proto_msgTypes, }.Build() File_warn_docs_proto_docs_proto = out.File file_warn_docs_proto_docs_proto_goTypes = nil file_warn_docs_proto_docs_proto_depIdxs = nil } buildtools-10.1.0/warn/docs/proto/docs.proto0000664000175000017500000000054415254573013020426 0ustar kenhyskenhyssyntax = "proto3"; package docs; option go_package = "github.com/bazel-contrib/buildtools/v10/warn/docs/proto"; message Warnings { message Warning { repeated string name = 1; string header = 2; string description = 3; bool autofix = 4; string bazel_flag = 5; string bazel_flag_link = 6; }; repeated Warning warnings = 1; } buildtools-10.1.0/warn/docs/proto/BUILD.bazel0000664000175000017500000000170715254573013020271 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") load("//build:build_defs.bzl", "go_proto_checkedin_test") # gazelle:exclude docs.gen.pb.go go_proto_checkedin_test( src = "docs.gen.pb.go", allowed_diffs = ["^[[:space:]]*//.*protoc"], ) proto_library( name = "docs_proto", srcs = ["docs.proto"], visibility = ["//visibility:public"], ) go_proto_library( name = "proto_go_proto", importpath = "github.com/bazel-contrib/buildtools/v10/warn/docs/proto", proto = ":docs_proto", visibility = ["//visibility:public"], ) go_library( name = "proto", embed = [":proto_go_proto"], importpath = "github.com/bazel-contrib/buildtools/v10/warn/docs/proto", visibility = ["//visibility:public"], ) alias( name = "go_default_library", actual = ":proto", visibility = ["//visibility:public"], ) buildtools-10.1.0/warn/docs/docs.go0000664000175000017500000001155115254573013016525 0ustar kenhyskenhys/* Copyright 2021 Google LLC 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 https://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. */ // Documentation generator package main import ( "bytes" "flag" "fmt" "os" "sort" "strings" "github.com/bazel-contrib/buildtools/v10/warn" "github.com/golang/protobuf/proto" docspb "github.com/bazel-contrib/buildtools/v10/warn/docs/proto" ) func readWarningsFromFile(path string) (*docspb.Warnings, error) { content, err := os.ReadFile(path) if err != nil { return nil, err } warnings := &docspb.Warnings{} if err := proto.UnmarshalText(string(content), warnings); err != nil { return nil, err } return warnings, nil } func isExistingWarning(name string) bool { for _, n := range warn.AllWarnings { if n == name { return true } } return false } func isDisabledWarning(name string) bool { if !isExistingWarning(name) { return false } for _, n := range warn.DefaultWarnings { if n == name { return false } } return true } func generateWarningsDocs(warnings *docspb.Warnings) string { var b bytes.Buffer b.WriteString(`# Buildifier warnings Warning categories supported by buildifier's linter: `) // Table of contents var names []string for _, w := range warnings.Warnings { names = append(names, w.Name...) } sort.Strings(names) for _, n := range names { fmt.Fprintf(&b, " * [`%s`](#%s)\n", n, n) } // Misc b.WriteString(` ### How to disable warnings All warnings can be disabled / suppressed / ignored by adding a special comment ` + "`" + `# buildifier: disable=` + "`" + ` to the expression that causes the warning. Historically comments with ` + "`" + `buildozer` + "`" + ` instead of ` + "`" + `buildifier` + "`" + ` are also supported, they are equivalent. To disable multiple warnings, you can specify them as a comma-separated list on a single line: ` + "```" + `python # buildifier: disable=warning1,warning2 ` + "```" + ` Or place them on separate lines: ` + "```" + `python # buildifier: disable=warning1 # buildifier: disable=warning2 ` + "```" + ` #### Examples ` + "```" + `python # buildifier: disable=no-effect """ A multiline comment as a string literal. Docstrings don't trigger the warning if they are first statements of a file or a function. """ if debug: print("Debug information:", foo) # buildifier: disable=print ` + "```\n") // Individual warnings sort.Slice(warnings.Warnings, func(i, j int) bool { return strings.Compare(warnings.Warnings[i].Name[0], warnings.Warnings[j].Name[0]) < 0 }) for _, w := range warnings.Warnings { // Header b.WriteString("\n--------------------------------------------------------------------------------\n\n## ") for _, n := range w.Name { fmt.Fprintf(&b, "", n) } fmt.Fprintf(&b, "%s\n\n", w.Header) // Name(s) if len(w.Name) == 1 { fmt.Fprintf(&b, " * Category name: `%s`\n", w.Name[0]) } else { b.WriteString(" * Category names:\n") for _, n := range w.Name { fmt.Fprintf(&b, " * `%s`\n", n) } } // Bazel --incompatible flag if w.BazelFlag != "" { label := fmt.Sprintf("`%s`", w.BazelFlag) if w.BazelFlagLink != "" { label = fmt.Sprintf("[%s](%s)", label, w.BazelFlagLink) } fmt.Fprintf(&b, " * Flag in Bazel: %s\n", label) } // Automatic fix fix := "no" if w.Autofix { fix = "yes" } fmt.Fprintf(&b, " * Automatic fix: %s\n", fix) // Disabled by default if isDisabledWarning(w.Name[0]) { b.WriteString(" * [Disabled by default](buildifier/README.md#linter)\n") } // Non-existent if !isExistingWarning(w.Name[0]) { b.WriteString(" * Not supported by the latest version of Buildifier\n") } // Suppress the warning b.WriteString(" * [Suppress the warning](#suppress): ") for i, n := range w.Name { if i != 0 { b.WriteString(", ") } fmt.Fprintf(&b, "`# buildifier: disable=%s`", n) } b.WriteString("\n") // Description fmt.Fprintf(&b, "\n%s\n", w.Description) } return b.String() } func writeWarningsDocs(docs, path string) error { f, err := os.Create(path) if err != nil { return err } if _, err := f.WriteString(docs); err != nil { return err } return f.Close() } func main() { flag.Parse() warnings, err := readWarningsFromFile(flag.Arg(0)) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } docs := generateWarningsDocs(warnings) if err := writeWarningsDocs(docs, flag.Arg(1)); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } buildtools-10.1.0/warn/docs/docs_test.go0000664000175000017500000000431215254573013017561 0ustar kenhyskenhys/* Copyright 2021 Google LLC 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 https://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 main import ( "os" "path/filepath" "testing" "github.com/bazel-contrib/buildtools/v10/testutils" "github.com/bazel-contrib/buildtools/v10/warn" ) func TestAllWarningsAreDocumented(t *testing.T) { testdata := filepath.Join( filepath.FromSlash(os.Getenv("TEST_SRCDIR")), filepath.FromSlash(os.Getenv("TEST_WORKSPACE"))) textprotoPath := filepath.Join(testdata, "warn", "docs", "warnings.textproto") warnings, err := readWarningsFromFile(textprotoPath) if err != nil { t.Fatalf("getWarnings(%q) = %v", textprotoPath, err) } names := make(map[string]bool) for _, w := range warnings.Warnings { for _, n := range w.Name { names[n] = true } } for _, n := range warn.AllWarnings { if !names[n] { t.Errorf("warning %q is not documented in warn/docs/warnings.textproto", n) } } } func TestFilesMatch(t *testing.T) { testdata := filepath.Join( filepath.FromSlash(os.Getenv("TEST_SRCDIR")), filepath.FromSlash(os.Getenv("TEST_WORKSPACE"))) generatedPath := filepath.Join(testdata, "warn", "docs", "WARNINGS.md") generated, err := os.ReadFile(generatedPath) if err != nil { t.Fatalf("ReadFile(%q) = %v", generatedPath, err) } checkedInPath := filepath.Join(testdata, "WARNINGS.md") checkedIn, err := os.ReadFile(checkedInPath) if err != nil { t.Fatalf("ReadFile(%q) = %v", checkedInPath, err) } d, err := testutils.Diff(generated, checkedIn) if err != nil { t.Fatalf("diff(generated, checkedIn) returns error:", err) } if len(d) != 0 { t.Errorf("diff(generated, checkedIn) = %v\n", string(d)) t.Errorf("To update the documentation, run `bazel build //warn/docs:warnings_docs && cp bazel-bin/warn/docs/WARNINGS.md .`") } } buildtools-10.1.0/warn/docs/warnings.textproto0000664000175000017500000012637215254573013021100 0ustar kenhyskenhys# proto-file: warn/docs/docs.proto # proto-message: Warnings # After modifying this file, run `bazel build //warn/docs:warnings_docs && cp bazel-bin/warn/docs/WARNINGS.md .` warnings: { name: "allowed-symbol-load-locations" header: "Symbol must be loaded from a specific location" description: "Warns when a symbol is loaded from a location other than the expected ones.\n" "Expected locations are specified in the tables file:\n\n" "```json\n" "{\n" " \"AllowedSymbolLoadLocations\": {\n" " \"genrule\": [\n" " \"//tools/bazel:genrule.bzl\"\n" " ]\n" " }\n" "}\n" "```" } warnings: { name: "attr-cfg" header: "`cfg = \"data\"` for attr definitions has no effect" description: "The [Configuration](https://docs.bazel.build/versions/main/skylark/rules.html#configurations)\n" "`cfg = \"data\"` is deprecated and has no effect. Consider removing it.\n" "The [Configuration](https://docs.bazel.build/versions/main/skylark/rules.html#configurations)\n" "`cfg = \"host\"` is deprecated. Consider replacing it with `cfg = \"exec\"`." bazel_flag: "--incompatible_disallow_data_transition" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/6153" autofix: true } warnings: { name: "attr-license" header: "`attr.license()` is deprecated and shouldn't be used" description: "The `attr.license()` method is almost never used and being deprecated." bazel_flag: "--incompatible_no_attr_license" autofix: false } warnings: { name: "attr-licenses" header: "Do not use `licenses` as an attribute name." description: "Using licenses as an attribute name may cause unexpected behavior." autofix: false } warnings: { name: "attr-applicable_licenses" header: "Do not use `applicable_licenses` as an attribute name." description: "Using `applicable_licenses` as an attribute name may cause unexpected behavior. Its use may be prohibited in future Bazel releases." autofix: false } warnings: { name: "attr-package-metadata" header: "Do not use `package_metadata` as an attribute name." description: "Using `package_metadata` as an attribute name may cause unexpected behavior. Its use may be prohibited in future Bazel releases." autofix: false } warnings: { name: "attr-non-empty" header: "`non_empty` attribute for attr definitions is deprecated" description: "The `non_empty` [attribute](https://docs.bazel.build/versions/main/skylark/lib/attr.html)\n" "for attr definitions is deprecated, please use `allow_empty` with an opposite value instead." bazel_flag: "--incompatible_disable_deprecated_attr_params" autofix: true } warnings: { name: "attr-output-default" header: "The `default` parameter for `attr.output()`is deprecated" description: "The `default` parameter of `attr.output()` is bug-prone, as two targets of the same rule would be\n" "unable to exist in the same package under default behavior. Use Starlark macros to specify defaults\n" "for these attributes instead." bazel_flag: "--incompatible_no_output_attr_default" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/7950" autofix: false } warnings: { name: "attr-single-file" header: "`single_file` is deprecated" description: "The `single_file` [attribute](https://docs.bazel.build/versions/main/skylark/lib/attr.html)\n" "is deprecated, please use `allow_single_file` instead." bazel_flag: "--incompatible_disable_deprecated_attr_params" autofix: true } warnings: { name: "build-args-kwargs" header: "`*args` and `**kwargs` are not allowed in BUILD files" description: "Having `*args` or `**kwargs` makes BUILD files hard to read and manipulate. The list of\n" "arguments should be explicit." bazel_flag: "--incompatible_no_kwargs_in_build_files" autofix: false } warnings: { name: "bzl-visibility" header: "Module shouldn't be used directly" description: "If a directory `foo` contains a subdirectory `internal` or `private`, only files located under `foo`\n" "can access it.\n\n" "For example, `dir/rules_mockascript/private/foo.bzl` can be loaded from\n" "`dir/rules_mockascript/private/bar.bzl` or `dir/rules_mockascript/sub/public.bzl`,\n" "but not from `dir/other_rule/file.bzl`." autofix: false } warnings: { name: "confusing-name" header: "Never use `l`, `I`, or `O` as names" description: "The names `l`, `I`, or `O` can be easily confused with `I`, `l`, or `0` correspondingly." autofix: false } warnings: { name: "constant-glob" header: "Glob pattern has no wildcard ('*')" description: "[Glob function](https://docs.bazel.build/versions/main/be/functions.html#glob)\n" "is used to get a list of files from the depot. The patterns (the first argument)\n" "typically include a wildcard (* character). A pattern without a wildcard is\n" "often useless and sometimes harmful.\n\n" "To fix the warning, move the string out of the glob:\n\n" "```diff\n" "- glob([\"*.cc\", \"test.cpp\"])\n" "+ glob([\"*.cc\"]) + [\"test.cpp\"]\n" "```\n\n" "**There’s one important difference**: before the change, Bazel would silently\n" "ignore test.cpp if file is missing; after the change, Bazel will throw an error\n" "if file is missing.\n\n" "If `test.cpp` doesn’t exist, the fix becomes:\n\n" "```diff\n" "- glob([\"*.cc\", \"test.cpp\"])\n" "+ glob([\"*.cc\"])\n" "```\n\n" "which improves maintenance and readability.\n\n" "If no pattern has a wildcard, just remove the glob. It will also improve build\n" "performance (glob can be relatively slow):\n\n" "```diff\n" "- glob([\"test.cpp\"])\n" "+ [\"test.cpp\"]\n" "```" } warnings: { name: "ctx-actions" header: "`ctx.{action_name}` is deprecated" description: "The following [actions](https://docs.bazel.build/versions/main/skylark/lib/actions.html)\n" "are deprecated, please use the new API:\n\n" " * [`ctx.new_file`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#new_file) → [`ctx.actions.declare_file`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#declare_file)\n" " * `ctx.experimental_new_directory` → [`ctx.actions.declare_directory`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#declare_directory)\n" " * [`ctx.file_action`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#file_action) → [`ctx.actions.write`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#write)\n" " * [`ctx.action(command = \"...\")`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#action) → [`ctx.actions.run_shell`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#run_shell)\n" " * [`ctx.action(executable = \"...\")`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#action) → [`ctx.actions.run`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#run)\n" " * [`ctx.empty_action`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#empty_action) → [`ctx.actions.do_nothing`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#do_nothing)\n" " * [`ctx.template_action`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#template_action) → [`ctx.actions.expand_template`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#expand_template)" bazel_flag: "--incompatible_new_actions_api" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5825" autofix: true } warnings: { name: "ctx-args" header: "`ctx.actions.args().add()` for multiple arguments is deprecated" description: "It's deprecated to use the [`add`](https://docs.bazel.build/versions/main/skylark/lib/Args.html#add)\n" "method of `ctx.actions.args()` to add a list (or a depset) of variables. Please use either\n" "[`add_all`](https://docs.bazel.build/versions/main/skylark/lib/Args.html#add_all) or\n" "[`add_joined`](https://docs.bazel.build/versions/main/skylark/lib/Args.html#add_joined),\n" "depending on the desired behavior." bazel_flag: "--incompatible_disallow_old_style_args_add" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5822" autofix: true } warnings: { name: "deprecated-function" header: "The function is deprecated" description: "The function defined in another .bzl file has a docstring stating that it's deprecated, i.e. it\n" "contains a `Deprecated:` section. The convention for function docstrings is described by\n" "the [`function-docstring`](#function-docstring) warning." } warnings: { name: "depset-items" header: "Depset's \"items\" parameter is deprecated" description: "The `items` parameter for [`depset`](https://docs.bazel.build/versions/main/skylark/lib/globals.html#depset)\n" "is deprecated. In its old form it's either a list of direct elements to be\n" "added (use the `direct` or unnamed first parameter instead) or a depset that\n" "becomes a transitive element of the new depset (use the `transitive` parameter\n" "instead)." bazel_flag: "--incompatible_disable_depset_items" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/9017" } warnings: { name: "depset-iteration" header: "Depset iteration is deprecated" description: "Depsets are complex structures, iterations over them and lookups require flattening them to\n" "a list which may be a heavy operation. To make it more obvious it's now required to call\n" "the `.to_list()` method on them in order to be able to iterate their items:\n\n" "```python\n" "deps = depset()\n" "[x.path for x in deps] # deprecated\n" "[x.path for x in deps.to_list()] # recommended\n" "```" bazel_flag: "--incompatible_depset_is_not_iterable" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5816" autofix: true } warnings: { name: "depset-union" header: "Depsets should be joined using the depset constructor" description: "The following ways to merge two depsets are deprecated:\n\n" "```python\n" "depset1 + depset2\n" "depset1 | depset2\n" "depset1.union(depset2)\n" "```\n\n" "Please use the [depset](https://docs.bazel.build/versions/main/skylark/lib/depset.html) constructor\n" "instead:\n\n" "```python\n" "depset(transitive = [depset1, depset2])\n" "```\n\n" "When fixing this issue, make sure you\n" "[understand depsets](https://docs.bazel.build/versions/main/skylark/depsets.html)\n" "and try to\n" "[reduce the number of calls to depset](https://docs.bazel.build/versions/main/skylark/performance.html#reduce-the-number-of-calls-to-depset).\n" "See this [explanation](https://github.com/bazelbuild/bazel/issues/5817#issuecomment-496910826) for more detail." bazel_flag: "--incompatible_depset_union" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5817" } warnings: { name: "dict-concatenation" header: "Dictionary concatenation is deprecated" description: "The `+` operator to concatenate dicts is deprecated. The operator used to create a new dict and\n" "copy the data to it. There are several ways to avoid it, for example, instead of `d = d1 + d2 + d3`\n" "you can use one of the following:\n\n" " * Use [Skylib](https://github.com/bazelbuild/bazel-skylib):\n\n" "```python\n" "load(\"@bazel_skylib//lib:dicts.bzl\", \"dicts\")\n\n" "d = dicts.add(d1, d2, d3)\n" "```\n\n" " * The same if you don't want to use Skylib:\n\n" "```python\n" "d = dict(d1.items() + d2.items() + d3.items())\n" "```\n\n" " * The same in several steps:\n\n" "```python\n" "d = dict(d1) # If you don't want `d1` to be mutated\n" "d.update(d2)\n" "d.update(d3)\n" "```" bazel_flag: "--incompatible_disallow_dict_plus" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/6461" } warnings: { name: "dict-method-named-arg" header: "Dict methods do not have a named argument `default`" description: "Dict methods `get`, `pop` and `setdefault` do not accept a named argument\n" "called `default`. Due to a bug, Bazel currently accepts that named argument.\n" "It is better to use a positional argument instead:\n\n" "```diff\n" "- mydict.get(5, default = 0)\n" "+ mydict.get(5, 0)\n" "```" } warnings: { name: "duplicated-name" header: "A rule with name `foo` was already found on line" description: "Each label in Bazel has a unique name, and Bazel doesn’t allow two rules to have\n" "the same name. With macros, this may be accepted by Bazel (if each macro\n" "generates different rules):\n\n" "```python\n" "my_first_macro(name = \"foo\")\n" "my_other_macro(name = \"foo\")\n" "```\n\n" "Although the build may work, this code can be very confusing. It can confuse\n" "users reading a BUILD file (if they look for the rule “fooâ€�, they may read see\n" "only one of the macros). It will also confuse tools that edit BUILD files.\n\n" "To fix the issue just change the name attribute of one rule/macro." } warnings: { name: "filetype" header: "The `FileType` function is deprecated" description: "The function `FileType` is deprecated. Instead of using it as an argument to the\n" "[`rule` function](https://docs.bazel.build/versions/main/skylark/lib/globals.html#rule)\n" "just use a list of strings." bazel_flag: "--incompatible_disallow_filetype" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5831" } warnings: { name: "function-docstring" name: "function-docstring-header" name: "function-docstring-args" name: "function-docstring-return" header: "Function docstring" description: "Public functions should have docstrings describing functions and their signatures.\n" "A docstring is a string literal (not a comment) which should be the first statement\n" "of a function (it may follow comment lines). Function docstrings are expected to be\n" "formatted in the following way:\n\n" "```python\n" "\"\"\"One-line summary: must be followed and may be preceded by a blank line.\n" "\n" "Optional additional description like this.\n" "\n" "If it's a function docstring and the function has more than one argument, the docstring has\n" "to document these parameters as follows:\n" "\n" "Args:\n" " parameter1: description of the first parameter. Each parameter line\n" " should be indented by one, preferably two, spaces (as here).\n" " parameter2: description of the second\n" " parameter that spans two lines. Each additional line should have a\n" " hanging indentation of at least one, preferably two, additional spaces (as here).\n" " another_parameter (unused, mutable): a parameter may be followed\n" " by additional attributes in parentheses\n" "\n" "Returns:\n" " Description of the return value.\n" " Should be indented by at least one, preferably two spaces (as here)\n" " Can span multiple lines.\n" "\n" "Deprecated:\n" " Optional, description of why the function is deprecated and what should be used instead.\n" "\"\"\"\n" "```\n\n" "Docstrings are required for all public functions with at least 5 statements. If a docstring exists\n" "it should start with a one-line summary line followed by an empty line. If a docstring is required\n" "or it describes some arguments, it should describe all of them. If a docstring is required and\n" "the function returns a value, it should be described." } warnings: { name: "git-repository" header: "Function `git_repository` is not global anymore" description: "Native `git_repository` and `new_git_repository` functions are removed.\n" "Please use the Starlark version instead:\n\n" "```python\n" "load(\"@bazel_tools//tools/build_defs/repo:git.bzl\", \"git_repository\")\n" "```" bazel_flag: "--incompatible_remove_native_git_repository" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/6569" autofix: true } warnings: { name: "http-archive" header: "Function `http_archive` is not global anymore" description: "Native `http_archive` function is removed.\n" "Please use the Starlark version instead:\n\n" "```python\n" "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n" "```" bazel_flag: "--incompatible_remove_native_http_archive" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/6570" autofix: true } warnings: { name: "integer-division" header: "The `/` operator for integer division is deprecated" description: "The `/` operator is deprecated in favor of `//`, please use the latter for\n" "integer division:\n\n" "```python\n" "a = b // c\n" "d //= e\n" "```" bazel_flag: "--incompatible_disallow_slash_operator" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5823" autofix: true } warnings: { name: "keyword-positional-params" header: "Keyword parameter should be positional" description: "Some parameters for builtin functions in Starlark are keyword for legacy reasons;\n" "their names are not meaningful (e.g. `x`). Making them positional-only will improve\n" "the readability." autofix: true } warnings: { name: "list-append" header: "Prefer using `.append()` to adding a single element list" description: "Transforming `x += [expr]` to `x.append(expr)` avoids a list allocation.\n" "NOTE: .append() does not work if the target is a select, hence this warning can safely\n" "be ignored or suppressed." autofix: true } warnings: { name: "load" header: "Loaded symbol is unused" description: "### Background\n\n" "[load](https://docs.bazel.build/versions/main/skylark/concepts.html#loading-an-extension)\n" "is used to import definitions in a BUILD file. If the definition is not used in\n" "the file, the load can be safely removed. If a symbol is loaded two times, you\n" "will get a warning on the second occurrence.\n\n" "### How to fix it\n\n" "Delete the line. When load is used to import multiple symbols, you can remove\n" "the unused symbols from the list. To fix your BUILD files automatically, try\n" "this command:\n\n" "```bash\n" "$ buildozer 'fix unusedLoads' path/to/BUILD\n" "```\n\n" "If you want to keep the load, you can disable the warning by adding a comment\n" "`# @unused`." autofix: true } warnings: { name: "load-on-top" header: "Load statements should be at the top of the file" description: "Obsolete; the warning has been implemented in the formatter and the fix " "is now automatically applied to all files except `WORKSPACE` files " "(unless suppressed).\n\n" "Load statements should be first statements (with the exception of `WORKSPACE` files),\n" "they can follow only comments and docstrings." bazel_flag: "--incompatible_bzl_disallow_load_after_statement" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5815" autofix: true } warnings: { name: "make-location" header: "The `$(location)` make variable is deprecated" description: "The `$(location)` and `$(locations)` make variables are legacy synonyms for\n" "`$(execpath)` and `$(rootpath)` whose behavior depends on the attribute being\n" "expanded. Use `$(execpath ...)` when you need the execution path, or\n" "`$(rootpath ...)` when you need the runfiles path.\n\n" "Examples that trigger this warning:\n\n" "```python\n" "genrule(\n" " name = \"example\",\n" " srcs = [\":input\"],\n" " outs = [\"output\"],\n" " cmd = \"cp $(location :input) $@\",\n" ")\n" "```\n\n" "Instead, use an explicit make variable:\n\n" "```python\n" "cmd = \"cp $(execpath :input) $@\",\n" "```" autofix: false } warnings: { name: "module-docstring" header: "The file has no module docstring" description: "`.bzl` files should have docstrings on top of them. A docstring is a string literal\n" "(not a comment) which should be the first statement of the file (it may follow\n" "comment lines). For example:\n\n" "```python\n" "\"\"\"\n" "This module contains build rules for my project.\n" "\"\"\"\n" "\n" "...\n" "```" } warnings: { name: "name-conventions" header: "Name conventions" description: "By convention, all variables should be lower_snake_case, constant should be\n" "UPPER_SNAKE_CASE, and providers should be UpperCamelCase ending with `Info`." } warnings: { name: "native-android" header: "All Android build rules should be loaded from Starlark" description: "The Android build rules should be loaded from Starlark.\n\n" "Update: the plans for disabling native rules\n" "[have been postponed](https://groups.google.com/g/bazel-discuss/c/XNvpWcge4AE/m/aJ-aQzszAwAJ),\n" "at the moment it's not required to load Starlark rules." bazel_flag: "--incompatible_disable_native_android_rules" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/8391" autofix: true } warnings: { name: "native-build" header: "The `native` module shouldn't be used in BUILD files" description: "There's no need in using `native.` in BUILD files, its members are available\n" "as global symbols there." autofix: true } warnings: { name: "native-cc-binary" name: "native-cc-common" name: "native-cc-debug-package-info" name: "native-cc-fdo-prefetch-hints" name: "native-cc-fdo-profile" name: "native-cc-import" name: "native-cc-info" name: "native-cc-library" name: "native-cc-memprof-profile" name: "native-cc-objc-import" name: "native-cc-objc-library" name: "native-cc-propeller-optimize" name: "native-cc-shared-library" name: "native-cc-shared-library-hint-info" name: "native-cc-shared-library-info" name: "native-cc-test" name: "native-cc-toolchain" name: "native-cc-toolchain-suite" header: "All C++ build rules should be loaded from Starlark" description: "The C++ build rules should be loaded from @rules_cc." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-binary" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-import" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-library" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-plugin" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-test" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-package-config" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-runtime" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-toolchain" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-common" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-info" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-plugin-info" header: "All Java build rules should be loaded from Starlark" description: "The Java build rules should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-package" header: "`native.package()` shouldn't be used in .bzl files" description: "It's discouraged and will be disallowed to use `native.package()` in .bzl files.\n" "It can silently modify the semantics of a BUILD file and makes it hard to maintain." } warnings: { name: "native-proto" header: "proto_library rule should be loaded from Starlark" description: "The proto_library rule should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-proto" header: "java_proto_library rule should be loaded from Starlark" description: "The java_proto_library rule should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-java-lite-proto" header: "java_lite_proto_library rule should be loaded from Starlark" description: "The java_lite_proto_library rule should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-cc-proto" header: "cc_proto_library rule should be loaded from Starlark" description: "The cc_proto_library rule should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-proto-lang-toolchain" header: "proto_lang_toolchain rule should be loaded from Starlark" description: "The proto_lang_toolchain rule should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-proto-info" header: "ProtoInfo provider should be loaded from Starlark" description: "The ProtoInfo provider should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-proto-lang-toolchain-info" header: "ProtoLangToolchainInfo provider should be loaded from Starlark" description: "The ProtoLangToolchainInfo provider should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-proto-common" header: "proto_common module should be loaded from Starlark" description: "The proto_common module should be loaded from Starlark." bazel_flag: "--incompatible_autoload_externally" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/23043" autofix: true } warnings: { name: "native-py" header: "All Python build rules should be loaded from Starlark" description: "The Python build rules should be loaded from Starlark.\n\n" "Update: the plans for disabling native rules\n" "[have been postponed](https://groups.google.com/g/bazel-discuss/c/XNvpWcge4AE/m/aJ-aQzszAwAJ),\n" "at the moment it's not required to load Starlark rules." bazel_flag: "--incompatible_load_python_rules_from_bzl" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/9006" autofix: true } warnings: { name: "native-sh-binary" header: "sh_binary build rules should be loaded from Starlark" description: "The sh_binary build rules should be loaded from Starlark." autofix: true } warnings: { name: "native-sh-library" header: "sh_library build rules should be loaded from Starlark" description: "The sh_library build rules should be loaded from Starlark." autofix: true } warnings: { name: "native-sh-test" header: "sh_test build rules should be loaded from Starlark" description: "The sh_test build rules should be loaded from Starlark." autofix: true } warnings: { name: "no-effect" header: "Expression result is not used" description: "The statement has no effect. Consider removing it or storing its result in a variable." } warnings: { name: "out-of-order-load" header: "Load statements should be ordered by their labels" description: "Obsolete; the warning has been implemented in the formatter and the fix " "is now automatically applied to all files (unless suppressed).\n\n" "Load statements should be ordered by their first argument - extension file label.\n" "This makes it easier to developers to locate loads of interest and reduces chances\n" "for conflicts when performing large-scale automated refactoring.\n\n" "When applying automated fixes, it's highly recommended to also use\n" "[`load-on-top`](#load-on-top) fixes, since otherwise the relative order\n" "of a symbol load and its usage can change resulting in runtime error." autofix: true } warnings: { name: "output-group" header: "`ctx.attr.dep.output_group` is deprecated" description: "The `output_group` field of a target is deprecated in favor of the\n" "[`OutputGroupInfo` provider](https://docs.bazel.build/versions/main/skylark/lib/OutputGroupInfo.html)." bazel_flag: "--incompatible_no_target_output_group" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/7949" autofix: true } warnings: { name: "overly-nested-depset" header: "The depset is potentially overly nested" description: "If a depset is iteratively chained in a for loop, e.g. the following pattern is used:\n\n" "```python\n" "for ...:\n" " x = depset(..., transitive = [..., x, ...])\n" "```\n\n" "this can result in an overly nested depset with a long chain of transitive elements. Such patterns\n" "can lead to performance problems, consider refactoring the code to create a flat list of transitive\n" "elements and call the depset constructor just once:\n\n" "```python\n" "transitive = []\n" "\n" "for ...:\n" " transitive += ...\n" "\n" "x = depset(..., transitive = transitive)\n" "```\n\n" "Or in simple cases you can use list comprehensions instead:\n\n" "```python\n" "x = depset(..., transitive = [y.deps for y in ...])\n" "```\n\n" "For more information, read Bazel documentation about\n" "[depsets](https://docs.bazel.build/versions/main/skylark/depsets.html)\n" "and\n" "[reducing the number of calls to depset](https://docs.bazel.build/versions/main/skylark/performance.html#reduce-the-number-of-calls-to-depset)." } warnings: { name: "package-name" header: "Global variable `PACKAGE_NAME` is deprecated" description: "The global variable `PACKAGE_NAME` is deprecated, please use\n" "[`native.package_name()`](https://docs.bazel.build/versions/main/skylark/lib/native.html#package_name)\n" "instead." bazel_flag: "--incompatible_package_name_is_a_function" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5827" autofix: true } warnings: { name: "package-on-top" header: "Package declaration should be at the top of the file" description: "Here is a typical structure of a BUILD file:\n\n" " * `load()` statements\n" " * `package()`\n" " * calls to rules, macros\n\n" "Instantiating a rule and setting the package defaults later can be very\n" "confusing, and has been a source of bugs (tools and humans sometimes believe\n" "package applies to everything in a BUILD file). This might become an error in\n" "the future.\n\n" "### What can be used before package()?\n\n" "The linter allows the following to be before `package()`:\n\n" " * comments\n" " * `load()`\n" " * variable declarations\n" " * `package_group()`\n" " * `licenses()`" } warnings: { name: "positional-args" header: "Keyword arguments should be used over positional arguments" description: "All macro and rule calls should use keyword args over positional arguments.\n" "Positional arguments can cause subtle errors if the order is switched or if\n" "an argument is removed. Keyword args also greatly improve readability.\n" "Additionally, positional arguments prevent migration from [Legacy Macros](https://bazel.build/extending/legacy-macros)\n" "to [Symbolic Macros](https://bazel.build/extending/macros).\n\n" "```diff\n" "- my_macro(\"foo\", \"bar\")\n" "+ my_macro(name = \"foo\", env = \"bar\")\n" "```\n\n" "The linter allows the following functions to be called with positional arguments:\n\n" " * `load()`\n" " * `vardef()`\n" " * `export_files()`\n" " * `licenses()`\n" " * `print()`" } warnings: { name: "print" header: "`print()` is a debug function and shouldn't be submitted" description: "Using the `print()` function for warnings is discouraged: they are often spammy and\n" "non actionable, the people who see the warning are usually not the people who can\n" "fix the code to make the warning disappear, and the actual maintainers of the code\n" "may never see the warning." } warnings: { name: "provider-params" header: "Calls to `provider` should specify a list of fields and a documentation" description: "Calls to `provider` should specify a documentation string and a list of fields:\n\n" "```python\n" "ServerAddressInfo = provider(\n" " \"The address of an HTTP server. Fields are host (string) and port (int).\",\n" " fields = [\"host\", \"port\"]\n" ")\n" "```\n\n" "Fields should also be documented when needed:\n\n" "```python\n" "ServerAddressInfo = provider(\n" " \"The address of an HTTP server.\",\n" " fields = {\n" " \"host\": \"string, e.g. 'example.com'\",\n" " \"port\": \"int, a TCP port number\",\n" " }\n" ")\n" "```\n\n" "Note that specifying a list of fields is a breaking change. It is an error if a\n" "call to the provider uses undeclared fields. If you cannot declare the list of\n" "fields, you may explicitly set it to None (and explain why in a comment).\n\n" "```python\n" "AllInfo = provider(\"This provider accepts any field.\", fields = None)\n" "\n" "NoneInfo = provider(\"This provider cannot have fields.\", fields = [])\n" "```\n\n" "See the [documentation for providers](https://docs.bazel.build/versions/main/skylark/lib/globals.html#provider)." } warnings: { name: "redefined-variable" header: "Variable has already been defined" description: "### Background\n\n" "In .bzl files, redefining a global variable is already forbidden. This helps\n" "both humans and tools reason about the code. For consistency, we want to bring\n" "this restriction also to BUILD files.\n\n" "### How to fix it\n\n" "Rename one of the variables.\n\n" "Note that the content of lists and dictionaries can still be modified. We will\n" "forbid reassignment, but not every side-effect." } warnings: { name: "repository-name" header: "Global variable `REPOSITORY_NAME` is deprecated" description: "The global variable `REPOSITORY_NAME` is deprecated, please use\n" "[`native.repository_name()`](https://docs.bazel.build/versions/main/skylark/lib/native.html#repository_name)\n" "instead." bazel_flag: "--incompatible_package_name_is_a_function" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5827" autofix: true } warnings: { name: "return-value" header: "Some but not all execution paths of a function return a value" description: "Some but not all execution paths of a function return a value. Either there's\n" "an explicit empty `return` statement, or an implicit return in the end of a\n" "function. If it is intentional, make it explicit using `return None`. If you\n" "know certain parts of the code cannot be reached, add the statement\n" "`fail(\"unreachable\")` to them." } warnings: { name: "rule-impl-return" header: "Avoid using the legacy provider syntax" description: "Returning structs from rule implementation functions is\n" "[deprecated](https://docs.bazel.build/versions/main/skylark/rules.html#migrating-from-legacy-providers),\n" "consider using\n" "[providers](https://docs.bazel.build/versions/main/skylark/rules.html#providers)\n" "or lists of providers instead." } warnings: { name: "same-origin-load" header: "Same label is used for multiple loads" description: "Obsolete; the warning has been implemented in the formatter and the fix " "is now automatically applied to all files except `WORKSPACE` files " "(unless suppressed).\n\n" "### Background\n\n" "[load](https://docs.bazel.build/versions/main/skylark/concepts.html#loading-an-extension)\n" "is used to import definitions in a BUILD file. If the same label is used for loading\n" "symbols more the ones, all such loads can be merged into a single one.\n\n" "### How to fix it\n\n" "Merge all loads into a single one. For example,\n\n" "```python\n" "load(\":f.bzl\", \"s1\")\n" "load(\":f.bzl\", \"s2\")\n" "```\n\n" "can be written more compactly as\n\n" "```python\n" "load(\":f.bzl\", \"s1\", \"s2\")\n" "```" autofix: true } warnings: { name: "skylark-comment" name: "skylark-docstring" header: "\"Skylark\" is an outdated name of the language, please use \"starlark\" instead" description: "The configuration language for Bazel is called \"Starlark\" now, the name \"Skylark\" is\n" "outdated and shouldn't be used." autofix: true } warnings: { name: "string-iteration" header: "String iteration is deprecated" description: "Iteration over strings often leads to confusion with iteration over a sequence of strings,\n" "therefore strings won't be recognized as sequences of 1-element strings (like in Python).\n" "Use string indexing and `len` instead:\n\n" "```python\n" "my_string = \"hello world\"\n" "for i in range(len(my_string)):\n" " char = my_string[i]\n" " # do something with char\n" "```" bazel_flag: "--incompatible_string_is_not_iterable" bazel_flag_link: "https://github.com/bazelbuild/bazel/issues/5830" } warnings: { name: "uninitialized" header: "Variable may not have been initialized" description: "The local value can be not initialized at the time of execution. It may happen if it's\n" "initialized in one of the if-else clauses but not in all of them, or in a for-loop which\n" "can potentially be empty." } warnings: { name: "unnamed-macro" header: "The macro should have a keyword argument called \"name\"" description: "By convention all macro functions should have a keyword argument called `name`\n" "(even if they don't use it). This is important for tooling and automation.\n\n" "A macro is a function that calls a rule (either directly or indirectly by calling other\n" "macros).\n\n" "If this function is a helper function that's not supposed to be used outside of its file,\n" "please make it private (rename it so that the name starts with `_`), this will\n" "prevent loading the function from BUILD files and suppress the warning." } warnings: { name: "unreachable" header: "The statement is unreachable" description: "The statement is unreachable because it follows a `return`, `break`, `continue`,\n" "or `fail()` statement." } warnings: { name: "unsorted-dict-items" header: "Dictionary items should be ordered by their keys" description: "Dictionary items should be sorted lexicographically by their keys. This makes\n" "it easier to find the item of interest and reduces chances of conflicts when\n" "performing large-scale automated refactoring.\n\n" "The order is affected by `NamePriority` dictionary passed using `-tables` or\n" "`-add_tables` flags.\n\n" "If you want to preserve the original dictionary items order, you can disable\n" "the warning by adding a comment `# @unsorted-dict-items` to the dictionary\n" "expression or any of its enclosing expressions (binary, if etc). For example,\n\n" "```python\n" "# @unsorted-dict-items\n" "d = {\n" " \"b\": \"bvalue\",\n" " \"a\": \"avalue\",\n" "}\n" "```\n\n" "will not be reported as an issue because the assignment operation that uses\n" "the dictionary with unsorted items has a comment disabling this warning." autofix: true } warnings: { name: "unused-variable" header: "Variable is unused" description: "This happens when a variable or function is set but not used in the file, e.g.\n\n" "```python\n" "x = [1, 2]\n" "```\n\n" "The line can often be safely removed.\n\n" "If you want to keep the variable, you can disable the warning by adding a\n" "comment `# @unused`. For a function parameter, place the comment on the\n" "parameter itself.\n\n" "```python\n" "x = [1, 2] # @unused\n" "\n" "# @unused\n" "def f(\n" " x,\n" " y # @unused\n" "):\n" " pass\n" "```\n\n" "For backward compatibility, an `# @unused` comment at the end of a function\n" "header applies to its final parameter. New code should prefer attaching the\n" "comment directly to the parameter as shown above.\n\n" "```python\n" "def f(x, y): # @unused applies to y\n" " pass\n" "```\n\n" "If an unused variable is used for partially unpacking tuples, just prefix\n" "its name with an underscore to suppress the warning:\n\n" "```python\n" "x, _y = foo()\n" "for _, (a, _b) in iterable:\n" " print(a + x)\n" "```\n\n" "The same applies for function arguments that are not used by design:\n\n" "```python\n" "def foo(a, _b, *_args):\n" " return bar(a)\n" "```\n\n" "If a tuple is unpacked not in a for-loop and all variables are unused,\n" "it'll still trigger a warning, even if all variables are underscored:\n\n" "```python\n" "_a, _b = pair\n" "_unused = 3\n" "```" } buildtools-10.1.0/warn/docs/build_defs.bzl0000664000175000017500000000245015254573013020055 0ustar kenhyskenhys"""Provides documentation rule Copyright 2021 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. """ def _documentation_impl(ctx): ctx.actions.run( executable = ctx.executable.bin, inputs = [ctx.files.textproto[0]], outputs = [ctx.outputs.markdown], arguments = [ctx.files.textproto[0].path, ctx.outputs.markdown.path], ) return DefaultInfo( files = depset([ctx.outputs.markdown]), ) documentation = rule( implementation = _documentation_impl, attrs = { "textproto": attr.label( mandatory = True, allow_single_file = True, ), "markdown": attr.output(), "bin": attr.label( allow_single_file = True, executable = True, cfg = "exec", ), }, ) buildtools-10.1.0/warn/docs/BUILD.bazel0000664000175000017500000000167615254573013017133 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") load(":build_defs.bzl", "documentation") documentation( name = "warnings_docs", bin = ":go_default_binary", markdown = "WARNINGS.md", textproto = "warnings.textproto", ) go_binary( name = "go_default_binary", embed = [":docs_lib"], visibility = ["//visibility:public"], ) go_library( name = "docs_lib", srcs = ["docs.go"], importpath = "github.com/bazel-contrib/buildtools/v10/warn/docs", visibility = ["//visibility:private"], deps = [ "//warn", "//warn/docs/proto", "@com_github_golang_protobuf//proto:go_default_library", ], ) go_test( name = "docs_test", size = "small", srcs = ["docs_test.go"], data = [ "warnings.textproto", ":warnings_docs", "//:warnings", ], embed = [":docs_lib"], deps = [ "//testutils", "//warn", ], ) buildtools-10.1.0/warn/warn_operation_test.go0000664000175000017500000000647715254573013020746 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestIntegerDivision(t *testing.T) { checkFindingsAndFix(t, "integer-division", ` a = 1 b = int(2.3) c = 1.0 d = float(2) e = a / b f = a / c g = c / a h = c / d a /= b a /= c c /= a c /= d `, ` a = 1 b = int(2.3) c = 1.0 d = float(2) e = a // b f = a / c g = c / a h = c / d a //= b a /= c c /= a c /= d `, []string{ ":6: The \"/\" operator for integer division is deprecated in favor of \"//\".", ":11: The \"/=\" operator for integer division is deprecated in favor of \"//=\".", }, scopeEverywhere) } func TestDictionaryConcatenation(t *testing.T) { checkFindings(t, "dict-concatenation", ` d = {} d + foo foo + d d + foo + bar # Should trigger 2 warnings: (d + foo) is recognized as a dict foo + bar + d # Should trigger 1 warning: (foo + bar) is unknown d += foo + bar `, []string{ ":3: Dictionary concatenation is deprecated.", ":4: Dictionary concatenation is deprecated.", ":5: Dictionary concatenation is deprecated.", ":5: Dictionary concatenation is deprecated.", ":6: Dictionary concatenation is deprecated.", ":7: Dictionary concatenation is deprecated.", }, scopeEverywhere) } func TestStringIteration(t *testing.T) { checkFindings(t, "string-iteration", ` s = "foo" + bar max(s) min(s) all(s) any(s) reversed(s) zip(s, a, b) zip(a, s) [foo(x) for x in s] for x in s: pass # The following iterations over a list don't trigger warnings l = list() max(l) zip(l, foo) [foo(x) for x in l] for x in l: pass `, []string{ ":3: String iteration is deprecated.", ":4: String iteration is deprecated.", ":5: String iteration is deprecated.", ":6: String iteration is deprecated.", ":7: String iteration is deprecated.", ":8: String iteration is deprecated.", ":9: String iteration is deprecated.", ":11: String iteration is deprecated.", ":13: String iteration is deprecated.", }, scopeEverywhere) } func TestListAppend(t *testing.T) { checkFindingsAndFix(t, "list-append", ` x = [] x += y x += [1] x += [2, 3] x += [4 for y in z] x += 5 x += [foo( bar, baz, )] `, ` x = [] x += y x.append(1) x += [2, 3] x += [4 for y in z] x += 5 x.append(foo( bar, baz, )) `, []string{ `:3: Prefer using ".append()" to adding a single element list`, `:7: Prefer using ".append()" to adding a single element list`, }, scopeEverywhere) } func TestDictMethodNamedArg(t *testing.T) { checkFindings(t, "dict-method-named-arg", ` d = dict() d.get("a", "b") [].get("a", default = "b") d.get("a", default = "b") # warning d.pop("a", default = "b") # warning {}.setdefault("a", default = "b") # warning `, []string{ `:5: Named argument "default" not allowed`, `:6: Named argument "default" not allowed`, `:7: Named argument "default" not allowed`, }, scopeEverywhere) } buildtools-10.1.0/warn/warn_macro.go0000664000175000017500000002403315254573013016774 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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. */ // Warnings for using deprecated functions package warn import ( "fmt" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/labels" ) // Internal constant that represents the native module const nativeModule = "" // function represents a function identifier, which is a pair (module name, function name). type function struct { pkg string // package where the function is defined filename string // name of a .bzl file relative to the package name string // original name of the function } func (f function) label() string { return f.pkg + ":" + f.filename } // funCall represents a call to another function. It contains information of the function itself as well as some // information about the environment type funCall struct { function nameAlias string // function name alias (it could be loaded with a different name or assigned to a new variable). line int // line on which the function is being called } // acceptsNameArgument checks whether a function can accept a named argument called "name", // either directly or via **kwargs. func acceptsNameArgument(def *build.DefStmt) bool { for _, param := range def.Params { if name, op := build.GetParamName(param); name == "name" || op == "**" { return true } } return false } // fileData represents information about rules and functions extracted from a file type fileData struct { loadedSymbols map[string]function // Symbols loaded from other files. rulesOrMacros map[string]bool // all rules or macros defined in the file. functions map[string]map[string]funCall // outer map: all functions defined in the file, inner map: all distinct function calls from the given function aliases map[string]function // all top-level aliases (e.g. `foo = bar`). } // resolvesExternal takes a local function definition and replaces it with an external one if it's been defined // in another file and loaded func resolveExternal(fn function, externalSymbols map[string]function) function { if external, ok := externalSymbols[fn.name]; ok { return external } return fn } // exprLine returns the start line of an expression func exprLine(expr build.Expr) int { start, _ := expr.Span() return start.Line } // getFunCalls extracts information about functions that are being called from the given function func getFunCalls(def *build.DefStmt, pkg, filename string, externalSymbols map[string]function) map[string]funCall { funCalls := make(map[string]funCall) build.Walk(def, func(expr build.Expr, stack []build.Expr) { call, ok := expr.(*build.CallExpr) if !ok { return } if ident, ok := call.X.(*build.Ident); ok { funCalls[ident.Name] = funCall{ function: resolveExternal(function{pkg, filename, ident.Name}, externalSymbols), nameAlias: ident.Name, line: exprLine(call), } return } dot, ok := call.X.(*build.DotExpr) if !ok { return } if ident, ok := dot.X.(*build.Ident); !ok || ident.Name != "native" { return } name := "native." + dot.Name funCalls[name] = funCall{ function: function{ name: dot.Name, filename: nativeModule, }, nameAlias: name, line: exprLine(dot), } }) return funCalls } // analyzeFile extracts the information about rules and functions defined in the file func analyzeFile(f *build.File) fileData { if f == nil { return fileData{} } report := fileData{ loadedSymbols: make(map[string]function), rulesOrMacros: make(map[string]bool), functions: make(map[string]map[string]funCall), aliases: make(map[string]function), } // Collect loaded symbols for _, stmt := range f.Stmt { load, ok := stmt.(*build.LoadStmt) if !ok { continue } label := labels.ParseRelative(load.Module.Value, f.Pkg) if label.Repository != "" || label.Target == "" { continue } for i, from := range load.From { report.loadedSymbols[load.To[i].Name] = function{label.Package, label.Target, from.Name} } } for _, stmt := range f.Stmt { switch stmt := stmt.(type) { case *build.AssignExpr: // Analyze aliases (`foo = bar`) or rule declarations (`foo = rule(...)`) lhsIdent, ok := stmt.LHSIdent() if !ok { continue } if rhsIdent, ok := stmt.RHS.(*build.Ident); ok { report.aliases[lhsIdent.Name] = resolveExternal(function{f.Pkg, f.Label, rhsIdent.Name}, report.loadedSymbols) continue } call, ok := stmt.RHS.(*build.CallExpr) if !ok { continue } if ident, ok := call.X.(*build.Ident); ok { if ident.Name == "rule" || ident.Name == "macro" { report.rulesOrMacros[lhsIdent.Name] = true continue } } case *build.DefStmt: report.functions[stmt.Name] = getFunCalls(stmt, f.Pkg, f.Label, report.loadedSymbols) default: continue } } return report } // functionReport represents the analysis result of a function type functionReport struct { isRuleOrMacro bool // whether the function is a macro (or a rule) fc *funCall // a call to the rule or another macro } // macroAnalyzer is an object that analyzes the directed graph of functions calling each other, // loading other files lazily if necessary. type macroAnalyzer struct { fileReader *FileReader files map[string]fileData cache map[function]functionReport } // getFileData retrieves a file using the fileReader object and extracts information about functions and rules // defined in the file. func (ma macroAnalyzer) getFileData(pkg, label string) fileData { filename := pkg + ":" + label if fd, ok := ma.files[filename]; ok { return fd } if ma.fileReader == nil { fd := fileData{} ma.files[filename] = fd return fd } f := ma.fileReader.GetFile(pkg, label) fd := analyzeFile(f) ma.files[filename] = fd return fd } // IsMacro is a public function that checks whether the given function is a macro func (ma macroAnalyzer) IsRuleOrMacro(fn function) (report functionReport) { // Check the cache first if cached, ok := ma.cache[fn]; ok { return cached } // Write a negative result to the cache before analyzing. This will prevent stack overflow crashes // if the input data contains recursion. ma.cache[fn] = report defer func() { // Update the cache with the actual result ma.cache[fn] = report }() // Check for native rules if fn.filename == nativeModule { switch fn.name { case "glob", "existing_rule", "existing_rules", "package_name", "repository_name", "exports_files": // Not a rule default: report.isRuleOrMacro = true } return } fileData := ma.getFileData(fn.pkg, fn.filename) // Check whether fn.name is an alias for another function if alias, ok := fileData.aliases[fn.name]; ok { if ma.IsRuleOrMacro(alias).isRuleOrMacro { report.isRuleOrMacro = true } return } // Check whether fn.name is a rule or macro if fileData.rulesOrMacros[fn.name] { report.isRuleOrMacro = true return } // Check whether fn.name is a loaded symbol from another file if externalFn, ok := fileData.loadedSymbols[fn.name]; ok { if ma.IsRuleOrMacro(externalFn).isRuleOrMacro { report.isRuleOrMacro = true return } } // Check whether fn.name is an ordinary function funCalls, ok := fileData.functions[fn.name] if !ok { return } // Prioritize function calls from already loaded files. If some of the function calls are from the same file // (or another file that has been loaded already), check them first. var knownFunCalls, newFunCalls []funCall for _, fc := range funCalls { if _, ok := ma.files[fc.function.pkg+":"+fc.function.filename]; ok || fc.function.filename == nativeModule { knownFunCalls = append(knownFunCalls, fc) } else { newFunCalls = append(newFunCalls, fc) } } for _, fc := range append(knownFunCalls, newFunCalls...) { if ma.IsRuleOrMacro(fc.function).isRuleOrMacro { report.isRuleOrMacro = true report.fc = &fc return } } return } // newMacroAnalyzer creates and initiates an instance of macroAnalyzer. func newMacroAnalyzer(fileReader *FileReader) macroAnalyzer { return macroAnalyzer{ fileReader: fileReader, files: make(map[string]fileData), cache: make(map[function]functionReport), } } func unnamedMacroWarning(f *build.File, fileReader *FileReader) []*LinterFinding { if f.Type != build.TypeBzl { return nil } macroAnalyzer := newMacroAnalyzer(fileReader) macroAnalyzer.files[f.Pkg+":"+f.Label] = analyzeFile(f) findings := []*LinterFinding{} for _, stmt := range f.Stmt { def, ok := stmt.(*build.DefStmt) if !ok { continue } if strings.HasPrefix(def.Name, "_") || acceptsNameArgument(def) { continue } report := macroAnalyzer.IsRuleOrMacro(function{f.Pkg, f.Label, def.Name}) if !report.isRuleOrMacro { continue } msg := fmt.Sprintf(`The macro %q should have a keyword argument called "name".`, def.Name) if report.fc != nil { // fc shouldn't be nil because that's the only node that can be found inside a function. msg += fmt.Sprintf(` It is considered a macro because it calls a rule or another macro %q on line %d. By convention, every public macro needs a "name" argument (even if it doesn't use it). This is important for tooling and automation. * If this function is a helper function that's not supposed to be used outside of this file, please make it private (e.g. rename it to "_%s"). * Otherwise, add a "name" argument. If possible, use that name when calling other macros/rules.`, report.fc.nameAlias, report.fc.line, def.Name) } finding := makeLinterFinding(def, msg) _, finding.End = def.HeaderSpan() findings = append(findings, finding) } return findings } buildtools-10.1.0/warn/BUILD.bazel0000664000175000017500000000274715254573013016203 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "warn", srcs = [ "multifile.go", "types.go", "warn.go", "warn_bazel.go", "warn_bazel_api.go", "warn_bazel_operation.go", "warn_control_flow.go", "warn_cosmetic.go", "warn_deprecated.go", "warn_docstring.go", "warn_load.go", "warn_macro.go", "warn_naming.go", "warn_operation.go", "warn_visibility.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/warn", visibility = ["//visibility:public"], deps = [ "//build", "//bzlenv", "//edit", "//edit/bzlmod", "//labels", "//tables", ], ) go_test( name = "warn_test", size = "small", srcs = [ "types_test.go", "warn_bazel_api_test.go", "warn_bazel_operation_test.go", "warn_bazel_test.go", "warn_control_flow_test.go", "warn_cosmetic_test.go", "warn_deprecated_test.go", "warn_docstring_test.go", "warn_load_test.go", "warn_macro_test.go", "warn_naming_test.go", "warn_operation_test.go", "warn_test.go", "warn_visibility_test.go", ], embed = [":warn"], deps = [ "//build", "//tables", "//testutils", ], ) alias( name = "go_default_library", actual = ":warn", visibility = ["//visibility:public"], ) buildtools-10.1.0/warn/warn_bazel_test.go0000664000175000017500000001476015254573013020035 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 warn import "testing" func TestConstantGlob(t *testing.T) { checkFindings(t, "constant-glob", ` cc_library(srcs = glob(["foo.cc"])) cc_library(srcs = glob(include = ["foo.cc"])) cc_library(srcs = glob(include = ["foo.cc"], exclude = ["bar.cc"])) cc_library(srcs = glob(exclude = ["bar.cc"], include = ["foo.cc"])) cc_library(srcs = ["constant"] + glob([ "*.cc", "test.cpp", ]) ) cc_library(srcs = glob(["*.cc"])) cc_library(srcs = glob(["*.cc"], exclude = ["bar.cc"])) cc_library(srcs = glob(include = ["*.cc"], exclude = ["bar.cc"])) cc_library(srcs = glob(exclude = ["bar.cc"], include = ["*.cc"]))`, []string{`:1: Glob pattern "foo.cc" has no wildcard`, `:2: Glob pattern "foo.cc" has no wildcard`, `:3: Glob pattern "foo.cc" has no wildcard`, `:4: Glob pattern "foo.cc" has no wildcard`, `:8: Glob pattern "test.cpp" has no wildcard`}, scopeBuild|scopeBzl|scopeWorkspace) } func TestNativeInBuildFiles(t *testing.T) { checkFindingsAndFix(t, "native-build", ` native.package("foo") native.cc_library(name = "lib") `, ` package("foo") cc_library(name = "lib") `, []string{ `:1: The "native" module shouldn't be used in BUILD files, its members are available as global symbols.`, `:3: The "native" module shouldn't be used in BUILD files, its members are available as global symbols.`, }, scopeBuild) } func TestNativePackage(t *testing.T) { checkFindings(t, "native-package", ` native.package("foo") native.cc_library(name = "lib") `, []string{ `:1: "native.package()" shouldn't be used in .bzl files.`, }, scopeBzl) } func TestDuplicatedName(t *testing.T) { checkFindings(t, "duplicated-name", ` cc_library(name = "x") cc_library(name = "y") py_library(name = "x") py_library(name = "z") php_library(name = "x")`, []string{ `:3: A rule with name "x" was already found on line 1`, `:5: A rule with name "x" was already found on line 1`, }, scopeBuild|scopeWorkspace) checkFindings(t, "duplicated-name", ` exports_files(["foo.txt"]) [macro(name = "bar_%s" % i) for i in ii] `, []string{}, scopeBuild|scopeWorkspace) } func TestPositionalArgumentsDoesNotWarnForNamedArguments(t *testing.T) { checkFindings(t, "positional-args", ` my_macro = macro() my_rule = rule() def my_function(foo): pass my_macro(foo = "bar") my_rule(foo = "bar") my_function(foo = "bar") `, []string{}, scopeBuild) } func TestPositionalArgumentsDoesNotWarnForAllowlistedFunctions(t *testing.T) { checkFindings(t, "positional-args", ` register_toolchains( "//foo", "//bar", )`, []string{}, scopeBuild) } func TestPositionalArgumentsWarnsForPositionalMacrosOrRuleCalls(t *testing.T) { checkFindings(t, "positional-args", ` my_macro = macro() my_rule = rule() def my_function(foo): pass my_macro("foo", "bar") my_rule("foo", "bar") my_function("foo", "bar") `, []string{ `6: All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax. Found call to rule or macro "my_macro" with positional arguments.`, `7: All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax. Found call to rule or macro "my_rule" with positional arguments.`, }, scopeBuild) } func TestPositionalArgumentsWarnsWhenCalledInNestedContexts(t *testing.T) { checkFindings(t, "positional-args", ` my_macro = macro() my_rule = rule() def my_function(foo): pass other_function(foo = my_macro(x)) other_function(foo = my_rule(x)) other_function(foo = my_function(x)) [my_macro(foo) for foo in bar] [my_rule(foo) for foo in bar] [my_function(foo) for foo in bar] `, []string{ `6: All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax. Found call to rule or macro "my_macro" with positional arguments.`, `7: All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax. Found call to rule or macro "my_rule" with positional arguments.`, `10: All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax. Found call to rule or macro "my_macro" with positional arguments.`, `11: All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax. Found call to rule or macro "my_rule" with positional arguments.`, }, scopeBuild) } func TestKwargsInBuildFilesWarning(t *testing.T) { checkFindings(t, "build-args-kwargs", ` cc_library( name = "foo", *args, **kwargs, ) foo(*bar(**kgs))`, []string{ ":3: *args are not allowed in BUILD files.", ":4: **kwargs are not allowed in BUILD files.", ":7: *args are not allowed in BUILD files.", ":7: **kwargs are not allowed in BUILD files.", }, scopeBuild) checkFindings(t, "build-args-kwargs", ` cc_library( name = "foo", -args, ) foo(not bar(-kgs))`, []string{}, scopeBuild) } func TestPrintWarning(t *testing.T) { checkFindings(t, "print", ` foo() print("foo") def f(x): print(x) g(x) or print("not g") `, []string{ `:3: "print()" is a debug function and shouldn't be submitted.`, `:6: "print()" is a debug function and shouldn't be submitted.`, `:8: "print()" is a debug function and shouldn't be submitted.`, }, scopeBazel) } func TestMakeLocationVariable(t *testing.T) { checkFindings(t, "make-location", ` genrule( name = "a", srcs = [":foo"], outs = ["out"], cmd = "cp $(location :foo) $@", ) genrule( name = "b", srcs = [":a", ":b"], outs = ["out"], cmd = "cat $(locations :a :b) > $@", ) genrule( name = "c", srcs = [":foo"], outs = ["out"], cmd = "cp $$(location :foo) $$@", ) cc_test( name = "d", args = ["--config=$(execpath :cfg)"], ) load("location", "symbol") `, []string{ `:5: The "$(location)" and "$(locations)" make variables are deprecated. Use "$(execpath ...)" or "$(rootpath ...)" instead.`, `:12: The "$(location)" and "$(locations)" make variables are deprecated. Use "$(execpath ...)" or "$(rootpath ...)" instead.`, `:19: The "$(location)" and "$(locations)" make variables are deprecated. Use "$(execpath ...)" or "$(rootpath ...)" instead.`, }, scopeBuild) } buildtools-10.1.0/.bazelci/0000775000175000017500000000000015254573013015033 5ustar kenhyskenhysbuildtools-10.1.0/.bazelci/presubmit.yml0000664000175000017500000000063115254573013017570 0ustar kenhyskenhys--- buildifier: version: latest warnings: "default" platforms: ubuntu2204: test_targets: - "//..." macos: test_targets: - "//..." macos_arm64: test_targets: - "//..." windows: test_flags: - "--enable_runfiles" test_targets: - "--" - "//:tests" - "-//warn/docs:docs_test" - "-//build:build_test" - "-//buildifier:buildifier_integration_test" buildtools-10.1.0/wspace/0000775000175000017500000000000015254573013014626 5ustar kenhyskenhysbuildtools-10.1.0/wspace/workspace_test.go0000664000175000017500000001352415254573013020217 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 import ( "os" "path/filepath" "reflect" "testing" ) type testCase struct { input string expectedRoot, expectedRest string } func runBasicTestWithRepoRootFile(t *testing.T, repoRootFile string) { tmp, err := os.MkdirTemp("", "wspace") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) if err := os.MkdirAll(filepath.Join(tmp, "a", "b", "c"), 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, repoRootFile), nil, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tmp, "a", "b", repoRootFile), nil, 0755); err != nil { t.Fatal(err) } for _, tc := range []testCase{ {tmp, tmp, ""}, {filepath.Join(tmp, "a"), tmp, "a"}, {filepath.Join(tmp, "a", "b"), filepath.Join(tmp, "a", "b"), ""}, {filepath.Join(tmp, "a", "b", "c"), filepath.Join(tmp, "a", "b"), "c"}, {"a", "", ""}, // error case } { root, rest := FindWorkspaceRoot(tc.input) if root != tc.expectedRoot || rest != tc.expectedRest { t.Errorf("FindWorkspaceRoot(%q) = %q, %q; want %q, %q", tc.input, root, rest, tc.expectedRoot, tc.expectedRest) } } } func TestBasic(t *testing.T) { runBasicTestWithRepoRootFile(t, ".buckconfig") runBasicTestWithRepoRootFile(t, workspaceFile) } func TestFindRepoBuildfiles(t *testing.T) { tmp, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) workspace := []byte(` git_repository( name = "a", build_file = "a.BUILD", ) new_http_archive( name = "b", build_file = "b.BUILD", ) new_local_repository( name = "c", build_file = "c.BUILD", ) new_git_repository( name = "d", build_file = "d.BUILD", ) git_repository( name = "e", build_file_content = "n/a", ) new_http_archive( name = "f", build_file = "//third_party:f.BUILD", ) `) if err := os.WriteFile(filepath.Join(tmp, workspaceFile), workspace, 0755); err != nil { t.Fatal(err) } files, err := FindRepoBuildFiles(tmp) if err != nil { t.Fatal(err) } expected := map[string]string{ "a": filepath.Join(tmp, "a.BUILD"), "b": filepath.Join(tmp, "b.BUILD"), "c": filepath.Join(tmp, "c.BUILD"), "d": filepath.Join(tmp, "d.BUILD"), "f": filepath.Join(tmp, "third_party/f.BUILD"), } if !reflect.DeepEqual(files, expected) { t.Errorf("FileRepoBuildFiles(`%s`) = %q; want %q", workspace, files, expected) } } func checkSplitFilePathOutput(t *testing.T, name, filename, expectedWorkspaceRoot, expectedPkg, expectedLabel string) { workspaceRoot, pkg, label := SplitFilePath(filename) if workspaceRoot != expectedWorkspaceRoot { t.Errorf("%s: expected the workspace root to be %q, was %q instead", name, expectedWorkspaceRoot, workspaceRoot) } if pkg != expectedPkg { t.Errorf("%s: expected the package name to be %q, was %q instead", name, expectedPkg, pkg) } if label != expectedLabel { t.Errorf("%s: expected the label to be %q, was %q instead", name, expectedLabel, label) } } func TestSplitFilePath(t *testing.T) { dir, err := os.MkdirTemp("", "") if err != nil { t.Error(err) } defer os.RemoveAll(dir) if err := os.MkdirAll(filepath.Join(dir, "path", "to", "package"), os.ModePerm); err != nil { t.Error(err) } filename := filepath.Join(dir, "path", "to", "package", "file.bzl") checkSplitFilePathOutput(t, "No WORKSPACE file", filename, "", "", "") // Create a WORKSPACE file and try again (dir/WORKSPACE) if err := os.WriteFile(filepath.Join(dir, "WORKSPACE"), []byte{}, os.ModePerm); err != nil { t.Error(err) } checkSplitFilePathOutput(t, "WORKSPACE file exists", filename, dir, "", "path/to/package/file.bzl") checkSplitFilePathOutput(t, "WORKSPACE file exists, empty package", filepath.Join(dir, "file.bzl"), dir, "", "file.bzl") // Add a BUILD file buildPath := filepath.Join(dir, "path", "to", "BUILD") if err := os.WriteFile(buildPath, []byte{}, os.ModePerm); err != nil { t.Error(err) } checkSplitFilePathOutput(t, "WORKSPACE and BUILD files exists 1", buildPath, dir, "path/to", "BUILD") checkSplitFilePathOutput(t, "WORKSPACE and BUILD files exists 2", filename, dir, "path/to", "package/file.bzl") // Add a subpackage BUILD file subBuildPath := filepath.Join(dir, "path", "to", "package", "BUILD.bazel") if err := os.WriteFile(subBuildPath, []byte{}, os.ModePerm); err != nil { t.Error(err) } checkSplitFilePathOutput(t, "WORKSPACE and two BUILD files exists 1", subBuildPath, dir, "path/to/package", "BUILD.bazel") checkSplitFilePathOutput(t, "WORKSPACE and two BUILD files exists 2", filename, dir, "path/to/package", "file.bzl") // Rename WORKSPACE to WORKSPACE.bazel and try again (dir/WORKSPACE.bazel) if err := os.Rename(filepath.Join(dir, "WORKSPACE"), filepath.Join(dir, "WORKSPACE.bazel")); err != nil { t.Error(err) } checkSplitFilePathOutput(t, "WORKSPACE file exists", filename, dir, "path/to/package", "file.bzl") checkSplitFilePathOutput(t, "WORKSPACE file exists, empty package", filepath.Join(dir, "file.bzl"), dir, "", "file.bzl") // Create another WORKSPACE file and try again (dir/path/WORKSPACE) newRoot := filepath.Join(dir, "path") if err := os.MkdirAll(newRoot, os.ModePerm); err != nil { t.Error(err) } if err := os.WriteFile(filepath.Join(newRoot, "WORKSPACE"), []byte{}, os.ModePerm); err != nil { t.Error(err) } checkSplitFilePathOutput(t, "Two WORKSPACE files exist", filename, newRoot, "to/package", "file.bzl") } buildtools-10.1.0/wspace/workspace.go0000664000175000017500000001274415254573013017163 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 ( "os" "path/filepath" "sort" "strings" "github.com/bazel-contrib/buildtools/v10/build" ) const workspaceFile = "WORKSPACE" const buildFile = "BUILD" // IsRegularFile returns true if the path refers to a regular file after // following symlinks. func IsRegularFile(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() } func isFile(fi os.FileInfo) bool { return fi.Mode()&os.ModeType == 0 } func isExecutable(fi os.FileInfo) bool { return isFile(fi) && fi.Mode()&0100 == 0100 } var repoRootFiles = map[string]func(os.FileInfo) bool{ workspaceFile: isFile, workspaceFile + ".bazel": isFile, "MODULE.bazel": isFile, "REPO.bazel": isFile, ".buckconfig": isFile, "pants": isExecutable, } var packageRootFiles = map[string]func(os.FileInfo) bool{ buildFile: isFile, buildFile + ".bazel": isFile, } // 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, repoRootFiles); 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 file that satisfies a condition of `rootFiles` // returning the directory containing it, or an error if none found in the tree. func Find(dir string, rootFiles map[string]func(os.FileInfo) bool) (string, error) { if dir == "" || dir == "/" || dir == "." || (len(dir) == 3 && strings.HasSuffix(dir, ":\\")) { return "", os.ErrNotExist } // Sort the files to make the function deterministic rootFilesSorted := make([]string, 0, len(rootFiles)) for file := range rootFiles { rootFilesSorted = append(rootFilesSorted, file) } sort.Strings(rootFilesSorted) for _, repoRootFile := range rootFilesSorted { fiFunc := rootFiles[repoRootFile] if fi, err := os.Stat(filepath.Join(dir, repoRootFile)); err == nil && fiFunc(fi) { return dir, nil } else if err != nil && !os.IsNotExist(err) { return "", err } } return Find(filepath.Dir(dir), rootFiles) } // FindRepoBuildFiles parses the WORKSPACE to find BUILD files for non-Bazel // external repositories, specifically those defined by one of these rules: // // git_repository(), new_local_repository(), new_http_archive() func FindRepoBuildFiles(root string) (map[string]string, error) { ws := filepath.Join(root, workspaceFile) kinds := []string{ "git_repository", "new_local_repository", "new_git_repository", "new_http_archive", } data, err := os.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 } // relPath returns a path for `target` relative to `base`, but an empty string // instead of "." if the directories are equivalent, and with forward slashes. func relPath(base, target string) (string, error) { rel, err := filepath.Rel(base, target) if err != nil { return "", err } if rel == "." { return "", nil } return strings.ReplaceAll(rel, string(os.PathSeparator), "/"), nil } // SplitFilePath splits a file path into the workspace root, package name and label. // Workspace root is determined as the last directory in the file path that // contains a WORKSPACE (or WORKSPACE.bazel) file. // Package and label are always separated with forward slashes. // Returns empty strings if no WORKSPACE file is found. func SplitFilePath(filename string) (workspaceRoot, pkg, label string) { dir := filepath.Dir(filename) workspaceRoot, err := Find(dir, repoRootFiles) if err != nil { return "", "", "" } packageRoot, err := Find(dir, packageRootFiles) if err != nil || !strings.HasPrefix(packageRoot, workspaceRoot) { // No BUILD file or it's outside of the workspace. Shouldn't happen, // but assume it's in the workspace root. packageRoot = workspaceRoot } pkg, err = relPath(workspaceRoot, packageRoot) if err != nil { return "", "", "" } label, err = relPath(packageRoot, filename) if err != nil { return "", "", "" } return workspaceRoot, pkg, label } buildtools-10.1.0/wspace/BUILD.bazel0000664000175000017500000000075015254573013016506 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "wspace", srcs = ["workspace.go"], importpath = "github.com/bazel-contrib/buildtools/v10/wspace", visibility = ["//visibility:public"], deps = ["//build"], ) go_test( name = "wspace_test", size = "small", srcs = ["workspace_test.go"], embed = [":wspace"], ) alias( name = "go_default_library", actual = ":wspace", visibility = ["//visibility:public"], ) buildtools-10.1.0/testutils/0000775000175000017500000000000015254573013015404 5ustar kenhyskenhysbuildtools-10.1.0/testutils/diff.go0000664000175000017500000000275015254573013016647 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 testutils provides some useful helpers for buildozer/buildifer tests. package testutils import ( "os" "os/exec" "testing" ) // Diff returns the output of running diff on b1 and b2. func Diff(b1, b2 []byte) ([]byte, error) { f1, err := os.CreateTemp("", "testdiff") if err != nil { return nil, err } defer os.Remove(f1.Name()) defer f1.Close() f2, err := os.CreateTemp("", "testdiff") if err != nil { return nil, err } defer os.Remove(f2.Name()) defer f2.Close() f1.Write(b1) f2.Write(b2) data, err := exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() if len(data) > 0 { // diff exits with a non-zero status when the files don't match. // Ignore that failure as long as we get output. err = nil } return data, err } // Tdiff logs the Diff output to t.Error. func Tdiff(t *testing.T, a, b []byte) { data, err := Diff(a, b) if err != nil { t.Error(err) return } t.Error(string(data)) } buildtools-10.1.0/testutils/find.go0000664000175000017500000000247615254573013016664 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 testutils import ( "os" "path/filepath" "testing" ) // FindTests finds all files matching the given pattern. // It changes the working directory to `directory`, and returns a function // to call to change back to the current directory. // This allows tests to assert on alias finding between absolute and relative labels. func FindTests(t *testing.T, directory, pattern string) ([]string, func()) { wd, err := os.Getwd() if err != nil { t.Fatal(err) } if err := os.Chdir(filepath.Join(os.Getenv("TEST_SRCDIR"), os.Getenv("TEST_WORKSPACE"), directory)); err != nil { t.Fatal(err) } files, err := filepath.Glob(pattern) if err != nil { t.Fatal(err) } if len(files) == 0 { t.Fatal("Didn't find any test cases") } return files, func() { os.Chdir(wd) } } buildtools-10.1.0/testutils/BUILD.bazel0000664000175000017500000000056615254573013017271 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "testutils", srcs = [ "diff.go", "find.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/testutils", visibility = ["//visibility:public"], ) alias( name = "go_default_library", actual = ":testutils", visibility = ["//visibility:public"], ) buildtools-10.1.0/differ/0000775000175000017500000000000015254573013014603 5ustar kenhyskenhysbuildtools-10.1.0/differ/diff.go0000664000175000017500000000702215254573013016043 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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 differ determines how to invoke diff in the given environment. package differ import ( "fmt" "os" "os/exec" "runtime" "strings" ) // Invocation of different diff commands, according to environment variables. // A Differ describes how to invoke diff. type Differ struct { Cmd string // command MultiDiff bool // diff accepts list of multiple pairs Args []string // accumulated arguments } // run runs the given command with args. func (d *Differ) run(command string, args ...string) error { // The special diff command ":" means don't run anything. if d.Cmd == ":" { return nil } // Pass args to bash and reference with $@ to avoid shell injection in args. var cmd *exec.Cmd if command == "FC" { cmd = exec.Command(command, "/T") } else { cmd = exec.Command("/usr/bin/env", "bash", "-c", command+` "$@"`, "--") } cmd.Args = append(cmd.Args, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { // Couldn't even start bash. Worth reporting. return fmt.Errorf("buildifier: %s: %v", command, err) } // Assume bash reported anything else worth reporting. // As long as the program started (above), we don't care about the // exact exit status. In the most common case, the diff command // will exit 1, because there are diffs, causing bash to exit 1. return cmd.Wait() } // Show diffs old and new. // For a single-pair diff program, Show runs the diff program before returning. // For a multi-pair diff program, Show records the pair for later use by Run. func (d *Differ) Show(old, new string) error { if !d.MultiDiff { return d.run(d.Cmd, old, new) } d.Args = append(d.Args, ":", old, new) return nil } // Run runs any pending diffs. // For a single-pair diff program, Show already ran diff; Run is a no-op. // For a multi-pair diff program, Run displays the diffs queued by Show. func (d *Differ) Run() error { if !d.MultiDiff { return nil } if len(d.Args) == 0 { return nil } return d.run(d.Cmd, d.Args...) } // Find returns the differ to use, using various environment variables. func Find() (*Differ, bool) { d := &Differ{} deprecationWarning := false if cmd := os.Getenv("BUILDIFIER_DIFF"); cmd != "" { deprecationWarning = true d.Cmd = cmd } // Load MultiDiff setting from environment. knowMultiDiff := false if md := os.Getenv("BUILDIFIER_MULTIDIFF"); md == "0" || md == "1" { deprecationWarning = true d.MultiDiff = md == "1" knowMultiDiff = true } if d.Cmd != "" { if !knowMultiDiff { lower := strings.ToLower(d.Cmd) d.MultiDiff = strings.Contains(lower, "tkdiff") && isatty(1) && os.Getenv("DISPLAY") != "" } } else { if !knowMultiDiff { d.MultiDiff = isatty(1) && os.Getenv("DISPLAY") != "" if d.MultiDiff { deprecationWarning = true } } if d.MultiDiff { d.Cmd = "tkdiff" } else { if runtime.GOOS == "windows" { deprecationWarning = true d.Cmd = "FC" } else { d.Cmd = "diff --unified" } } } return d, deprecationWarning } buildtools-10.1.0/differ/isatty_other.go0000664000175000017500000000164215254573013017653 0ustar kenhyskenhys// Copyright 2017 Google LLC // // 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 // // https://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. // +build !windows package differ import "syscall" // isatty reports whether fd is a tty. // Actually it reports whether fd is a character device, which is close enough. func isatty(fd int) bool { var st syscall.Stat_t if err := syscall.Fstat(fd, &st); err != nil { return false } return st.Mode&syscall.S_IFMT == syscall.S_IFCHR } buildtools-10.1.0/differ/isatty_windows.go0000664000175000017500000000130215254573013020215 0ustar kenhyskenhys/* Copyright 2017 Google LLC 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 https://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. */ // +build windows package differ // isatty reports whether fd is a tty. // On Windows we just say no. func isatty(fd int) bool { return false } buildtools-10.1.0/differ/BUILD.bazel0000664000175000017500000000062215254573013016461 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "differ", srcs = [ "diff.go", "isatty_other.go", "isatty_windows.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/differ", visibility = ["//visibility:public"], ) alias( name = "go_default_library", actual = ":differ", visibility = ["//visibility:public"], ) buildtools-10.1.0/WORKSPACE.bzlmod0000664000175000017500000000000015254573013016101 0ustar kenhyskenhysbuildtools-10.1.0/README.md0000664000175000017500000000124015254573013014620 0ustar kenhyskenhys# Buildtools for bazel This repository contains developer tools for working with Google's `bazel` buildtool. * [buildifier](buildifier/README.md) For formatting BUILD, BUILD.bazel and BUCK files in a standard way * [buildozer](buildozer/README.md) For doing command-line operations on these files. * [unused_deps](unused_deps/README.md) For finding unneeded dependencies in [java_library](https://docs.bazel.build/versions/main/be/java.html#java_library) rules. [![Build status](https://badge.buildkite.com/6a80fcf7909883296cada2e474286ea627994b9130aed110e2.svg)](https://buildkite.com/bazel/buildtools-postsubmit) ## Setup See instructions in each tool's directory. buildtools-10.1.0/extra_actions_base_proto/0000775000175000017500000000000015254573013020424 5ustar kenhyskenhysbuildtools-10.1.0/extra_actions_base_proto/extra_actions_base.proto0000664000175000017500000001442315254573013025352 0ustar kenhyskenhys// Copyright 2014 The Bazel Authors. 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. // // proto definitions for the blaze extra_action feature. syntax = "proto2"; package blaze; option java_multiple_files = true; option java_package = "com.google.devtools.build.lib.actions.extra"; // option cc_api_version = 2; // option java_api_version = 2; // A list of extra actions and metadata for the print_action command. message ExtraActionSummary { repeated DetailedExtraActionInfo action = 1; } // An individual action printed by the print_action command. message DetailedExtraActionInfo { // If the given action was included in the output due to a request for a // specific file, then this field contains the name of that file so that the // caller can correctly associate the extra action with that file. // // The data in this message is currently not sufficient to run the action on a // production machine, because not all necessary input files are identified, // especially for C++. // // There is no easy way to fix this; we could require that all header files // are declared and then add all of them here (which would be a huge superset // of the files that are actually required), or we could run the include // scanner and add those files here. optional string triggering_file = 1; // The actual action. required ExtraActionInfo action = 2; } // Provides information to an extra_action on the original action it is // shadowing. message ExtraActionInfo { extensions 1000 to max; // The label of the ActionOwner of the shadowed action. optional string owner = 1; // Only set if the owner is an Aspect. // Corresponds to AspectValue.AspectKey.getAspectClass.getName() // This field is deprecated as there might now be // multiple aspects applied to the same target. // This is the aspect name of the last aspect // in 'aspects' (8) field. optional string aspect_name = 6 [deprecated = true]; // Only set if the owner is an Aspect. // Corresponds to AspectValue.AspectKey.getParameters() // This field is deprecated as there might now be // multiple aspects applied to the same target. // These are the aspect parameters of the last aspect // in 'aspects' (8) field. map aspect_parameters = 7 [deprecated = true]; message StringList { option deprecated = true; repeated string value = 1; } message AspectDescriptor { // Corresponds to AspectDescriptor.getName() optional string aspect_name = 1; // Corresponds to AspectDescriptor.getParameters() map aspect_parameters = 2; message StringList { repeated string value = 1; } } // If the owner is an aspect, all aspects applied to the target repeated AspectDescriptor aspects = 8; // An id uniquely describing the shadowed action at the ActionOwner level. optional string id = 2; // The mnemonic of the shadowed action. Used to distinguish actions with the // same ActionType. optional string mnemonic = 5; } message EnvironmentVariable { // It is possible that this name is not a valid variable identifier. required string name = 1; // The value is unescaped and unquoted. required string value = 2; } // Provides access to data that is specific to spawn actions. // Usually provided by actions using the "Spawn" & "Genrule" Mnemonics. message SpawnInfo { extend ExtraActionInfo { optional SpawnInfo spawn_info = 1003; } repeated string argument = 1; // A list of environment variables and their values. No order is enforced. repeated EnvironmentVariable variable = 2; repeated string input_file = 4; repeated string output_file = 5; } // Provides access to data that is specific to C++ compile actions. // Usually provided by actions using the "CppCompile" Mnemonic. message CppCompileInfo { extend ExtraActionInfo { optional CppCompileInfo cpp_compile_info = 1001; } optional string tool = 1; repeated string compiler_option = 2; optional string source_file = 3; optional string output_file = 4; // Due to header discovery, this won't include headers unless the build is // actually performed. If set, this field will include the value of // "source_file" in addition to the headers. repeated string sources_and_headers = 5; // A list of environment variables and their values. No order is enforced. repeated EnvironmentVariable variable = 6; } // Provides access to data that is specific to C++ link actions. // Usually provided by actions using the "CppLink" Mnemonic. message CppLinkInfo { extend ExtraActionInfo { optional CppLinkInfo cpp_link_info = 1002; } repeated string input_file = 1; optional string output_file = 2; optional string interface_output_file = 3; optional string link_target_type = 4; optional string link_staticness = 5; repeated string link_stamp = 6; repeated string build_info_header_artifact = 7; // The list of command line options used for running the linking tool. repeated string link_opt = 8; } // Provides access to data that is specific to java compile actions. // Usually provided by actions using the "Javac" Mnemonic. message JavaCompileInfo { extend ExtraActionInfo { optional JavaCompileInfo java_compile_info = 1000; } optional string outputjar = 1; repeated string classpath = 2; repeated string sourcepath = 3; repeated string source_file = 4; repeated string javac_opt = 5; repeated string processor = 6; repeated string processorpath = 7; repeated string bootclasspath = 8; repeated string argument = 9; } // Provides access to data that is specific to python rules. // Usually provided by actions using the "Python" Mnemonic. message PythonInfo { extend ExtraActionInfo { optional PythonInfo python_info = 1005; } repeated string source_file = 1; repeated string dep_file = 2; } buildtools-10.1.0/extra_actions_base_proto/extra_actions_base.gen.pb.go0000775000175000017500000011050615254573013025766 0ustar kenhyskenhys// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.10 // protoc v7.34.0 // source: extra_actions_base_proto/extra_actions_base.proto package extra_actions_base_proto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ExtraActionSummary struct { state protoimpl.MessageState `protogen:"open.v1"` Action []*DetailedExtraActionInfo `protobuf:"bytes,1,rep,name=action" json:"action,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraActionSummary) Reset() { *x = ExtraActionSummary{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraActionSummary) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraActionSummary) ProtoMessage() {} func (x *ExtraActionSummary) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraActionSummary.ProtoReflect.Descriptor instead. func (*ExtraActionSummary) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{0} } func (x *ExtraActionSummary) GetAction() []*DetailedExtraActionInfo { if x != nil { return x.Action } return nil } type DetailedExtraActionInfo struct { state protoimpl.MessageState `protogen:"open.v1"` TriggeringFile *string `protobuf:"bytes,1,opt,name=triggering_file,json=triggeringFile" json:"triggering_file,omitempty"` Action *ExtraActionInfo `protobuf:"bytes,2,req,name=action" json:"action,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DetailedExtraActionInfo) Reset() { *x = DetailedExtraActionInfo{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DetailedExtraActionInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*DetailedExtraActionInfo) ProtoMessage() {} func (x *DetailedExtraActionInfo) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DetailedExtraActionInfo.ProtoReflect.Descriptor instead. func (*DetailedExtraActionInfo) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{1} } func (x *DetailedExtraActionInfo) GetTriggeringFile() string { if x != nil && x.TriggeringFile != nil { return *x.TriggeringFile } return "" } func (x *DetailedExtraActionInfo) GetAction() *ExtraActionInfo { if x != nil { return x.Action } return nil } type ExtraActionInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Owner *string `protobuf:"bytes,1,opt,name=owner" json:"owner,omitempty"` // Deprecated: Marked as deprecated in extra_actions_base_proto/extra_actions_base.proto. AspectName *string `protobuf:"bytes,6,opt,name=aspect_name,json=aspectName" json:"aspect_name,omitempty"` // Deprecated: Marked as deprecated in extra_actions_base_proto/extra_actions_base.proto. AspectParameters map[string]*ExtraActionInfo_StringList `protobuf:"bytes,7,rep,name=aspect_parameters,json=aspectParameters" json:"aspect_parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Aspects []*ExtraActionInfo_AspectDescriptor `protobuf:"bytes,8,rep,name=aspects" json:"aspects,omitempty"` Id *string `protobuf:"bytes,2,opt,name=id" json:"id,omitempty"` Mnemonic *string `protobuf:"bytes,5,opt,name=mnemonic" json:"mnemonic,omitempty"` extensionFields protoimpl.ExtensionFields unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraActionInfo) Reset() { *x = ExtraActionInfo{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraActionInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraActionInfo) ProtoMessage() {} func (x *ExtraActionInfo) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraActionInfo.ProtoReflect.Descriptor instead. func (*ExtraActionInfo) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{2} } func (x *ExtraActionInfo) GetOwner() string { if x != nil && x.Owner != nil { return *x.Owner } return "" } // Deprecated: Marked as deprecated in extra_actions_base_proto/extra_actions_base.proto. func (x *ExtraActionInfo) GetAspectName() string { if x != nil && x.AspectName != nil { return *x.AspectName } return "" } // Deprecated: Marked as deprecated in extra_actions_base_proto/extra_actions_base.proto. func (x *ExtraActionInfo) GetAspectParameters() map[string]*ExtraActionInfo_StringList { if x != nil { return x.AspectParameters } return nil } func (x *ExtraActionInfo) GetAspects() []*ExtraActionInfo_AspectDescriptor { if x != nil { return x.Aspects } return nil } func (x *ExtraActionInfo) GetId() string { if x != nil && x.Id != nil { return *x.Id } return "" } func (x *ExtraActionInfo) GetMnemonic() string { if x != nil && x.Mnemonic != nil { return *x.Mnemonic } return "" } type EnvironmentVariable struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Value *string `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EnvironmentVariable) Reset() { *x = EnvironmentVariable{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EnvironmentVariable) String() string { return protoimpl.X.MessageStringOf(x) } func (*EnvironmentVariable) ProtoMessage() {} func (x *EnvironmentVariable) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EnvironmentVariable.ProtoReflect.Descriptor instead. func (*EnvironmentVariable) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{3} } func (x *EnvironmentVariable) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *EnvironmentVariable) GetValue() string { if x != nil && x.Value != nil { return *x.Value } return "" } type SpawnInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Argument []string `protobuf:"bytes,1,rep,name=argument" json:"argument,omitempty"` Variable []*EnvironmentVariable `protobuf:"bytes,2,rep,name=variable" json:"variable,omitempty"` InputFile []string `protobuf:"bytes,4,rep,name=input_file,json=inputFile" json:"input_file,omitempty"` OutputFile []string `protobuf:"bytes,5,rep,name=output_file,json=outputFile" json:"output_file,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SpawnInfo) Reset() { *x = SpawnInfo{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SpawnInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*SpawnInfo) ProtoMessage() {} func (x *SpawnInfo) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SpawnInfo.ProtoReflect.Descriptor instead. func (*SpawnInfo) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{4} } func (x *SpawnInfo) GetArgument() []string { if x != nil { return x.Argument } return nil } func (x *SpawnInfo) GetVariable() []*EnvironmentVariable { if x != nil { return x.Variable } return nil } func (x *SpawnInfo) GetInputFile() []string { if x != nil { return x.InputFile } return nil } func (x *SpawnInfo) GetOutputFile() []string { if x != nil { return x.OutputFile } return nil } type CppCompileInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Tool *string `protobuf:"bytes,1,opt,name=tool" json:"tool,omitempty"` CompilerOption []string `protobuf:"bytes,2,rep,name=compiler_option,json=compilerOption" json:"compiler_option,omitempty"` SourceFile *string `protobuf:"bytes,3,opt,name=source_file,json=sourceFile" json:"source_file,omitempty"` OutputFile *string `protobuf:"bytes,4,opt,name=output_file,json=outputFile" json:"output_file,omitempty"` SourcesAndHeaders []string `protobuf:"bytes,5,rep,name=sources_and_headers,json=sourcesAndHeaders" json:"sources_and_headers,omitempty"` Variable []*EnvironmentVariable `protobuf:"bytes,6,rep,name=variable" json:"variable,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CppCompileInfo) Reset() { *x = CppCompileInfo{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CppCompileInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*CppCompileInfo) ProtoMessage() {} func (x *CppCompileInfo) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CppCompileInfo.ProtoReflect.Descriptor instead. func (*CppCompileInfo) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{5} } func (x *CppCompileInfo) GetTool() string { if x != nil && x.Tool != nil { return *x.Tool } return "" } func (x *CppCompileInfo) GetCompilerOption() []string { if x != nil { return x.CompilerOption } return nil } func (x *CppCompileInfo) GetSourceFile() string { if x != nil && x.SourceFile != nil { return *x.SourceFile } return "" } func (x *CppCompileInfo) GetOutputFile() string { if x != nil && x.OutputFile != nil { return *x.OutputFile } return "" } func (x *CppCompileInfo) GetSourcesAndHeaders() []string { if x != nil { return x.SourcesAndHeaders } return nil } func (x *CppCompileInfo) GetVariable() []*EnvironmentVariable { if x != nil { return x.Variable } return nil } type CppLinkInfo struct { state protoimpl.MessageState `protogen:"open.v1"` InputFile []string `protobuf:"bytes,1,rep,name=input_file,json=inputFile" json:"input_file,omitempty"` OutputFile *string `protobuf:"bytes,2,opt,name=output_file,json=outputFile" json:"output_file,omitempty"` InterfaceOutputFile *string `protobuf:"bytes,3,opt,name=interface_output_file,json=interfaceOutputFile" json:"interface_output_file,omitempty"` LinkTargetType *string `protobuf:"bytes,4,opt,name=link_target_type,json=linkTargetType" json:"link_target_type,omitempty"` LinkStaticness *string `protobuf:"bytes,5,opt,name=link_staticness,json=linkStaticness" json:"link_staticness,omitempty"` LinkStamp []string `protobuf:"bytes,6,rep,name=link_stamp,json=linkStamp" json:"link_stamp,omitempty"` BuildInfoHeaderArtifact []string `protobuf:"bytes,7,rep,name=build_info_header_artifact,json=buildInfoHeaderArtifact" json:"build_info_header_artifact,omitempty"` LinkOpt []string `protobuf:"bytes,8,rep,name=link_opt,json=linkOpt" json:"link_opt,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CppLinkInfo) Reset() { *x = CppLinkInfo{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CppLinkInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*CppLinkInfo) ProtoMessage() {} func (x *CppLinkInfo) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CppLinkInfo.ProtoReflect.Descriptor instead. func (*CppLinkInfo) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{6} } func (x *CppLinkInfo) GetInputFile() []string { if x != nil { return x.InputFile } return nil } func (x *CppLinkInfo) GetOutputFile() string { if x != nil && x.OutputFile != nil { return *x.OutputFile } return "" } func (x *CppLinkInfo) GetInterfaceOutputFile() string { if x != nil && x.InterfaceOutputFile != nil { return *x.InterfaceOutputFile } return "" } func (x *CppLinkInfo) GetLinkTargetType() string { if x != nil && x.LinkTargetType != nil { return *x.LinkTargetType } return "" } func (x *CppLinkInfo) GetLinkStaticness() string { if x != nil && x.LinkStaticness != nil { return *x.LinkStaticness } return "" } func (x *CppLinkInfo) GetLinkStamp() []string { if x != nil { return x.LinkStamp } return nil } func (x *CppLinkInfo) GetBuildInfoHeaderArtifact() []string { if x != nil { return x.BuildInfoHeaderArtifact } return nil } func (x *CppLinkInfo) GetLinkOpt() []string { if x != nil { return x.LinkOpt } return nil } type JavaCompileInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Outputjar *string `protobuf:"bytes,1,opt,name=outputjar" json:"outputjar,omitempty"` Classpath []string `protobuf:"bytes,2,rep,name=classpath" json:"classpath,omitempty"` Sourcepath []string `protobuf:"bytes,3,rep,name=sourcepath" json:"sourcepath,omitempty"` SourceFile []string `protobuf:"bytes,4,rep,name=source_file,json=sourceFile" json:"source_file,omitempty"` JavacOpt []string `protobuf:"bytes,5,rep,name=javac_opt,json=javacOpt" json:"javac_opt,omitempty"` Processor []string `protobuf:"bytes,6,rep,name=processor" json:"processor,omitempty"` Processorpath []string `protobuf:"bytes,7,rep,name=processorpath" json:"processorpath,omitempty"` Bootclasspath []string `protobuf:"bytes,8,rep,name=bootclasspath" json:"bootclasspath,omitempty"` Argument []string `protobuf:"bytes,9,rep,name=argument" json:"argument,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *JavaCompileInfo) Reset() { *x = JavaCompileInfo{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *JavaCompileInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*JavaCompileInfo) ProtoMessage() {} func (x *JavaCompileInfo) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use JavaCompileInfo.ProtoReflect.Descriptor instead. func (*JavaCompileInfo) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{7} } func (x *JavaCompileInfo) GetOutputjar() string { if x != nil && x.Outputjar != nil { return *x.Outputjar } return "" } func (x *JavaCompileInfo) GetClasspath() []string { if x != nil { return x.Classpath } return nil } func (x *JavaCompileInfo) GetSourcepath() []string { if x != nil { return x.Sourcepath } return nil } func (x *JavaCompileInfo) GetSourceFile() []string { if x != nil { return x.SourceFile } return nil } func (x *JavaCompileInfo) GetJavacOpt() []string { if x != nil { return x.JavacOpt } return nil } func (x *JavaCompileInfo) GetProcessor() []string { if x != nil { return x.Processor } return nil } func (x *JavaCompileInfo) GetProcessorpath() []string { if x != nil { return x.Processorpath } return nil } func (x *JavaCompileInfo) GetBootclasspath() []string { if x != nil { return x.Bootclasspath } return nil } func (x *JavaCompileInfo) GetArgument() []string { if x != nil { return x.Argument } return nil } type PythonInfo struct { state protoimpl.MessageState `protogen:"open.v1"` SourceFile []string `protobuf:"bytes,1,rep,name=source_file,json=sourceFile" json:"source_file,omitempty"` DepFile []string `protobuf:"bytes,2,rep,name=dep_file,json=depFile" json:"dep_file,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PythonInfo) Reset() { *x = PythonInfo{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PythonInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*PythonInfo) ProtoMessage() {} func (x *PythonInfo) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PythonInfo.ProtoReflect.Descriptor instead. func (*PythonInfo) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{8} } func (x *PythonInfo) GetSourceFile() []string { if x != nil { return x.SourceFile } return nil } func (x *PythonInfo) GetDepFile() []string { if x != nil { return x.DepFile } return nil } // Deprecated: Marked as deprecated in extra_actions_base_proto/extra_actions_base.proto. type ExtraActionInfo_StringList struct { state protoimpl.MessageState `protogen:"open.v1"` Value []string `protobuf:"bytes,1,rep,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraActionInfo_StringList) Reset() { *x = ExtraActionInfo_StringList{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraActionInfo_StringList) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraActionInfo_StringList) ProtoMessage() {} func (x *ExtraActionInfo_StringList) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraActionInfo_StringList.ProtoReflect.Descriptor instead. func (*ExtraActionInfo_StringList) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{2, 1} } func (x *ExtraActionInfo_StringList) GetValue() []string { if x != nil { return x.Value } return nil } type ExtraActionInfo_AspectDescriptor struct { state protoimpl.MessageState `protogen:"open.v1"` AspectName *string `protobuf:"bytes,1,opt,name=aspect_name,json=aspectName" json:"aspect_name,omitempty"` AspectParameters map[string]*ExtraActionInfo_AspectDescriptor_StringList `protobuf:"bytes,2,rep,name=aspect_parameters,json=aspectParameters" json:"aspect_parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraActionInfo_AspectDescriptor) Reset() { *x = ExtraActionInfo_AspectDescriptor{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraActionInfo_AspectDescriptor) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraActionInfo_AspectDescriptor) ProtoMessage() {} func (x *ExtraActionInfo_AspectDescriptor) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraActionInfo_AspectDescriptor.ProtoReflect.Descriptor instead. func (*ExtraActionInfo_AspectDescriptor) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{2, 2} } func (x *ExtraActionInfo_AspectDescriptor) GetAspectName() string { if x != nil && x.AspectName != nil { return *x.AspectName } return "" } func (x *ExtraActionInfo_AspectDescriptor) GetAspectParameters() map[string]*ExtraActionInfo_AspectDescriptor_StringList { if x != nil { return x.AspectParameters } return nil } type ExtraActionInfo_AspectDescriptor_StringList struct { state protoimpl.MessageState `protogen:"open.v1"` Value []string `protobuf:"bytes,1,rep,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraActionInfo_AspectDescriptor_StringList) Reset() { *x = ExtraActionInfo_AspectDescriptor_StringList{} mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraActionInfo_AspectDescriptor_StringList) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraActionInfo_AspectDescriptor_StringList) ProtoMessage() {} func (x *ExtraActionInfo_AspectDescriptor_StringList) ProtoReflect() protoreflect.Message { mi := &file_extra_actions_base_proto_extra_actions_base_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraActionInfo_AspectDescriptor_StringList.ProtoReflect.Descriptor instead. func (*ExtraActionInfo_AspectDescriptor_StringList) Descriptor() ([]byte, []int) { return file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP(), []int{2, 2, 1} } func (x *ExtraActionInfo_AspectDescriptor_StringList) GetValue() []string { if x != nil { return x.Value } return nil } var file_extra_actions_base_proto_extra_actions_base_proto_extTypes = []protoimpl.ExtensionInfo{ { ExtendedType: (*ExtraActionInfo)(nil), ExtensionType: (*SpawnInfo)(nil), Field: 1003, Name: "blaze.SpawnInfo.spawn_info", Tag: "bytes,1003,opt,name=spawn_info", Filename: "extra_actions_base_proto/extra_actions_base.proto", }, { ExtendedType: (*ExtraActionInfo)(nil), ExtensionType: (*CppCompileInfo)(nil), Field: 1001, Name: "blaze.CppCompileInfo.cpp_compile_info", Tag: "bytes,1001,opt,name=cpp_compile_info", Filename: "extra_actions_base_proto/extra_actions_base.proto", }, { ExtendedType: (*ExtraActionInfo)(nil), ExtensionType: (*CppLinkInfo)(nil), Field: 1002, Name: "blaze.CppLinkInfo.cpp_link_info", Tag: "bytes,1002,opt,name=cpp_link_info", Filename: "extra_actions_base_proto/extra_actions_base.proto", }, { ExtendedType: (*ExtraActionInfo)(nil), ExtensionType: (*JavaCompileInfo)(nil), Field: 1000, Name: "blaze.JavaCompileInfo.java_compile_info", Tag: "bytes,1000,opt,name=java_compile_info", Filename: "extra_actions_base_proto/extra_actions_base.proto", }, { ExtendedType: (*ExtraActionInfo)(nil), ExtensionType: (*PythonInfo)(nil), Field: 1005, Name: "blaze.PythonInfo.python_info", Tag: "bytes,1005,opt,name=python_info", Filename: "extra_actions_base_proto/extra_actions_base.proto", }, } // Extension fields to ExtraActionInfo. var ( // optional blaze.SpawnInfo spawn_info = 1003; E_SpawnInfo_SpawnInfo = &file_extra_actions_base_proto_extra_actions_base_proto_extTypes[0] // optional blaze.CppCompileInfo cpp_compile_info = 1001; E_CppCompileInfo_CppCompileInfo = &file_extra_actions_base_proto_extra_actions_base_proto_extTypes[1] // optional blaze.CppLinkInfo cpp_link_info = 1002; E_CppLinkInfo_CppLinkInfo = &file_extra_actions_base_proto_extra_actions_base_proto_extTypes[2] // optional blaze.JavaCompileInfo java_compile_info = 1000; E_JavaCompileInfo_JavaCompileInfo = &file_extra_actions_base_proto_extra_actions_base_proto_extTypes[3] // optional blaze.PythonInfo python_info = 1005; E_PythonInfo_PythonInfo = &file_extra_actions_base_proto_extra_actions_base_proto_extTypes[4] ) var File_extra_actions_base_proto_extra_actions_base_proto protoreflect.FileDescriptor const file_extra_actions_base_proto_extra_actions_base_proto_rawDesc = "" + "\n" + "1extra_actions_base_proto/extra_actions_base.proto\x12\x05blaze\"L\n" + "\x12ExtraActionSummary\x126\n" + "\x06action\x18\x01 \x03(\v2\x1e.blaze.DetailedExtraActionInfoR\x06action\"r\n" + "\x17DetailedExtraActionInfo\x12'\n" + "\x0ftriggering_file\x18\x01 \x01(\tR\x0etriggeringFile\x12.\n" + "\x06action\x18\x02 \x02(\v2\x16.blaze.ExtraActionInfoR\x06action\"\xf4\x05\n" + "\x0fExtraActionInfo\x12\x14\n" + "\x05owner\x18\x01 \x01(\tR\x05owner\x12#\n" + "\vaspect_name\x18\x06 \x01(\tB\x02\x18\x01R\n" + "aspectName\x12]\n" + "\x11aspect_parameters\x18\a \x03(\v2,.blaze.ExtraActionInfo.AspectParametersEntryB\x02\x18\x01R\x10aspectParameters\x12A\n" + "\aaspects\x18\b \x03(\v2'.blaze.ExtraActionInfo.AspectDescriptorR\aaspects\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12\x1a\n" + "\bmnemonic\x18\x05 \x01(\tR\bmnemonic\x1af\n" + "\x15AspectParametersEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x127\n" + "\x05value\x18\x02 \x01(\v2!.blaze.ExtraActionInfo.StringListR\x05value:\x028\x01\x1a&\n" + "\n" + "StringList\x12\x14\n" + "\x05value\x18\x01 \x03(\tR\x05value:\x02\x18\x01\x1a\xbc\x02\n" + "\x10AspectDescriptor\x12\x1f\n" + "\vaspect_name\x18\x01 \x01(\tR\n" + "aspectName\x12j\n" + "\x11aspect_parameters\x18\x02 \x03(\v2=.blaze.ExtraActionInfo.AspectDescriptor.AspectParametersEntryR\x10aspectParameters\x1aw\n" + "\x15AspectParametersEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12H\n" + "\x05value\x18\x02 \x01(\v22.blaze.ExtraActionInfo.AspectDescriptor.StringListR\x05value:\x028\x01\x1a\"\n" + "\n" + "StringList\x12\x14\n" + "\x05value\x18\x01 \x03(\tR\x05value*\t\b\xe8\a\x10\x80\x80\x80\x80\x02\"?\n" + "\x13EnvironmentVariable\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x12\x14\n" + "\x05value\x18\x02 \x02(\tR\x05value\"\xe9\x01\n" + "\tSpawnInfo\x12\x1a\n" + "\bargument\x18\x01 \x03(\tR\bargument\x126\n" + "\bvariable\x18\x02 \x03(\v2\x1a.blaze.EnvironmentVariableR\bvariable\x12\x1d\n" + "\n" + "input_file\x18\x04 \x03(\tR\tinputFile\x12\x1f\n" + "\voutput_file\x18\x05 \x03(\tR\n" + "outputFile2H\n" + "\n" + "spawn_info\x12\x16.blaze.ExtraActionInfo\x18\xeb\a \x01(\v2\x10.blaze.SpawnInfoR\tspawnInfo\"\xd1\x02\n" + "\x0eCppCompileInfo\x12\x12\n" + "\x04tool\x18\x01 \x01(\tR\x04tool\x12'\n" + "\x0fcompiler_option\x18\x02 \x03(\tR\x0ecompilerOption\x12\x1f\n" + "\vsource_file\x18\x03 \x01(\tR\n" + "sourceFile\x12\x1f\n" + "\voutput_file\x18\x04 \x01(\tR\n" + "outputFile\x12.\n" + "\x13sources_and_headers\x18\x05 \x03(\tR\x11sourcesAndHeaders\x126\n" + "\bvariable\x18\x06 \x03(\v2\x1a.blaze.EnvironmentVariableR\bvariable2X\n" + "\x10cpp_compile_info\x12\x16.blaze.ExtraActionInfo\x18\xe9\a \x01(\v2\x15.blaze.CppCompileInfoR\x0ecppCompileInfo\"\x9c\x03\n" + "\vCppLinkInfo\x12\x1d\n" + "\n" + "input_file\x18\x01 \x03(\tR\tinputFile\x12\x1f\n" + "\voutput_file\x18\x02 \x01(\tR\n" + "outputFile\x122\n" + "\x15interface_output_file\x18\x03 \x01(\tR\x13interfaceOutputFile\x12(\n" + "\x10link_target_type\x18\x04 \x01(\tR\x0elinkTargetType\x12'\n" + "\x0flink_staticness\x18\x05 \x01(\tR\x0elinkStaticness\x12\x1d\n" + "\n" + "link_stamp\x18\x06 \x03(\tR\tlinkStamp\x12;\n" + "\x1abuild_info_header_artifact\x18\a \x03(\tR\x17buildInfoHeaderArtifact\x12\x19\n" + "\blink_opt\x18\b \x03(\tR\alinkOpt2O\n" + "\rcpp_link_info\x12\x16.blaze.ExtraActionInfo\x18\xea\a \x01(\v2\x12.blaze.CppLinkInfoR\vcppLinkInfo\"\x8e\x03\n" + "\x0fJavaCompileInfo\x12\x1c\n" + "\toutputjar\x18\x01 \x01(\tR\toutputjar\x12\x1c\n" + "\tclasspath\x18\x02 \x03(\tR\tclasspath\x12\x1e\n" + "\n" + "sourcepath\x18\x03 \x03(\tR\n" + "sourcepath\x12\x1f\n" + "\vsource_file\x18\x04 \x03(\tR\n" + "sourceFile\x12\x1b\n" + "\tjavac_opt\x18\x05 \x03(\tR\bjavacOpt\x12\x1c\n" + "\tprocessor\x18\x06 \x03(\tR\tprocessor\x12$\n" + "\rprocessorpath\x18\a \x03(\tR\rprocessorpath\x12$\n" + "\rbootclasspath\x18\b \x03(\tR\rbootclasspath\x12\x1a\n" + "\bargument\x18\t \x03(\tR\bargument2[\n" + "\x11java_compile_info\x12\x16.blaze.ExtraActionInfo\x18\xe8\a \x01(\v2\x16.blaze.JavaCompileInfoR\x0fjavaCompileInfo\"\x95\x01\n" + "\n" + "PythonInfo\x12\x1f\n" + "\vsource_file\x18\x01 \x03(\tR\n" + "sourceFile\x12\x19\n" + "\bdep_file\x18\x02 \x03(\tR\adepFile2K\n" + "\vpython_info\x12\x16.blaze.ExtraActionInfo\x18\xed\a \x01(\v2\x11.blaze.PythonInfoR\n" + "pythonInfoB/\n" + "+com.google.devtools.build.lib.actions.extraP\x01" var ( file_extra_actions_base_proto_extra_actions_base_proto_rawDescOnce sync.Once file_extra_actions_base_proto_extra_actions_base_proto_rawDescData []byte ) func file_extra_actions_base_proto_extra_actions_base_proto_rawDescGZIP() []byte { file_extra_actions_base_proto_extra_actions_base_proto_rawDescOnce.Do(func() { file_extra_actions_base_proto_extra_actions_base_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_extra_actions_base_proto_extra_actions_base_proto_rawDesc), len(file_extra_actions_base_proto_extra_actions_base_proto_rawDesc))) }) return file_extra_actions_base_proto_extra_actions_base_proto_rawDescData } var file_extra_actions_base_proto_extra_actions_base_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_extra_actions_base_proto_extra_actions_base_proto_goTypes = []any{ (*ExtraActionSummary)(nil), // 0: blaze.ExtraActionSummary (*DetailedExtraActionInfo)(nil), // 1: blaze.DetailedExtraActionInfo (*ExtraActionInfo)(nil), // 2: blaze.ExtraActionInfo (*EnvironmentVariable)(nil), // 3: blaze.EnvironmentVariable (*SpawnInfo)(nil), // 4: blaze.SpawnInfo (*CppCompileInfo)(nil), // 5: blaze.CppCompileInfo (*CppLinkInfo)(nil), // 6: blaze.CppLinkInfo (*JavaCompileInfo)(nil), // 7: blaze.JavaCompileInfo (*PythonInfo)(nil), // 8: blaze.PythonInfo nil, // 9: blaze.ExtraActionInfo.AspectParametersEntry (*ExtraActionInfo_StringList)(nil), // 10: blaze.ExtraActionInfo.StringList (*ExtraActionInfo_AspectDescriptor)(nil), // 11: blaze.ExtraActionInfo.AspectDescriptor nil, // 12: blaze.ExtraActionInfo.AspectDescriptor.AspectParametersEntry (*ExtraActionInfo_AspectDescriptor_StringList)(nil), // 13: blaze.ExtraActionInfo.AspectDescriptor.StringList } var file_extra_actions_base_proto_extra_actions_base_proto_depIdxs = []int32{ 1, // 0: blaze.ExtraActionSummary.action:type_name -> blaze.DetailedExtraActionInfo 2, // 1: blaze.DetailedExtraActionInfo.action:type_name -> blaze.ExtraActionInfo 9, // 2: blaze.ExtraActionInfo.aspect_parameters:type_name -> blaze.ExtraActionInfo.AspectParametersEntry 11, // 3: blaze.ExtraActionInfo.aspects:type_name -> blaze.ExtraActionInfo.AspectDescriptor 3, // 4: blaze.SpawnInfo.variable:type_name -> blaze.EnvironmentVariable 3, // 5: blaze.CppCompileInfo.variable:type_name -> blaze.EnvironmentVariable 10, // 6: blaze.ExtraActionInfo.AspectParametersEntry.value:type_name -> blaze.ExtraActionInfo.StringList 12, // 7: blaze.ExtraActionInfo.AspectDescriptor.aspect_parameters:type_name -> blaze.ExtraActionInfo.AspectDescriptor.AspectParametersEntry 13, // 8: blaze.ExtraActionInfo.AspectDescriptor.AspectParametersEntry.value:type_name -> blaze.ExtraActionInfo.AspectDescriptor.StringList 2, // 9: blaze.SpawnInfo.spawn_info:extendee -> blaze.ExtraActionInfo 2, // 10: blaze.CppCompileInfo.cpp_compile_info:extendee -> blaze.ExtraActionInfo 2, // 11: blaze.CppLinkInfo.cpp_link_info:extendee -> blaze.ExtraActionInfo 2, // 12: blaze.JavaCompileInfo.java_compile_info:extendee -> blaze.ExtraActionInfo 2, // 13: blaze.PythonInfo.python_info:extendee -> blaze.ExtraActionInfo 4, // 14: blaze.SpawnInfo.spawn_info:type_name -> blaze.SpawnInfo 5, // 15: blaze.CppCompileInfo.cpp_compile_info:type_name -> blaze.CppCompileInfo 6, // 16: blaze.CppLinkInfo.cpp_link_info:type_name -> blaze.CppLinkInfo 7, // 17: blaze.JavaCompileInfo.java_compile_info:type_name -> blaze.JavaCompileInfo 8, // 18: blaze.PythonInfo.python_info:type_name -> blaze.PythonInfo 19, // [19:19] is the sub-list for method output_type 19, // [19:19] is the sub-list for method input_type 14, // [14:19] is the sub-list for extension type_name 9, // [9:14] is the sub-list for extension extendee 0, // [0:9] is the sub-list for field type_name } func init() { file_extra_actions_base_proto_extra_actions_base_proto_init() } func file_extra_actions_base_proto_extra_actions_base_proto_init() { if File_extra_actions_base_proto_extra_actions_base_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_extra_actions_base_proto_extra_actions_base_proto_rawDesc), len(file_extra_actions_base_proto_extra_actions_base_proto_rawDesc)), NumEnums: 0, NumMessages: 14, NumExtensions: 5, NumServices: 0, }, GoTypes: file_extra_actions_base_proto_extra_actions_base_proto_goTypes, DependencyIndexes: file_extra_actions_base_proto_extra_actions_base_proto_depIdxs, MessageInfos: file_extra_actions_base_proto_extra_actions_base_proto_msgTypes, ExtensionInfos: file_extra_actions_base_proto_extra_actions_base_proto_extTypes, }.Build() File_extra_actions_base_proto_extra_actions_base_proto = out.File file_extra_actions_base_proto_extra_actions_base_proto_goTypes = nil file_extra_actions_base_proto_extra_actions_base_proto_depIdxs = nil } buildtools-10.1.0/extra_actions_base_proto/BUILD.bazel0000664000175000017500000000206015254573013022300 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") load("//build:build_defs.bzl", "go_proto_checkedin_test") # gazelle:exclude extra_actions_base.gen.pb.go # extra_actions_base.proto is copied from Bazel 0.22.0 # https://github.com/bazelbuild/bazel/tree/0.22.0/src/main/protobuf go_proto_checkedin_test( src = "extra_actions_base.gen.pb.go", allowed_diffs = ["^[[:space:]]*//.*protoc"], ) proto_library( name = "blaze_proto", srcs = ["extra_actions_base.proto"], visibility = ["//visibility:public"], ) go_proto_library( name = "go_default_library", importpath = "github.com/bazel-contrib/buildtools/v10/extra_actions_base_proto", proto = ":blaze_proto", visibility = ["//visibility:public"], ) go_library( name = "extra_actions_base_proto", embed = [":go_default_library"], importpath = "github.com/bazel-contrib/buildtools/v10/extra_actions_base_proto", visibility = ["//visibility:public"], ) buildtools-10.1.0/update_generated.sh0000775000175000017500000000021115254573013017175 0ustar kenhyskenhys#!/usr/bin/env bash set -eux for r in $(bazel query 'kind(sh_binary, //...)' 2> /dev/null | grep _copy | xargs) do bazel run $r done buildtools-10.1.0/go.mod0000664000175000017500000000066615254573013014462 0ustar kenhyskenhysmodule github.com/bazel-contrib/buildtools/v10 go 1.26.0 toolchain go1.27.1 require ( github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.7.0 github.com/google/safeopen v0.0.0-20260327150837-43626d6f4685 go.starlark.net v0.0.0-20210223155950-e043a3d3c984 google.golang.org/protobuf v1.36.10 ) require ( golang.org/x/sys v0.48.0 // indirect golang.org/x/tools v0.50.0 // indirect ) tool golang.org/x/tools/cmd/goyacc buildtools-10.1.0/labels/0000775000175000017500000000000015254573013014606 5ustar kenhyskenhysbuildtools-10.1.0/labels/labels_test.go0000664000175000017500000000663415254573013017447 0ustar kenhyskenhys/* * Copyright 2020 Google LLC * * 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 * * https://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 labels import ( "testing" ) var parseLabelTests = []struct { in string repo string pkg string target string }{ {"//devtools/buildozer:rule", "", "devtools/buildozer", "rule"}, {"devtools/buildozer:rule", "", "devtools/buildozer", "rule"}, {"//devtools/buildozer", "", "devtools/buildozer", "buildozer"}, {"//base", "", "base", "base"}, {"//base:", "", "base", "base"}, {"@r//devtools/buildozer:rule", "r", "devtools/buildozer", "rule"}, {"@r//devtools/buildozer", "r", "devtools/buildozer", "buildozer"}, {"@r//base", "r", "base", "base"}, {"@r//base:", "r", "base", "base"}, {"@foo", "foo", "", "foo"}, {":label", "", "", "label"}, {"label", "", "", "label"}, {"/abs/path/to/WORKSPACE:rule", "", "/abs/path/to/WORKSPACE", "rule"}, } func TestParseLabel(t *testing.T) { for i, tt := range parseLabelTests { l := Parse(tt.in) if l.Repository != tt.repo || l.Package != tt.pkg || l.Target != tt.target { t.Errorf("%d. Parse(%q) => (%q, %q, %q), want (%q, %q, %q)", i, tt.in, l.Repository, l.Package, l.Target, tt.repo, tt.pkg, tt.target) } } } var shortenLabelTests = []struct { in string pkg string result string }{ {"//devtools/buildozer:rule", "devtools/buildozer", ":rule"}, {"@//devtools/buildozer:rule", "devtools/buildozer", ":rule"}, {"//devtools/buildozer:rule", "devtools", "//devtools/buildozer:rule"}, {"//base:rule", "devtools", "//base:rule"}, {"//base:base", "devtools", "//base"}, {"//base", "base", ":base"}, {"//devtools/buildozer:buildozer", "", "//devtools/buildozer"}, {"@r//devtools/buildozer:buildozer", "devtools/buildozer", "@r//devtools/buildozer"}, {"@r//devtools/buildozer", "devtools/buildozer", "@r//devtools/buildozer"}, {"@r//devtools", "devtools", "@r//devtools"}, {"@r:rule", "", "@r:rule"}, {"@r", "", "@r"}, {"@foo//:foo", "", "@foo"}, {"@foo//devtools:foo", "", "@foo//devtools:foo"}, {"@foo//devtools:foo", "devtools", "@foo//devtools:foo"}, {"@foo//foo:foo", "", "@foo//foo"}, {":local", "", ":local"}, {"something else", "", "something else"}, {"/path/to/file", "path/to", "/path/to/file"}, {"\"//baz\"", "", "\"//baz\""}, } func TestShortenLabel(t *testing.T) { for i, tt := range shortenLabelTests { result := Shorten(tt.in, tt.pkg) if result != tt.result { t.Errorf("%d. Shorten(%q, %q) => %q, want %q", i, tt.in, tt.pkg, result, tt.result) } } } var labelsEqualTests = []struct { label1 string label2 string pkg string expected bool }{ {"//devtools/buildozer:rule", "rule", "devtools/buildozer", true}, {"//devtools/buildozer:rule", "rule:jar", "devtools", false}, } func TestLabelsEqual(t *testing.T) { for i, tt := range labelsEqualTests { if got := Equal(tt.label1, tt.label2, tt.pkg); got != tt.expected { t.Errorf("%d. Equal(%q, %q, %q) => %v, want %v", i, tt.label1, tt.label2, tt.pkg, got, tt.expected) } } } buildtools-10.1.0/labels/labels.go0000664000175000017500000000760415254573013016406 0ustar kenhyskenhys/* * Copyright 2020 Google LLC * * 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 * * https://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 labels contains helper functions for working with labels. package labels import ( "bytes" "path" "strings" ) // Label represents a Bazel target label. type Label struct { Repository string // Repository of the target, can be empty if the target belongs to the current repository Package string // Package of a target, can be empty for top packages Target string // Name of the target, should be always non-empty } // Format returns a string representation of a label. It's always absolute but // the target name is omitted if it's equal to the package directory, e.g. // "//package/foo:foo" is formatted as "//package/foo". func (l Label) Format() string { b := new(bytes.Buffer) if l.Repository != "" { b.WriteString("@") b.WriteString(l.Repository) } if l.Repository == l.Target && l.Package == "" { return b.String() } b.WriteString("//") b.WriteString(l.Package) if l.Target != path.Base(l.Package) { b.WriteString(":") b.WriteString(l.Target) } return b.String() } // FormatRelative returns a string representation of a label relative to `pkg` // (relative label if it represents a target in the same package, absolute otherwise) func (l Label) FormatRelative(pkg string) string { if l.Repository != "" || pkg != l.Package { // External repository or different package return l.Format() } return ":" + l.Target } // Parse parses an absolute Bazel label (eg. //devtools/buildozer:rule) // and returns the corresponding Label object. func Parse(target string) Label { label := Label{} if strings.HasPrefix(target, "@") { target = strings.TrimLeft(target, "@") parts := strings.SplitN(target, "/", 2) if len(parts) == 1 { // "@foo" -> @foo//:foo return Label{target, "", target} } label.Repository = parts[0] target = "/" + parts[1] } parts := strings.SplitN(target, ":", 2) parts[0] = strings.TrimPrefix(parts[0], "//") label.Package = parts[0] if len(parts) == 2 && parts[1] != "" { label.Target = parts[1] } else if !strings.HasPrefix(target, "//") { // Maybe not really a label, store everything in Target label.Target = target label.Package = "" } else { // "//absolute/pkg" -> "absolute/pkg", "pkg" label.Target = path.Base(parts[0]) } return label } // ParseRelative parses a label `input` which may be absolute or relative. // If it's relative then it's considered to belong to `pkg` func ParseRelative(input, pkg string) Label { if !strings.HasPrefix(input, "@") && !strings.HasPrefix(input, "//") { return Label{Package: pkg, Target: strings.TrimLeft(input, ":")} } return Parse(input) } // Shorten rewrites labels to use the canonical form (the form // recommended by build-style). // "//foo/bar:bar" => "//foo/bar", or ":bar" if the label belongs to pkg func Shorten(input, pkg string) string { if !strings.HasPrefix(input, "//") && !strings.HasPrefix(input, "@") { // It doesn't look like a long label, so we preserve it. // Maybe it's not a label at all, e.g. a filename. return input } label := Parse(input) return label.FormatRelative(pkg) } // Equal 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 (relative to pkg). func Equal(label1, label2, pkg string) bool { return ParseRelative(label1, pkg) == ParseRelative(label2, pkg) } buildtools-10.1.0/labels/BUILD.bazel0000664000175000017500000000066615254573013016474 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "labels", srcs = ["labels.go"], importpath = "github.com/bazel-contrib/buildtools/v10/labels", visibility = ["//visibility:public"], ) go_test( name = "labels_test", srcs = ["labels_test.go"], embed = [":labels"], ) alias( name = "go_default_library", actual = ":labels", visibility = ["//visibility:public"], ) buildtools-10.1.0/file/0000775000175000017500000000000015254573013014263 5ustar kenhyskenhysbuildtools-10.1.0/file/file.go0000664000175000017500000000517715254573013015543 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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" "os" "path/filepath" "runtime" "github.com/bazel-contrib/buildtools/v10/wspace" "github.com/google/safeopen" ) // ReadFile can be updated from the caller to change the API // for reading a file. var ReadFile = readFile // WriteFile can be updated from the caller to change the API // for writing a file. var WriteFile = writeFile // WriteFileMode can be updated from the caller to change the API // for writing a file with a specific mode. var WriteFileMode = writeFileMode // OpenReadFile can be updated from the caller to change the API // for opening a file. var OpenReadFile = openReadFile // DisableSymlinkSafety disables symlink safety checks for WriteFile and WriteFileMode. var DisableSymlinkSafety bool // readFile is like os.ReadFile. func readFile(name string) ([]byte, os.FileInfo, error) { fi, err := os.Stat(name) if err != nil { return nil, nil, err } data, err := os.ReadFile(name) return data, fi, err } // writeFile is like os.WriteFile. func writeFile(name string, data []byte) error { return WriteFileMode(name, data, 0644) } // writeFile is like os.WriteFile. func writeFileMode(name string, data []byte, mode os.FileMode) error { if DisableSymlinkSafety || runtime.GOOS != "linux" { return os.WriteFile(name, data, mode) } // If we are in a workspace, we allow writes to any symlinked file within the workspace. if wsRoot, _ := wspace.FindWorkspaceRoot(name); wsRoot != "" { relPath, err := filepath.Rel(wsRoot, name) if err != nil { return err } return safeopen.WriteFileBeneath(wsRoot, relPath, data, mode) } dir, file := filepath.Split(name) absDir, err := filepath.Abs(dir) if err != nil { return err } // If we are not in a workspace, we only allow writes to the directory where the file is located. return safeopen.WriteFileBeneath(absDir, file, data, mode) } // 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 } buildtools-10.1.0/file/BUILD.bazel0000664000175000017500000000063715254573013016147 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "file", srcs = ["file.go"], importpath = "github.com/bazel-contrib/buildtools/v10/file", visibility = ["//visibility:public"], deps = [ "//wspace", "@com_github_google_safeopen//:safeopen", ], ) alias( name = "go_default_library", actual = ":file", visibility = ["//visibility:public"], ) buildtools-10.1.0/status.py0000775000175000017500000000124015254573013015241 0ustar kenhyskenhys#!/usr/bin/env python3 from __future__ import print_function, unicode_literals from subprocess import Popen, PIPE from sys import exit def run(*cmd): process = Popen(cmd, stdout=PIPE) output, err = process.communicate() if process.wait(): exit(1) return output.strip().decode() def main(): tags = run("git", "describe", "--tags") version = tags.lstrip('v') print("STABLE_buildVersion", version) # rules_nodejs expects to read from volatile-status.txt print("BUILD_SCM_VERSION", version) revision = run("git", "rev-parse", "HEAD") print("STABLE_buildScmRevision", revision) if __name__ == "__main__": main() buildtools-10.1.0/release/0000775000175000017500000000000015254573013014764 5ustar kenhyskenhysbuildtools-10.1.0/release/npm.sh0000775000175000017500000000114515254573013016116 0ustar kenhyskenhys#!/usr/bin/env bash set -u -e -o pipefail # Googlers: you should npm login using the go/npm-publish service: # $ npm login --registry https://wombat-dressing-room.appspot.com # Non-googlers: you should run this script with # $ NPM_REGISTRY=https://registry.npmjs.org ./release.sh ... REGISTRY=${NPM_REGISTRY:-https://wombat-dressing-room.appspot.com} readonly NPM_ARGS=( "--access public" "--tag latest" "--registry $REGISTRY" # Uncomment for testing # "--dry-run" ) for pkg in buildifier buildozer; do bazel run --config=release //$pkg/npm:$pkg.publish -- ${NPM_ARGS[@]} done buildtools-10.1.0/release/build_binaries.sh0000775000175000017500000000310415254573013020274 0ustar kenhyskenhys#!/usr/bin/env bash # # Builds the release binaries of buildifier, buildozer and unused_deps for all # supported platforms and copies them into the directory given as the first # argument, using the names under which they are attached to GitHub releases # (e.g. buildifier-linux-amd64 or unused-deps-windows-amd64.exe). # # Usage: release/build_binaries.sh set -o errexit -o nounset -o pipefail if [[ $# -ne 1 ]]; then echo "Usage: $0 " >&2 exit 1 fi OUT_DIR="$(mkdir -p "$1" && cd "$1" && pwd)" cd "$(dirname "$0")/.." TOOLS=(buildifier buildozer unused_deps) PLATFORMS=( darwin-amd64 darwin-arm64 linux-amd64 linux-arm64 linux-riscv64 linux-s390x windows-amd64 windows-arm64 ) TARGETS=() for tool in "${TOOLS[@]}"; do for platform in "${PLATFORMS[@]}"; do TARGETS+=("//${tool}:${tool}-${platform}") done done bazel build --config=release -- "${TARGETS[@]}" bazel cquery --config=release --output=files -- "set(${TARGETS[*]})" | while IFS= read -r output; do # Bazel output names use underscores (unused_deps-linux_amd64), whereas # release assets use dashes throughout (unused-deps-linux-amd64). name="$(basename "${output}")" cp "${output}" "${OUT_DIR}/${name//_/-}" done for tool in "${TOOLS[@]}"; do for platform in "${PLATFORMS[@]}"; do asset="${tool//_/-}-${platform}" if [[ "${platform}" == windows-* ]]; then asset="${asset}.exe" fi if [[ ! -f "${OUT_DIR}/${asset}" ]]; then echo "Expected release asset ${asset} was not built" >&2 exit 1 fi done done ls -l "${OUT_DIR}" buildtools-10.1.0/buildifier/0000775000175000017500000000000015254573013015462 5ustar kenhyskenhysbuildtools-10.1.0/buildifier/runner.bat.template0000664000175000017500000000242115254573013021274 0ustar kenhyskenhys@echo off set ARGS=@@ARGS@@ rem Remove the leading '(' and trailing ')' characters that surround the arguments set stripped_args=%ARGS:~1,-1% rem Unquote the arguments set stripped_args=%stripped_args:'=% rem Get the absolute path to the buildifier executable for /f "tokens=2" %%i in ('findstr /r "\" MANIFEST') do (set buildifier_abs_path=%%i) powershell ^ $Files = Get-ChildItem -LiteralPath '%BUILD_WORKSPACE_DIRECTORY:/=\%' -Recurse -File -ErrorAction SilentlyContinue ^|^ Where-Object {^ $_.Name -eq 'BUILD' `^ -or $_.Name -eq 'WORKSPACE' `^ -or $_.Name -eq 'WORKSPACE.oss' `^ -or $_.Name -eq 'WORKSPACE.bzlmod' `^ -or $_.Name -eq 'BUILD.oss' `^ -or $_.Name -clike '*.bazel' `^ -or $_.Name -clike '*.bzl' `^ -or $_.Name -clike '*.sky' `^ -or $_.Name -clike '*.star' `^ -or $_.Name -clike '*.BUILD' `^ -or $_.Name -clike 'BUILD.*.oss' `^ -or $_.Name -clike 'WORKSPACE.*.oss'^ };^ ^<# Process files in batches of 100- to avoid exceeding CreateProcess' maximum length of 32,767 characters #^> ^ $i = 0;^ while ($i -lt $Files.Count)^ {^ $Batch = $Files[$i..($i + 99)];^ ^& '%buildifier_abs_path%' %stripped_args% $Batch.FullName;^ $i += $Batch.Count;^ }; buildtools-10.1.0/buildifier/internal/0000775000175000017500000000000015254573013017276 5ustar kenhyskenhysbuildtools-10.1.0/buildifier/internal/factory.bzl0000664000175000017500000001624515254573013021466 0ustar kenhyskenhys""" This module contains factory methods for simple rule and implementation generation """ load("@bazel_skylib//lib:shell.bzl", "shell") # buildifier: disable=print def _value_deprecation(ctx, attr, value): """ Prints a deprecation message related to a specific value for an attr. Args: ctx: The execution context attr: A String representing the attribute name value: The deprecated value """ print("DEPRECATION NOTICE: value '%s' for attribute '%s' will be removed in the future. Migrate '%s' to buildifier_test." % (value, attr, ctx.label)) # buildifier: disable=print def _attr_deprecation(ctx, attr): """ Prints an attribute deprecation message. Args: ctx: The execution context attr: A String representing the deprecated attribute name """ print("DEPRECATION NOTICE: attribute '%s' will be removed in the future. Migrate '%s' to buildifier_test." % (attr, ctx.label)) def buildifier_attr_factory(test_rule = False): """ Helper macro to generate a struct of attrs for use in a rule() definition. Args: test_rule: Whether or not to generate attrs for a test rule. Returns: A dictionary of attributes relevant to the rule """ attrs = { "buildifier": attr.label( default = "//buildifier", cfg = "exec", executable = True, ), "verbose": attr.bool( doc = "Print verbose information on standard error", ), "exclude_patterns": attr.string_list( allow_empty = True, doc = "A list of glob patterns passed to the find command. E.g. './vendor/*' to exclude the Go vendor directory. In test rules, this attribute requires the use of the no_sandbox attribute.", ), "mode": attr.string( default = "fix" if not test_rule else "diff", doc = "Formatting mode", values = ["check", "diff", "print_if_changed"] + ["fix"] if not test_rule else [], ), "lint_mode": attr.string( doc = "Linting mode", values = ["", "warn"] + ["fix"] if not test_rule else [], ), "lint_warnings": attr.string_list( allow_empty = True, doc = "all prefixed with +/- if you want to include in or exclude from the default set of warnings, or none prefixed with +/- if you want to override the default set, or 'all' for all available warnings", ), "diff_command": attr.string( doc = "Command to use to show diff, with mode=diff. E.g. 'diff -u'", ), "multi_diff": attr.bool( default = False, doc = "Set to True if the diff command specified by the 'diff_command' can diff multiple files in the style of 'tkdiff'", ), "add_tables": attr.label( mandatory = False, doc = "path to JSON file with custom table definitions which will be merged with the built-in tables", allow_single_file = True, ), "_runner": attr.label( default = "//buildifier:runner.bash.template", allow_single_file = True, ), "_windows_runner": attr.label( default = "//buildifier:runner.bat.template", allow_single_file = True, ), } if test_rule: attrs.update({ "srcs": attr.label_list( allow_files = [ ".bazel", ".bzl", ".oss", ".sky", "BUILD", "WORKSPACE", "WORKSPACE.bzlmod", ], doc = "A list of labels representing the starlark files to include in the test", ), "no_sandbox": attr.bool( default = False, doc = "Set to True to enable running buildifier on all files in the workspace", ), "workspace": attr.label( allow_single_file = True, doc = "Label of the WORKSPACE file; required when the no-sandbox attribute is True", ), }) return attrs def buildifier_impl_factory(ctx, test_rule = False): """ Helper macro to generate a buildifier or buildifier_test rule. This macro does not depend on defaults encoded in the binary, instead preferring to set explicit values for each flag. Args: ctx: The execution context. test_rule: Whether or not to generate a test rule. Returns: A DefaultInfo provider """ if not test_rule and ctx.attr.mode in ["check", "diff", "print_if_changed"]: _value_deprecation(ctx, "mode", ctx.attr.mode) args = [ "-mode=%s" % ctx.attr.mode, "-v=%s" % str(ctx.attr.verbose).lower(), ] if ctx.attr.lint_mode: args.append("-lint=%s" % ctx.attr.lint_mode) if ctx.attr.lint_warnings: if not ctx.attr.lint_mode: fail("Cannot pass 'lint_warnings' without a 'lint_mode'") args.append("--warnings={}".format(",".join(ctx.attr.lint_warnings))) if ctx.attr.multi_diff: args.append("-multi_diff") if not test_rule: _attr_deprecation(ctx, "multi_diff") if ctx.attr.diff_command: args.append("-diff_command=%s" % ctx.attr.diff_command) if not test_rule: _attr_deprecation(ctx, "diff_command") if ctx.attr.add_tables: args.append("-add_tables=%s" % ctx.file.add_tables.path) exclude_patterns_str = "" if ctx.attr.exclude_patterns: if test_rule and not ctx.attr.no_sandbox: fail("Cannot use 'exclude_patterns' in a test rule without 'no_sandbox'") exclude_patterns = ["\\! -path %s" % shell.quote(pattern) for pattern in ctx.attr.exclude_patterns] exclude_patterns_str = " ".join(exclude_patterns) workspace = "" if test_rule and ctx.attr.no_sandbox: if not ctx.file.workspace: fail("Cannot use 'no_sandbox' without a 'workspace'") workspace = ctx.file.workspace.path # buildifier: disable=canonical-repository substitutions = { "@@ARGS@@": shell.array_literal(args), "@@BUILDIFIER_SHORT_PATH@@": shell.quote(ctx.executable.buildifier.short_path), "@@EXCLUDE_PATTERNS@@": exclude_patterns_str, "@@WORKSPACE@@": workspace, } if ctx.executable.buildifier.extension.lower() == "exe": out_file = ctx.actions.declare_file(ctx.label.name + ".bat") runner_template = ctx.file._windows_runner else: out_file = ctx.actions.declare_file(ctx.label.name + ".bash") runner_template = ctx.file._runner ctx.actions.expand_template( template = runner_template, output = out_file, substitutions = substitutions, is_executable = True, ) runfiles = [ctx.executable.buildifier] if ctx.attr.add_tables: runfiles.append(ctx.file.add_tables) if test_rule: runfiles.extend(ctx.files.srcs) if ctx.attr.no_sandbox: runfiles.append(ctx.file.workspace) return DefaultInfo( files = depset([out_file]), runfiles = ctx.runfiles(files = runfiles), executable = out_file, ) buildtools-10.1.0/buildifier/internal/BUILD0000664000175000017500000000000015254573013020046 0ustar kenhyskenhysbuildtools-10.1.0/buildifier/deps.bzl0000664000175000017500000000116215254573013017126 0ustar kenhyskenhys""" Buildifier dependencies. """ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") def buildifier_dependencies(): _maybe( http_archive, name = "bazel_skylib", sha256 = "7363ae6721c1648017e23a200013510c9e71ca69f398d52886ee6af7f26af436", strip_prefix = "bazel-skylib-c00ef493869e2966d47508e8625aae723a4a3054", url = "https://github.com/bazelbuild/bazel-skylib/archive/c00ef493869e2966d47508e8625aae723a4a3054.tar.gz", # 2018-12-06 ) def _maybe(repo_rule, name, **kwargs): if name not in native.existing_rules(): repo_rule(name = name, **kwargs) buildtools-10.1.0/buildifier/utils/0000775000175000017500000000000015254573013016622 5ustar kenhyskenhysbuildtools-10.1.0/buildifier/utils/utils_test.go0000664000175000017500000000526315254573013021356 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 utils import ( "testing" ) func TestIsStarlarkFile(t *testing.T) { tests := []struct { filename string ok bool }{ { filename: "BUILD", ok: true, }, { filename: "BUILD.bazel", ok: true, }, { filename: "BUILD.oss", ok: true, }, { filename: "BUILD.foo.bazel", ok: true, }, { filename: "BUILD.foo.oss", ok: true, }, { filename: "build.foo.bazel", ok: true, }, { filename: "foo.BUILD.bazel", ok: true, }, { filename: "foo.BUILD", ok: true, }, { filename: "build.foo.oss", ok: false, }, { filename: "build.oss", ok: false, }, { filename: "WORKSPACE", ok: true, }, { filename: "WORKSPACE.bazel", ok: true, }, { filename: "WORKSPACE.oss", ok: true, }, { filename: "WORKSPACE.foo.bazel", ok: true, }, { filename: "WORKSPACE.foo.oss", ok: true, }, { filename: "workspace.foo.bazel", ok: true, }, { filename: "workspace.foo.oss", ok: false, }, { filename: "workspace.oss", ok: false, }, { filename: "build.gradle", ok: false, }, { filename: "workspace.xml", ok: false, }, { filename: "foo.bzl", ok: true, }, { filename: "foo.BZL", ok: false, }, { filename: "build.bzl", ok: true, }, { filename: "workspace.sky", ok: true, }, { filename: "foo.star", ok: true, }, { filename: "foo.bar", ok: false, }, { filename: "foo.build", ok: false, }, { filename: "foo.workspace", ok: false, }, { filename: "MODULE.bazel", ok: true, }, { filename: "my.MODULE.bazel", ok: true, }, { filename: "MODULE.bazel.other", ok: false, }, { filename: "foo.bazel", ok: true, }, { filename: "REPO.bazel", ok: true, }, } for _, tc := range tests { if isStarlarkFile(tc.filename) != tc.ok { t.Errorf("Wrong result for %q, want %t", tc.filename, tc.ok) } } } buildtools-10.1.0/buildifier/utils/tempfile.go0000664000175000017500000000270115254573013020756 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 utils import ( "fmt" "os" ) // TempFile keeps track of temporary files and cleans them up in the end type TempFile struct { filenames []string } // WriteTemp writes data to a temporary file and returns the name of the file. func (tf *TempFile) WriteTemp(data []byte) (file string, err error) { f, err := os.CreateTemp("", "buildifier-tmp-") if err != nil { return "", fmt.Errorf("creating temporary file: %v", err) } defer func() { e := f.Close() if e != nil { err = e } }() name := f.Name() if _, err := f.Write(data); err != nil { return "", fmt.Errorf("writing temporary file: %v", err) } tf.filenames = append(tf.filenames, name) return name, nil } // Clean removes all created temporary files func (tf *TempFile) Clean() error { for _, file := range tf.filenames { if err := os.Remove(file); err != nil { return err } } tf.filenames = []string{} return nil } buildtools-10.1.0/buildifier/utils/diagnostics.go0000664000175000017500000000756415254573013021474 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 utils import ( "encoding/json" "fmt" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/warn" ) // Diagnostics contains diagnostic information returned by formatter and linter type Diagnostics struct { Success bool `json:"success"` // overall success (whether all files are formatted properly and have no warnings) Files []*FileDiagnostics `json:"files"` // diagnostics per file } // Format formats a Diagnostics object either as plain text or as json func (d *Diagnostics) Format(format string, verbose bool) string { switch format { case "text", "": var output strings.Builder for _, f := range d.Files { for _, w := range f.Warnings { formatString := "%s:%d: %s: %s (%s)\n" if !w.Actionable { formatString = "%s:%d: %s: %s [%s]\n" } output.WriteString(fmt.Sprintf(formatString, f.Filename, w.Start.Line, w.Category, w.Message, w.URL)) } if !f.Formatted { output.WriteString(fmt.Sprintf("%s # reformat\n", f.Filename)) } } return output.String() case "json": var result []byte if verbose { result, _ = json.MarshalIndent(*d, "", " ") } else { result, _ = json.Marshal(*d) } return string(result) + "\n" } return "" } // FileDiagnostics contains diagnostics information for a file type FileDiagnostics struct { Filename string `json:"filename"` Formatted bool `json:"formatted"` Valid bool `json:"valid"` Warnings []*warning `json:"warnings"` } type warning struct { Start position `json:"start"` End position `json:"end"` Category string `json:"category"` Actionable bool `json:"actionable"` AutoFixable bool `json:"autoFixable"` Message string `json:"message"` URL string `json:"url"` } type position struct { Line int `json:"line"` Column int `json:"column"` } // NewDiagnostics returns a new Diagnostics object func NewDiagnostics(fileDiagnostics ...*FileDiagnostics) *Diagnostics { diagnostics := &Diagnostics{ Success: true, Files: fileDiagnostics, } for _, file := range diagnostics.Files { if !file.Formatted || len(file.Warnings) > 0 { diagnostics.Success = false break } } return diagnostics } // NewFileDiagnostics returns a new FileDiagnostics object func NewFileDiagnostics(filename string, warnings []*warn.Finding) *FileDiagnostics { fileDiagnostics := FileDiagnostics{ Filename: filename, Formatted: true, Valid: true, Warnings: []*warning{}, } for _, w := range warnings { fileDiagnostics.Warnings = append(fileDiagnostics.Warnings, &warning{ Start: makePosition(w.Start), End: makePosition(w.End), Category: w.Category, Actionable: w.Actionable, AutoFixable: w.AutoFixable, Message: w.Message, URL: w.URL, }) } return &fileDiagnostics } // InvalidFileDiagnostics returns a new FileDiagnostics object for an invalid file func InvalidFileDiagnostics(filename string) *FileDiagnostics { fileDiagnostics := &FileDiagnostics{ Filename: filename, Formatted: false, Valid: false, Warnings: []*warning{}, } if filename == "" { fileDiagnostics.Filename = "" } return fileDiagnostics } func makePosition(p build.Position) position { return position{ Line: p.Line, Column: p.LineRune, } } buildtools-10.1.0/buildifier/utils/utils.go0000664000175000017500000000716215254573013020317 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 utils contains shared methods that can be used by different implementations of // buildifier binary package utils import ( "os" "path/filepath" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/warn" ) func isStarlarkFile(name string) bool { ext := filepath.Ext(name) switch ext { case ".bazel", ".BUILD", ".bzl", ".sky", ".star": return true } switch ext { case ".oss": return strings.HasPrefix(name, "BUILD.") || strings.HasPrefix(name, "WORKSPACE.") } return name == "BUILD" || name == "WORKSPACE" || name == "WORKSPACE.bzlmod" } func skip(info os.FileInfo) bool { return info.IsDir() && info.Name() == ".git" } // ExpandDirectories takes a list of file/directory names and returns a list with file names // by traversing each directory recursively and searching for relevant Starlark files. func ExpandDirectories(args *[]string) ([]string, error) { files := []string{} for _, arg := range *args { info, err := os.Stat(arg) if err != nil { return []string{}, err } if !info.IsDir() { files = append(files, arg) continue } err = filepath.Walk(arg, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if skip(info) { return filepath.SkipDir } if !info.IsDir() && isStarlarkFile(info.Name()) { // Don't traverse into directory symlinks such as bazel-foo.bzl // for a project called foo.bzl. if info.Mode()&os.ModeSymlink != 0 { stat, err := os.Stat(path) if err != nil || stat.IsDir() { return nil } } files = append(files, path) } return nil }) if err != nil { return []string{}, err } } return files, nil } // GetParser returns a parser for a given file type func GetParser(inputType string) func(filename string, data []byte) (*build.File, error) { switch inputType { case "build": return build.ParseBuild case "bzl": return build.ParseBzl case "auto": return build.Parse case "workspace": return build.ParseWorkspace case "module": return build.ParseModule case "repo": return build.ParseRepo case "vendor": return build.ParseVendor default: return build.ParseDefault } } // getFileReader returns a *FileReader object that reads files from the local // filesystem if the workspace root is known. func getFileReader(workspaceRoot string) *warn.FileReader { if workspaceRoot == "" { return nil } readFile := func(filename string) ([]byte, error) { // Use OS-specific path separators filename = strings.ReplaceAll(filename, "/", string(os.PathSeparator)) path := filepath.Join(workspaceRoot, filename) return os.ReadFile(path) } return warn.NewFileReader(readFile) } // Lint calls the linter and returns a list of unresolved findings func Lint(f *build.File, lint string, warningsList *[]string, verbose bool) []*warn.Finding { fileReader := getFileReader(f.WorkspaceRoot) switch lint { case "warn": return warn.FileWarnings(f, *warningsList, nil, warn.ModeWarn, fileReader) case "fix": warn.FixWarnings(f, *warningsList, verbose, fileReader) } return nil } buildtools-10.1.0/buildifier/utils/BUILD.bazel0000664000175000017500000000107415254573013020502 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "utils", srcs = [ "diagnostics.go", "tempfile.go", "utils.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/buildifier/utils", visibility = ["//buildifier:__subpackages__"], deps = [ "//build", "//warn", ], ) go_test( name = "utils_test", srcs = ["utils_test.go"], embed = [":utils"], ) alias( name = "go_default_library", actual = ":utils", visibility = ["//visibility:public"], ) buildtools-10.1.0/buildifier/README.md0000664000175000017500000002034015254573013016740 0ustar kenhyskenhys# Buildifier buildifier is a tool for formatting bazel BUILD and .bzl files with a standard convention. ## Setup Build the tool: * Checkout the repo and then either via `go install` or `bazel build //buildifier` * If you already have 'go' installed, then build a binary via: `go install github.com/bazel-contrib/buildtools/v10/buildifier@latest` ## Usage Use buildifier to create standardized formatting for BUILD and .bzl files in the same way that clang-format is used for source files. ```bash buildifier path/to/file ``` You can also process multiple files at once: ```bash buildifier path/to/file1 path/to/file2 ``` You can make buildifier automatically find all Starlark files (i.e. BUILD, WORKSPACE, .bzl, or .sky) in a directory recursively: ```bash buildifier -r path/to/dir ``` Buildifier supports the following file types: `BUILD`, `WORKSPACE`, `.bzl`, and default, the latter is reserved for Starlark files buildifier doesn't know about (e.g. configuration files for third-party projects that use Starlark). The formatting rules for WORKSPACE files are the same as for BUILD files (both are declarative and have stricter formatting rules), and default files are formatted similarly to .bzl files, allowing more flexibility. Different linter warnings may be limited to any subset of these file types, e.g. a certain warning may be only relevant to Bazel files (i.e. `BUILD`, `WORKSPACE`, and `.bzl`) or to non-WORKSPACE files. Buildifier automatically detects the file type by its filename, taking into account optional prefixes and suffixes, e.g. `BUILD`, `BUILD.oss`, or `BUILD.bazel` will be detected as BUILD files, and `build_defs.bzl.oss` is a .bzl file. Files with unknown names (e.g. `foo.bar`) or files passed via stdin will be treated as default file type. To override the automatic file type detection use the `--type` flag explicitly: ```bash cat foo.bar | buildifier --type=build cat foo.bar | buildifier --type=bzl cat foo.bar | buildifier --type=workspace cat foo.bar | buildifier --type=default cat foo.bar | buildifier --type=module cat foo.bar | buildifier --type=repo cat foo.bar | buildifier --type=vendor ``` ## Linter Buildifier has an integrated linter that can point out and in some cases automatically fix various issues. To use it launch one of the following commands to show and to fix the issues correspondingly (note that some issues cannot be fixed automatically): ```bash buildifier --lint=warn path/to/file buildifier --lint=fix path/to/file ``` By default, the linter searches for all known issues relevant for the given file type except those that are marked with "[Disabled by default](../WARNINGS.md)" in the documentation. You can specify the categories using the `--warnings` flag either by providing the categories explicitly: ```bash buildifier --lint=warn --warnings=positional-args,duplicated-name ``` or by modifying the default warnings set by using `+` or `-` modifiers before each warning category: ```bash buildifier --lint=warn --warnings=-positional-args,+unsorted-dict-items ``` It's also possible to provide `--warnings=all` to use all supported warnings categories (they will still be limited to relevant warnings for the given file type). See also the [full list](../WARNINGS.md) or the supported warnings. ## Setup and usage via Bazel You can also invoke buildifier via the Bazel rule. `WORKSPACE` file: ```bzl load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # buildifier is written in Go and hence needs rules_go to be built. # See https://github.com/bazelbuild/rules_go for the up to date setup instructions. http_archive( name = "io_bazel_rules_go", sha256 = "6dc2da7ab4cf5d7bfc7c949776b1b7c733f05e56edc4bcd9022bb249d2e2a996", urls = [ "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.39.1/rules_go-v0.39.1.zip", "https://github.com/bazelbuild/rules_go/releases/download/v0.39.1/rules_go-v0.39.1.zip", ], ) load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies") go_rules_dependencies() load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains") go_register_toolchains(version = "1.20.3") http_archive( name = "bazel_gazelle", sha256 = "727f3e4edd96ea20c29e8c2ca9e8d2af724d8c7778e7923a854b2c80952bc405", urls = [ "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.30.0/bazel-gazelle-v0.30.0.tar.gz", "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.30.0/bazel-gazelle-v0.30.0.tar.gz", ], ) load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") # If you use WORKSPACE.bazel, use the following line instead of the bare gazelle_dependencies(): # gazelle_dependencies(go_repository_default_config = "@//:WORKSPACE.bazel") gazelle_dependencies() http_archive( name = "com_google_protobuf", sha256 = "3bd7828aa5af4b13b99c191e8b1e884ebfa9ad371b0ce264605d347f135d2568", strip_prefix = "protobuf-3.19.4", urls = [ "https://github.com/protocolbuffers/protobuf/archive/v3.19.4.tar.gz", ], ) load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() http_archive( name = "com_github_bazelbuild_buildtools", sha256 = "ae34c344514e08c23e90da0e2d6cb700fcd28e80c02e23e4d5715dddcb42f7b3", strip_prefix = "buildtools-4.2.2", urls = [ "https://github.com/bazel-contrib/buildtools/archive/refs/tags/4.2.2.tar.gz", ], ) ``` `BUILD.bazel` typically in the workspace root: ```bzl load("@com_github_bazelbuild_buildtools//buildifier:def.bzl", "buildifier") buildifier( name = "buildifier", ) ``` Invoke with ```bash bazel run //:buildifier ``` ## File diagnostics in json Buildifier supports diagnostics output in machine-readable format (json), triggered by `--format=json` (only works in combination with `--mode=check`). If used in combination with `-v`, the output json will be indented for better readability. The output format is the following: ```jsonc { "success": false, // true if all files are formatted and generate no warnings, false otherwise "files": [ // list of all files processed by buildifier { "filename": "file_1.bzl", "formatted": true, // whether the file is correctly formatted "valid": true, // whether the file is a valid Starlark file. Can only be false if formatted = false "warnings": [ // a list of warnings { "start": { "line": 1, "column": 5 }, "end": { "line": 1, "column": 10 }, "category": "integer-division", "actionable": true, "autoFixable": true, "message": "The \"/\" operator for integer division is deprecated in favor of \"//\".", "url": "https://github.com/bazel-contrib/buildtools/blob/main/WARNINGS.md#integer-division" } ] }, { "filename": "file_2.bzl", "formatted": false, "valid": true, "warnings": [], "rewrites": { // technical information, a list of rewrites buildifier applies during reformatting "editoctal": 1 } }, { "filename": "file_3.bzl", "formatted": true, "valid": true, "warnings": [] }, { "filename": "file_4.not_bzl", "formatted": false, "valid": false, "warnings": [] } ] } ``` When the `--format` flag is provided, buildifier always returns `0` unless there are internal failures or wrong input parameters, this means the output can be parsed as JSON, and its `success` field should be used to determine whether the diagnostics result is positive. ## Symlink Safety By default, Buildifier will not write to symlinks pointing outside of the Bazel workspace to prevent unintended path traversal. To disable this behavior and allow writing through symlinks pointing outside the workspace, use the `--disable_symlink_safety` flag or set `"disable_symlink_safety": true` in `.buildifier.json`: ```bash buildifier --disable_symlink_safety path/to/file ``` buildtools-10.1.0/buildifier/buildifier.go0000664000175000017500000002733715254573013020143 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // Buildifier, a tool to parse and format BUILD files. package main import ( "bytes" "flag" "fmt" "io" "os" "path/filepath" "runtime" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/buildifier/config" "github.com/bazel-contrib/buildtools/v10/buildifier/utils" "github.com/bazel-contrib/buildtools/v10/differ" "github.com/bazel-contrib/buildtools/v10/file" "github.com/bazel-contrib/buildtools/v10/wspace" ) var buildVersion = "redacted" var buildScmRevision = "redacted" func usage() { fmt.Fprintf(flag.CommandLine.Output(), `usage: buildifier [-d] [-v] [-r] [-config=path.json] [-diff_command=command] [-help] [-multi_diff] [-mode=mode] [-lint=lint_mode] [-path=path] [files...] Buildifier applies standard formatting to the named Starlark files. The mode flag selects the processing: check, diff, fix, or print_if_changed. In check mode, buildifier prints a list of files that need reformatting. In diff mode, buildifier shows the diffs that it would make. It creates the diffs by running a diff command, which can be specified using the -diff_command flag. You can indicate that the diff command can show differences between more than two files in the manner of tkdiff by specifying the -multi_diff flag. In fix mode, buildifier updates the files that need reformatting and, if the -v flag is given, prints their names to standard error. In print_if_changed mode, buildifier shows the file contents it would write. The default mode is fix. -d is an alias for -mode=diff. The lint flag selects the lint mode to be used: off, warn, fix. In off mode, the linting is not performed. In warn mode, buildifier prints warnings for common mistakes and suboptimal coding practices that include links providing more context and fix suggestions. In fix mode, buildifier updates the files with all warning resolutions produced by automated fixes. The default lint mode is off. If no files are listed, buildifier reads a Starlark file from standard input. In fix mode, it writes the reformatted Starlark file to standard output, even if no changes are necessary. Buildifier's reformatting depends in part on the path to the file relative to the workspace directory. Normally buildifier deduces that path from the file names given, but the path can be given explicitly with the -path argument. This is especially useful when reformatting standard input, or in scripts that reformat a temporary copy of a file. Return codes used by buildifier: 0: success, everything went well 1: syntax errors in input 2: usage errors: invoked incorrectly 3: unexpected runtime errors: file I/O problems or internal bugs 4: check mode failed (reformat is needed) Full list of flags with their defaults: `) flag.PrintDefaults() fmt.Fprintf(flag.CommandLine.Output(), ` Buildifier can also be configured via a JSON file. The location of the file is given by the -config flag, the BUILDIFIER_CONFIG environment variable, or a file named '.buildifier.json' at the root of the workspace (e.g., in the same directory as the WORKSPACE file). The PWD environment variable or process working directory is used to help find the workspace root. If present, the file is loaded into memory and becomes the base configuration that command line flags override. A sample configuration file can be printed to stdout by running buildifier -config=example. The config file feature can be disabled completely with -config=off. `) } func main() { c := config.New() flags := c.FlagSet("buildifier", flag.ExitOnError) flag.CommandLine = flags flag.Usage = usage flags.Parse(os.Args[1:]) args := flags.Args() if c.Help { flag.CommandLine.SetOutput(os.Stdout) usage() fmt.Println() os.Exit(0) } if c.Version { fmt.Printf("buildifier version: %s \n", buildVersion) fmt.Printf("buildifier scm revision: %s \n", buildScmRevision) os.Exit(0) } if c.ConfigPath == "" { c.ConfigPath = config.FindConfigPath("") } if c.ConfigPath != "" { if c.ConfigPath == "example" { fmt.Println(config.Example().String()) os.Exit(0) } if c.ConfigPath != "off" { if err := c.LoadFile(); err != nil { fmt.Fprintf(os.Stderr, "buildifier: %s\n", err) os.Exit(2) } // re-parse with new possibly new defaults flags = c.FlagSet("buildifier", flag.ExitOnError) flag.CommandLine = flags flag.Usage = usage flags.Parse(os.Args[1:]) } } if err := c.Validate(args); err != nil { fmt.Fprintf(os.Stderr, "buildifier: %s\n", err) os.Exit(2) } // Pass down debug flags into build package build.DisableRewrites = c.DisableRewrites build.AllowSort = c.AllowSort file.DisableSymlinkSafety = c.DisableSymlinkSafety differ, deprecationWarning := differ.Find() if c.DiffCommand != "" { differ.Cmd = c.DiffCommand differ.MultiDiff = c.MultiDiff } else { if deprecationWarning && c.Mode == "diff" { fmt.Fprintf(os.Stderr, "buildifier: selecting diff program with the BUILDIFIER_DIFF, BUILDIFIER_MULTIDIFF, and DISPLAY environment variables is deprecated, use flags -diff_command and -multi_diff instead\n") } } b := buildifier{c, differ} exitCode := b.run(args) os.Exit(exitCode) } type buildifier struct { config *config.Config differ *differ.Differ } func (b *buildifier) run(args []string) int { tf := &utils.TempFile{} defer tf.Clean() exitCode := 0 var diagnostics *utils.Diagnostics if len(args) == 0 || (len(args) == 1 && (args)[0] == "-") { // Read from stdin, write to stdout. data, err := io.ReadAll(os.Stdin) if err != nil { fmt.Fprintf(os.Stderr, "buildifier: reading stdin: %v\n", err) return 2 } if b.config.Mode == "fix" { b.config.Mode = "pipe" } var fileDiagnostics *utils.FileDiagnostics fileDiagnostics, exitCode = b.processFile("", data, false, tf) diagnostics = utils.NewDiagnostics(fileDiagnostics) } else { files := args if b.config.Recursive { var err error files, err = utils.ExpandDirectories(&args) if err != nil { fmt.Fprintf(os.Stderr, "buildifier: %v\n", err) return 3 } } diagnostics, exitCode = b.processFiles(files, tf) } diagnosticsOutput := diagnostics.Format(b.config.Format, b.config.Verbose) if b.config.Format != "" { // Explicitly provided --format means the diagnostics are printed to stdout fmt.Print(diagnosticsOutput) // Exit code should be set to 0 so that other tools know they can safely parse the json exitCode = 0 } else { // --format is not provided, stdout is reserved for file contents fmt.Fprint(os.Stderr, diagnosticsOutput) } if err := b.differ.Run(); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return 2 } return exitCode } func (b *buildifier) processFiles(files []string, tf *utils.TempFile) (*utils.Diagnostics, int) { // Decide how many file reads to run in parallel. // At most 100, and at most one per 10 input files. nworker := 100 if n := (len(files) + 9) / 10; nworker > n { nworker = n } runtime.GOMAXPROCS(nworker + 1) // Start nworker workers reading stripes of the input // argument list and sending the resulting data on // separate channels. file[k] is read by worker k%nworker // and delivered on ch[k%nworker]. type result struct { file string data []byte err error } ch := make([]chan result, nworker) for i := 0; i < nworker; i++ { ch[i] = make(chan result, 1) go func(i int) { for j := i; j < len(files); j += nworker { file := files[j] data, err := os.ReadFile(file) ch[i] <- result{file, data, err} } }(i) } exitCode := 0 fileDiagnostics := []*utils.FileDiagnostics{} // Process files. The processing still runs in a single goroutine // in sequence. Only the reading of the files has been parallelized. // The goal is to optimize for runs where most files are already // formatted correctly, so that reading is the bulk of the I/O. for i, file := range files { res := <-ch[i%nworker] if res.file != file { fmt.Fprintf(os.Stderr, "buildifier: internal phase error: got %s for %s", res.file, file) os.Exit(3) } if res.err != nil { fmt.Fprintf(os.Stderr, "buildifier: %v\n", res.err) exitCode = 3 continue } fd, newExitCode := b.processFile(file, res.data, len(files) > 1, tf) if fd != nil { fileDiagnostics = append(fileDiagnostics, fd) } if newExitCode != 0 { exitCode = newExitCode } } return utils.NewDiagnostics(fileDiagnostics...), exitCode } // processFile processes a single file containing data. // It has been read from filename and should be written back if fixing. func (b *buildifier) processFile(filename string, data []byte, displayFileNames bool, tf *utils.TempFile) (*utils.FileDiagnostics, int) { var exitCode int displayFilename := filename if b.config.WorkspaceRelativePath != "" { displayFilename = b.config.WorkspaceRelativePath } parser := utils.GetParser(b.config.InputType) f, err := parser(displayFilename, data) if err != nil { // Do not use buildifier: prefix on this error. // Since it is a parse error, it begins with file:line: // and we want that to be the first thing in the error. fmt.Fprintf(os.Stderr, "%v\n", err) if exitCode < 1 { exitCode = 1 } return utils.InvalidFileDiagnostics(displayFilename), exitCode } if absoluteFilename, err := filepath.Abs(displayFilename); err == nil { f.WorkspaceRoot, f.Pkg, f.Label = wspace.SplitFilePath(absoluteFilename) } warnings := utils.Lint(f, b.config.Lint, &b.config.LintWarnings, b.config.Verbose) if len(warnings) > 0 { exitCode = 4 } fileDiagnostics := utils.NewFileDiagnostics(f.DisplayPath(), warnings) ndata := build.Format(f) switch b.config.Mode { case "check": // check mode: print names of files that need formatting. if !bytes.Equal(data, ndata) { fileDiagnostics.Formatted = false return fileDiagnostics, 4 } case "diff": // diff mode: run diff on old and new. if bytes.Equal(data, ndata) { return fileDiagnostics, exitCode } outfile, err := tf.WriteTemp(ndata) if err != nil { fmt.Fprintf(os.Stderr, "buildifier: %v\n", err) return fileDiagnostics, 3 } infile := filename if filename == "" { // data was read from standard filename. // Write it to a temporary file so diff can read it. infile, err = tf.WriteTemp(data) if err != nil { fmt.Fprintf(os.Stderr, "buildifier: %v\n", err) return fileDiagnostics, 3 } } if displayFileNames { fmt.Fprintf(os.Stderr, "%v:\n", f.DisplayPath()) } if err := b.differ.Show(infile, outfile); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return fileDiagnostics, 4 } case "pipe": // pipe mode - reading from stdin, writing to stdout. // ("pipe" is not from the command line; it is set above in main.) os.Stdout.Write(ndata) case "fix": // fix mode: update files in place as needed. if bytes.Equal(data, ndata) { return fileDiagnostics, exitCode } err := file.WriteFileMode(filename, ndata, 0666) if err != nil { fmt.Fprintf(os.Stderr, "buildifier: %s\n", err) return fileDiagnostics, 3 } if b.config.Verbose { fmt.Fprintf(os.Stderr, "fixed %s\n", f.DisplayPath()) } case "print_if_changed": if bytes.Equal(data, ndata) { return fileDiagnostics, exitCode } if _, err := os.Stdout.Write(ndata); err != nil { fmt.Fprintf(os.Stderr, "buildifier: error writing output: %v\n", err) return fileDiagnostics, 3 } } return fileDiagnostics, exitCode } buildtools-10.1.0/buildifier/npm/0000775000175000017500000000000015254573013016254 5ustar kenhyskenhysbuildtools-10.1.0/buildifier/npm/package.json0000664000175000017500000000073315254573013020545 0ustar kenhyskenhys{ "name": "@bazel/buildifier", "description": "A tool for formatting bazel BUILD and .bzl files", "version": "0.0.0-PLACEHOLDER", "license": "Apache-2.0", "bin": { "buildifier": "buildifier.js" }, "keywords": [ "bazel" ], "repository": { "type": "git", "url": "https://github.com/bazel-contrib/buildtools.git" }, "bugs": { "url": "https://github.com/bazel-contrib/buildtools/issues" } } buildtools-10.1.0/buildifier/npm/test.js0000664000175000017500000000063415254573013017574 0ustar kenhyskenhysconst dir = require('path').join( process.env['TEST_SRCDIR'], process.env['JS_BINARY__WORKSPACE'], 'buildifier/npm/buildifier'); process.chdir(dir); const {stdout} = require('child_process').spawnSync( process.argv0, ['./buildifier.js', '--help'], {encoding: 'utf-8'}); if (!/usage: buildifier/.test(stdout)) { throw new Error('buildifier --help should include usage: buildifier'); } buildtools-10.1.0/buildifier/npm/BUILD.bazel0000664000175000017500000000320115254573013020126 0ustar kenhyskenhysload("@aspect_rules_js//js:defs.bzl", "js_test") load("@aspect_rules_js//npm:defs.bzl", "npm_package", "stamped_package_json") load("@bazel_skylib//rules:copy_file.bzl", "copy_file") copy_file( name = "copy_LICENSE", src = "//:LICENSE", out = "LICENSE", ) genrule( name = "buildifier_launcher", srcs = ["//:launcher.js"], outs = ["buildifier.js"], cmd = "sed s/_TOOL_/buildifier/ $< > $@", ) # npm rules live in this subdirectory to avoid a load() statement # in the parent package leaking to users. This means we need # to copy the output files so the pkg_npm will find them in the # output directory for this package. _PARENT_PACKAGE_FILES = [ "README.md", "buildifier-darwin_amd64", "buildifier-darwin_arm64", "buildifier-linux_amd64", "buildifier-linux_arm64", "buildifier-linux_riscv64", "buildifier-windows_amd64.exe", "buildifier-windows_arm64.exe", ] [ copy_file( name = "copy_%s" % s, # go_binary doesn't give a predeclared output for # the file in "out" so we have to construct a # label to reference the go_binary rule itself. src = "//buildifier:%s" % s.replace(".exe", "").replace("_", "-"), out = s, ) for s in _PARENT_PACKAGE_FILES ] stamped_package_json( name = "package", stamp_var = "BUILD_SCM_VERSION", ) npm_package( name = "buildifier", srcs = [ "LICENSE", "buildifier.js", ":package", ] + _PARENT_PACKAGE_FILES, package = "@bazel/buildifier", publishable = True, ) js_test( name = "integration_test", data = [":buildifier"], entry_point = "test.js", ) buildtools-10.1.0/buildifier/integration_test.sh0000775000175000017500000006435715254573013021422 0ustar kenhyskenhys#!/bin/bash # --- begin runfiles.bash initialization --- # Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash). set -euo pipefail if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then if [[ -f "$0.runfiles_manifest" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" elif [[ -f "$0.runfiles/MANIFEST" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then export RUNFILES_DIR="$0.runfiles" fi fi if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" else echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" exit 1 fi # --- end runfiles.bash initialization --- die () { echo "$1" 1>&2 exit 1 } [[ "$1" =~ external/* ]] && buildifier="${{1#external/}}" || buildifier="$TEST_WORKSPACE/$1" buildifier="$(rlocation "$buildifier")" touch WORKSPACE.bazel [[ -d test_dir ]] && rm -r test_dir mkdir -p test_dir/subdir mkdir -p golden INPUT="load(':foo.bzl', 'foo'); foo(tags=['b', 'a'],srcs=['d', 'c'])" # formatted differently in build and bzl modes echo -e "$INPUT" > test_dir/BUILD echo -e "$INPUT" > test_dir/test.bzl echo -e "$INPUT" > test_dir/subdir/test.bzl echo -e "$INPUT" > test_dir/subdir/build # lowercase, should be ignored by -r echo -e "$INPUT" > test.bzl # outside the test_dir directory echo -e "$INPUT" > test2.bzl # outside the test_dir directory echo -e "not valid +" > test_dir/foo.bar echo -e '{ "type": "build" }' > test_dir/.buildifier.test.json # demonstrate config file works by overriding input type to format a bzl file as a BUILD file. mkdir test_dir/workspace # name of a starlark file, but a directory mkdir test_dir/.git # contents should be ignored echo -e "a+b" > test_dir/.git/git.bzl cat > test_dir/MODULE.bazel <<'EOF' module(name='my-module',version='1.0',compatibility_level=1) include("cpp.MODULE.bazel") include("go.MODULE.bazel") include("web.MODULE.bazel") bazel_dep(name='rules_cc',version='0.0.1') bazel_dep(name='protobuf',repo_name='com_google_protobuf',version='3.19.0') bazel_dep( name='rules_go', version='0.37.0', repo_name='io_bazel_rules_go', ) go_sdk=use_extension("@io_bazel_rules_go//go:extensions.bzl","go_sdk") # Known to exist since it is instantiated by rules_go itself. use_repo(go_sdk,"go_default_sdk") non_module_deps = use_extension("//internal/bzlmod:non_module_deps.bzl","non_module_deps") use_repo( non_module_deps, "bazel_gazelle_go_repository_tools", "bazel_gazelle_go_repository_config", "bazel_gazelle_go_repository_cache", ) rules_go_non_module_deps = use_extension("@io_bazel_rules_go//go/private:extensions.bzl","non_module_dependencies",dev_dependency=True) use_repo(rules_go_non_module_deps,"go_googleapis") go_deps = use_extension("//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") use_repo( go_deps, "com_github_fsnotify_fsnotify", "com_github_fsnotify_fsnotify", "com_github_bmatcuk_doublestar_v4", "com_github_bazelbuild_buildtools", "com_github_google_go_cmp", "com_github_pelletier_go_toml", "org_golang_x_mod", "com_github_pmezard_go_difflib", # Separated by comment. "org_golang_x_sync", "org_golang_x_tools", # Used internally by the go_deps module extension. "bazel_gazelle_go_repository_directives", c = "a", b = "b", a = "c", ) go_deps.module(name = "foo") use_repo(go_deps, "foo") bazel_dep(name="foo",version="1.0") git_override(module_name="foo",remote="foo.git",commit="1234567890") bazel_dep(name="bar",version="1.0") archive_override(module_name="not_bar",integrity="sha256-1234567890") # do not sort use_repo(go_deps, "b", "b", "a") use_repo( # do not sort go_deps, "b", "b", "a", ) use_repo( go_deps, # do not sort "b", "b", "a", ) bazel_dep(name='prod_dep',version='3.19.0') bazel_dep(name='other_prod_dep',version='3.19.0',dev_dependency=False) bazel_dep(name='dev_dep',version='3.19.0',dev_dependency=True) bazel_dep(name = "weird_dep", version = "3.19.0", dev_dependency = "True" == "True") bazel_dep(name='yet_another_prod_dep',version='3.19.0') EOF cat > test_dir/REPO.bazel <<'EOF' repo(default_visibility='//visibility:private',default_testonly=False) EOF cat > test_dir/VENDOR.bazel << 'EOF' ignore ('foo') EOF cp test_dir/foo.bar golden/foo.bar cp test_dir/subdir/build golden/build cp test_dir/.git/git.bzl golden/git.bzl "$buildifier" < test_dir/BUILD > stdout "$buildifier" -r test_dir "$buildifier" test.bzl "$buildifier" --path=foo.bzl test2.bzl "$buildifier" --config=test_dir/.buildifier.test.json < test_dir/test.bzl > test_dir/test.bzl.BUILD.out "$buildifier" --config=example > test_dir/.buildifier.example.json cat > golden/BUILD.golden < golden/test.bzl.golden < golden/MODULE.bazel.golden < golden/REPO.bazel.golden <<'EOF' repo( default_testonly = False, default_visibility = "//visibility:private", ) EOF cat > golden/VENDOR.bazel.golden <<'EOF' ignore("foo") EOF cat > golden/.buildifier.example.json < test_dir/to_fix.bzl < test_dir/fixed_golden.bzl < test_dir/fixed_golden_all.bzl < test_dir/fixed_golden_dict_cfg.bzl < test_dir/fixed_golden_cfg.bzl < test_dir/fix_report_golden < golden/error_golden with_replaced_dir="${4//test_dir/another_test_dir}" echo "${with_replaced_dir//\/\/to_fix_tmp.bzl///another_test_dir/to_fix_tmp.bzl}" > golden/error_golden_another cat > golden/fix_report_golden < test_dir/error || ret=$? if [[ $ret -ne 4 ]]; then die "$1: warn: Expected buildifier to exit with 4, actual: $ret" fi diff -u test_dir/error golden/error_golden || die "$1: wrong console output for --mode=check --lint=warn" diff -u test_dir/to_fix.bzl test_dir/to_fix.bzl || die "$1: --mode=check --lint=warn shouldn't modify files" # --lint=warn $buildifier --lint=warn $2 test_dir/to_fix_tmp.bzl 2> test_dir/error || ret=$? if [[ $ret -ne 4 ]]; then die "$1: warn: Expected buildifier to exit with 4, actual: $ret" fi diff -u test_dir/error golden/error_golden || die "$1: wrong console output for --lint=warn" # --lint=warn with --path $buildifier --lint=warn --path=another_test_dir/to_fix_tmp.bzl $2 test_dir/to_fix_tmp.bzl 2> test_dir/error || ret=$? if [[ $ret -ne 4 ]]; then die "$1: warn: Expected buildifier to exit with 4, actual: $ret" fi diff -u test_dir/error golden/error_golden_another || die "$1: wrong console output for --lint=warn and --path" # --lint=fix $buildifier --lint=fix $2 -v test_dir/to_fix_tmp.bzl 2> test_dir/fix_report || ret=$? if [[ $ret -ne 4 ]]; then die "$1: fix: Expected buildifier to exit with 4, actual: $ret" fi diff -u test_dir/to_fix_tmp.bzl $3 || die "$1: wrong file output for --lint=fix" diff -u test_dir/fix_report golden/fix_report_golden || die "$1: wrong console output for --lint=fix" } test_lint "default" "" "test_dir/fixed_golden.bzl" "$error_docstring"$'\n'"$error_bzl"$'\n'"$error_integer"$'\n'"$error_cfg" 2 test_lint "all" "--warnings=all" "test_dir/fixed_golden_all.bzl" "$error_docstring"$'\n'"$error_bzl"$'\n'"$error_integer"$'\n'"$error_dict"$'\n'"$error_cfg" 2 test_lint "cfg" "--warnings=attr-cfg" "test_dir/fixed_golden_cfg.bzl" "$error_cfg" 0 test_lint "custom" "--warnings=-bzl-visibility,-integer-division,+unsorted-dict-items" "test_dir/fixed_golden_dict_cfg.bzl" "$error_docstring"$'\n'"$error_dict"$'\n'"$error_cfg" 1 # Test --format=json mkdir test_dir/json cp test_dir/to_fix.bzl test_dir/json # just not formatted cat > test_dir/json/to_fix_2.bzl < test_dir/json/to_fix_3.bzl < test_dir/json/to_fix_4.bzl < test_dir/json/foo.bar < golden/json_report_golden < golden/json_report_small_golden < golden/json_report_stdin_golden < golden/json_report_invalid_file_golden < json_report diff -u json_report ../../golden/json_report_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with many files" $buildifier --mode=check --format=json --lint=warn --warnings=-module-docstring -v to_fix_4.bzl > json_report diff -u json_report ../../golden/json_report_small_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with a single file" $buildifier --mode=check --format=json --lint=warn --warnings=-module-docstring -v < to_fix_4.bzl > json_report diff -u json_report ../../golden/json_report_stdin_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with stdin" $buildifier --mode=check --format=json --lint=warn --warnings=-module-docstring -v to_fix_4.bzl foo.bar > json_report diff -u json_report ../../golden/json_report_invalid_file_golden || die "$1: wrong console output for --mode=check --format=json --lint=warn with an invalid file" cd ../.. # Test the multifile functionality mkdir multifile cd multifile cat > lib.bzl < BUILD < report_golden < report || ret=$? diff -u report_golden report || die "$1: wrong console output for multifile warnings (WORKSPACE exists)" cd .. # Test allowed symbol load locations mkdir test_dir/allowed_locations cd test_dir/allowed_locations cat > BUILD < buildifier.tables < report_golden < report || true diff -u report_golden report || die "$1: wrong console output for allowed symbol load locations" cd ../.. # Test that buildifier cannot write to symlinks pointing outside the workspace or directory if [[ "$(uname -s)" == "Linux" ]]; then # 1. Inside a workspace: symlink pointing outside the workspace should fail mkdir -p test_dir/symlinks_ws/ws mkdir -p test_dir/symlinks_ws/outside touch test_dir/symlinks_ws/ws/WORKSPACE.bazel UNFORMATTED="foo( b=2, a=1 )" echo "$UNFORMATTED" > test_dir/symlinks_ws/outside/target.bzl ln -s ../outside/target.bzl test_dir/symlinks_ws/ws/symlink_outside.bzl ret=0 "$buildifier" test_dir/symlinks_ws/ws/symlink_outside.bzl 2> /dev/null || ret=$? if [[ $ret -ne 3 ]]; then die "Symlink outside workspace: expected buildifier to exit with 3, actual: $ret" fi diff -u <(echo "$UNFORMATTED") test_dir/symlinks_ws/outside/target.bzl || die "Symlink outside workspace should not modify target file" # Disabling symlink safety via flag should allow modifying target through symlink "$buildifier" --disable_symlink_safety test_dir/symlinks_ws/ws/symlink_outside.bzl || die "Symlink outside workspace with --disable_symlink_safety: expected buildifier to succeed" diff -u <(echo "$UNFORMATTED") test_dir/symlinks_ws/outside/target.bzl > /dev/null && die "Symlink outside workspace with --disable_symlink_safety: target file should be formatted" # Disabling symlink safety via config file should allow modifying target through symlink echo "$UNFORMATTED" > test_dir/symlinks_ws/outside/target.bzl echo '{"disable_symlink_safety": true}' > test_dir/symlinks_ws/ws/.buildifier.json "$buildifier" --config=test_dir/symlinks_ws/ws/.buildifier.json test_dir/symlinks_ws/ws/symlink_outside.bzl || die "Symlink outside workspace with .buildifier.json: expected buildifier to succeed" diff -u <(echo "$UNFORMATTED") test_dir/symlinks_ws/outside/target.bzl > /dev/null && die "Symlink outside workspace with .buildifier.json: target file should be formatted" rm -f test_dir/symlinks_ws/ws/.buildifier.json # Symlink pointing inside the workspace should succeed echo "$UNFORMATTED" > test_dir/symlinks_ws/ws/target_inside.bzl ln -s target_inside.bzl test_dir/symlinks_ws/ws/symlink_inside.bzl "$buildifier" test_dir/symlinks_ws/ws/symlink_inside.bzl || die "Symlink inside workspace: expected buildifier to succeed" diff -u <(echo "$UNFORMATTED") test_dir/symlinks_ws/ws/target_inside.bzl > /dev/null && die "Symlink inside workspace: target file should be formatted" # 2. Outside a workspace: symlink pointing outside the directory should fail NOWS_DIR="${TEST_TMPDIR:-/tmp}/nowspace_test_$$" mkdir -p "$NOWS_DIR/dir" mkdir -p "$NOWS_DIR/outside" echo "$UNFORMATTED" > "$NOWS_DIR/outside/target.bzl" ln -s ../outside/target.bzl "$NOWS_DIR/dir/symlink_outside.bzl" ret=0 "$buildifier" "$NOWS_DIR/dir/symlink_outside.bzl" 2> /dev/null || ret=$? if [[ $ret -ne 3 ]]; then die "Symlink outside directory (no workspace): expected buildifier to exit with 3, actual: $ret" fi diff -u <(echo "$UNFORMATTED") "$NOWS_DIR/outside/target.bzl" || die "Symlink outside directory should not modify target file" # Disabling symlink safety via flag should allow modifying target through symlink "$buildifier" --disable_symlink_safety "$NOWS_DIR/dir/symlink_outside.bzl" || die "Symlink outside directory with --disable_symlink_safety: expected buildifier to succeed" diff -u <(echo "$UNFORMATTED") "$NOWS_DIR/outside/target.bzl" > /dev/null && die "Symlink outside directory with --disable_symlink_safety: target file should be formatted" # Symlink pointing inside the directory should succeed echo "$UNFORMATTED" > "$NOWS_DIR/dir/target_inside.bzl" ln -s target_inside.bzl "$NOWS_DIR/dir/symlink_inside.bzl" "$buildifier" "$NOWS_DIR/dir/symlink_inside.bzl" || die "Symlink inside directory: expected buildifier to succeed" diff -u <(echo "$UNFORMATTED") "$NOWS_DIR/dir/target_inside.bzl" > /dev/null && die "Symlink inside directory: target file should be formatted" rm -rf "$NOWS_DIR" fi buildtools-10.1.0/buildifier/buildifier.bzl0000664000175000017500000000270115254573013020311 0ustar kenhyskenhys""" The module defines buildifier as a Bazel rule. """ load( "//buildifier/internal:factory.bzl", "buildifier_attr_factory", "buildifier_impl_factory", ) def _buildifier_impl(ctx): return [buildifier_impl_factory(ctx)] _buildifier = rule( implementation = _buildifier_impl, attrs = buildifier_attr_factory(), executable = True, ) def buildifier(**kwargs): """ Wrapper for the _buildifier rule. Adds 'manual' to the tags. Args: **kwargs: all parameters for _buildifier """ tags = kwargs.get("tags", []) if "manual" not in tags: tags.append("manual") kwargs["tags"] = tags _buildifier(**kwargs) def _buildifier_test_impl(ctx): return [buildifier_impl_factory(ctx, test_rule = True)] _buildifier_test = rule( implementation = _buildifier_test_impl, attrs = buildifier_attr_factory(True), test = True, ) def buildifier_test(**kwargs): """ Wrapper for the _buildifier_test rule. Optionally disables sandboxing and caching. Args: **kwargs: all parameters for _buildifier_test """ if kwargs.get("no_sandbox", False): tags = kwargs.get("tags", []) # Note: the "external" tag is a workaround for # https://github.com/bazelbuild/bazel/issues/15516. for t in ["no-sandbox", "no-cache", "external"]: if t not in tags: tags.append(t) kwargs["tags"] = tags _buildifier_test(**kwargs) buildtools-10.1.0/buildifier/runner.bash.template0000664000175000017500000000365415254573013021454 0ustar kenhyskenhys#! /usr/bin/env bash BUILDIFIER_SHORT_PATH=@@BUILDIFIER_SHORT_PATH@@ ARGS=@@ARGS@@ WORKSPACE="@@WORKSPACE@@" # Get the absolute path to the buildifier executable buildifier_short_path=$(readlink "$BUILDIFIER_SHORT_PATH") # Use TEST_WORKSPACE to determine if the script is being ran under a test if [[ ! -z "${TEST_WORKSPACE+x}" && -z "${BUILD_WORKSPACE_DIRECTORY+x}" ]]; then FIND_FILE_TYPE="l" # If WORKSPACE was provided, then the script is being run under a test in no_sandbox mode if [[ ! -z "${WORKSPACE:+x}" ]]; then FIND_FILE_TYPE="f" # resolve the WORKSPACE symlink # use `realpath` if available and `readlink` otherwise (typically macOS) if command -v realpath &> /dev/null; then WORKSPACE_LINK="$(realpath ${WORKSPACE})" elif command -v readlink &> /dev/null; then WORKSPACE_LINK="$(readlink ${WORKSPACE})" else echo "Unable to resolve symlink (WORKSPACE: ${WORKSPACE})" exit 1 fi # Find the directory containing the WORKSPACE file WORKSPACE_PATH="$(dirname "$WORKSPACE_LINK")" # Change the working directory to the WORKSPACE parent if ! cd "$WORKSPACE_PATH" ; then echo "Unable to change to workspace (WORKSPACE_PATH: ${WORKSPACE_PATH})" fi fi else # Change into the workspace directory if this is _not_ a test if ! cd "$BUILD_WORKSPACE_DIRECTORY"; then echo "Unable to change to workspace (BUILD_WORKSPACE_DIRECTORY: ${BUILD_WORKSPACE_DIRECTORY})" exit 1 fi fi # Run buildifier on all starlark files find . \ -type "${FIND_FILE_TYPE:-f}" \ @@EXCLUDE_PATTERNS@@ \ \( -name '*.bzl' \ -o -name '*.sky' \ -o -name '*.star' \ -o -name '*.bazel' \ -o -name BUILD \ -o -name '*.BUILD' \ -o -name BUILD.oss \ -o -name 'BUILD.*.oss' \ -o -name WORKSPACE \ -o -name WORKSPACE.oss \ -o -name WORKSPACE.bzlmod \ -o -name 'WORKSPACE.*.oss' \ \) -print | xargs "$buildifier_short_path" "${ARGS[@]}" buildtools-10.1.0/buildifier/config/0000775000175000017500000000000015254573013016727 5ustar kenhyskenhysbuildtools-10.1.0/buildifier/config/config.go0000664000175000017500000002616115254573013020531 0ustar kenhyskenhys/* Copyright 2022 Google LLC 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 https://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 config provides configuration objects for buildifier package config import ( "encoding/json" "flag" "fmt" "io" "log" "os" "path/filepath" "strings" "github.com/bazel-contrib/buildtools/v10/tables" "github.com/bazel-contrib/buildtools/v10/warn" "github.com/bazel-contrib/buildtools/v10/wspace" ) const buildifierJSONFilename = ".buildifier.json" // New constructs a Config with default values. func New() *Config { return &Config{ InputType: "auto", } } // FindConfigPath locates the nearest buildifier configuration file. First // tries the value of the BUILDIFIER_CONFIG environment variable. If no // environment variable is defined, The configuration file will be resolved // starting from the process cwd and searching up the file tree until a config // file is (or isn't) found. func FindConfigPath(rootDir string) string { if filename, ok := os.LookupEnv("BUILDIFIER_CONFIG"); ok { return filename } if rootDir == "" { rootDir, _ = os.Getwd() // best-effort, ignore error } dirname, err := wspace.Find( rootDir, map[string]func(os.FileInfo) bool{ buildifierJSONFilename: func(fi os.FileInfo) bool { return fi.Mode()&os.ModeType == 0 }, }, ) if err != nil { return "" } return filepath.Join(dirname, buildifierJSONFilename) } // findTablesPath locates the specified table file starting from the process's // current working directory. It searches upward through the directory tree // until the file is found or the root of the workspace is reached. func findTablesPath(file string) (string, error) { if wspace.IsRegularFile(file) { return file, nil } rootDir, _ := os.Getwd() dirname, err := wspace.Find( rootDir, map[string]func(os.FileInfo) bool{ file: func(fi os.FileInfo) bool { return fi.Mode()&os.ModeType == 0 }, }, ) if err != nil { return file, err } return filepath.Join(dirname, file), nil } // Config is used to configure buildifier type Config struct { // InputType determines the input file type: build (for BUILD files), bzl // (for .bzl files), workspace (for WORKSPACE files), default (for generic // Starlark files), module (for MODULE.bazel files), // repo (for REPO.bazel files), vendor (for VENDOR.bazel files), // or auto (default, based on the filename) InputType string `json:"type,omitempty"` // Format sets the diagnostics format: text or json (default text) Format string `json:"format,omitempty"` // Mode determines the formatting mode: check, diff, or fix (default fix) Mode string `json:"mode,omitempty"` // DiffMode is an alias for DiffMode bool `json:"diffMode,omitempty"` // Lint determines the lint mode: off, warn, or fix (default off) Lint string `json:"lint,omitempty"` // Warnings is a comma-separated list of warning identifiers used in the lint mode or "all" Warnings string `json:"warnings,omitempty"` // WarningsList is a list of warnings (alternative to comma-separated warnings string) WarningsList []string `json:"warningsList,omitempty"` // Recursive instructs buildifier to find starlark files recursively Recursive bool `json:"recursive,omitempty"` // Verbose instructs buildifier to output verbose diagnostics Verbose bool `json:"verbose,omitempty"` // DiffCommand is the command to run when the formatting mode is diff // (default uses the BUILDIFIER_DIFF, BUILDIFIER_MULTIDIFF, and DISPLAY // environment variables to create the diff command) DiffCommand string `json:"diffCommand,omitempty"` // MultiDiff means the command specified by the -diff_command flag can diff // multiple files in the style of tkdiff (default false) MultiDiff bool `json:"multiDiff,omitempty"` // TablesPath is the path to JSON file with custom table definitions that // will replace the built-in tables TablesPath string `json:"tables,omitempty"` // AddTablesPath path to JSON file with custom table definitions which will be merged with the built-in tables AddTablesPath string `json:"addTables,omitempty"` // WorkspaceRelativePath - assume BUILD file has this path relative to the workspace directory WorkspaceRelativePath string `json:"path,omitempty"` // DisableRewrites configures the list of buildifier rewrites to disable DisableRewrites ArrayFlags `json:"buildifier_disable,omitempty"` // AllowSort specifies additional sort contexts to treat as safe AllowSort ArrayFlags `json:"allowsort,omitempty"` // Per default Buildifier will not write to symlinks pointing outside of the Bazel workspace. // Setting this to true will disable this behavior. DisableSymlinkSafety bool `json:"disable_symlink_safety,omitempty"` // Help is true if the -h flag is set Help bool `json:"-"` // Version is true if the -v flag is set Version bool `json:"-"` // ConfigPath is the path to this config ConfigPath string `json:"-"` // LintWarnings is the final validated list of Lint/Fix warnings LintWarnings []string `json:"-"` } // LoadFile unmarshals JSON file from the ConfigPath field. func (c *Config) LoadFile() error { file, err := os.Open(c.ConfigPath) if err != nil { return err } defer file.Close() return c.LoadReader(file) } // LoadReader unmarshals JSON data from the given reader. func (c *Config) LoadReader(in io.Reader) error { data, err := io.ReadAll(in) if err != nil { return fmt.Errorf("reading config: %w", err) } if err := json.Unmarshal(data, c); err != nil { return err } return nil } // FlagSet returns a flag.FlagSet that can be used to override the config. func (c *Config) FlagSet(name string, errorHandling flag.ErrorHandling) *flag.FlagSet { flags := flag.NewFlagSet(name, errorHandling) flags.BoolVar(&c.Help, "help", false, "print usage information") flags.BoolVar(&c.Version, "version", false, "print the version of buildifier") flags.BoolVar(&c.Verbose, "v", c.Verbose, "print verbose information to standard error") flags.BoolVar(&c.DiffMode, "d", c.DiffMode, "alias for -mode=diff") flags.BoolVar(&c.Recursive, "r", c.Recursive, "find starlark files recursively") flags.BoolVar(&c.MultiDiff, "multi_diff", c.MultiDiff, "the command specified by the -diff_command flag can diff multiple files in the style of tkdiff (default false)") flags.StringVar(&c.Mode, "mode", c.Mode, "formatting mode: check, diff, or fix (default fix)") flags.StringVar(&c.Format, "format", c.Format, "diagnostics format: text or json (default text)") flags.StringVar(&c.DiffCommand, "diff_command", c.DiffCommand, "command to run when the formatting mode is diff (default uses the BUILDIFIER_DIFF, BUILDIFIER_MULTIDIFF, and DISPLAY environment variables to create the diff command)") flags.StringVar(&c.Lint, "lint", c.Lint, "lint mode: off, warn, or fix (default off)") flags.StringVar(&c.Warnings, "warnings", c.Warnings, "comma-separated warnings used in the lint mode or \"all\"") flags.StringVar(&c.WorkspaceRelativePath, "path", c.WorkspaceRelativePath, "assume BUILD file has this path relative to the workspace directory") flags.StringVar(&c.TablesPath, "tables", c.TablesPath, "path to JSON file with custom table definitions which will replace the built-in tables") flags.StringVar(&c.AddTablesPath, "add_tables", c.AddTablesPath, "path to JSON file with custom table definitions which will be merged with the built-in tables") flags.StringVar(&c.InputType, "type", c.InputType, "Input file type: build (for BUILD files), bzl (for .bzl files), workspace (for WORKSPACE files), module (for MODULE.bazel files), repo (for REPO.bazel files), vendor (for VENDOR.bazel files), default (for generic Starlark files) or auto (default, based on the filename)") flags.StringVar(&c.ConfigPath, "config", "", "path to .buildifier.json config file") flags.Var(&c.AllowSort, "allowsort", "additional sort contexts to treat as safe") flags.Var(&c.DisableRewrites, "buildifier_disable", "list of buildifier rewrites to disable") flags.BoolVar(&c.DisableSymlinkSafety, "disable_symlink_safety", c.DisableSymlinkSafety, "per default Buildifier will not write to symlinks pointing outside of the Bazel workspace. Setting this to true will disable this behavior") return flags } // Validate checks that the input type, format, and lint modes are correctly // set. It computes the final set of warnings used for linting. The tables // package is configured as a side-effect. func (c *Config) Validate(args []string) error { if err := ValidateInputType(&c.InputType); err != nil { return err } if err := ValidateFormat(&c.Format, &c.Mode); err != nil { return err } if err := ValidateModes(&c.Mode, &c.Lint, &c.DiffMode); err != nil { return err } // If the path flag is set, must only be formatting a single file. // It doesn't make sense for multiple files to have the same path. if (c.WorkspaceRelativePath != "" || c.Mode == "print_if_changed") && len(args) > 1 { return fmt.Errorf("can only format one file when using -path flag or -mode=print_if_changed") } if c.TablesPath != "" { foundTablesPath, err := findTablesPath(c.TablesPath) if err != nil { return fmt.Errorf("failed to find %s for -tables: %w", c.TablesPath, err) } if err := tables.ParseAndUpdateJSONDefinitions(foundTablesPath, false); err != nil { return fmt.Errorf("failed to parse %s for -tables: %w", foundTablesPath, err) } } if c.AddTablesPath != "" { foundTablesPath, err := findTablesPath(c.AddTablesPath) if err != nil { return fmt.Errorf("failed to find %s for -add_tables: %w", c.AddTablesPath, err) } if err := tables.ParseAndUpdateJSONDefinitions(foundTablesPath, true); err != nil { return fmt.Errorf("failed to parse %s for -add_tables: %w", foundTablesPath, err) } } warningsList := c.WarningsList if c.Warnings != "" { warningsList = append(warningsList, c.Warnings) } warnings := strings.Join(warningsList, ",") lintWarnings, err := ValidateWarnings(&warnings, &warn.AllWarnings, &warn.DefaultWarnings) if err != nil { return err // TODO(pcj) return nil? } c.LintWarnings = lintWarnings return nil } // String renders the config as a formatted JSON string and satisfies the // Stringer interface. func (c *Config) String() string { data, err := json.MarshalIndent(c, "", " ") if err != nil { log.Panicf("config marshal json: %v", err) } return string(data) } // ArrayFlags is a string slice that satisfies the flag.Value interface type ArrayFlags []string // String implements part of the flag.Value interface func (i *ArrayFlags) String() string { return strings.Join(*i, ",") } // Set implements part of the flag.Value interface func (i *ArrayFlags) Set(value string) error { *i = append(*i, value) return nil } // Example creates a sample configuration file for the -config=example flag. func Example() *Config { c := New() c.InputType = "auto" c.Mode = "fix" c.Lint = "fix" c.WarningsList = warn.AllWarnings return c } buildtools-10.1.0/buildifier/config/validation.go0000664000175000017500000000762715254573013021424 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 config import ( "fmt" "maps" "slices" "strings" ) // ValidateInputType validates the value of --type func ValidateInputType(inputType *string) error { switch *inputType { case "build", "bzl", "workspace", "default", "module", "repo", "vendor", "auto": return nil default: return fmt.Errorf("unrecognized input type %s; valid types are build, bzl, workspace, default, module, repo, vendor, auto", *inputType) } } // ValidateFormat validates the value of --format func ValidateFormat(format, mode *string) error { switch *format { case "": return nil case "text", "json": if *mode != "check" { return fmt.Errorf("cannot specify --format without --mode=check") } default: return fmt.Errorf("unrecognized format %s; valid types are text, json", *format) } return nil } // isRecognizedMode checks whether the given mode is one of the valid modes. func isRecognizedMode(validModes []string, mode string) bool { for _, m := range validModes { if mode == m { return true } } return false } // ValidateModes validates flags --mode, --lint, and -d func ValidateModes(mode, lint *string, dflag *bool, additionalModes ...string) error { if *dflag { if *mode != "" { return fmt.Errorf("cannot specify both -d and -mode flags") } *mode = "diff" } // Check mode. validModes := []string{"check", "diff", "fix", "print_if_changed"} validModes = append(validModes, additionalModes...) if *mode == "" { *mode = "fix" } else if !isRecognizedMode(validModes, *mode) { return fmt.Errorf("unrecognized mode %s; valid modes are %s", *mode, strings.Join(validModes, ", ")) } // Check lint mode. switch *lint { case "": *lint = "off" case "off", "warn": // ok case "fix": if *mode != "fix" { return fmt.Errorf("--lint=fix is only compatible with --mode=fix") } default: return fmt.Errorf("unrecognized lint mode %s; valid modes are warn and fix", *lint) } return nil } // ValidateWarnings validates the value of the --warnings flag func ValidateWarnings(warnings *string, allWarnings, defaultWarnings *[]string) ([]string, error) { // Check lint warnings warningsMap := make(map[string]bool) switch *warnings { case "", "default": for _, w := range *defaultWarnings { warningsMap[w] = true } case "all": for _, w := range *allWarnings { warningsMap[w] = true } default: // Either all or no warning categories should start with "+" or "-". // If all of them start with "+" or "-", the semantics is // "default set of warnings + something - something". plus := map[string]bool{} minus := map[string]bool{} for _, warning := range strings.Split(*warnings, ",") { if strings.HasPrefix(warning, "+") { plus[warning[1:]] = true } else if strings.HasPrefix(warning, "-") { minus[warning[1:]] = true } else { warningsMap[warning] = true } } if len(warningsMap) > 0 && (len(plus) > 0 || len(minus) > 0) { return []string{}, fmt.Errorf("warning categories with modifiers (\"+\" or \"-\") can't be mixed with raw warning categories") } if len(warningsMap) == 0 { for _, warning := range *defaultWarnings { if !minus[warning] { warningsMap[warning] = true } } for warning := range plus { warningsMap[warning] = true } } } ws := slices.Collect(maps.Keys(warningsMap)) // Sorts the warnings to ensure behaviors are deterministic. slices.Sort(ws) return ws, nil } buildtools-10.1.0/buildifier/config/config_test.go0000664000175000017500000006043415254573013021571 0ustar kenhyskenhys/* Copyright 2022 Google LLC 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 https://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 config import ( "flag" "fmt" "os" "path/filepath" "strings" "testing" ) func ExampleNew() { c := New() fmt.Print(c.String()) // Output: // { // "type": "auto" // } } func ExampleExample() { c := Example() fmt.Print(c.String()) // Output: // { // "type": "auto", // "mode": "fix", // "lint": "fix", // "warningsList": [ // "allowed-symbol-load-locations", // "attr-applicable_licenses", // "attr-cfg", // "attr-license", // "attr-licenses", // "attr-non-empty", // "attr-output-default", // "attr-single-file", // "build-args-kwargs", // "bzl-visibility", // "confusing-name", // "constant-glob", // "ctx-actions", // "ctx-args", // "deprecated-function", // "depset-items", // "depset-iteration", // "depset-union", // "dict-concatenation", // "dict-method-named-arg", // "duplicated-name", // "filetype", // "function-docstring", // "function-docstring-args", // "function-docstring-header", // "function-docstring-return", // "git-repository", // "http-archive", // "integer-division", // "keyword-positional-params", // "list-append", // "load", // "make-location", // "module-docstring", // "name-conventions", // "native-android", // "native-build", // "native-cc-binary", // "native-cc-common", // "native-cc-debug-package-info", // "native-cc-fdo-prefetch-hints", // "native-cc-fdo-profile", // "native-cc-import", // "native-cc-info", // "native-cc-library", // "native-cc-memprof-profile", // "native-cc-objc-import", // "native-cc-objc-library", // "native-cc-propeller-optimize", // "native-cc-proto", // "native-cc-shared-library", // "native-cc-shared-library-hint-info", // "native-cc-shared-library-info", // "native-cc-test", // "native-cc-toolchain", // "native-cc-toolchain-suite", // "native-java-binary", // "native-java-common", // "native-java-import", // "native-java-info", // "native-java-library", // "native-java-lite-proto", // "native-java-package-config", // "native-java-plugin", // "native-java-plugin-info", // "native-java-proto", // "native-java-runtime", // "native-java-test", // "native-java-toolchain", // "native-package", // "native-proto", // "native-proto-common", // "native-proto-info", // "native-proto-lang-toolchain", // "native-proto-lang-toolchain-info", // "native-py", // "native-sh-binary", // "native-sh-library", // "native-sh-test", // "no-effect", // "output-group", // "overly-nested-depset", // "package-name", // "package-on-top", // "positional-args", // "print", // "provider-params", // "redefined-variable", // "repository-name", // "return-value", // "rule-impl-return", // "skylark-comment", // "skylark-docstring", // "string-iteration", // "uninitialized", // "unnamed-macro", // "unreachable", // "unsorted-dict-items", // "unused-variable" // ] // } } func ExampleFlagSet() { c := New() flags := c.FlagSet("buildifier", flag.ExitOnError) flags.VisitAll(func(f *flag.Flag) { fmt.Printf("%s: %s (%q)\n", f.Name, f.Usage, f.DefValue) }) // Output: // add_tables: path to JSON file with custom table definitions which will be merged with the built-in tables ("") // allowsort: additional sort contexts to treat as safe ("") // buildifier_disable: list of buildifier rewrites to disable ("") // config: path to .buildifier.json config file ("") // d: alias for -mode=diff ("false") // diff_command: command to run when the formatting mode is diff (default uses the BUILDIFIER_DIFF, BUILDIFIER_MULTIDIFF, and DISPLAY environment variables to create the diff command) ("") // disable_symlink_safety: per default Buildifier will not write to symlinks pointing outside of the Bazel workspace. Setting this to true will disable this behavior ("false") // format: diagnostics format: text or json (default text) ("") // help: print usage information ("false") // lint: lint mode: off, warn, or fix (default off) ("") // mode: formatting mode: check, diff, or fix (default fix) ("") // multi_diff: the command specified by the -diff_command flag can diff multiple files in the style of tkdiff (default false) ("false") // path: assume BUILD file has this path relative to the workspace directory ("") // r: find starlark files recursively ("false") // tables: path to JSON file with custom table definitions which will replace the built-in tables ("") // type: Input file type: build (for BUILD files), bzl (for .bzl files), workspace (for WORKSPACE files), module (for MODULE.bazel files), repo (for REPO.bazel files), vendor (for VENDOR.bazel files), default (for generic Starlark files) or auto (default, based on the filename) ("auto") // v: print verbose information to standard error ("false") // version: print the version of buildifier ("false") // warnings: comma-separated warnings used in the lint mode or "all" ("") } func ExampleFlagSet_parse() { c := New() flags := c.FlagSet("buildifier", flag.ExitOnError) flags.Parse([]string{ "--add_tables=/path/to/add_tables.json", "--allowsort=proto_library.deps", "--allowsort=proto_library.srcs", "--buildifier_disable=unsafesort", "--config=/path/to/.buildifier.json", "-d", "--diff_command=diff", "--disable_symlink_safety=true", "--format=json", "--help", "--lint=fix", "--mode=fix", "--multi_diff=true", "--path=pkg/foo", "-r", "--tables=/path/to/tables.json", "--type=default", "-v", "--version", "--warnings=+print,-no-effect", }) fmt.Println("help:", c.Help) fmt.Println("version:", c.Version) fmt.Println("configPath:", c.ConfigPath) fmt.Print(c.String()) // Output: // help: true // version: true // configPath: /path/to/.buildifier.json // { // "type": "default", // "format": "json", // "mode": "fix", // "diffMode": true, // "lint": "fix", // "warnings": "+print,-no-effect", // "recursive": true, // "verbose": true, // "diffCommand": "diff", // "multiDiff": true, // "tables": "/path/to/tables.json", // "addTables": "/path/to/add_tables.json", // "path": "pkg/foo", // "buildifier_disable": [ // "unsafesort" // ], // "allowsort": [ // "proto_library.deps", // "proto_library.srcs" // ], // "disable_symlink_safety": true // } } func TestValidate(t *testing.T) { for name, tc := range map[string]struct { options string args string wantErr error wantMode string // optional wantLint string // optional wantWarnings []string // optional }{ "mode not set": {wantMode: "fix"}, "mode check": {options: "--mode=check", wantMode: "check"}, "mode diff": {options: "--mode=diff", wantMode: "diff"}, "mode d": {options: "-d", wantMode: "diff"}, "mode d error": {options: "--mode=diff -d", wantErr: fmt.Errorf("cannot specify both -d and -mode flags")}, "mode fix": {options: "--mode=fix", wantMode: "fix"}, "mode print_if_changed": {options: "--mode=print_if_changed", wantMode: "print_if_changed"}, "mode error": {options: "--mode=foo", wantErr: fmt.Errorf("unrecognized mode foo; valid modes are check, diff, fix, print_if_changed")}, "lint not set": {wantLint: "off"}, "lint off": {options: "--lint=off", wantLint: "off"}, "lint warn": {options: "--lint=warn", wantLint: "warn"}, "lint fix": {options: "--lint=fix", wantLint: "fix"}, "lint fix error": {options: "--lint=fix --mode=check", wantErr: fmt.Errorf("--lint=fix is only compatible with --mode=fix")}, "format mode error": {options: "--mode=fix --format=text", wantErr: fmt.Errorf("cannot specify --format without --mode=check")}, "format text": {options: "--mode=check --format=text"}, "format json": {options: "--mode=check --format=json"}, "format error": {options: "--mode=check --format=foo", wantErr: fmt.Errorf("unrecognized format foo; valid types are text, json")}, "type build": {options: "--type=build"}, "type bzl": {options: "--type=bzl"}, "type workspace": {options: "--type=workspace"}, "type default": {options: "--type=default"}, "type module": {options: "--type=module"}, "type repo": {options: "--type=repo"}, "type vendor": {options: "--type=vendor"}, "type auto": {options: "--type=auto"}, "type error": {options: "--type=foo", wantErr: fmt.Errorf("unrecognized input type foo; valid types are build, bzl, workspace, default, module, repo, vendor, auto")}, "warnings all": {options: "--warnings=all", wantWarnings: []string{ "allowed-symbol-load-locations", "attr-applicable_licenses", "attr-cfg", "attr-license", "attr-licenses", "attr-non-empty", "attr-output-default", "attr-single-file", "build-args-kwargs", "bzl-visibility", "confusing-name", "constant-glob", "ctx-actions", "ctx-args", "deprecated-function", "depset-items", "depset-iteration", "depset-union", "dict-concatenation", "dict-method-named-arg", "duplicated-name", "filetype", "function-docstring", "function-docstring-args", "function-docstring-header", "function-docstring-return", "git-repository", "http-archive", "integer-division", "keyword-positional-params", "list-append", "load", "make-location", "module-docstring", "name-conventions", "native-android", "native-build", "native-cc-binary", "native-cc-common", "native-cc-debug-package-info", "native-cc-fdo-prefetch-hints", "native-cc-fdo-profile", "native-cc-import", "native-cc-info", "native-cc-library", "native-cc-memprof-profile", "native-cc-objc-import", "native-cc-objc-library", "native-cc-propeller-optimize", "native-cc-proto", "native-cc-shared-library", "native-cc-shared-library-hint-info", "native-cc-shared-library-info", "native-cc-test", "native-cc-toolchain", "native-cc-toolchain-suite", "native-java-binary", "native-java-common", "native-java-import", "native-java-info", "native-java-library", "native-java-lite-proto", "native-java-package-config", "native-java-plugin", "native-java-plugin-info", "native-java-proto", "native-java-runtime", "native-java-test", "native-java-toolchain", "native-package", "native-proto", "native-proto-common", "native-proto-info", "native-proto-lang-toolchain", "native-proto-lang-toolchain-info", "native-py", "native-sh-binary", "native-sh-library", "native-sh-test", "no-effect", "output-group", "overly-nested-depset", "package-name", "package-on-top", "positional-args", "print", "provider-params", "redefined-variable", "repository-name", "return-value", "rule-impl-return", "skylark-comment", "skylark-docstring", "string-iteration", "uninitialized", "unnamed-macro", "unreachable", "unsorted-dict-items", "unused-variable", }}, "warnings default": {options: "--warnings=default", wantWarnings: []string{ "allowed-symbol-load-locations", "attr-applicable_licenses", "attr-cfg", "attr-license", "attr-licenses", "attr-non-empty", "attr-output-default", "attr-single-file", "build-args-kwargs", "bzl-visibility", "confusing-name", "constant-glob", "ctx-actions", "ctx-args", "deprecated-function", "depset-items", "depset-iteration", "depset-union", "dict-concatenation", "dict-method-named-arg", "duplicated-name", "filetype", "function-docstring", "function-docstring-args", "function-docstring-header", "function-docstring-return", "git-repository", "http-archive", "integer-division", "keyword-positional-params", "list-append", "load", "make-location", "module-docstring", "name-conventions", "native-android", "native-build", "native-cc-binary", "native-cc-common", "native-cc-debug-package-info", "native-cc-fdo-prefetch-hints", "native-cc-fdo-profile", "native-cc-import", "native-cc-info", "native-cc-library", "native-cc-memprof-profile", "native-cc-objc-import", "native-cc-objc-library", "native-cc-propeller-optimize", "native-cc-proto", "native-cc-shared-library", "native-cc-shared-library-hint-info", "native-cc-shared-library-info", "native-cc-test", "native-cc-toolchain", "native-cc-toolchain-suite", "native-java-binary", "native-java-common", "native-java-import", "native-java-info", "native-java-library", "native-java-lite-proto", "native-java-package-config", "native-java-plugin", "native-java-plugin-info", "native-java-proto", "native-java-runtime", "native-java-test", "native-java-toolchain", "native-package", "native-proto", "native-proto-common", "native-proto-info", "native-proto-lang-toolchain", "native-proto-lang-toolchain-info", "native-py", "native-sh-binary", "native-sh-library", "native-sh-test", "no-effect", "output-group", "overly-nested-depset", "package-name", "package-on-top", "positional-args", "print", "provider-params", "redefined-variable", "repository-name", "return-value", "rule-impl-return", "skylark-comment", "skylark-docstring", "string-iteration", "uninitialized", "unnamed-macro", "unreachable", // "unsorted-dict-items", "unused-variable", }}, "warnings plus/minus": {options: "--warnings=+unsorted-dict-items,-print,-deprecated-function", wantWarnings: []string{ "allowed-symbol-load-locations", "attr-applicable_licenses", "attr-cfg", "attr-license", "attr-licenses", "attr-non-empty", "attr-output-default", "attr-single-file", "build-args-kwargs", "bzl-visibility", "confusing-name", "constant-glob", "ctx-actions", "ctx-args", // "deprecated-function", "depset-items", "depset-iteration", "depset-union", "dict-concatenation", "dict-method-named-arg", "duplicated-name", "filetype", "function-docstring", "function-docstring-args", "function-docstring-header", "function-docstring-return", "git-repository", "http-archive", "integer-division", "keyword-positional-params", "list-append", "load", "make-location", "module-docstring", "name-conventions", "native-android", "native-build", "native-cc-binary", "native-cc-common", "native-cc-debug-package-info", "native-cc-fdo-prefetch-hints", "native-cc-fdo-profile", "native-cc-import", "native-cc-info", "native-cc-library", "native-cc-memprof-profile", "native-cc-objc-import", "native-cc-objc-library", "native-cc-propeller-optimize", "native-cc-proto", "native-cc-shared-library", "native-cc-shared-library-hint-info", "native-cc-shared-library-info", "native-cc-test", "native-cc-toolchain", "native-cc-toolchain-suite", "native-java-binary", "native-java-common", "native-java-import", "native-java-info", "native-java-library", "native-java-lite-proto", "native-java-package-config", "native-java-plugin", "native-java-plugin-info", "native-java-proto", "native-java-runtime", "native-java-test", "native-java-toolchain", "native-package", "native-proto", "native-proto-common", "native-proto-info", "native-proto-lang-toolchain", "native-proto-lang-toolchain-info", "native-py", "native-sh-binary", "native-sh-library", "native-sh-test", "no-effect", "output-group", "overly-nested-depset", "package-name", "package-on-top", "positional-args", // "print", "provider-params", "redefined-variable", "repository-name", "return-value", "rule-impl-return", "skylark-comment", "skylark-docstring", "string-iteration", "uninitialized", "unnamed-macro", "unreachable", "unsorted-dict-items", "unused-variable", }}, "warnings no duplicates": {options: "--warnings=+unused-variable", wantWarnings: []string{ "allowed-symbol-load-locations", "attr-applicable_licenses", "attr-cfg", "attr-license", "attr-licenses", "attr-non-empty", "attr-output-default", "attr-single-file", "build-args-kwargs", "bzl-visibility", "confusing-name", "constant-glob", "ctx-actions", "ctx-args", "deprecated-function", "depset-items", "depset-iteration", "depset-union", "dict-concatenation", "dict-method-named-arg", "duplicated-name", "filetype", "function-docstring", "function-docstring-args", "function-docstring-header", "function-docstring-return", "git-repository", "http-archive", "integer-division", "keyword-positional-params", "list-append", "load", "make-location", "module-docstring", "name-conventions", "native-android", "native-build", "native-cc-binary", "native-cc-common", "native-cc-debug-package-info", "native-cc-fdo-prefetch-hints", "native-cc-fdo-profile", "native-cc-import", "native-cc-info", "native-cc-library", "native-cc-memprof-profile", "native-cc-objc-import", "native-cc-objc-library", "native-cc-propeller-optimize", "native-cc-proto", "native-cc-shared-library", "native-cc-shared-library-hint-info", "native-cc-shared-library-info", "native-cc-test", "native-cc-toolchain", "native-cc-toolchain-suite", "native-java-binary", "native-java-common", "native-java-import", "native-java-info", "native-java-library", "native-java-lite-proto", "native-java-package-config", "native-java-plugin", "native-java-plugin-info", "native-java-proto", "native-java-runtime", "native-java-test", "native-java-toolchain", "native-package", "native-proto", "native-proto-common", "native-proto-info", "native-proto-lang-toolchain", "native-proto-lang-toolchain-info", "native-py", "native-sh-binary", "native-sh-library", "native-sh-test", "no-effect", "output-group", "overly-nested-depset", "package-name", "package-on-top", "positional-args", "print", "provider-params", "redefined-variable", "repository-name", "return-value", "rule-impl-return", "skylark-comment", "skylark-docstring", "string-iteration", "uninitialized", "unnamed-macro", "unreachable", "unused-variable", }}, "warnings error": {options: "--warnings=native-py,-print,-deprecated-function", wantErr: fmt.Errorf(`warning categories with modifiers ("+" or "-") can't be mixed with raw warning categories`)}, } { t.Run(name, func(t *testing.T) { c := New() flags := c.FlagSet("buildifier", flag.ExitOnError) flags.Parse(strings.Fields(tc.options)) got := c.Validate(strings.Fields(tc.args)) if tc.wantMode != "" && tc.wantMode != c.Mode { t.Fatalf("--mode mismatch: want %v, got %v", tc.wantMode, c.Mode) } if tc.wantLint != "" && tc.wantLint != c.Lint { t.Fatalf("--lint mismatch: want %v, got %v", tc.wantLint, c.Lint) } if len(tc.wantWarnings) > 0 { if len(tc.wantWarnings) != len(c.LintWarnings) { t.Fatalf("--warnings mismatch: want %v, got %v", tc.wantWarnings, c.LintWarnings) } for i, wantWarning := range tc.wantWarnings { gotWarning := c.LintWarnings[i] if wantWarning != gotWarning { t.Errorf("warning mismatch at list position %d: want %s, got %s", i, wantWarning, gotWarning) } } } if tc.wantErr == nil && got == nil { return } if tc.wantErr == nil && got != nil { t.Fatalf("unexpected error: %v", got) } if tc.wantErr != nil && got == nil { t.Fatalf("expected error did not occur: %v", tc.wantErr) } if tc.wantErr.Error() != got.Error() { t.Fatalf("error mismatch: want %v, got %v", tc.wantErr.Error(), got.Error()) } }) } } func TestFindConfigPath(t *testing.T) { for name, tc := range map[string]struct { files map[string]string env map[string]string want string }{ "no-config-file": { want: "", }, "default": { files: map[string]string{ ".buildifier.json": "{}", }, want: ".buildifier.json", }, "BUILDIFIER_CONFIG-override": { env: map[string]string{ "BUILDIFIER_CONFIG": ".buildifier_x.json", }, want: ".buildifier_x.json", }, } { t.Run(name, func(t *testing.T) { for k, v := range tc.env { os.Setenv(k, v) defer os.Unsetenv(k) } tmp, err := os.MkdirTemp("", name+"*") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) if err := os.Chdir(tmp); err != nil { t.Fatal(err) } t.Log("tmp:", tmp) for rel, content := range tc.files { dir := filepath.Join(tmp, filepath.Dir(rel)) if dir != "." { if err := os.MkdirAll(dir, os.ModePerm); err != nil { t.Fatal(err) } } filename := filepath.Join(dir, rel) if err := os.WriteFile(filename, []byte(content), 0644); err != nil { t.Fatal(err) } } got := FindConfigPath(tmp) got = strings.TrimPrefix(got, tmp) got = strings.TrimPrefix(got, "/") if tc.want != got { t.Errorf("FindConfigPath: want %q, got %q", tc.want, got) } }) } } func TestFindTablePath(t *testing.T) { tests := []struct { name string file string files []string wd string want string wantErr error }{ { name: "default", file: ".buildifier-tables.json", files: []string{".buildifier-tables.json"}, wd: "", want: ".buildifier-tables.json", wantErr: nil, }, { name: "working-dir-is-subdir", file: ".buildifier-tables.json", files: []string{".buildifier-tables.json", "foo/BUILD.bazel"}, wd: "foo", want: ".buildifier-tables.json", wantErr: nil, }, { name: "relative-subdir", file: "bar/.buildifier-tables.json", files: []string{"bar/.buildifier-tables.json", "foo/BUILD.bazel"}, wd: "foo", want: "bar/.buildifier-tables.json", wantErr: nil, }, { name: "file-not-found", file: "nonexistentFile.json", files: []string{".buildifier-tables.json"}, wd: "", want: "nonexistentFile.json", wantErr: os.ErrNotExist, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { tmp := t.TempDir() // On MacOS "/tmp" is a symlink to "/private/tmp". Resolve it to make the testing easier tmp, err := filepath.EvalSymlinks(tmp) if err != nil { t.Fatalf("failed to resolve symlink for temporary directory: %v", err) } t.Log("tmp:", tmp) if tc.wd != "" { if err := os.MkdirAll(filepath.Join(tmp, tc.wd), os.ModePerm); err != nil { t.Fatalf("failed to create working directory: %v", err) } if err := os.Chdir(filepath.Join(tmp, tc.wd)); err != nil { t.Fatalf("failed to change working directory: %v", err) } } else { if err := os.Chdir(tmp); err != nil { t.Fatalf("failed to change working directory: %v", err) } } for _, file := range tc.files { filePath := filepath.Join(tmp, file) dir := filepath.Dir(filePath) if err := os.MkdirAll(dir, os.ModePerm); err != nil { t.Fatalf("failed to create directory %v: %v", dir, err) } if err := os.WriteFile(filePath, []byte("{}"), 0644); err != nil { t.Fatalf("failed to create file %v: %v", filePath, err) } } got, err := findTablesPath(tc.file) got = strings.TrimPrefix(got, tmp) got = strings.TrimPrefix(got, "/") if (err != nil) != (tc.wantErr != nil) || (err != nil && tc.wantErr.Error() != err.Error()) { t.Errorf("FindTablePath wantErr = %q, error = %q", tc.wantErr, err) } if tc.want != got { t.Errorf("FindTablePath want = %q, got = %q", tc.want, got) } }) } } buildtools-10.1.0/buildifier/config/BUILD.bazel0000664000175000017500000000107015254573013020603 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "config", srcs = [ "config.go", "validation.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/buildifier/config", visibility = ["//buildifier:__pkg__"], deps = [ "//tables", "//warn", "//wspace", ], ) go_test( name = "config_test", srcs = ["config_test.go"], embed = [":config"], ) alias( name = "go_default_library", actual = ":config", visibility = ["//visibility:public"], ) buildtools-10.1.0/buildifier/def.bzl0000664000175000017500000000034715254573013016735 0ustar kenhyskenhys""" This module is the public interface to buildifier rules """ load( ":buildifier.bzl", _buildifier = "buildifier", _buildifier_test = "buildifier_test", ) buildifier = _buildifier buildifier_test = _buildifier_test buildtools-10.1.0/buildifier/BUILD.bazel0000664000175000017500000000575015254573013017347 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") load("@rules_shell//shell:sh_test.bzl", "sh_test") go_binary( name = "buildifier", embed = [":buildifier_lib"], visibility = ["//visibility:public"], ) go_binary( name = "buildifier-darwin-amd64", out = "buildifier-darwin_amd64", embed = [":buildifier_lib"], goarch = "amd64", goos = "darwin", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildifier-linux-amd64", out = "buildifier-linux_amd64", embed = [":buildifier_lib"], goarch = "amd64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildifier-windows-amd64", out = "buildifier-windows_amd64.exe", embed = [":buildifier_lib"], goarch = "amd64", goos = "windows", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildifier-darwin-arm64", out = "buildifier-darwin_arm64", embed = [":buildifier_lib"], goarch = "arm64", goos = "darwin", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildifier-linux-arm64", out = "buildifier-linux_arm64", embed = [":buildifier_lib"], goarch = "arm64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildifier-windows-arm64", out = "buildifier-windows_arm64.exe", embed = [":buildifier_lib"], goarch = "arm64", goos = "windows", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildifier-linux-riscv64", out = "buildifier-linux_riscv64", embed = [":buildifier_lib"], goarch = "riscv64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildifier-linux-s390x", out = "buildifier-linux_s390x", embed = [":buildifier_lib"], goarch = "s390x", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) # Test that the buildifier binary works sh_test( name = "buildifier_integration_test", size = "small", srcs = ["integration_test.sh"], args = [ "$(rootpath :buildifier)", ], data = [ ":buildifier", ], deps = ["@bazel_tools//tools/bash/runfiles"], ) go_library( name = "buildifier_lib", srcs = ["buildifier.go"], importpath = "github.com/bazel-contrib/buildtools/v10/buildifier", visibility = ["//visibility:private"], x_defs = { "main.buildVersion": "{STABLE_buildVersion}", "main.buildScmRevision": "{STABLE_buildScmRevision}", }, deps = [ "//build", "//buildifier/config", "//buildifier/utils", "//differ", "//file", "//wspace", ], ) exports_files( [ "runner.bash.template", "runner.bat.template", ], visibility = ["//visibility:public"], ) exports_files( [ "README.md", ], visibility = ["//buildifier/npm:__pkg__"], ) buildtools-10.1.0/MODULE.bazel0000664000175000017500000000325215254573013015352 0ustar kenhyskenhysmodule( name = "buildtools", version = "0.0.0", ) bazel_dep(name = "aspect_bazel_lib", version = "2.22.5") bazel_dep(name = "aspect_rules_js", version = "3.4.1") bazel_dep(name = "bazel_features", version = "1.42.1") bazel_dep(name = "bazel_skylib", version = "1.9.0") bazel_dep(name = "gazelle", version = "0.54.0", repo_name = "bazel_gazelle") bazel_dep(name = "protobuf", version = "34.0.bcr.1", repo_name = "com_google_protobuf") bazel_dep(name = "rules_cc", version = "0.2.17") bazel_dep(name = "rules_go", version = "0.63.0", repo_name = "io_bazel_rules_go") bazel_dep(name = "rules_nodejs", version = "6.7.5") bazel_dep(name = "rules_proto", version = "7.1.0") bazel_dep(name = "rules_shell", version = "0.6.1") # -- bazel_dep definitions -- # go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk") go_sdk.from_file(go_mod = "//:go.mod") go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") go_deps.gazelle_override( build_file_generation = "clean", path = "github.com/google/safeopen", ) use_repo( go_deps, "com_github_golang_protobuf", "com_github_google_go_cmp", "com_github_google_safeopen", "net_starlark_go", "org_golang_google_protobuf", "org_golang_x_tools", ) node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") node.toolchain( name = "nodejs", node_version = "16.7.0", ) toolchains = use_extension("@aspect_bazel_lib//lib:extensions.bzl", "toolchains") use_repo(toolchains, "jq_toolchains") use_repo(toolchains, "copy_to_directory_toolchains") register_toolchains( "@jq_toolchains//:all", "@copy_to_directory_toolchains//:all", ) buildtools-10.1.0/deps_proto/0000775000175000017500000000000015254573013015522 5ustar kenhyskenhysbuildtools-10.1.0/deps_proto/deps.proto0000664000175000017500000000363315254573013017547 0ustar kenhyskenhys// Copyright 2014 The Bazel Authors. 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. // // Definitions for dependency reports. syntax = "proto2"; package blaze_deps; option java_package = "com.google.devtools.build.lib.view.proto"; // A specific location within a source file. message SourceLocation { required string path = 1; optional int32 line = 2; optional int32 column = 3; } message Dependency { enum Kind { // Dependency used explicitly in the source. EXPLICIT = 0; // Dependency that is implicitly loaded and used by the compiler. IMPLICIT = 1; // Unused dependency. UNUSED = 2; // Implicit dependency considered by the compiler but not completed. INCOMPLETE = 3; } // Path to the artifact representing this dependency. required string path = 1; // Dependency kind required Kind kind = 2; // Source file locations: compilers can pinpoint the uses of a dependency. repeated SourceLocation location = 3; } // Top-level message found in .deps artifacts message Dependencies { repeated Dependency dependency = 1; // Name of the rule being analyzed. optional string rule_label = 2; // Whether the action was successful; even when compilation fails, partial // dependency information can be useful. optional bool success = 3; // Packages contained in the output jar, sorted alphabetically. repeated string contained_package = 4; } buildtools-10.1.0/deps_proto/deps.gen.pb.go0000775000175000017500000002442315254573013020164 0ustar kenhyskenhys// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.10 // protoc v7.34.0 // source: deps_proto/deps.proto package deps_proto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Dependency_Kind int32 const ( Dependency_EXPLICIT Dependency_Kind = 0 Dependency_IMPLICIT Dependency_Kind = 1 Dependency_UNUSED Dependency_Kind = 2 Dependency_INCOMPLETE Dependency_Kind = 3 ) // Enum value maps for Dependency_Kind. var ( Dependency_Kind_name = map[int32]string{ 0: "EXPLICIT", 1: "IMPLICIT", 2: "UNUSED", 3: "INCOMPLETE", } Dependency_Kind_value = map[string]int32{ "EXPLICIT": 0, "IMPLICIT": 1, "UNUSED": 2, "INCOMPLETE": 3, } ) func (x Dependency_Kind) Enum() *Dependency_Kind { p := new(Dependency_Kind) *p = x return p } func (x Dependency_Kind) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Dependency_Kind) Descriptor() protoreflect.EnumDescriptor { return file_deps_proto_deps_proto_enumTypes[0].Descriptor() } func (Dependency_Kind) Type() protoreflect.EnumType { return &file_deps_proto_deps_proto_enumTypes[0] } func (x Dependency_Kind) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Do not use. func (x *Dependency_Kind) UnmarshalJSON(b []byte) error { num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } *x = Dependency_Kind(num) return nil } // Deprecated: Use Dependency_Kind.Descriptor instead. func (Dependency_Kind) EnumDescriptor() ([]byte, []int) { return file_deps_proto_deps_proto_rawDescGZIP(), []int{1, 0} } type SourceLocation struct { state protoimpl.MessageState `protogen:"open.v1"` Path *string `protobuf:"bytes,1,req,name=path" json:"path,omitempty"` Line *int32 `protobuf:"varint,2,opt,name=line" json:"line,omitempty"` Column *int32 `protobuf:"varint,3,opt,name=column" json:"column,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SourceLocation) Reset() { *x = SourceLocation{} mi := &file_deps_proto_deps_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SourceLocation) String() string { return protoimpl.X.MessageStringOf(x) } func (*SourceLocation) ProtoMessage() {} func (x *SourceLocation) ProtoReflect() protoreflect.Message { mi := &file_deps_proto_deps_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SourceLocation.ProtoReflect.Descriptor instead. func (*SourceLocation) Descriptor() ([]byte, []int) { return file_deps_proto_deps_proto_rawDescGZIP(), []int{0} } func (x *SourceLocation) GetPath() string { if x != nil && x.Path != nil { return *x.Path } return "" } func (x *SourceLocation) GetLine() int32 { if x != nil && x.Line != nil { return *x.Line } return 0 } func (x *SourceLocation) GetColumn() int32 { if x != nil && x.Column != nil { return *x.Column } return 0 } type Dependency struct { state protoimpl.MessageState `protogen:"open.v1"` Path *string `protobuf:"bytes,1,req,name=path" json:"path,omitempty"` Kind *Dependency_Kind `protobuf:"varint,2,req,name=kind,enum=blaze_deps.Dependency_Kind" json:"kind,omitempty"` Location []*SourceLocation `protobuf:"bytes,3,rep,name=location" json:"location,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Dependency) Reset() { *x = Dependency{} mi := &file_deps_proto_deps_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Dependency) String() string { return protoimpl.X.MessageStringOf(x) } func (*Dependency) ProtoMessage() {} func (x *Dependency) ProtoReflect() protoreflect.Message { mi := &file_deps_proto_deps_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Dependency.ProtoReflect.Descriptor instead. func (*Dependency) Descriptor() ([]byte, []int) { return file_deps_proto_deps_proto_rawDescGZIP(), []int{1} } func (x *Dependency) GetPath() string { if x != nil && x.Path != nil { return *x.Path } return "" } func (x *Dependency) GetKind() Dependency_Kind { if x != nil && x.Kind != nil { return *x.Kind } return Dependency_EXPLICIT } func (x *Dependency) GetLocation() []*SourceLocation { if x != nil { return x.Location } return nil } type Dependencies struct { state protoimpl.MessageState `protogen:"open.v1"` Dependency []*Dependency `protobuf:"bytes,1,rep,name=dependency" json:"dependency,omitempty"` RuleLabel *string `protobuf:"bytes,2,opt,name=rule_label,json=ruleLabel" json:"rule_label,omitempty"` Success *bool `protobuf:"varint,3,opt,name=success" json:"success,omitempty"` ContainedPackage []string `protobuf:"bytes,4,rep,name=contained_package,json=containedPackage" json:"contained_package,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Dependencies) Reset() { *x = Dependencies{} mi := &file_deps_proto_deps_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Dependencies) String() string { return protoimpl.X.MessageStringOf(x) } func (*Dependencies) ProtoMessage() {} func (x *Dependencies) ProtoReflect() protoreflect.Message { mi := &file_deps_proto_deps_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Dependencies.ProtoReflect.Descriptor instead. func (*Dependencies) Descriptor() ([]byte, []int) { return file_deps_proto_deps_proto_rawDescGZIP(), []int{2} } func (x *Dependencies) GetDependency() []*Dependency { if x != nil { return x.Dependency } return nil } func (x *Dependencies) GetRuleLabel() string { if x != nil && x.RuleLabel != nil { return *x.RuleLabel } return "" } func (x *Dependencies) GetSuccess() bool { if x != nil && x.Success != nil { return *x.Success } return false } func (x *Dependencies) GetContainedPackage() []string { if x != nil { return x.ContainedPackage } return nil } var File_deps_proto_deps_proto protoreflect.FileDescriptor const file_deps_proto_deps_proto_rawDesc = "" + "\n" + "\x15deps_proto/deps.proto\x12\n" + "blaze_deps\"P\n" + "\x0eSourceLocation\x12\x12\n" + "\x04path\x18\x01 \x02(\tR\x04path\x12\x12\n" + "\x04line\x18\x02 \x01(\x05R\x04line\x12\x16\n" + "\x06column\x18\x03 \x01(\x05R\x06column\"\xc9\x01\n" + "\n" + "Dependency\x12\x12\n" + "\x04path\x18\x01 \x02(\tR\x04path\x12/\n" + "\x04kind\x18\x02 \x02(\x0e2\x1b.blaze_deps.Dependency.KindR\x04kind\x126\n" + "\blocation\x18\x03 \x03(\v2\x1a.blaze_deps.SourceLocationR\blocation\">\n" + "\x04Kind\x12\f\n" + "\bEXPLICIT\x10\x00\x12\f\n" + "\bIMPLICIT\x10\x01\x12\n" + "\n" + "\x06UNUSED\x10\x02\x12\x0e\n" + "\n" + "INCOMPLETE\x10\x03\"\xac\x01\n" + "\fDependencies\x126\n" + "\n" + "dependency\x18\x01 \x03(\v2\x16.blaze_deps.DependencyR\n" + "dependency\x12\x1d\n" + "\n" + "rule_label\x18\x02 \x01(\tR\truleLabel\x12\x18\n" + "\asuccess\x18\x03 \x01(\bR\asuccess\x12+\n" + "\x11contained_package\x18\x04 \x03(\tR\x10containedPackageB*\n" + "(com.google.devtools.build.lib.view.proto" var ( file_deps_proto_deps_proto_rawDescOnce sync.Once file_deps_proto_deps_proto_rawDescData []byte ) func file_deps_proto_deps_proto_rawDescGZIP() []byte { file_deps_proto_deps_proto_rawDescOnce.Do(func() { file_deps_proto_deps_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_deps_proto_deps_proto_rawDesc), len(file_deps_proto_deps_proto_rawDesc))) }) return file_deps_proto_deps_proto_rawDescData } var file_deps_proto_deps_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_deps_proto_deps_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_deps_proto_deps_proto_goTypes = []any{ (Dependency_Kind)(0), // 0: blaze_deps.Dependency.Kind (*SourceLocation)(nil), // 1: blaze_deps.SourceLocation (*Dependency)(nil), // 2: blaze_deps.Dependency (*Dependencies)(nil), // 3: blaze_deps.Dependencies } var file_deps_proto_deps_proto_depIdxs = []int32{ 0, // 0: blaze_deps.Dependency.kind:type_name -> blaze_deps.Dependency.Kind 1, // 1: blaze_deps.Dependency.location:type_name -> blaze_deps.SourceLocation 2, // 2: blaze_deps.Dependencies.dependency:type_name -> blaze_deps.Dependency 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_deps_proto_deps_proto_init() } func file_deps_proto_deps_proto_init() { if File_deps_proto_deps_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_deps_proto_deps_proto_rawDesc), len(file_deps_proto_deps_proto_rawDesc)), NumEnums: 1, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_deps_proto_deps_proto_goTypes, DependencyIndexes: file_deps_proto_deps_proto_depIdxs, EnumInfos: file_deps_proto_deps_proto_enumTypes, MessageInfos: file_deps_proto_deps_proto_msgTypes, }.Build() File_deps_proto_deps_proto = out.File file_deps_proto_deps_proto_goTypes = nil file_deps_proto_deps_proto_depIdxs = nil } buildtools-10.1.0/deps_proto/BUILD.bazel0000664000175000017500000000173015254573013017401 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") load("//build:build_defs.bzl", "go_proto_checkedin_test") # gazelle:exclude deps.gen.pb.go # deps.proto is copied from Bazel 0.22.0 # https://github.com/bazelbuild/bazel/tree/0.22.0/src/main/protobuf go_proto_checkedin_test( src = "deps.gen.pb.go", allowed_diffs = ["^[[:space:]]*//.*protoc"], ) proto_library( name = "blaze_deps_proto", srcs = ["deps.proto"], visibility = ["//visibility:public"], ) go_proto_library( name = "go_default_library", importpath = "github.com/bazel-contrib/buildtools/v10/deps_proto", proto = ":blaze_deps_proto", visibility = ["//visibility:public"], ) go_library( name = "deps_proto", embed = [":go_default_library"], importpath = "github.com/bazel-contrib/buildtools/v10/deps_proto", visibility = ["//visibility:public"], ) buildtools-10.1.0/generatetables/0000775000175000017500000000000015254573013016331 5ustar kenhyskenhysbuildtools-10.1.0/generatetables/generate_tables.go0000664000175000017500000001143515254573013022010 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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. */ // generateTables is a tool that generates a go file from the Build language proto file. // It generates a Go map to find the type of an attribute. package main import ( "flag" "fmt" "log" "os" "sort" buildpb "github.com/bazel-contrib/buildtools/v10/build_proto" "github.com/golang/protobuf/proto" ) var inputPath = flag.String("input", "", "input file") var outputPath = flag.String("output", "", "output file") // bazelBuildLanguage reads a proto file and returns a BuildLanguage object. func bazelBuildLanguage(file string) (*buildpb.BuildLanguage, error) { data, err := os.ReadFile(file) if err != nil { fmt.Fprintf(os.Stderr, "Cannot read %s: %s\n", file, err) return nil, err } lang := &buildpb.BuildLanguage{} if err := proto.Unmarshal(data, lang); err != nil { return nil, err } return lang, nil } // generateTable returns a map that associate a type for each attribute name found in Bazel. func generateTable(rules []*buildpb.RuleDefinition) map[string]buildpb.Attribute_Discriminator { types := make(map[string]buildpb.Attribute_Discriminator) for _, r := range rules { for _, attr := range r.Attribute { types[*attr.Name] = *attr.Type } } // Because of inconsistencies in bazel, we need a few exceptions. types["resources"] = buildpb.Attribute_LABEL_LIST types["out"] = buildpb.Attribute_STRING types["outs"] = buildpb.Attribute_STRING_LIST types["stamp"] = buildpb.Attribute_TRISTATE types["strip"] = buildpb.Attribute_BOOLEAN // Surprisingly, the name argument is missing. types["name"] = buildpb.Attribute_STRING // package arguments are also not listed in the proto file types["default_hdrs_check"] = buildpb.Attribute_STRING types["default_visibility"] = types["visibility"] types["default_copts"] = buildpb.Attribute_STRING_LIST types["default_deprecation"] = types["deprecation"] types["default_testonly"] = types["testonly"] types["default_applicable_licenses"] = buildpb.Attribute_LABEL_LIST types["default_package_metadata"] = buildpb.Attribute_LABEL_LIST types["features"] = buildpb.Attribute_STRING_LIST types["extra_srcs"] = types["srcs"] types["pytype_deps"] = types["deps"] // Make sure we always have this. _, ok := types["package_metadata"] if !ok { types["package_metadata"] = buildpb.Attribute_LABEL_LIST } // The following attributes used to exist in the native Python rules but were removed // during Starlarkification. Add them here for backwards compatibility, at least for now. types["api_version"] = buildpb.Attribute_INTEGER types["bootstrap_template"] = buildpb.Attribute_LABEL types["buildpar"] = buildpb.Attribute_BOOLEAN types["coverage_tool"] = buildpb.Attribute_LABEL types["files"] = buildpb.Attribute_LABEL_LIST types["has_services"] = buildpb.Attribute_BOOLEAN types["interpreter"] = buildpb.Attribute_LABEL types["interpreter_path"] = buildpb.Attribute_STRING types["launcher"] = buildpb.Attribute_LABEL types["launcher_uses_whole_archive"] = buildpb.Attribute_BOOLEAN types["main"] = buildpb.Attribute_LABEL types["paropts"] = buildpb.Attribute_STRING_LIST types["python_version"] = buildpb.Attribute_STRING types["srcs_version"] = buildpb.Attribute_STRING types["stub_shebang"] = buildpb.Attribute_STRING // The following attributes used to exist in the native CC rules but were removed // during Starlarkification. Add them here for backwards compatibility, at least for now. types["copts"] = buildpb.Attribute_STRING_LIST types["linkstamp"] = buildpb.Attribute_LABEL return types } func main() { flag.Parse() if *inputPath == "" { log.Fatal("No input file specified") } lang, err := bazelBuildLanguage(*inputPath) if err != nil { log.Fatalf("%s\n", err) } types := generateTable(lang.Rule) // sort the keys to get deterministic output keys := make([]string, 0, len(types)) for i := range types { keys = append(keys, i) } sort.Strings(keys) f, err := os.Create(*outputPath) if err != nil { log.Fatalf("%s\n", err) } defer f.Close() // print fmt.Fprintf(f, `// Generated file, do not edit. package lang import buildpb "github.com/bazel-contrib/buildtools/v10/build_proto" var TypeOf = map[string]buildpb.Attribute_Discriminator{ `) for _, attr := range keys { fmt.Fprintf(f, " \"%s\": buildpb.Attribute_%s,\n", attr, types[attr]) } fmt.Fprintf(f, "}\n") } buildtools-10.1.0/generatetables/BUILD.bazel0000664000175000017500000000076315254573013020215 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "generatetables_lib", srcs = ["generate_tables.go"], importpath = "github.com/bazel-contrib/buildtools/v10/generatetables", visibility = ["//visibility:private"], deps = [ "//build_proto", "@com_github_golang_protobuf//proto:go_default_library", ], ) go_binary( name = "generatetables", embed = [":generatetables_lib"], visibility = ["//visibility:public"], ) buildtools-10.1.0/CONTRIBUTORS0000664000175000017500000000122515254573013015224 0ustar kenhyskenhys# People who have agreed to one of the CLAs and can contribute patches. # The AUTHORS file lists the copyright holders; this file # lists people. For example, Google employees are listed here # but not in AUTHORS, because Google holds the copyright. # # https://developers.google.com/open-source/cla/individual # https://developers.google.com/open-source/cla/corporate # # Names should be added to this file as: # Name Paul Bethe Russ Cox Laurent Le Brun Justine Alexandra Roberts Tunney Nilton Volpato Matthew C Brown buildtools-10.1.0/lang/0000775000175000017500000000000015254573013014265 5ustar kenhyskenhysbuildtools-10.1.0/lang/defs.bzl0000664000175000017500000000127215254573013015721 0ustar kenhyskenhys""" Helper rules for language proto. """ def _generate_tables_impl(ctx): args = ctx.actions.args() args.add("-input", ctx.file.src) args.add("-output", ctx.outputs.out) ctx.actions.run( executable = ctx.executable.bin, inputs = [ctx.file.src], outputs = [ctx.outputs.out], arguments = [args], ) generate_tables = rule( implementation = _generate_tables_impl, attrs = { "src": attr.label(allow_single_file = True), "out": attr.output(), "bin": attr.label( default = "//generatetables", executable = True, allow_single_file = True, cfg = "exec", ), }, ) buildtools-10.1.0/lang/tables.gen.go0000775000175000017500000004773515254573013016661 0ustar kenhyskenhys// Generated file, do not edit. package lang import buildpb "github.com/bazel-contrib/buildtools/v10/build_proto" var TypeOf = map[string]buildpb.Attribute_Discriminator{ "$aar_embedded_jars_extractor": buildpb.Attribute_LABEL, "$aar_native_libs_zip_creator": buildpb.Attribute_LABEL, "$aar_resources_extractor": buildpb.Attribute_LABEL, "$adb": buildpb.Attribute_LABEL, "$adb_static": buildpb.Attribute_LABEL, "$android_resources_busybox": buildpb.Attribute_LABEL, "$android_runtest": buildpb.Attribute_LABEL, "$build_incremental_dexmanifest": buildpb.Attribute_LABEL, "$build_java8_legacy_dex": buildpb.Attribute_LABEL, "$build_split_manifest": buildpb.Attribute_LABEL, "$cc_toolchain_split": buildpb.Attribute_LABEL, "$cc_toolchain_type": buildpb.Attribute_STRING, "$child_configuration_dummy": buildpb.Attribute_LABEL, "$collect_cc_coverage": buildpb.Attribute_LABEL, "$collect_coverage_script": buildpb.Attribute_LABEL, "$config_dependencies": buildpb.Attribute_LABEL_LIST, "$databinding_annotation_processor": buildpb.Attribute_LABEL, "$databinding_exec": buildpb.Attribute_LABEL, "$def_parser": buildpb.Attribute_LABEL, "$desugar": buildpb.Attribute_LABEL, "$desugar_java8_extra_bootclasspath": buildpb.Attribute_LABEL, "$desugared_java8_legacy_apis": buildpb.Attribute_LABEL, "$dex_list_obfuscator": buildpb.Attribute_LABEL, "$dexbuilder": buildpb.Attribute_LABEL, "$dexbuilder_after_proguard": buildpb.Attribute_LABEL, "$dexmerger": buildpb.Attribute_LABEL, "$dexsharder": buildpb.Attribute_LABEL, "$dummy_lib": buildpb.Attribute_LABEL, "$empty_snapshot_fs": buildpb.Attribute_LABEL, "$emulator_arm": buildpb.Attribute_LABEL, "$emulator_x86": buildpb.Attribute_LABEL, "$emulator_x86_bios": buildpb.Attribute_LABEL, "$genrule_setup": buildpb.Attribute_LABEL, "$googlemac_proto_compiler": buildpb.Attribute_LABEL, "$googlemac_proto_compiler_support": buildpb.Attribute_LABEL, "$grep_includes": buildpb.Attribute_LABEL, "$host_jdk": buildpb.Attribute_LABEL, "$idlclass": buildpb.Attribute_LABEL, "$implicit_tests": buildpb.Attribute_LABEL_LIST, "$import_deps_checker": buildpb.Attribute_LABEL, "$incremental_install": buildpb.Attribute_LABEL, "$incremental_split_stub_application": buildpb.Attribute_LABEL, "$incremental_stub_application": buildpb.Attribute_LABEL, "$instrumentation_test_check": buildpb.Attribute_LABEL, "$interface_library_builder": buildpb.Attribute_LABEL, "$is_executable": buildpb.Attribute_BOOLEAN, "$j2objc_dead_code_pruner": buildpb.Attribute_LABEL, "$jacocorunner": buildpb.Attribute_LABEL, "$java8_legacy_dex": buildpb.Attribute_LABEL, "$java_toolchain": buildpb.Attribute_LABEL, "$jvm": buildpb.Attribute_LABEL, "$launcher": buildpb.Attribute_LABEL, "$libtool": buildpb.Attribute_LABEL, "$link_dynamic_library_tool": buildpb.Attribute_LABEL, "$merge_dexzips": buildpb.Attribute_LABEL, "$mksd": buildpb.Attribute_LABEL, "$proguard_whitelister": buildpb.Attribute_LABEL, "$protobuf_well_known_types": buildpb.Attribute_LABEL_LIST, "$py_toolchain_type": buildpb.Attribute_STRING, "$python2to3": buildpb.Attribute_LABEL, "$resource_extractor": buildpb.Attribute_LABEL, "$robolectric_implicit_classpath": buildpb.Attribute_LABEL_LIST, "$sdk_path": buildpb.Attribute_LABEL, "$shuffle_jars": buildpb.Attribute_LABEL, "$strip_resources": buildpb.Attribute_LABEL, "$stubify_manifest": buildpb.Attribute_LABEL, "$test_entry_point": buildpb.Attribute_LABEL, "$test_runtime": buildpb.Attribute_LABEL_LIST, "$test_setup_script": buildpb.Attribute_LABEL, "$test_wrapper": buildpb.Attribute_LABEL, "$testing_shbase": buildpb.Attribute_LABEL, "$testsupport": buildpb.Attribute_LABEL, "$tools_repository": buildpb.Attribute_STRING, "$unified_launcher": buildpb.Attribute_LABEL, "$whitelist_allow_deps_without_srcs": buildpb.Attribute_LABEL, "$whitelist_android_device": buildpb.Attribute_LABEL, "$whitelist_config_feature_flag": buildpb.Attribute_LABEL, "$whitelist_disabling_parse_headers_and_layering_check_allowed": buildpb.Attribute_LABEL, "$whitelist_export_deps": buildpb.Attribute_LABEL, "$whitelist_loose_header_check_allowed_in_toolchain": buildpb.Attribute_LABEL, "$xcrunwrapper": buildpb.Attribute_LABEL, "$xml_generator_script": buildpb.Attribute_LABEL, "$xml_writer": buildpb.Attribute_LABEL, "$xvfb_support": buildpb.Attribute_LABEL, "$zip_filter": buildpb.Attribute_LABEL, "$zipper": buildpb.Attribute_LABEL, ":action_listener": buildpb.Attribute_LABEL_LIST, ":alias": buildpb.Attribute_LABEL, ":android_sdk": buildpb.Attribute_LABEL, ":aspect_proto_toolchain_for_javalite": buildpb.Attribute_LABEL, ":bytecode_optimizer": buildpb.Attribute_LABEL, ":cc_toolchain": buildpb.Attribute_LABEL, ":coverage_report_generator": buildpb.Attribute_LABEL, ":coverage_support": buildpb.Attribute_LABEL, ":csfdo_profile": buildpb.Attribute_LABEL, ":default_malloc": buildpb.Attribute_LABEL, ":extra_proguard_specs": buildpb.Attribute_LABEL_LIST, ":fdo_optimize": buildpb.Attribute_LABEL, ":fdo_prefetch_hints": buildpb.Attribute_LABEL, ":fdo_profile": buildpb.Attribute_LABEL, ":java_launcher": buildpb.Attribute_LABEL, ":java_plugins": buildpb.Attribute_LABEL_LIST, ":lcov_merger": buildpb.Attribute_LABEL, ":legacy_main_dex_list_generator": buildpb.Attribute_LABEL, ":libc_top": buildpb.Attribute_LABEL, ":proguard": buildpb.Attribute_LABEL, ":proto_compiler": buildpb.Attribute_LABEL, ":py_interpreter": buildpb.Attribute_LABEL, ":run_under": buildpb.Attribute_LABEL, ":target_libc_top": buildpb.Attribute_LABEL, ":xcode_config": buildpb.Attribute_LABEL, ":xfdo_profile": buildpb.Attribute_LABEL, ":zipper": buildpb.Attribute_LABEL, "aapt": buildpb.Attribute_LABEL, "aapt2": buildpb.Attribute_LABEL, "aar": buildpb.Attribute_LABEL, "absolute_path_profile": buildpb.Attribute_STRING, "actual": buildpb.Attribute_LABEL, "adb": buildpb.Attribute_LABEL, "additional_linker_inputs": buildpb.Attribute_LABEL_LIST, "aidl": buildpb.Attribute_LABEL, "aidl_lib": buildpb.Attribute_LABEL, "aliases": buildpb.Attribute_STRING_LIST, "all_files": buildpb.Attribute_LABEL, "allowed_values": buildpb.Attribute_STRING_LIST, "alwayslink": buildpb.Attribute_BOOLEAN, "android_jar": buildpb.Attribute_LABEL, "annotations_jar": buildpb.Attribute_LABEL, "api_level": buildpb.Attribute_INTEGER, "api_version": buildpb.Attribute_INTEGER, "apkbuilder": buildpb.Attribute_LABEL, "apksigner": buildpb.Attribute_LABEL, "applicable_licenses": buildpb.Attribute_LABEL_LIST, "application_resources": buildpb.Attribute_LABEL, "ar_files": buildpb.Attribute_LABEL, "archives": buildpb.Attribute_LABEL_LIST, "args": buildpb.Attribute_STRING_LIST, "as_files": buildpb.Attribute_LABEL, "assets": buildpb.Attribute_LABEL_LIST, "assets_dir": buildpb.Attribute_STRING, "avoid_deps": buildpb.Attribute_LABEL_LIST, "binary_type": buildpb.Attribute_STRING, "blacklisted_protos": buildpb.Attribute_LABEL_LIST, "bootclasspath": buildpb.Attribute_LABEL_LIST, "bootstrap_template": buildpb.Attribute_LABEL, "build_file": buildpb.Attribute_STRING, "build_file_content": buildpb.Attribute_STRING, "build_setting_default": buildpb.Attribute_LABEL, "build_tools_version": buildpb.Attribute_STRING, "buildpar": buildpb.Attribute_BOOLEAN, "bundle_loader": buildpb.Attribute_LABEL, "cache": buildpb.Attribute_INTEGER, "classpath_resources": buildpb.Attribute_LABEL_LIST, "cmd": buildpb.Attribute_STRING, "cmd_bash": buildpb.Attribute_STRING, "cmd_bat": buildpb.Attribute_STRING, "cmd_ps": buildpb.Attribute_STRING, "command_line": buildpb.Attribute_STRING, "compatible_javacopts": buildpb.Attribute_STRING_LIST_DICT, "compatible_with": buildpb.Attribute_LABEL_LIST, "compiler": buildpb.Attribute_STRING, "compiler_files": buildpb.Attribute_LABEL, "compiler_files_without_includes": buildpb.Attribute_LABEL, "constraint_setting": buildpb.Attribute_LABEL, "constraint_values": buildpb.Attribute_LABEL_LIST, "constraints": buildpb.Attribute_STRING_LIST, "copts": buildpb.Attribute_STRING_LIST, "coverage_files": buildpb.Attribute_LABEL, "coverage_tool": buildpb.Attribute_LABEL, "cpu": buildpb.Attribute_STRING, "cpu_constraints": buildpb.Attribute_LABEL_LIST, "create_executable": buildpb.Attribute_BOOLEAN, "crunch_png": buildpb.Attribute_BOOLEAN, "custom_package": buildpb.Attribute_STRING, "daemon": buildpb.Attribute_BOOLEAN, "data": buildpb.Attribute_LABEL_LIST, "debug_key": buildpb.Attribute_LABEL, "debug_signing_keys": buildpb.Attribute_LABEL_LIST, "debug_signing_lineage_file": buildpb.Attribute_LABEL, "default": buildpb.Attribute_LABEL, "default_applicable_licenses": buildpb.Attribute_LABEL_LIST, "default_constraint_value": buildpb.Attribute_STRING, "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_macos_sdk_version": buildpb.Attribute_STRING, "default_package_metadata": buildpb.Attribute_LABEL_LIST, "default_properties": buildpb.Attribute_LABEL, "default_testonly": buildpb.Attribute_BOOLEAN, "default_tvos_sdk_version": buildpb.Attribute_STRING, "default_value": buildpb.Attribute_STRING, "default_visibility": buildpb.Attribute_STRING_LIST, "default_watchos_sdk_version": buildpb.Attribute_STRING, "define_values": buildpb.Attribute_STRING_DICT, "defines": buildpb.Attribute_STRING_LIST, "densities": buildpb.Attribute_STRING_LIST, "deploy_env": buildpb.Attribute_LABEL_LIST, "deploy_manifest_lines": buildpb.Attribute_STRING_LIST, "deprecation": buildpb.Attribute_STRING, "deps": buildpb.Attribute_LABEL_LIST, "deps_mapping": buildpb.Attribute_LABEL_DICT_UNARY, "dex_shards": buildpb.Attribute_INTEGER, "dexopts": buildpb.Attribute_STRING_LIST, "distribs": buildpb.Attribute_DISTRIBUTION_SET, "dwp_files": buildpb.Attribute_LABEL, "dx": buildpb.Attribute_LABEL, "dylibs": buildpb.Attribute_LABEL_LIST, "dynamic_deps": buildpb.Attribute_LABEL_LIST, "dynamic_runtime_lib": buildpb.Attribute_LABEL, "enable_data_binding": buildpb.Attribute_BOOLEAN, "enable_modules": buildpb.Attribute_BOOLEAN, "entry_classes": buildpb.Attribute_STRING_LIST, "exec_compatible_with": buildpb.Attribute_LABEL_LIST, "exec_properties": buildpb.Attribute_STRING_DICT, "exec_tools": buildpb.Attribute_LABEL_LIST, "executable": buildpb.Attribute_BOOLEAN, "exported_plugins": buildpb.Attribute_LABEL_LIST, "exports": buildpb.Attribute_LABEL_LIST, "exports_manifest": buildpb.Attribute_TRISTATE, "expression": buildpb.Attribute_STRING, "extclasspath": buildpb.Attribute_LABEL_LIST, "extension_safe": buildpb.Attribute_BOOLEAN, "extra_actions": buildpb.Attribute_LABEL_LIST, "extra_srcs": buildpb.Attribute_LABEL_LIST, "feature_flags": buildpb.Attribute_LABEL_KEYED_STRING_DICT, "features": buildpb.Attribute_STRING_LIST, "files": buildpb.Attribute_LABEL_LIST, "fixtures": buildpb.Attribute_LABEL_LIST, "flag_values": buildpb.Attribute_LABEL_KEYED_STRING_DICT, "flaky": buildpb.Attribute_BOOLEAN, "forcibly_disable_header_compilation": buildpb.Attribute_BOOLEAN, "framework_aidl": buildpb.Attribute_LABEL, "fulfills": buildpb.Attribute_LABEL_LIST, "genclass": buildpb.Attribute_LABEL_LIST, "generates_api": buildpb.Attribute_BOOLEAN, "generator_function": buildpb.Attribute_STRING, "generator_location": buildpb.Attribute_STRING, "generator_name": buildpb.Attribute_STRING, "has_services": buildpb.Attribute_BOOLEAN, "hdrs": buildpb.Attribute_LABEL_LIST, "header_compiler": buildpb.Attribute_LABEL_LIST, "header_compiler_builtin_processors": buildpb.Attribute_STRING_LIST, "header_compiler_direct": buildpb.Attribute_LABEL_LIST, "heuristic_label_expansion": buildpb.Attribute_BOOLEAN, "horizontal_resolution": buildpb.Attribute_INTEGER, "host_platform": buildpb.Attribute_BOOLEAN, "idl_import_root": buildpb.Attribute_STRING, "idl_parcelables": buildpb.Attribute_LABEL_LIST, "idl_preprocessed": buildpb.Attribute_LABEL_LIST, "idl_srcs": buildpb.Attribute_LABEL_LIST, "ijar": buildpb.Attribute_LABEL_LIST, "import_prefix": buildpb.Attribute_STRING, "imports": buildpb.Attribute_STRING_LIST, "include_prefix": buildpb.Attribute_STRING, "includes": buildpb.Attribute_STRING_LIST, "incremental_dexing": buildpb.Attribute_TRISTATE, "inline_constants": buildpb.Attribute_BOOLEAN, "instruments": buildpb.Attribute_LABEL, "interface_library": buildpb.Attribute_LABEL, "interpreter": buildpb.Attribute_LABEL, "interpreter_path": buildpb.Attribute_STRING, "jacocorunner": buildpb.Attribute_LABEL, "jars": buildpb.Attribute_LABEL_LIST, "java": buildpb.Attribute_LABEL, "java_home": buildpb.Attribute_STRING, "javabuilder": buildpb.Attribute_LABEL_LIST, "javabuilder_jvm_opts": buildpb.Attribute_STRING_LIST, "javac": buildpb.Attribute_LABEL_LIST, "javac_supports_multiplex_workers": buildpb.Attribute_BOOLEAN, "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, "launcher": buildpb.Attribute_LABEL, "launcher_uses_whole_archive": buildpb.Attribute_BOOLEAN, "legacy_create_init": buildpb.Attribute_TRISTATE, "libc_top": 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, "local_defines": buildpb.Attribute_STRING_LIST, "local_versions": buildpb.Attribute_LABEL, "main": buildpb.Attribute_LABEL, "main_class": buildpb.Attribute_STRING, "main_dex_classes": buildpb.Attribute_LABEL, "main_dex_list": buildpb.Attribute_LABEL, "main_dex_list_creator": 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_values": buildpb.Attribute_STRING_DICT, "message": buildpb.Attribute_STRING, "minimum_os_version": buildpb.Attribute_STRING, "misc": buildpb.Attribute_STRING_LIST, "mnemonics": buildpb.Attribute_STRING_LIST, "module_map": buildpb.Attribute_LABEL, "module_name": buildpb.Attribute_STRING, "multidex": buildpb.Attribute_STRING, "name": buildpb.Attribute_STRING, "neverlink": buildpb.Attribute_BOOLEAN, "ninja_graph": buildpb.Attribute_LABEL, "ninja_srcs": buildpb.Attribute_LABEL_LIST, "nocompress_extensions": buildpb.Attribute_STRING_LIST, "nocopts": buildpb.Attribute_STRING, "non_arc_srcs": buildpb.Attribute_LABEL_LIST, "objcopy_files": buildpb.Attribute_LABEL, "oneversion": buildpb.Attribute_LABEL, "oneversion_whitelist": buildpb.Attribute_LABEL, "opts": buildpb.Attribute_STRING_LIST, "os_constraints": buildpb.Attribute_LABEL_LIST, "out": buildpb.Attribute_STRING, "out_templates": buildpb.Attribute_STRING_LIST, "output_group": buildpb.Attribute_STRING, "output_groups": buildpb.Attribute_STRING_LIST_DICT, "output_licenses": buildpb.Attribute_LICENSE, "output_root": buildpb.Attribute_STRING, "output_root_inputs": buildpb.Attribute_STRING_LIST, "output_root_symlinks": buildpb.Attribute_STRING_LIST, "output_to_bindir": buildpb.Attribute_BOOLEAN, "outs": buildpb.Attribute_STRING_LIST, "package_configuration": buildpb.Attribute_LABEL_LIST, "package_metadata": buildpb.Attribute_LABEL_LIST, "packages": buildpb.Attribute_LABEL_LIST, "parents": buildpb.Attribute_LABEL_LIST, "paropts": buildpb.Attribute_STRING_LIST, "path": buildpb.Attribute_STRING, "pch": buildpb.Attribute_LABEL, "platform_apks": buildpb.Attribute_LABEL_LIST, "platform_type": buildpb.Attribute_STRING, "plugin": buildpb.Attribute_LABEL, "plugins": buildpb.Attribute_LABEL_LIST, "pregenerate_oat_files_for_tests": buildpb.Attribute_BOOLEAN, "processor_class": buildpb.Attribute_STRING, "profile": buildpb.Attribute_LABEL, "proguard": buildpb.Attribute_LABEL, "proguard_apply_dictionary": buildpb.Attribute_LABEL, "proguard_apply_mapping": buildpb.Attribute_LABEL, "proguard_generate_mapping": buildpb.Attribute_BOOLEAN, "proguard_specs": buildpb.Attribute_LABEL_LIST, "proto_profile": buildpb.Attribute_LABEL, "provides_test_args": buildpb.Attribute_BOOLEAN, "python_version": buildpb.Attribute_STRING, "pytype_deps": buildpb.Attribute_LABEL_LIST, "ram": buildpb.Attribute_INTEGER, "reduced_classpath_incompatible_processors": buildpb.Attribute_STRING_LIST, "reduced_classpath_incompatible_targets": buildpb.Attribute_STRING_LIST, "reexport_deps": buildpb.Attribute_LABEL_LIST, "remote_execution_properties": buildpb.Attribute_STRING, "remote_versions": buildpb.Attribute_LABEL, "repo_mapping": buildpb.Attribute_STRING_DICT, "requires_action_output": buildpb.Attribute_BOOLEAN, "resource_configuration_filters": buildpb.Attribute_STRING_LIST, "resource_files": buildpb.Attribute_LABEL_LIST, "resource_jars": buildpb.Attribute_LABEL_LIST, "resource_strip_prefix": buildpb.Attribute_STRING, "resourcejar": buildpb.Attribute_LABEL_LIST, "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, "screen_density": buildpb.Attribute_INTEGER, "script": buildpb.Attribute_LABEL, "sdk_dylibs": buildpb.Attribute_STRING_LIST, "sdk_frameworks": buildpb.Attribute_STRING_LIST, "sdk_includes": buildpb.Attribute_STRING_LIST, "service_names": buildpb.Attribute_STRING_LIST, "shard_count": buildpb.Attribute_INTEGER, "shared_library": buildpb.Attribute_LABEL, "shrink_resources": buildpb.Attribute_TRISTATE, "shrinked_android_jar": buildpb.Attribute_LABEL, "singlejar": buildpb.Attribute_LABEL_LIST, "size": buildpb.Attribute_STRING, "source_properties": buildpb.Attribute_LABEL, "source_version": buildpb.Attribute_STRING, "srcjar": buildpb.Attribute_LABEL, "srcs": buildpb.Attribute_LABEL_LIST, "srcs_version": buildpb.Attribute_STRING, "stamp": buildpb.Attribute_TRISTATE, "static_library": buildpb.Attribute_LABEL, "static_runtime_lib": buildpb.Attribute_LABEL, "strict": buildpb.Attribute_BOOLEAN, "strict_deps": buildpb.Attribute_BOOLEAN, "strict_exit": buildpb.Attribute_BOOLEAN, "strip": buildpb.Attribute_BOOLEAN, "strip_files": buildpb.Attribute_LABEL, "strip_import_prefix": buildpb.Attribute_STRING, "strip_include_prefix": buildpb.Attribute_STRING, "stub_shebang": buildpb.Attribute_STRING, "support_apks": buildpb.Attribute_LABEL_LIST, "supports_header_parsing": buildpb.Attribute_BOOLEAN, "supports_param_files": buildpb.Attribute_BOOLEAN, "system_image": buildpb.Attribute_LABEL, "system_provided": buildpb.Attribute_BOOLEAN, "tags": buildpb.Attribute_STRING_LIST, "target_compatible_with": buildpb.Attribute_LABEL_LIST, "target_device": buildpb.Attribute_LABEL, "target_platform": buildpb.Attribute_BOOLEAN, "target_version": buildpb.Attribute_STRING, "test_app": buildpb.Attribute_LABEL, "test_class": buildpb.Attribute_STRING, "testonly": buildpb.Attribute_BOOLEAN, "tests": buildpb.Attribute_LABEL_LIST, "textual_hdrs": buildpb.Attribute_LABEL_LIST, "timeout": buildpb.Attribute_STRING, "timezone_data": buildpb.Attribute_LABEL, "toolchain": buildpb.Attribute_STRING, "toolchain_config": buildpb.Attribute_LABEL, "toolchain_identifier": buildpb.Attribute_STRING, "toolchain_type": buildpb.Attribute_LABEL, "toolchains": buildpb.Attribute_LABEL_LIST, "tools": buildpb.Attribute_LABEL_LIST, "transitive_configs": buildpb.Attribute_STRING_LIST, "turbine_incompatible_processors": buildpb.Attribute_STRING_LIST, "turbine_jvm_opts": buildpb.Attribute_STRING_LIST, "use_testrunner": buildpb.Attribute_BOOLEAN, "values": buildpb.Attribute_STRING_DICT, "version": buildpb.Attribute_STRING, "versions": buildpb.Attribute_LABEL_LIST, "vertical_resolution": buildpb.Attribute_INTEGER, "visibility": buildpb.Attribute_STRING_LIST, "vm_heap": buildpb.Attribute_INTEGER, "weak_sdk_frameworks": buildpb.Attribute_STRING_LIST, "win_def_file": buildpb.Attribute_LABEL, "working_directory": buildpb.Attribute_STRING, "workspace_file": buildpb.Attribute_STRING, "workspace_file_content": buildpb.Attribute_STRING, "xlint": buildpb.Attribute_STRING_LIST, "zipalign": buildpb.Attribute_LABEL, } buildtools-10.1.0/lang/build-language.pb0000664000175000017500000026343515254573013017505 0ustar kenhyskenhys ° aar_importm $aar_embedded_jars_extractor"08J97@bazel_tools//tools/android:aar_embedded_jars_extractorPX`hm $aar_native_libs_zip_creator"08J97@bazel_tools//tools/android:aar_native_libs_zip_creatorPX`he $aar_resources_extractor"08J53@bazel_tools//tools/android:aar_resources_extractorPX`hW $android_resources_busybox"08J%#@bazel_tools//tools/android:busyboxPX`h* $config_dependencies"08PX`hy "$desugar_java8_extra_bootclasspath"08J?=@bazel_tools//tools/android:desugar_java8_extra_bootclasspathPX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hG $idlclass"08J&$@bazel_tools//tools/android:IdlClassPX`ha $import_deps_checker"08J53@bazel_tools//tools/android:aar_import_deps_checkerPX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`h? $zipper"08J @bazel_tools//tools/zip:zipperPX`h& :action_listener"08PX`h/ :android_sdk" android_sdk08PX`h0 :java_plugins" java_plugin08PX`h aar"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh deps"08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh6 exports" aar_import java_import08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h srcjar"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Î action_listener* $config_dependencies"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh1 extra_actions" extra_action08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh mnemonics08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h » alias* $config_dependencies"08PX`h& :action_listener"08PX`h actual"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h É+ android_binaryW $android_resources_busybox"08J%#@bazel_tools//tools/android:busyboxPX`hq $build_incremental_dexmanifest"08J;9@bazel_tools//tools/android:build_incremental_dexmanifestPX`hc $build_java8_legacy_dex"08J42@bazel_tools//tools/android:build_java8_legacy_dexPX`h_ $build_split_manifest"08J20@bazel_tools//tools/android:build_split_manifestPX`hY $cc_toolchain_split"08J.,@bazel_tools//tools/cpp:current_cc_toolchainPX`hR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`hw !$databinding_annotation_processor"08J><@bazel_tools//tools/android:databinding_annotation_processorPX`hW $databinding_exec"08J.,@bazel_tools//tools/android:databinding_execPX`hK $desugar"08J+)@bazel_tools//tools/android:desugar_java8PX`hy "$desugar_java8_extra_bootclasspath"08J?=@bazel_tools//tools/android:desugar_java8_extra_bootclasspathPX`hm $desugared_java8_legacy_apis"08J97@bazel_tools//tools/android:desugared_java8_legacy_apisPX`h] $dex_list_obfuscator"08J1/@bazel_tools//tools/android:dex_list_obfuscatorPX`hK $dexbuilder"08J(&@bazel_tools//tools/android:dexbuilderPX`hi $dexbuilder_after_proguard"08J75@bazel_tools//tools/android:dexbuilder_after_proguardPX`hI $dexmerger"08J'%@bazel_tools//tools/android:dexmergerPX`hK $dexsharder"08J(&@bazel_tools//tools/android:dexsharderPX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hG $idlclass"08J&$@bazel_tools//tools/android:IdlClassPX`h] $incremental_install"08J1/@bazel_tools//tools/android:incremental_installPX`h{ #$incremental_split_stub_application"08J@>@bazel_tools//tools/android:incremental_split_stub_applicationPX`ho $incremental_stub_application"08J:8@bazel_tools//tools/android:incremental_stub_applicationPX`h1 $instrumentation_test_check"08PX`hW $java8_legacy_dex"08J.,@bazel_tools//tools/android:java8_legacy_dexPX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`hQ $merge_dexzips"08J+)@bazel_tools//tools/android:merge_dexzipsPX`h[ $resource_extractor"08J0.@bazel_tools//tools/android:resource_extractorPX`hO $shuffle_jars"08J*(@bazel_tools//tools/android:shuffle_jarsPX`hU $strip_resources"08J-+@bazel_tools//tools/android:strip_resourcesPX`hW $stubify_manifest"08J.,@bazel_tools//tools/android:stubify_manifestPX`h‰ "$whitelist_allow_deps_without_srcs"08JOM@bazel_tools//tools/android:allow_android_library_deps_without_srcs_allowlistPX`h4 $whitelist_config_feature_flag"08PX`ha $whitelist_export_deps"08J31@bazel_tools//tools/android:export_deps_allowlistPX`hK $zip_filter"08J(&@bazel_tools//tools/android:zip_filterPX`h& :action_listener"08PX`h/ :android_sdk" android_sdk08PX`h) :bytecode_optimizer"08PX`h# :cc_toolchain"08PX`h+ :extra_proguard_specs"08PX`h0 :java_plugins" java_plugin08PX`h5 :legacy_main_dex_list_generator"08PX`h) applicable_licenses"08PX`h+ application_resources"08PX`h assets"08PX`h assets_dir08PXh2 compatible_with" environment08PX`h crunch_png08JPXh custom_package08PXhM debug_key"08J,*@bazel_tools//tools/android:debug_keystorePX`h( debug_signing_keys"08PX`h0 debug_signing_lineage_file"08PX`h densities08PXh deprecation08PXhw deps"_ aar_importandroid_library cc_library java_import java_libraryjava_lite_proto_library08PX`h dex_shards08JPXh dexopts08PXh distribs08PXh# enable_data_binding08PXh* exec_compatible_with"08PX`h exec_properties 08PXh8 feature_flags"config_feature_flag08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh" incremental_dexing08PXh inline_constants08PXh1 instruments"android_binary08PX`h javacopts08PXh licenses 08PXh# main_dex_list"08PX`h" main_dex_list_opts08PXh- main_dex_proguard_specs"08PX`h manifest"08PX`h manifest_values 08PXh multidex08JoffPXh name08PXh% nocompress_extensions08PXh* plugins" java_plugin08PX`h/ proguard_apply_dictionary"08PX`h, proguard_apply_mapping"08PX`h) proguard_generate_mapping08PXh$ proguard_specs"08PX`h. resource_configuration_filters08PXh$ resource_files"08PX`h0 restricted_to" environment08PX`h shrink_resources08PXh srcs"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h – android_device= $adb"08J!@bazel_tools//tools/android:adbPX`hK $adb_static"08J(&@bazel_tools//tools/android:adb_staticPX`hU $android_runtest"08J-+@bazel_tools//tools/android:android_runtestPX`h* $config_dependencies"08PX`hb $empty_snapshot_fs"08J86@bazel_tools//tools/android/emulator:empty_snapshot_fsPX`hX $emulator_arm"08J31@bazel_tools//tools/android/emulator:emulator_armPX`hX $emulator_x86"08J31@bazel_tools//tools/android/emulator:emulator_x86PX`hb $emulator_x86_bios"08J86@bazel_tools//tools/android/emulator:emulator_x86_biosPX`h" $is_executable08JPXhH $mksd"08J+)@bazel_tools//tools/android/emulator:mksdPX`hP $sdk_path"08J/-@bazel_tools//tools/android/emulator:sdk_pathPX`hT $testing_shbase"08J-+@bazel_tools//tools/android/emulator:shbasePX`h` $unified_launcher"08J75@bazel_tools//tools/android/emulator:unified_launcherPX`hg $whitelist_android_device"08J64@bazel_tools//tools/android:android_device_allowlistPX`hX $xvfb_support"08J31@bazel_tools//tools/android/emulator:xvfb_supportPX`h& :action_listener"08PX`h) applicable_licenses"08PX`h cache08PXh2 compatible_with" environment08PX`h( default_properties"08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh% horizontal_resolution08PXh licenses 08PXh name08PXh# platform_apks"08PX`h/ pregenerate_oat_files_for_tests08PXh ram08PXh0 restricted_to" environment08PX`h screen_density08PXh" system_image"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h# vertical_resolution08PXh visibility"08PX`h vm_heap08PXh Ž android_device_script_fixture* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h cmd08PXh2 compatible_with" environment08PX`h daemon08PXh data"08PX`h deprecation08PXh deps"08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h script"08PX`h strict_exit08JPXh2 support_apks"android_binary08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ž android_host_service_fixture* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h daemon08PXh data"08PX`h deprecation08PXh deps"08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh executable"08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh" provides_test_args08PXh0 restricted_to" environment08PX`h service_names08PXh2 support_apks"android_binary08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ¥ android_instrumentation_testW $android_resources_busybox"08J%#@bazel_tools//tools/android:busyboxPX`h[ $collect_coverage_script"08J+)@bazel_tools//tools/test:collect_coveragePX`h* $config_dependencies"08PX`hy "$desugar_java8_extra_bootclasspath"08J?=@bazel_tools//tools/android:desugar_java8_extra_bootclasspathPX`hG $idlclass"08J&$@bazel_tools//tools/android:IdlClassPX`hg $test_entry_point"08J><@bazel_tools//tools/android:instrumentation_test_entry_pointPX`hI $test_runtime"08J$"" @bazel_tools//tools/test:runtimePX`hO $test_setup_script"08J%#@bazel_tools//tools/test:test_setupPX`hL $test_wrapper"08J'%@bazel_tools//tools/test:test_wrapperPX`hZ $xml_generator_script"08J-+@bazel_tools//tools/test:test_xml_generatorPX`hH $xml_writer"08J%#@bazel_tools//tools/test:xml_writerPX`h& :action_listener"08PX`h/ :android_sdk" android_sdk08PX`h0 :coverage_report_generator"08PX`h' :coverage_support"08PX`h :run_under"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh[ fixtures"?android_device_script_fixtureandroid_host_service_fixture08PX`h flaky08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh local08PXh name08PXh0 restricted_to" environment08PX`h( shard_count08J ÿÿÿÿÿÿÿÿÿPXh size08JmediumPXh2 support_apks"android_binary08PX`h tags08PXh# target_device"08PX`h. test_app"android_binary08PX`h testonly08JPXh timeout08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h Ñ android_libraryW $android_resources_busybox"08J%#@bazel_tools//tools/android:busyboxPX`h* $config_dependencies"08PX`hw !$databinding_annotation_processor"08J><@bazel_tools//tools/android:databinding_annotation_processorPX`hW $databinding_exec"08J.,@bazel_tools//tools/android:databinding_execPX`hy "$desugar_java8_extra_bootclasspath"08J?=@bazel_tools//tools/android:desugar_java8_extra_bootclasspathPX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hG $idlclass"08J&$@bazel_tools//tools/android:IdlClassPX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`h+ $proguard_whitelister"08PX`h‰ "$whitelist_allow_deps_without_srcs"08JOM@bazel_tools//tools/android:allow_android_library_deps_without_srcs_allowlistPX`h& :action_listener"08PX`h/ :android_sdk" android_sdk08PX`h0 :java_plugins" java_plugin08PX`h alwayslink08PXh) applicable_licenses"08PX`h assets"08PX`h assets_dir08PXh2 compatible_with" environment08PX`h custom_package08PXh data"08PX`h deprecation08PXhw deps"_ aar_importandroid_library cc_library java_import java_libraryjava_lite_proto_library08PX`h distribs08PXh# enable_data_binding08PXh* exec_compatible_with"08PX`h exec_properties 08PXh3 exported_plugins" java_plugin08PX`hz exports"_ aar_importandroid_library cc_library java_import java_libraryjava_lite_proto_library08PX`h$ exports_manifest08JPXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh idl_import_root08PXh% idl_parcelables"08PX`h& idl_preprocessed"08PX`h idl_srcs"08PX`h inline_constants08PXh javacopts08PXh licenses 08PXh manifest"08PX`h name08PXh neverlink08PXh* plugins" java_plugin08PX`h$ proguard_specs"08PX`h$ resource_files"08PX`h0 restricted_to" environment08PX`h srcs"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ë android_local_testW $android_resources_busybox"08J%#@bazel_tools//tools/android:busyboxPX`hR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h[ $collect_coverage_script"08J+)@bazel_tools//tools/test:collect_coveragePX`h* $config_dependencies"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`hH $launcher"08J'%@bazel_tools//tools/launcher:launcherPX`hb $robolectric_implicit_classpath"08J+")'@bazel_tools//tools/android:android_jarPX`hI $test_runtime"08J$"" @bazel_tools//tools/test:runtimePX`hO $test_setup_script"08J%#@bazel_tools//tools/test:test_setupPX`hL $test_wrapper"08J'%@bazel_tools//tools/test:test_wrapperPX`hS $testsupport"08J/-@bazel_tools//tools/jdk:TestRunner_deploy.jarPX`h4 $whitelist_config_feature_flag"08PX`hZ $xml_generator_script"08J-+@bazel_tools//tools/test:test_xml_generatorPX`hH $xml_writer"08J%#@bazel_tools//tools/test:xml_writerPX`h& :action_listener"08PX`h/ :android_sdk" android_sdk08PX`h# :cc_toolchain"08PX`h0 :coverage_report_generator"08PX`h' :coverage_support"08PX`h0 :java_plugins" java_plugin08PX`h :run_under"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h custom_package08PXh data"08PX`h densities08PXh deprecation08PXhk deps"S aar_importandroid_library java_import java_libraryjava_lite_proto_library08PX`h* exec_compatible_with"08PX`h exec_properties 08PXh8 feature_flags"config_feature_flag08PX`h features08PXh flaky08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh javacopts08PXh jvm_flags08PXh licenses 08PXh local08PXh manifest"08PX`h manifest_values 08PXh name08PXh% nocompress_extensions08PXh* plugins" java_plugin08PX`h. resource_configuration_filters08PXh# resource_jars"08PX`h% resource_strip_prefix08PXh0 restricted_to" environment08PX`h± runtime_deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h( shard_count08J ÿÿÿÿÿÿÿÿÿPXh size08JmediumPXh srcs"08PX`h stamp08JPXh tags08PXh test_class08PXh testonly08JPXh timeout08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h å android_ndk_repository api_level08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh path08PXh repo_mapping 08PXh ½ android_sdk* $config_dependencies"08PX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`h& :action_listener"08PX`h :proguard"08PX`h aapt"08PX`h aapt2"08PX`h adb"08PX`h aidl"08PX`h aidl_lib"08PX`h! android_jar"08PX`h% annotations_jar"08PX`h apkbuilder"08PX`h apksigner"08PX`h) applicable_licenses"08PX`h# build_tools_version08PXh2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh dx"08PX`h features08PXh$ framework_aidl"08PX`h" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh& main_dex_classes"08PX`h+ main_dex_list_creator"08PX`h name08PXh proguard"08PX`h0 restricted_to" environment08PX`h* shrinked_android_jar"08PX`h' source_properties"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h zipalign"08PX`h Š android_sdk_repository api_level08PXh# build_tools_version08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh path08PXh repo_mapping 08PXh — android_tools_defaults_jar* $config_dependencies"08PX`h& :action_listener"08PX`h/ :android_sdk" android_sdk08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ‰ apple_binaryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h` $child_configuration_dummy"08J.,@bazel_tools//tools/cpp:current_cc_toolchainPX`h* $config_dependencies"08PX`hF $dummy_lib"08J$"@bazel_tools//tools/objc:dummy_libPX`he $googlemac_proto_compiler"08J42@bazel_tools//tools/objc:protobuf_compiler_wrapperPX`hm !$googlemac_proto_compiler_support"08J42@bazel_tools//tools/objc:protobuf_compiler_supportPX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`h" $is_executable08JPXhb $j2objc_dead_code_pruner"08J20@bazel_tools//tools/objc:j2objc_dead_code_prunerPX`hB $libtool"08J" @bazel_tools//tools/objc:libtoolPX`hh $protobuf_well_known_types"08J6"42@bazel_tools//tools/objc:protobuf_well_known_typesPX`hL $xcrunwrapper"08J'%@bazel_tools//tools/objc:xcrunwrapperPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h1 :xcode_config" xcode_config08PX`h) applicable_licenses"08PX`h) binary_type08J  executablePXh# bundle_loader"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh& deps" cc_library08PX`h distribs08PXh dylibs"08PX`h* exec_compatible_with"08PX`h exec_properties 08PXh extension_safe08PXh8 feature_flags"config_feature_flag08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh linkopts08PXh" minimum_os_version08PXh name08PXh platform_type08PXh0 restricted_to" environment08PX`h sdk_dylibs08PXh sdk_frameworks08PXh stamp08JPXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h# weak_sdk_frameworks08PXh œ apple_cc_toolchainR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`he $interface_library_builder"08J31@bazel_tools//tools/cpp:interface_library_builderPX`h` $link_dynamic_library_tool"08J.,@bazel_tools//tools/cpp:link_dynamic_libraryPX`hÙ =$whitelist_disabling_parse_headers_and_layering_check_allowed"08Jƒ€@bazel_tools//tools/build_defs/cc/whitelists/parse_headers_and_layering_check:disabling_parse_headers_and_layering_check_allowedPX`h´ 2$whitelist_loose_header_check_allowed_in_toolchain"08Jjh@bazel_tools//tools/build_defs/cc/whitelists/starlark_hdrs_check:loose_header_check_allowed_in_toolchainPX`h& :action_listener"08PX`h1 :csfdo_profile" fdo_profile08PX`h# :fdo_optimize"08PX`h= :fdo_prefetch_hints"fdo_prefetch_hints08PX`h/ :fdo_profile" fdo_profile08PX`h :libc_top"08PX`h& :target_libc_top"08PX`h1 :xcode_config" xcode_config08PX`h0 :xfdo_profile" fdo_profile08PX`h :zipper"08PX`h all_files"08PX`h) applicable_licenses"08PX`h ar_files"08PX`h as_files"08PX`h2 compatible_with" environment08PX`h compiler08PXh$ compiler_files"08PX`h5 compiler_files_without_includes"08PX`h$ coverage_files"08PX`h cpu08PXh deprecation08PXh distribs08PXh dwp_files"08PX`h) dynamic_runtime_lib"08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh libc_top"08PX`h licenses 08PXh" linker_files"08PX`h module_map"08PX`h name08PXh# objcopy_files"08PX`h output_licenses 08PXh0 restricted_to" environment08PX`h( static_runtime_lib"08PX`h! strip_files"08PX`h' supports_header_parsing08PXh( supports_param_files08JPXh tags08PXh testonly08PXh& toolchain_config"08PX`h$ toolchain_identifier08PXh( transitive_configs"08PX`h visibility"08PX`h ò apple_static_libraryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h` $child_configuration_dummy"08J.,@bazel_tools//tools/cpp:current_cc_toolchainPX`h* $config_dependencies"08PX`hF $dummy_lib"08J$"@bazel_tools//tools/objc:dummy_libPX`he $googlemac_proto_compiler"08J42@bazel_tools//tools/objc:protobuf_compiler_wrapperPX`hm !$googlemac_proto_compiler_support"08J42@bazel_tools//tools/objc:protobuf_compiler_supportPX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hb $j2objc_dead_code_pruner"08J20@bazel_tools//tools/objc:j2objc_dead_code_prunerPX`hB $libtool"08J" @bazel_tools//tools/objc:libtoolPX`hh $protobuf_well_known_types"08J6"42@bazel_tools//tools/objc:protobuf_well_known_typesPX`hL $xcrunwrapper"08J'%@bazel_tools//tools/objc:xcrunwrapperPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h1 :xcode_config" xcode_config08PX`h) applicable_licenses"08PX`h, avoid_deps" cc_library08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh& deps" cc_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh8 feature_flags"config_feature_flag08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh linkopts08PXh" minimum_os_version08PXh name08PXh platform_type08PXh0 restricted_to" environment08PX`h sdk_dylibs08PXh sdk_frameworks08PXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h# weak_sdk_frameworks08PXh Ó available_xcodes* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h, default" xcode_version08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08PXh testonly08PXh( transitive_configs"08PX`h- versions" xcode_version08PX`h visibility"08PX`h î bind* $config_dependencies"08PX`h& :action_listener"08PX`h actual"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ë cc_binaryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`h! $def_parser"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`h" $is_executable08JPXh& :action_listener"08PX`h# :cc_toolchain"08PX`h% :default_malloc"08PX`h. additional_linker_inputs"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h copts08PXh data"08PX`h defines08PXh deprecation08PXhQ deps"9 cc_import cc_librarycc_proto_library objc_library08PX`h distribs08PXh" dynamic_deps"08PX`h* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh includes08PXh licenses 08PXh linkopts08PXh linkshared08PXh linkstatic08JPXh local_defines08PXhJ malloc" cc_library08J @bazel_tools//tools/cpp:mallocPX`h name08PXh nocopts08PXh output_licenses 08PXhZ reexport_deps"9 cc_import cc_librarycc_proto_library objc_library08PX`h0 restricted_to" environment08PX`h srcs"08PX`h stamp08PXh tags08PXh testonly08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h" win_def_file"08PX`h Í cc_host_toolchain_alias* $config_dependencies"08PX`h& :action_listener"08PX`h :alias"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ¸ cc_importR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h alwayslink08PXh) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh hdrs"08PX`h' interface_library"08PX`h licenses 08PXh name08PXh0 restricted_to" environment08PX`h$ shared_library"08PX`h$ static_library"08PX`h system_provided08PXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ç cc_libc_top_alias* $config_dependencies"08PX`h& :action_listener"08PX`h :alias"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ¶ cc_libraryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`h! $def_parser"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h alwayslink08PXh) applicable_licenses"08PX`h2 compatible_with" environment08PX`h copts08PXh data"08PX`h defines08PXh deprecation08PXhQ deps"9 cc_import cc_librarycc_proto_library objc_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh hdrs"08PX`h include_prefix08PXh includes08PXh licenses 08PXh linkopts08PXh linkstamp"08PX`h linkstatic08PXh local_defines08PXh name08PXh nocopts08PXhZ reexport_deps"9 cc_import cc_librarycc_proto_library objc_library08PX`h0 restricted_to" environment08PX`h srcs"08PX`h$ strip_include_prefix08PXh tags08PXh testonly08PXh" textual_hdrs"08PX`h toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h" win_def_file"08PX`h ð cc_proto_library* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh) deps" proto_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ç cc_testR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`hZ $collect_cc_coverage"08J.,@bazel_tools//tools/test:collect_cc_coveragePX`h[ $collect_coverage_script"08J+)@bazel_tools//tools/test:collect_coveragePX`h* $config_dependencies"08PX`h! $def_parser"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hI $test_runtime"08J$"" @bazel_tools//tools/test:runtimePX`hO $test_setup_script"08J%#@bazel_tools//tools/test:test_setupPX`hL $test_wrapper"08J'%@bazel_tools//tools/test:test_wrapperPX`hZ $xml_generator_script"08J-+@bazel_tools//tools/test:test_xml_generatorPX`hH $xml_writer"08J%#@bazel_tools//tools/test:xml_writerPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h0 :coverage_report_generator"08PX`h' :coverage_support"08PX`h% :default_malloc"08PX`h" :lcov_merger"08PX`h :run_under"08PX`h. additional_linker_inputs"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h copts08PXh data"08PX`h defines08PXh deprecation08PXhQ deps"9 cc_import cc_librarycc_proto_library objc_library08PX`h distribs08PXh" dynamic_deps"08PX`h* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh flaky08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh includes08PXh licenses 08PXh linkopts08PXh linkstatic08PXh local08PXh local_defines08PXhJ malloc" cc_library08J @bazel_tools//tools/cpp:mallocPX`h name08PXh nocopts08PXhZ reexport_deps"9 cc_import cc_librarycc_proto_library objc_library08PX`h0 restricted_to" environment08PX`h( shard_count08J ÿÿÿÿÿÿÿÿÿPXh size08JmediumPXh srcs"08PX`h stamp08JPXh tags08PXh testonly08JPXh timeout08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h" win_def_file"08PX`h ã cc_toolchainR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`he $interface_library_builder"08J31@bazel_tools//tools/cpp:interface_library_builderPX`h` $link_dynamic_library_tool"08J.,@bazel_tools//tools/cpp:link_dynamic_libraryPX`hÙ =$whitelist_disabling_parse_headers_and_layering_check_allowed"08Jƒ€@bazel_tools//tools/build_defs/cc/whitelists/parse_headers_and_layering_check:disabling_parse_headers_and_layering_check_allowedPX`h´ 2$whitelist_loose_header_check_allowed_in_toolchain"08Jjh@bazel_tools//tools/build_defs/cc/whitelists/starlark_hdrs_check:loose_header_check_allowed_in_toolchainPX`h& :action_listener"08PX`h1 :csfdo_profile" fdo_profile08PX`h# :fdo_optimize"08PX`h= :fdo_prefetch_hints"fdo_prefetch_hints08PX`h/ :fdo_profile" fdo_profile08PX`h :libc_top"08PX`h& :target_libc_top"08PX`h0 :xfdo_profile" fdo_profile08PX`h :zipper"08PX`h all_files"08PX`h) applicable_licenses"08PX`h ar_files"08PX`h as_files"08PX`h2 compatible_with" environment08PX`h compiler08PXh$ compiler_files"08PX`h5 compiler_files_without_includes"08PX`h$ coverage_files"08PX`h cpu08PXh deprecation08PXh distribs08PXh dwp_files"08PX`h) dynamic_runtime_lib"08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh libc_top"08PX`h licenses 08PXh" linker_files"08PX`h module_map"08PX`h name08PXh# objcopy_files"08PX`h output_licenses 08PXh0 restricted_to" environment08PX`h( static_runtime_lib"08PX`h! strip_files"08PX`h' supports_header_parsing08PXh( supports_param_files08JPXh tags08PXh testonly08PXh& toolchain_config"08PX`h$ toolchain_identifier08PXh( transitive_configs"08PX`h visibility"08PX`h È cc_toolchain_alias* $config_dependencies"08PX`h& :action_listener"08PX`h :alias"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ô cc_toolchain_suiteR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h š config_feature_flag* $config_dependencies"08PX`h~ $whitelist_config_feature_flag"08JHF@bazel_tools//tools/allowlists/config_feature_flag:config_feature_flagPX`h& :action_listener"08PX`h allowed_values08PXh) applicable_licenses"08PX`h default_value08PXh deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08J "manualPXh testonly08PXh visibility"08PX`h ¦ config_setting* $config_dependencies"08PX`h! $tools_repository08PXh& :action_listener"08PX`h) applicable_licenses"08PX`h' constraint_values"08PX`h define_values 08PXh deprecation08PXh distribs08PXh features08PXh! flag_values"08PX`h" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08J "manualPXh testonly08PXh( transitive_configs"08PX`h values 08PXh visibility"08PX`h � constraint_setting* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h. default_constraint_value"08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08J "manualPXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h � constraint_value* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h< constraint_setting"constraint_setting08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08J "manualPXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ª environment* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h deprecation08PXh distribs08PXh features08PXh+ fulfills" environment08PX`h" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08J "manualPXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ´ extra_action* $config_dependencies"08PX`h) applicable_licenses"08PX`h cmd08PXh2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh out_templates08PXh& requires_action_output08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh toolchains"08PX`h tools"08PX`h( transitive_configs"08PX`h visibility"08PX`h ¤ fdo_prefetch_hints* $config_dependencies"08PX`h& :action_listener"08PX`h% absolute_path_profile08PXh) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh profile"08PX`h0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h  fdo_profile* $config_dependencies"08PX`h& :action_listener"08PX`h% absolute_path_profile08PXh) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh profile"08PX`h# proto_profile"08PX`h0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h â filegroup* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh output_group08PXh output_licenses 08PXh path08PXh0 restricted_to" environment08PX`h srcs"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ä genquery* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh deps"08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh expression08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh opts08PXh0 restricted_to" environment08PX`h scope"08PX`h strict08JPXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h à genrule* $config_dependencies"08PX`hT $genrule_setup"08J.,@bazel_tools//tools/genrule:genrule-setup.shPX`h $is_executable08PXh& :action_listener"08PX`h) applicable_licenses"08PX`h cmd08PXh cmd_bash08PXh cmd_bat08PXh cmd_ps08PXh2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh exec_tools"08PX`h executable08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh) heuristic_label_expansion08PXh licenses 08PXh local08PXh message08PXh name08PXh output_licenses 08PXh output_to_bindir08PXh outs"08PX`h0 restricted_to" environment08PX`h srcs"08PX`h stamp08PXh tags08PXh testonly08PXh toolchains"08PX`h tools"08PX`h( transitive_configs"08PX`h visibility"08PX`h À j2objc_libraryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`hB $libtool"08J" @bazel_tools//tools/objc:libtoolPX`hL $xcrunwrapper"08J'%@bazel_tools//tools/objc:xcrunwrapperPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h1 :xcode_config" xcode_config08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXhY deps"Aj2objc_library java_import java_libraryjava_proto_library08PX`h distribs08PXh entry_classes08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh, jre_deps" objc_library08PX`h licenses 08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ý java_binaryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`h $is_executable08PXhS $jacocorunner"08J.,@bazel_tools//tools/jdk:JacocoCoverageRunnerPX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`hH $launcher"08J'%@bazel_tools//tools/launcher:launcherPX`h" $testsupport"08PX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h$ :java_launcher"08PX`h0 :java_plugins" java_plugin08PX`h) applicable_licenses"08PX`h args08PXh) classpath_resources"08PX`h2 compatible_with" environment08PX`h% create_executable08JPXh data"08PX`h- deploy_env" java_binary08PX`h% deploy_manifest_lines08PXh deprecation08PXh© deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh javacopts08PXh jvm_flags08PXh launcher"08PX`h licenses 08PXh main_class08PXh name08PXh output_licenses 08PXh* plugins" java_plugin08PX`h# resource_jars"08PX`h% resource_strip_prefix08PXh resources"08PX`h0 restricted_to" environment08PX`h± runtime_deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h srcs"08PX`h stamp08PXh tags08PXh testonly08PXh toolchains"08PX`h( transitive_configs"08PX`h use_testrunner08PXh visibility"08PX`h © java_import* $config_dependencies"08PX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`h+ $proguard_whitelister"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h constraints08PXh data"08PX`h deprecation08PXhL deps"4 cc_binary cc_library java_import java_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXhO exports"4 cc_binary cc_library java_import java_library08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh jars"08PX`h licenses 08PXh name08PXh neverlink08PXh$ proguard_specs"08PX`h0 restricted_to" environment08PX`hT runtime_deps"4 cc_binary cc_library java_import java_library08PX`h srcjar"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ð java_library* $config_dependencies"08PX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`h+ $proguard_whitelister"08PX`h& :action_listener"08PX`h0 :java_plugins" java_plugin08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh© deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh3 exported_plugins" java_plugin08PX`h¬ exports"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh javacopts08PXh licenses 08PXh name08PXh neverlink08PXh* plugins" java_plugin08PX`h$ proguard_specs"08PX`h# resource_jars"08PX`h% resource_strip_prefix08PXh resources"08PX`h0 restricted_to" environment08PX`h± runtime_deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h srcs"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ƒ java_lite_proto_library* $config_dependencies"08PX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`h& :action_listener"08PX`h: $:aspect_proto_toolchain_for_javalite"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh) deps" proto_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h strict_deps08JPXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Þ java_package_configuration* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh javacopts08PXh licenses 08PXh name08PXh output_licenses 08PXh packages"08PX`h0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ¸ java_plugin* $config_dependencies"08PX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`h+ $proguard_whitelister"08PX`h& :action_listener"08PX`h0 :java_plugins" java_plugin08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh© deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh generates_api08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh javacopts08PXh licenses 08PXh name08PXh neverlink08PXh output_licenses 08PXh* plugins" java_plugin08PX`h processor_class08PXh$ proguard_specs"08PX`h# resource_jars"08PX`h% resource_strip_prefix08PXh resources"08PX`h0 restricted_to" environment08PX`h srcs"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h “ java_proto_library* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh) deps" proto_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h strict_deps08JPXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ì java_runtime* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh java"08PX`h java_home08PXh licenses 08PXh name08PXh output_licenses 08PXh0 restricted_to" environment08PX`h srcs"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h È java_runtime_alias* $config_dependencies"08PX`h& :action_listener"08PX`h :alias"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ò java_testR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h[ $collect_coverage_script"08J+)@bazel_tools//tools/test:collect_coveragePX`h* $config_dependencies"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hT $host_jdk"08J31@bazel_tools//tools/jdk:current_host_java_runtimePX`hS $jacocorunner"08J.,@bazel_tools//tools/jdk:JacocoCoverageRunnerPX`hW $java_toolchain"08J0.@bazel_tools//tools/jdk:current_java_toolchainPX`hJ $jvm"08J.,@bazel_tools//tools/jdk:current_java_runtimePX`hH $launcher"08J'%@bazel_tools//tools/launcher:launcherPX`hI $test_runtime"08J$"" @bazel_tools//tools/test:runtimePX`hO $test_setup_script"08J%#@bazel_tools//tools/test:test_setupPX`hL $test_wrapper"08J'%@bazel_tools//tools/test:test_wrapperPX`h" $testsupport"08PX`hZ $xml_generator_script"08J-+@bazel_tools//tools/test:test_xml_generatorPX`hH $xml_writer"08J%#@bazel_tools//tools/test:xml_writerPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h0 :coverage_report_generator"08PX`h' :coverage_support"08PX`h$ :java_launcher"08PX`h0 :java_plugins" java_plugin08PX`h" :lcov_merger"08PX`h :run_under"08PX`h) applicable_licenses"08PX`h args08PXh) classpath_resources"08PX`h2 compatible_with" environment08PX`h% create_executable08JPXh data"08PX`h% deploy_manifest_lines08PXh deprecation08PXh© deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh flaky08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh javacopts08PXh jvm_flags08PXh launcher"08PX`h licenses 08PXh local08PXh main_class08PXh name08PXh* plugins" java_plugin08PX`h# resource_jars"08PX`h% resource_strip_prefix08PXh resources"08PX`h0 restricted_to" environment08PX`h± runtime_deps"� cc_binary cc_librarygenrule java_import java_libraryjava_lite_proto_libraryjava_proto_library proto_library sh_binary sh_library08PX`h( shard_count08J ÿÿÿÿÿÿÿÿÿPXh size08JmediumPXh srcs"08PX`h stamp08JPXh tags08PXh test_class08PXh testonly08JPXh timeout08PXh toolchains"08PX`h( transitive_configs"08PX`h" use_testrunner08JPXh visibility"08PX`h » java_toolchain* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h# bootclasspath"08PX`h$ compatible_javacopts 08PXh2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh" extclasspath"08PX`h features08PXh3 #forcibly_disable_header_compilation08PXh genclass"08PX`h" generator_function08PXh" generator_location08PXh generator_name08PXh% header_compiler"08PX`h2 "header_compiler_builtin_processors08PXh, header_compiler_direct"08PX`h ijar"08PX`h" jacocorunner"08PX`h! javabuilder"08PX`h$ javabuilder_jvm_opts08PXh javac"08PX`h0 javac_supports_multiplex_workers08PXh* javac_supports_workers08JPXh javacopts08PXh jvm_opts08PXh licenses 08PXh misc08PXh name08PXh oneversion"08PX`h* oneversion_whitelist"08PX`h output_licenses 08PXh+ package_configuration"08PX`h9 )reduced_classpath_incompatible_processors08PXh< &reduced_classpath_incompatible_targets"08PX`h! resourcejar"08PX`h0 restricted_to" environment08PX`h singlejar"08PX`h source_version08PXh tags08PXh target_version08PXh testonly08PXh# timezone_data"08PX`h tools"08PX`h( transitive_configs"08PX`h/ turbine_incompatible_processors08PXh turbine_jvm_opts08PXh visibility"08PX`h xlint08PXh Ê java_toolchain_alias* $config_dependencies"08PX`h& :action_listener"08PX`h :alias"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h í label_flag* $config_dependencies"08PX`h& :action_listener"08PX`h :alias"08PX`h) applicable_licenses"08PX`h+ build_setting_default"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ð label_setting* $config_dependencies"08PX`h& :action_listener"08PX`h :alias"08PX`h) applicable_licenses"08PX`h+ build_setting_default"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ³ local_config_platform" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh repo_mapping 08PXh Ä local_repository" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh path08PXh repo_mapping 08PXh Ð new_local_repository build_file08PXh" build_file_content08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh path08PXh repo_mapping 08PXh workspace_file08PXh& workspace_file_content08PXh Ê ninja_build* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh" deps_mapping"08PX`h distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh. ninja_graph" ninja_graph08PX`h output_groups 08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ÿ ninja_graph* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh main"08PX`h name08PXh ninja_srcs"08PX`h output_root08PXh" output_root_inputs08PXh$ output_root_symlinks08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h! working_directory08PXh » objc_import* $config_dependencies"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`h& :action_listener"08PX`h alwayslink08PXh) applicable_licenses"08PX`h archives"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh hdrs"08PX`h includes08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h sdk_dylibs08PXh sdk_frameworks08PXh sdk_includes08PXh tags08PXh testonly08PXh" textual_hdrs"08PX`h( transitive_configs"08PX`h visibility"08PX`h# weak_sdk_frameworks08PXh › objc_libraryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hB $libtool"08J" @bazel_tools//tools/objc:libtoolPX`hL $xcrunwrapper"08J'%@bazel_tools//tools/objc:xcrunwrapperPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h1 :xcode_config" xcode_config08PX`h alwayslink08PXh) applicable_licenses"08PX`h2 compatible_with" environment08PX`h copts08PXh data"08PX`h defines08PXh deprecation08PXh& deps" cc_library08PX`h distribs08PXh enable_modules08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh hdrs"08PX`h includes08PXh licenses 08PXh module_map"08PX`h module_name08PXh name08PXh" non_arc_srcs"08PX`h pch"08PX`h0 restricted_to" environment08PX`h" runtime_deps"08PX`h sdk_dylibs08PXh sdk_frameworks08PXh sdk_includes08PXh srcs"08PX`h tags08PXh testonly08PXh" textual_hdrs"08PX`h toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h# weak_sdk_frameworks08PXh É platform* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h' constraint_values"08PX`h% cpu_constraints"08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh host_platform08PXh licenses 08PXh name08PXh$ os_constraints"08PX`h parents"08PX`h+ remote_execution_properties08PXh tags08J "manualPXh target_platform08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ² proto_lang_toolchain* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h( blacklisted_protos"08PX`h command_line08PXh2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh plugin"08PX`h0 restricted_to" environment08PX`h runtime"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ¢ proto_library* $config_dependencies"08PX`h& :action_listener"08PX`h% :proto_compiler"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh) deps" proto_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh, exports" proto_library08PX`h features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh import_prefix08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h srcs"08PX`h# strip_import_prefix08PXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ù py_binaryR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h* $config_dependencies"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`h" $is_executable08JPXhH $launcher"08J'%@bazel_tools//tools/launcher:launcherPX`hU $py_toolchain_type"08J+)@bazel_tools//tools/python:toolchain_typePX`hD $python2to3"08J!@bazel_tools//tools/python:2to3PX`h? $zipper"08J @bazel_tools//tools/zip:zipperPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h% :py_interpreter"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh deps"08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh imports08PXh" legacy_create_init08PXh licenses 08PXh main"08PX`h name08PXh output_licenses 08PXh4 python_version08J_INTERNAL_SENTINELPXh0 restricted_to" environment08PX`h srcs"08PX`h' srcs_version08J PY2AND3PXh stamp08PXh tags08PXh testonly08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h ÿ py_library* $config_dependencies"08PX`hD $python2to3"08J!@bazel_tools//tools/python:2to3PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh deps"08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh imports08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h srcs"08PX`h' srcs_version08J PY2AND3PXh tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h � py_runtime* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh files"08PX`h" generator_function08PXh" generator_location08PXh generator_name08PXh! interpreter"08PX`h interpreter_path08PXh licenses 08PXh name08PXh output_licenses 08PXh4 python_version08J_INTERNAL_SENTINELPXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h ¹ py_testR $cc_toolchain_type"08J(&@bazel_tools//tools/cpp:toolchain_typePX`h[ $collect_coverage_script"08J+)@bazel_tools//tools/test:collect_coveragePX`h* $config_dependencies"08PX`hM $grep_includes"08J'%@bazel_tools//tools/cpp:grep-includesPX`hH $launcher"08J'%@bazel_tools//tools/launcher:launcherPX`hU $py_toolchain_type"08J+)@bazel_tools//tools/python:toolchain_typePX`hD $python2to3"08J!@bazel_tools//tools/python:2to3PX`hI $test_runtime"08J$"" @bazel_tools//tools/test:runtimePX`hO $test_setup_script"08J%#@bazel_tools//tools/test:test_setupPX`hL $test_wrapper"08J'%@bazel_tools//tools/test:test_wrapperPX`hZ $xml_generator_script"08J-+@bazel_tools//tools/test:test_xml_generatorPX`hH $xml_writer"08J%#@bazel_tools//tools/test:xml_writerPX`h? $zipper"08J @bazel_tools//tools/zip:zipperPX`h& :action_listener"08PX`h# :cc_toolchain"08PX`h0 :coverage_report_generator"08PX`h' :coverage_support"08PX`h" :lcov_merger"08PX`h% :py_interpreter"08PX`h :run_under"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh deps"08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh flaky08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh imports08PXh" legacy_create_init08PXh licenses 08PXh local08PXh main"08PX`h name08PXh4 python_version08J_INTERNAL_SENTINELPXh0 restricted_to" environment08PX`h( shard_count08J ÿÿÿÿÿÿÿÿÿPXh size08JmediumPXh srcs"08PX`h' srcs_version08J PY2AND3PXh stamp08JPXh tags08PXh testonly08JPXh timeout08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h É sh_binary* $config_dependencies"08PX`h" $is_executable08JPXhH $launcher"08J'%@bazel_tools//tools/launcher:launcherPX`h& :action_listener"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh& deps" sh_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh output_licenses 08PXh0 restricted_to" environment08PX`h srcs"08PX`h tags08PXh testonly08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h ƒ sh_library* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh& deps" sh_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h srcs"08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h � sh_testZ $collect_cc_coverage"08J.,@bazel_tools//tools/test:collect_cc_coveragePX`h[ $collect_coverage_script"08J+)@bazel_tools//tools/test:collect_coveragePX`h* $config_dependencies"08PX`hH $launcher"08J'%@bazel_tools//tools/launcher:launcherPX`hI $test_runtime"08J$"" @bazel_tools//tools/test:runtimePX`hO $test_setup_script"08J%#@bazel_tools//tools/test:test_setupPX`hL $test_wrapper"08J'%@bazel_tools//tools/test:test_wrapperPX`hZ $xml_generator_script"08J-+@bazel_tools//tools/test:test_xml_generatorPX`hH $xml_writer"08J%#@bazel_tools//tools/test:xml_writerPX`h& :action_listener"08PX`h0 :coverage_report_generator"08PX`h' :coverage_support"08PX`h" :lcov_merger"08PX`h :run_under"08PX`h) applicable_licenses"08PX`h args08PXh2 compatible_with" environment08PX`h data"08PX`h deprecation08PXh& deps" sh_library08PX`h distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh flaky08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh local08PXh name08PXh0 restricted_to" environment08PX`h( shard_count08J ÿÿÿÿÿÿÿÿÿPXh size08JmediumPXh srcs"08PX`h tags08PXh testonly08JPXh timeout08PXh toolchains"08PX`h( transitive_configs"08PX`h visibility"08PX`h ž test_suite* $config_dependencies"08PX`h% $implicit_tests"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08JPXh tests"08PX`h( transitive_configs"08PX`h visibility"08PX`h Í toolchain* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h deprecation08PXh distribs08PXh* exec_compatible_with"08PX`h exec_properties 08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08J "manualPXh, target_compatible_with"08PX`h testonly08PXh toolchain"08PX`h4 toolchain_type"toolchain_type08PX`h( transitive_configs"08PX`h visibility"08PX`h ¦ toolchain_type* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h À xcode_config* $config_dependencies"08PX`h& :action_listener"08PX`h) applicable_licenses"08PX`h, default" xcode_version08PX`h deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh6 local_versions"available_xcodes08PX`h name08PXh7 remote_versions"available_xcodes08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h- versions" xcode_version08PX`h visibility"08PX`h Ý xcode_config_alias* $config_dependencies"08PX`h& :action_listener"08PX`h1 :xcode_config" xcode_config08PX`h) applicable_licenses"08PX`h2 compatible_with" environment08PX`h deprecation08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh name08PXh0 restricted_to" environment08PX`h tags08PXh testonly08PXh( transitive_configs"08PX`h visibility"08PX`h Ð xcode_version* $config_dependencies"08PX`h& :action_listener"08PX`h aliases08PXh) applicable_licenses"08PX`h' default_ios_sdk_version08PXh) default_macos_sdk_version08PXh( default_tvos_sdk_version08PXh+ default_watchos_sdk_version08PXh deprecation08PXh distribs08PXh features08PXh" generator_function08PXh" generator_location08PXh generator_name08PXh licenses 08PXh name08PXh tags08PXh testonly08PXh( transitive_configs"08PX`h version08PXh visibility"08PX`hbuildtools-10.1.0/lang/BUILD.bazel0000664000175000017500000000146615254573013016152 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") load("//build:build_defs.bzl", "genfile_check_test") load(":defs.bzl", "generate_tables") # gazelle:exclude tables.gen.go genfile_check_test( src = "tables.gen.go", gen = "tables.go", ) # generated by: bazel info build-language > build-language.pb # Unable to get this info from within a single bazel. generate_tables( name = "generateTablesFile", src = "build-language.pb", out = "tables.go", ) go_library( name = "lang", srcs = [ "tables.go", # keep ], importpath = "github.com/bazel-contrib/buildtools/v10/lang", visibility = ["//visibility:public"], deps = [ "//build_proto", # keep ], ) alias( name = "go_default_library", actual = ":lang", visibility = ["//visibility:public"], ) buildtools-10.1.0/build_proto/0000775000175000017500000000000015254573013015666 5ustar kenhyskenhysbuildtools-10.1.0/build_proto/build.gen.pb.go0000775000175000017500000025737315254573013020510 0ustar kenhyskenhys// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.10 // protoc v7.34.0 // source: build_proto/build.proto package build_proto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type FilesetEntry_SymlinkBehavior int32 const ( FilesetEntry_COPY FilesetEntry_SymlinkBehavior = 1 FilesetEntry_DEREFERENCE FilesetEntry_SymlinkBehavior = 2 ) // Enum value maps for FilesetEntry_SymlinkBehavior. var ( FilesetEntry_SymlinkBehavior_name = map[int32]string{ 1: "COPY", 2: "DEREFERENCE", } FilesetEntry_SymlinkBehavior_value = map[string]int32{ "COPY": 1, "DEREFERENCE": 2, } ) func (x FilesetEntry_SymlinkBehavior) Enum() *FilesetEntry_SymlinkBehavior { p := new(FilesetEntry_SymlinkBehavior) *p = x return p } func (x FilesetEntry_SymlinkBehavior) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (FilesetEntry_SymlinkBehavior) Descriptor() protoreflect.EnumDescriptor { return file_build_proto_build_proto_enumTypes[0].Descriptor() } func (FilesetEntry_SymlinkBehavior) Type() protoreflect.EnumType { return &file_build_proto_build_proto_enumTypes[0] } func (x FilesetEntry_SymlinkBehavior) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Do not use. func (x *FilesetEntry_SymlinkBehavior) UnmarshalJSON(b []byte) error { num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } *x = FilesetEntry_SymlinkBehavior(num) return nil } // Deprecated: Use FilesetEntry_SymlinkBehavior.Descriptor instead. func (FilesetEntry_SymlinkBehavior) EnumDescriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{6, 0} } type Attribute_Discriminator int32 const ( Attribute_INTEGER Attribute_Discriminator = 1 Attribute_STRING Attribute_Discriminator = 2 Attribute_LABEL Attribute_Discriminator = 3 Attribute_OUTPUT Attribute_Discriminator = 4 Attribute_STRING_LIST Attribute_Discriminator = 5 Attribute_LABEL_LIST Attribute_Discriminator = 6 Attribute_OUTPUT_LIST Attribute_Discriminator = 7 Attribute_DISTRIBUTION_SET Attribute_Discriminator = 8 Attribute_LICENSE Attribute_Discriminator = 9 Attribute_STRING_DICT Attribute_Discriminator = 10 Attribute_FILESET_ENTRY_LIST Attribute_Discriminator = 11 Attribute_LABEL_LIST_DICT Attribute_Discriminator = 12 Attribute_STRING_LIST_DICT Attribute_Discriminator = 13 Attribute_BOOLEAN Attribute_Discriminator = 14 Attribute_TRISTATE Attribute_Discriminator = 15 Attribute_INTEGER_LIST Attribute_Discriminator = 16 Attribute_UNKNOWN Attribute_Discriminator = 18 Attribute_LABEL_DICT_UNARY Attribute_Discriminator = 19 Attribute_SELECTOR_LIST Attribute_Discriminator = 20 Attribute_LABEL_KEYED_STRING_DICT Attribute_Discriminator = 21 Attribute_DEPRECATED_STRING_DICT_UNARY Attribute_Discriminator = 17 ) // Enum value maps for Attribute_Discriminator. var ( Attribute_Discriminator_name = map[int32]string{ 1: "INTEGER", 2: "STRING", 3: "LABEL", 4: "OUTPUT", 5: "STRING_LIST", 6: "LABEL_LIST", 7: "OUTPUT_LIST", 8: "DISTRIBUTION_SET", 9: "LICENSE", 10: "STRING_DICT", 11: "FILESET_ENTRY_LIST", 12: "LABEL_LIST_DICT", 13: "STRING_LIST_DICT", 14: "BOOLEAN", 15: "TRISTATE", 16: "INTEGER_LIST", 18: "UNKNOWN", 19: "LABEL_DICT_UNARY", 20: "SELECTOR_LIST", 21: "LABEL_KEYED_STRING_DICT", 17: "DEPRECATED_STRING_DICT_UNARY", } Attribute_Discriminator_value = map[string]int32{ "INTEGER": 1, "STRING": 2, "LABEL": 3, "OUTPUT": 4, "STRING_LIST": 5, "LABEL_LIST": 6, "OUTPUT_LIST": 7, "DISTRIBUTION_SET": 8, "LICENSE": 9, "STRING_DICT": 10, "FILESET_ENTRY_LIST": 11, "LABEL_LIST_DICT": 12, "STRING_LIST_DICT": 13, "BOOLEAN": 14, "TRISTATE": 15, "INTEGER_LIST": 16, "UNKNOWN": 18, "LABEL_DICT_UNARY": 19, "SELECTOR_LIST": 20, "LABEL_KEYED_STRING_DICT": 21, "DEPRECATED_STRING_DICT_UNARY": 17, } ) func (x Attribute_Discriminator) Enum() *Attribute_Discriminator { p := new(Attribute_Discriminator) *p = x return p } func (x Attribute_Discriminator) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Attribute_Discriminator) Descriptor() protoreflect.EnumDescriptor { return file_build_proto_build_proto_enumTypes[1].Descriptor() } func (Attribute_Discriminator) Type() protoreflect.EnumType { return &file_build_proto_build_proto_enumTypes[1] } func (x Attribute_Discriminator) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Do not use. func (x *Attribute_Discriminator) UnmarshalJSON(b []byte) error { num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } *x = Attribute_Discriminator(num) return nil } // Deprecated: Use Attribute_Discriminator.Descriptor instead. func (Attribute_Discriminator) EnumDescriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{7, 0} } type Attribute_Tristate int32 const ( Attribute_NO Attribute_Tristate = 0 Attribute_YES Attribute_Tristate = 1 Attribute_AUTO Attribute_Tristate = 2 ) // Enum value maps for Attribute_Tristate. var ( Attribute_Tristate_name = map[int32]string{ 0: "NO", 1: "YES", 2: "AUTO", } Attribute_Tristate_value = map[string]int32{ "NO": 0, "YES": 1, "AUTO": 2, } ) func (x Attribute_Tristate) Enum() *Attribute_Tristate { p := new(Attribute_Tristate) *p = x return p } func (x Attribute_Tristate) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Attribute_Tristate) Descriptor() protoreflect.EnumDescriptor { return file_build_proto_build_proto_enumTypes[2].Descriptor() } func (Attribute_Tristate) Type() protoreflect.EnumType { return &file_build_proto_build_proto_enumTypes[2] } func (x Attribute_Tristate) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Do not use. func (x *Attribute_Tristate) UnmarshalJSON(b []byte) error { num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } *x = Attribute_Tristate(num) return nil } // Deprecated: Use Attribute_Tristate.Descriptor instead. func (Attribute_Tristate) EnumDescriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{7, 1} } type Target_Discriminator int32 const ( Target_RULE Target_Discriminator = 1 Target_SOURCE_FILE Target_Discriminator = 2 Target_GENERATED_FILE Target_Discriminator = 3 Target_PACKAGE_GROUP Target_Discriminator = 4 Target_ENVIRONMENT_GROUP Target_Discriminator = 5 ) // Enum value maps for Target_Discriminator. var ( Target_Discriminator_name = map[int32]string{ 1: "RULE", 2: "SOURCE_FILE", 3: "GENERATED_FILE", 4: "PACKAGE_GROUP", 5: "ENVIRONMENT_GROUP", } Target_Discriminator_value = map[string]int32{ "RULE": 1, "SOURCE_FILE": 2, "GENERATED_FILE": 3, "PACKAGE_GROUP": 4, "ENVIRONMENT_GROUP": 5, } ) func (x Target_Discriminator) Enum() *Target_Discriminator { p := new(Target_Discriminator) *p = x return p } func (x Target_Discriminator) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Target_Discriminator) Descriptor() protoreflect.EnumDescriptor { return file_build_proto_build_proto_enumTypes[3].Descriptor() } func (Target_Discriminator) Type() protoreflect.EnumType { return &file_build_proto_build_proto_enumTypes[3] } func (x Target_Discriminator) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Do not use. func (x *Target_Discriminator) UnmarshalJSON(b []byte) error { num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } *x = Target_Discriminator(num) return nil } // Deprecated: Use Target_Discriminator.Descriptor instead. func (Target_Discriminator) EnumDescriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{15, 0} } type AllowedRuleClassInfo_AllowedRuleClasses int32 const ( AllowedRuleClassInfo_ANY AllowedRuleClassInfo_AllowedRuleClasses = 1 AllowedRuleClassInfo_SPECIFIED AllowedRuleClassInfo_AllowedRuleClasses = 2 ) // Enum value maps for AllowedRuleClassInfo_AllowedRuleClasses. var ( AllowedRuleClassInfo_AllowedRuleClasses_name = map[int32]string{ 1: "ANY", 2: "SPECIFIED", } AllowedRuleClassInfo_AllowedRuleClasses_value = map[string]int32{ "ANY": 1, "SPECIFIED": 2, } ) func (x AllowedRuleClassInfo_AllowedRuleClasses) Enum() *AllowedRuleClassInfo_AllowedRuleClasses { p := new(AllowedRuleClassInfo_AllowedRuleClasses) *p = x return p } func (x AllowedRuleClassInfo_AllowedRuleClasses) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (AllowedRuleClassInfo_AllowedRuleClasses) Descriptor() protoreflect.EnumDescriptor { return file_build_proto_build_proto_enumTypes[4].Descriptor() } func (AllowedRuleClassInfo_AllowedRuleClasses) Type() protoreflect.EnumType { return &file_build_proto_build_proto_enumTypes[4] } func (x AllowedRuleClassInfo_AllowedRuleClasses) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Do not use. func (x *AllowedRuleClassInfo_AllowedRuleClasses) UnmarshalJSON(b []byte) error { num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } *x = AllowedRuleClassInfo_AllowedRuleClasses(num) return nil } // Deprecated: Use AllowedRuleClassInfo_AllowedRuleClasses.Descriptor instead. func (AllowedRuleClassInfo_AllowedRuleClasses) EnumDescriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{17, 0} } type License struct { state protoimpl.MessageState `protogen:"open.v1"` LicenseType []string `protobuf:"bytes,1,rep,name=license_type,json=licenseType" json:"license_type,omitempty"` Exception []string `protobuf:"bytes,2,rep,name=exception" json:"exception,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *License) Reset() { *x = License{} mi := &file_build_proto_build_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *License) String() string { return protoimpl.X.MessageStringOf(x) } func (*License) ProtoMessage() {} func (x *License) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use License.ProtoReflect.Descriptor instead. func (*License) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{0} } func (x *License) GetLicenseType() []string { if x != nil { return x.LicenseType } return nil } func (x *License) GetException() []string { if x != nil { return x.Exception } return nil } type StringDictEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` Value *string `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StringDictEntry) Reset() { *x = StringDictEntry{} mi := &file_build_proto_build_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StringDictEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*StringDictEntry) ProtoMessage() {} func (x *StringDictEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StringDictEntry.ProtoReflect.Descriptor instead. func (*StringDictEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{1} } func (x *StringDictEntry) GetKey() string { if x != nil && x.Key != nil { return *x.Key } return "" } func (x *StringDictEntry) GetValue() string { if x != nil && x.Value != nil { return *x.Value } return "" } type LabelDictUnaryEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` Value *string `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LabelDictUnaryEntry) Reset() { *x = LabelDictUnaryEntry{} mi := &file_build_proto_build_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LabelDictUnaryEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*LabelDictUnaryEntry) ProtoMessage() {} func (x *LabelDictUnaryEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LabelDictUnaryEntry.ProtoReflect.Descriptor instead. func (*LabelDictUnaryEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{2} } func (x *LabelDictUnaryEntry) GetKey() string { if x != nil && x.Key != nil { return *x.Key } return "" } func (x *LabelDictUnaryEntry) GetValue() string { if x != nil && x.Value != nil { return *x.Value } return "" } type LabelListDictEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` Value []string `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LabelListDictEntry) Reset() { *x = LabelListDictEntry{} mi := &file_build_proto_build_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LabelListDictEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*LabelListDictEntry) ProtoMessage() {} func (x *LabelListDictEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LabelListDictEntry.ProtoReflect.Descriptor instead. func (*LabelListDictEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{3} } func (x *LabelListDictEntry) GetKey() string { if x != nil && x.Key != nil { return *x.Key } return "" } func (x *LabelListDictEntry) GetValue() []string { if x != nil { return x.Value } return nil } type LabelKeyedStringDictEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` Value *string `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LabelKeyedStringDictEntry) Reset() { *x = LabelKeyedStringDictEntry{} mi := &file_build_proto_build_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LabelKeyedStringDictEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*LabelKeyedStringDictEntry) ProtoMessage() {} func (x *LabelKeyedStringDictEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LabelKeyedStringDictEntry.ProtoReflect.Descriptor instead. func (*LabelKeyedStringDictEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{4} } func (x *LabelKeyedStringDictEntry) GetKey() string { if x != nil && x.Key != nil { return *x.Key } return "" } func (x *LabelKeyedStringDictEntry) GetValue() string { if x != nil && x.Value != nil { return *x.Value } return "" } type StringListDictEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` Value []string `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StringListDictEntry) Reset() { *x = StringListDictEntry{} mi := &file_build_proto_build_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StringListDictEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*StringListDictEntry) ProtoMessage() {} func (x *StringListDictEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StringListDictEntry.ProtoReflect.Descriptor instead. func (*StringListDictEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{5} } func (x *StringListDictEntry) GetKey() string { if x != nil && x.Key != nil { return *x.Key } return "" } func (x *StringListDictEntry) GetValue() []string { if x != nil { return x.Value } return nil } type FilesetEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Source *string `protobuf:"bytes,1,req,name=source" json:"source,omitempty"` DestinationDirectory *string `protobuf:"bytes,2,req,name=destination_directory,json=destinationDirectory" json:"destination_directory,omitempty"` FilesPresent *bool `protobuf:"varint,7,opt,name=files_present,json=filesPresent" json:"files_present,omitempty"` File []string `protobuf:"bytes,3,rep,name=file" json:"file,omitempty"` Exclude []string `protobuf:"bytes,4,rep,name=exclude" json:"exclude,omitempty"` SymlinkBehavior *FilesetEntry_SymlinkBehavior `protobuf:"varint,5,opt,name=symlink_behavior,json=symlinkBehavior,enum=blaze_query.FilesetEntry_SymlinkBehavior,def=1" json:"symlink_behavior,omitempty"` StripPrefix *string `protobuf:"bytes,6,opt,name=strip_prefix,json=stripPrefix" json:"strip_prefix,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } // Default values for FilesetEntry fields. const ( Default_FilesetEntry_SymlinkBehavior = FilesetEntry_COPY ) func (x *FilesetEntry) Reset() { *x = FilesetEntry{} mi := &file_build_proto_build_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FilesetEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*FilesetEntry) ProtoMessage() {} func (x *FilesetEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FilesetEntry.ProtoReflect.Descriptor instead. func (*FilesetEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{6} } func (x *FilesetEntry) GetSource() string { if x != nil && x.Source != nil { return *x.Source } return "" } func (x *FilesetEntry) GetDestinationDirectory() string { if x != nil && x.DestinationDirectory != nil { return *x.DestinationDirectory } return "" } func (x *FilesetEntry) GetFilesPresent() bool { if x != nil && x.FilesPresent != nil { return *x.FilesPresent } return false } func (x *FilesetEntry) GetFile() []string { if x != nil { return x.File } return nil } func (x *FilesetEntry) GetExclude() []string { if x != nil { return x.Exclude } return nil } func (x *FilesetEntry) GetSymlinkBehavior() FilesetEntry_SymlinkBehavior { if x != nil && x.SymlinkBehavior != nil { return *x.SymlinkBehavior } return Default_FilesetEntry_SymlinkBehavior } func (x *FilesetEntry) GetStripPrefix() string { if x != nil && x.StripPrefix != nil { return *x.StripPrefix } return "" } type Attribute struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` ExplicitlySpecified *bool `protobuf:"varint,13,opt,name=explicitly_specified,json=explicitlySpecified" json:"explicitly_specified,omitempty"` Nodep *bool `protobuf:"varint,20,opt,name=nodep" json:"nodep,omitempty"` Type *Attribute_Discriminator `protobuf:"varint,2,req,name=type,enum=blaze_query.Attribute_Discriminator" json:"type,omitempty"` IntValue *int32 `protobuf:"varint,3,opt,name=int_value,json=intValue" json:"int_value,omitempty"` StringValue *string `protobuf:"bytes,5,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` BooleanValue *bool `protobuf:"varint,14,opt,name=boolean_value,json=booleanValue" json:"boolean_value,omitempty"` TristateValue *Attribute_Tristate `protobuf:"varint,15,opt,name=tristate_value,json=tristateValue,enum=blaze_query.Attribute_Tristate" json:"tristate_value,omitempty"` StringListValue []string `protobuf:"bytes,6,rep,name=string_list_value,json=stringListValue" json:"string_list_value,omitempty"` License *License `protobuf:"bytes,7,opt,name=license" json:"license,omitempty"` StringDictValue []*StringDictEntry `protobuf:"bytes,8,rep,name=string_dict_value,json=stringDictValue" json:"string_dict_value,omitempty"` FilesetListValue []*FilesetEntry `protobuf:"bytes,9,rep,name=fileset_list_value,json=filesetListValue" json:"fileset_list_value,omitempty"` LabelListDictValue []*LabelListDictEntry `protobuf:"bytes,10,rep,name=label_list_dict_value,json=labelListDictValue" json:"label_list_dict_value,omitempty"` StringListDictValue []*StringListDictEntry `protobuf:"bytes,11,rep,name=string_list_dict_value,json=stringListDictValue" json:"string_list_dict_value,omitempty"` IntListValue []int32 `protobuf:"varint,17,rep,name=int_list_value,json=intListValue" json:"int_list_value,omitempty"` LabelDictUnaryValue []*LabelDictUnaryEntry `protobuf:"bytes,19,rep,name=label_dict_unary_value,json=labelDictUnaryValue" json:"label_dict_unary_value,omitempty"` LabelKeyedStringDictValue []*LabelKeyedStringDictEntry `protobuf:"bytes,22,rep,name=label_keyed_string_dict_value,json=labelKeyedStringDictValue" json:"label_keyed_string_dict_value,omitempty"` SelectorList *Attribute_SelectorList `protobuf:"bytes,21,opt,name=selector_list,json=selectorList" json:"selector_list,omitempty"` DEPRECATEDStringDictUnaryValue [][]byte `protobuf:"bytes,18,rep,name=DEPRECATED_string_dict_unary_value,json=DEPRECATEDStringDictUnaryValue" json:"DEPRECATED_string_dict_unary_value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Attribute) Reset() { *x = Attribute{} mi := &file_build_proto_build_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Attribute) String() string { return protoimpl.X.MessageStringOf(x) } func (*Attribute) ProtoMessage() {} func (x *Attribute) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Attribute.ProtoReflect.Descriptor instead. func (*Attribute) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{7} } func (x *Attribute) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *Attribute) GetExplicitlySpecified() bool { if x != nil && x.ExplicitlySpecified != nil { return *x.ExplicitlySpecified } return false } func (x *Attribute) GetNodep() bool { if x != nil && x.Nodep != nil { return *x.Nodep } return false } func (x *Attribute) GetType() Attribute_Discriminator { if x != nil && x.Type != nil { return *x.Type } return Attribute_INTEGER } func (x *Attribute) GetIntValue() int32 { if x != nil && x.IntValue != nil { return *x.IntValue } return 0 } func (x *Attribute) GetStringValue() string { if x != nil && x.StringValue != nil { return *x.StringValue } return "" } func (x *Attribute) GetBooleanValue() bool { if x != nil && x.BooleanValue != nil { return *x.BooleanValue } return false } func (x *Attribute) GetTristateValue() Attribute_Tristate { if x != nil && x.TristateValue != nil { return *x.TristateValue } return Attribute_NO } func (x *Attribute) GetStringListValue() []string { if x != nil { return x.StringListValue } return nil } func (x *Attribute) GetLicense() *License { if x != nil { return x.License } return nil } func (x *Attribute) GetStringDictValue() []*StringDictEntry { if x != nil { return x.StringDictValue } return nil } func (x *Attribute) GetFilesetListValue() []*FilesetEntry { if x != nil { return x.FilesetListValue } return nil } func (x *Attribute) GetLabelListDictValue() []*LabelListDictEntry { if x != nil { return x.LabelListDictValue } return nil } func (x *Attribute) GetStringListDictValue() []*StringListDictEntry { if x != nil { return x.StringListDictValue } return nil } func (x *Attribute) GetIntListValue() []int32 { if x != nil { return x.IntListValue } return nil } func (x *Attribute) GetLabelDictUnaryValue() []*LabelDictUnaryEntry { if x != nil { return x.LabelDictUnaryValue } return nil } func (x *Attribute) GetLabelKeyedStringDictValue() []*LabelKeyedStringDictEntry { if x != nil { return x.LabelKeyedStringDictValue } return nil } func (x *Attribute) GetSelectorList() *Attribute_SelectorList { if x != nil { return x.SelectorList } return nil } func (x *Attribute) GetDEPRECATEDStringDictUnaryValue() [][]byte { if x != nil { return x.DEPRECATEDStringDictUnaryValue } return nil } type Rule struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` RuleClass *string `protobuf:"bytes,2,req,name=rule_class,json=ruleClass" json:"rule_class,omitempty"` Location *string `protobuf:"bytes,3,opt,name=location" json:"location,omitempty"` Attribute []*Attribute `protobuf:"bytes,4,rep,name=attribute" json:"attribute,omitempty"` RuleInput []string `protobuf:"bytes,5,rep,name=rule_input,json=ruleInput" json:"rule_input,omitempty"` ConfiguredRuleInput []*ConfiguredRuleInput `protobuf:"bytes,15,rep,name=configured_rule_input,json=configuredRuleInput" json:"configured_rule_input,omitempty"` RuleOutput []string `protobuf:"bytes,6,rep,name=rule_output,json=ruleOutput" json:"rule_output,omitempty"` DefaultSetting []string `protobuf:"bytes,7,rep,name=default_setting,json=defaultSetting" json:"default_setting,omitempty"` DEPRECATEDPublicByDefault *bool `protobuf:"varint,9,opt,name=DEPRECATED_public_by_default,json=DEPRECATEDPublicByDefault" json:"DEPRECATED_public_by_default,omitempty"` DEPRECATEDIsSkylark *bool `protobuf:"varint,10,opt,name=DEPRECATED_is_skylark,json=DEPRECATEDIsSkylark" json:"DEPRECATED_is_skylark,omitempty"` SkylarkEnvironmentHashCode *string `protobuf:"bytes,12,opt,name=skylark_environment_hash_code,json=skylarkEnvironmentHashCode" json:"skylark_environment_hash_code,omitempty"` InstantiationStack []string `protobuf:"bytes,13,rep,name=instantiation_stack,json=instantiationStack" json:"instantiation_stack,omitempty"` DefinitionStack []string `protobuf:"bytes,14,rep,name=definition_stack,json=definitionStack" json:"definition_stack,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Rule) Reset() { *x = Rule{} mi := &file_build_proto_build_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Rule) String() string { return protoimpl.X.MessageStringOf(x) } func (*Rule) ProtoMessage() {} func (x *Rule) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Rule.ProtoReflect.Descriptor instead. func (*Rule) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{8} } func (x *Rule) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *Rule) GetRuleClass() string { if x != nil && x.RuleClass != nil { return *x.RuleClass } return "" } func (x *Rule) GetLocation() string { if x != nil && x.Location != nil { return *x.Location } return "" } func (x *Rule) GetAttribute() []*Attribute { if x != nil { return x.Attribute } return nil } func (x *Rule) GetRuleInput() []string { if x != nil { return x.RuleInput } return nil } func (x *Rule) GetConfiguredRuleInput() []*ConfiguredRuleInput { if x != nil { return x.ConfiguredRuleInput } return nil } func (x *Rule) GetRuleOutput() []string { if x != nil { return x.RuleOutput } return nil } func (x *Rule) GetDefaultSetting() []string { if x != nil { return x.DefaultSetting } return nil } func (x *Rule) GetDEPRECATEDPublicByDefault() bool { if x != nil && x.DEPRECATEDPublicByDefault != nil { return *x.DEPRECATEDPublicByDefault } return false } func (x *Rule) GetDEPRECATEDIsSkylark() bool { if x != nil && x.DEPRECATEDIsSkylark != nil { return *x.DEPRECATEDIsSkylark } return false } func (x *Rule) GetSkylarkEnvironmentHashCode() string { if x != nil && x.SkylarkEnvironmentHashCode != nil { return *x.SkylarkEnvironmentHashCode } return "" } func (x *Rule) GetInstantiationStack() []string { if x != nil { return x.InstantiationStack } return nil } func (x *Rule) GetDefinitionStack() []string { if x != nil { return x.DefinitionStack } return nil } type ConfiguredRuleInput struct { state protoimpl.MessageState `protogen:"open.v1"` Label *string `protobuf:"bytes,1,opt,name=label" json:"label,omitempty"` ConfigurationChecksum *string `protobuf:"bytes,2,opt,name=configuration_checksum,json=configurationChecksum" json:"configuration_checksum,omitempty"` ConfigurationId *uint32 `protobuf:"varint,3,opt,name=configuration_id,json=configurationId" json:"configuration_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfiguredRuleInput) Reset() { *x = ConfiguredRuleInput{} mi := &file_build_proto_build_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfiguredRuleInput) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfiguredRuleInput) ProtoMessage() {} func (x *ConfiguredRuleInput) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfiguredRuleInput.ProtoReflect.Descriptor instead. func (*ConfiguredRuleInput) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{9} } func (x *ConfiguredRuleInput) GetLabel() string { if x != nil && x.Label != nil { return *x.Label } return "" } func (x *ConfiguredRuleInput) GetConfigurationChecksum() string { if x != nil && x.ConfigurationChecksum != nil { return *x.ConfigurationChecksum } return "" } func (x *ConfiguredRuleInput) GetConfigurationId() uint32 { if x != nil && x.ConfigurationId != nil { return *x.ConfigurationId } return 0 } type RuleSummary struct { state protoimpl.MessageState `protogen:"open.v1"` Rule *Rule `protobuf:"bytes,1,req,name=rule" json:"rule,omitempty"` Dependency []*Rule `protobuf:"bytes,2,rep,name=dependency" json:"dependency,omitempty"` Location *string `protobuf:"bytes,3,opt,name=location" json:"location,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RuleSummary) Reset() { *x = RuleSummary{} mi := &file_build_proto_build_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RuleSummary) String() string { return protoimpl.X.MessageStringOf(x) } func (*RuleSummary) ProtoMessage() {} func (x *RuleSummary) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RuleSummary.ProtoReflect.Descriptor instead. func (*RuleSummary) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{10} } func (x *RuleSummary) GetRule() *Rule { if x != nil { return x.Rule } return nil } func (x *RuleSummary) GetDependency() []*Rule { if x != nil { return x.Dependency } return nil } func (x *RuleSummary) GetLocation() string { if x != nil && x.Location != nil { return *x.Location } return "" } type PackageGroup struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` ContainedPackage []string `protobuf:"bytes,2,rep,name=contained_package,json=containedPackage" json:"contained_package,omitempty"` IncludedPackageGroup []string `protobuf:"bytes,3,rep,name=included_package_group,json=includedPackageGroup" json:"included_package_group,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PackageGroup) Reset() { *x = PackageGroup{} mi := &file_build_proto_build_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PackageGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*PackageGroup) ProtoMessage() {} func (x *PackageGroup) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PackageGroup.ProtoReflect.Descriptor instead. func (*PackageGroup) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{11} } func (x *PackageGroup) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *PackageGroup) GetContainedPackage() []string { if x != nil { return x.ContainedPackage } return nil } func (x *PackageGroup) GetIncludedPackageGroup() []string { if x != nil { return x.IncludedPackageGroup } return nil } type EnvironmentGroup struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Environment []string `protobuf:"bytes,2,rep,name=environment" json:"environment,omitempty"` Default []string `protobuf:"bytes,3,rep,name=default" json:"default,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EnvironmentGroup) Reset() { *x = EnvironmentGroup{} mi := &file_build_proto_build_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EnvironmentGroup) String() string { return protoimpl.X.MessageStringOf(x) } func (*EnvironmentGroup) ProtoMessage() {} func (x *EnvironmentGroup) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EnvironmentGroup.ProtoReflect.Descriptor instead. func (*EnvironmentGroup) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{12} } func (x *EnvironmentGroup) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *EnvironmentGroup) GetEnvironment() []string { if x != nil { return x.Environment } return nil } func (x *EnvironmentGroup) GetDefault() []string { if x != nil { return x.Default } return nil } type SourceFile struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Location *string `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"` Subinclude []string `protobuf:"bytes,3,rep,name=subinclude" json:"subinclude,omitempty"` PackageGroup []string `protobuf:"bytes,4,rep,name=package_group,json=packageGroup" json:"package_group,omitempty"` VisibilityLabel []string `protobuf:"bytes,5,rep,name=visibility_label,json=visibilityLabel" json:"visibility_label,omitempty"` Feature []string `protobuf:"bytes,6,rep,name=feature" json:"feature,omitempty"` License *License `protobuf:"bytes,8,opt,name=license" json:"license,omitempty"` PackageContainsErrors *bool `protobuf:"varint,9,opt,name=package_contains_errors,json=packageContainsErrors" json:"package_contains_errors,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SourceFile) Reset() { *x = SourceFile{} mi := &file_build_proto_build_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SourceFile) String() string { return protoimpl.X.MessageStringOf(x) } func (*SourceFile) ProtoMessage() {} func (x *SourceFile) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SourceFile.ProtoReflect.Descriptor instead. func (*SourceFile) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{13} } func (x *SourceFile) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *SourceFile) GetLocation() string { if x != nil && x.Location != nil { return *x.Location } return "" } func (x *SourceFile) GetSubinclude() []string { if x != nil { return x.Subinclude } return nil } func (x *SourceFile) GetPackageGroup() []string { if x != nil { return x.PackageGroup } return nil } func (x *SourceFile) GetVisibilityLabel() []string { if x != nil { return x.VisibilityLabel } return nil } func (x *SourceFile) GetFeature() []string { if x != nil { return x.Feature } return nil } func (x *SourceFile) GetLicense() *License { if x != nil { return x.License } return nil } func (x *SourceFile) GetPackageContainsErrors() bool { if x != nil && x.PackageContainsErrors != nil { return *x.PackageContainsErrors } return false } type GeneratedFile struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` GeneratingRule *string `protobuf:"bytes,2,req,name=generating_rule,json=generatingRule" json:"generating_rule,omitempty"` Location *string `protobuf:"bytes,3,opt,name=location" json:"location,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GeneratedFile) Reset() { *x = GeneratedFile{} mi := &file_build_proto_build_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GeneratedFile) String() string { return protoimpl.X.MessageStringOf(x) } func (*GeneratedFile) ProtoMessage() {} func (x *GeneratedFile) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GeneratedFile.ProtoReflect.Descriptor instead. func (*GeneratedFile) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{14} } func (x *GeneratedFile) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *GeneratedFile) GetGeneratingRule() string { if x != nil && x.GeneratingRule != nil { return *x.GeneratingRule } return "" } func (x *GeneratedFile) GetLocation() string { if x != nil && x.Location != nil { return *x.Location } return "" } type Target struct { state protoimpl.MessageState `protogen:"open.v1"` Type *Target_Discriminator `protobuf:"varint,1,req,name=type,enum=blaze_query.Target_Discriminator" json:"type,omitempty"` Rule *Rule `protobuf:"bytes,2,opt,name=rule" json:"rule,omitempty"` SourceFile *SourceFile `protobuf:"bytes,3,opt,name=source_file,json=sourceFile" json:"source_file,omitempty"` GeneratedFile *GeneratedFile `protobuf:"bytes,4,opt,name=generated_file,json=generatedFile" json:"generated_file,omitempty"` PackageGroup *PackageGroup `protobuf:"bytes,5,opt,name=package_group,json=packageGroup" json:"package_group,omitempty"` EnvironmentGroup *EnvironmentGroup `protobuf:"bytes,6,opt,name=environment_group,json=environmentGroup" json:"environment_group,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Target) Reset() { *x = Target{} mi := &file_build_proto_build_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Target) String() string { return protoimpl.X.MessageStringOf(x) } func (*Target) ProtoMessage() {} func (x *Target) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Target.ProtoReflect.Descriptor instead. func (*Target) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{15} } func (x *Target) GetType() Target_Discriminator { if x != nil && x.Type != nil { return *x.Type } return Target_RULE } func (x *Target) GetRule() *Rule { if x != nil { return x.Rule } return nil } func (x *Target) GetSourceFile() *SourceFile { if x != nil { return x.SourceFile } return nil } func (x *Target) GetGeneratedFile() *GeneratedFile { if x != nil { return x.GeneratedFile } return nil } func (x *Target) GetPackageGroup() *PackageGroup { if x != nil { return x.PackageGroup } return nil } func (x *Target) GetEnvironmentGroup() *EnvironmentGroup { if x != nil { return x.EnvironmentGroup } return nil } type QueryResult struct { state protoimpl.MessageState `protogen:"open.v1"` Target []*Target `protobuf:"bytes,1,rep,name=target" json:"target,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *QueryResult) Reset() { *x = QueryResult{} mi := &file_build_proto_build_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *QueryResult) String() string { return protoimpl.X.MessageStringOf(x) } func (*QueryResult) ProtoMessage() {} func (x *QueryResult) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use QueryResult.ProtoReflect.Descriptor instead. func (*QueryResult) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{16} } func (x *QueryResult) GetTarget() []*Target { if x != nil { return x.Target } return nil } type AllowedRuleClassInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Policy *AllowedRuleClassInfo_AllowedRuleClasses `protobuf:"varint,1,req,name=policy,enum=blaze_query.AllowedRuleClassInfo_AllowedRuleClasses" json:"policy,omitempty"` AllowedRuleClass []string `protobuf:"bytes,2,rep,name=allowed_rule_class,json=allowedRuleClass" json:"allowed_rule_class,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AllowedRuleClassInfo) Reset() { *x = AllowedRuleClassInfo{} mi := &file_build_proto_build_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AllowedRuleClassInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*AllowedRuleClassInfo) ProtoMessage() {} func (x *AllowedRuleClassInfo) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AllowedRuleClassInfo.ProtoReflect.Descriptor instead. func (*AllowedRuleClassInfo) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{17} } func (x *AllowedRuleClassInfo) GetPolicy() AllowedRuleClassInfo_AllowedRuleClasses { if x != nil && x.Policy != nil { return *x.Policy } return AllowedRuleClassInfo_ANY } func (x *AllowedRuleClassInfo) GetAllowedRuleClass() []string { if x != nil { return x.AllowedRuleClass } return nil } type AttributeDefinition struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Type *Attribute_Discriminator `protobuf:"varint,2,req,name=type,enum=blaze_query.Attribute_Discriminator" json:"type,omitempty"` Mandatory *bool `protobuf:"varint,3,opt,name=mandatory" json:"mandatory,omitempty"` AllowedRuleClasses *AllowedRuleClassInfo `protobuf:"bytes,4,opt,name=allowed_rule_classes,json=allowedRuleClasses" json:"allowed_rule_classes,omitempty"` Documentation *string `protobuf:"bytes,5,opt,name=documentation" json:"documentation,omitempty"` AllowEmpty *bool `protobuf:"varint,6,opt,name=allow_empty,json=allowEmpty" json:"allow_empty,omitempty"` AllowSingleFile *bool `protobuf:"varint,7,opt,name=allow_single_file,json=allowSingleFile" json:"allow_single_file,omitempty"` Default *AttributeValue `protobuf:"bytes,9,opt,name=default" json:"default,omitempty"` Executable *bool `protobuf:"varint,10,opt,name=executable" json:"executable,omitempty"` Configurable *bool `protobuf:"varint,11,opt,name=configurable" json:"configurable,omitempty"` Nodep *bool `protobuf:"varint,12,opt,name=nodep" json:"nodep,omitempty"` CfgIsHost *bool `protobuf:"varint,13,opt,name=cfg_is_host,json=cfgIsHost" json:"cfg_is_host,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AttributeDefinition) Reset() { *x = AttributeDefinition{} mi := &file_build_proto_build_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AttributeDefinition) String() string { return protoimpl.X.MessageStringOf(x) } func (*AttributeDefinition) ProtoMessage() {} func (x *AttributeDefinition) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AttributeDefinition.ProtoReflect.Descriptor instead. func (*AttributeDefinition) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{18} } func (x *AttributeDefinition) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *AttributeDefinition) GetType() Attribute_Discriminator { if x != nil && x.Type != nil { return *x.Type } return Attribute_INTEGER } func (x *AttributeDefinition) GetMandatory() bool { if x != nil && x.Mandatory != nil { return *x.Mandatory } return false } func (x *AttributeDefinition) GetAllowedRuleClasses() *AllowedRuleClassInfo { if x != nil { return x.AllowedRuleClasses } return nil } func (x *AttributeDefinition) GetDocumentation() string { if x != nil && x.Documentation != nil { return *x.Documentation } return "" } func (x *AttributeDefinition) GetAllowEmpty() bool { if x != nil && x.AllowEmpty != nil { return *x.AllowEmpty } return false } func (x *AttributeDefinition) GetAllowSingleFile() bool { if x != nil && x.AllowSingleFile != nil { return *x.AllowSingleFile } return false } func (x *AttributeDefinition) GetDefault() *AttributeValue { if x != nil { return x.Default } return nil } func (x *AttributeDefinition) GetExecutable() bool { if x != nil && x.Executable != nil { return *x.Executable } return false } func (x *AttributeDefinition) GetConfigurable() bool { if x != nil && x.Configurable != nil { return *x.Configurable } return false } func (x *AttributeDefinition) GetNodep() bool { if x != nil && x.Nodep != nil { return *x.Nodep } return false } func (x *AttributeDefinition) GetCfgIsHost() bool { if x != nil && x.CfgIsHost != nil { return *x.CfgIsHost } return false } type AttributeValue struct { state protoimpl.MessageState `protogen:"open.v1"` Int *int32 `protobuf:"varint,1,opt,name=int" json:"int,omitempty"` String_ *string `protobuf:"bytes,2,opt,name=string" json:"string,omitempty"` Bool *bool `protobuf:"varint,3,opt,name=bool" json:"bool,omitempty"` List []*AttributeValue `protobuf:"bytes,4,rep,name=list" json:"list,omitempty"` Dict []*AttributeValue_DictEntry `protobuf:"bytes,5,rep,name=dict" json:"dict,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AttributeValue) Reset() { *x = AttributeValue{} mi := &file_build_proto_build_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AttributeValue) String() string { return protoimpl.X.MessageStringOf(x) } func (*AttributeValue) ProtoMessage() {} func (x *AttributeValue) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AttributeValue.ProtoReflect.Descriptor instead. func (*AttributeValue) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{19} } func (x *AttributeValue) GetInt() int32 { if x != nil && x.Int != nil { return *x.Int } return 0 } func (x *AttributeValue) GetString_() string { if x != nil && x.String_ != nil { return *x.String_ } return "" } func (x *AttributeValue) GetBool() bool { if x != nil && x.Bool != nil { return *x.Bool } return false } func (x *AttributeValue) GetList() []*AttributeValue { if x != nil { return x.List } return nil } func (x *AttributeValue) GetDict() []*AttributeValue_DictEntry { if x != nil { return x.Dict } return nil } type RuleDefinition struct { state protoimpl.MessageState `protogen:"open.v1"` Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Attribute []*AttributeDefinition `protobuf:"bytes,2,rep,name=attribute" json:"attribute,omitempty"` Documentation *string `protobuf:"bytes,3,opt,name=documentation" json:"documentation,omitempty"` Label *string `protobuf:"bytes,4,opt,name=label" json:"label,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RuleDefinition) Reset() { *x = RuleDefinition{} mi := &file_build_proto_build_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RuleDefinition) String() string { return protoimpl.X.MessageStringOf(x) } func (*RuleDefinition) ProtoMessage() {} func (x *RuleDefinition) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RuleDefinition.ProtoReflect.Descriptor instead. func (*RuleDefinition) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{20} } func (x *RuleDefinition) GetName() string { if x != nil && x.Name != nil { return *x.Name } return "" } func (x *RuleDefinition) GetAttribute() []*AttributeDefinition { if x != nil { return x.Attribute } return nil } func (x *RuleDefinition) GetDocumentation() string { if x != nil && x.Documentation != nil { return *x.Documentation } return "" } func (x *RuleDefinition) GetLabel() string { if x != nil && x.Label != nil { return *x.Label } return "" } type BuildLanguage struct { state protoimpl.MessageState `protogen:"open.v1"` Rule []*RuleDefinition `protobuf:"bytes,1,rep,name=rule" json:"rule,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BuildLanguage) Reset() { *x = BuildLanguage{} mi := &file_build_proto_build_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BuildLanguage) String() string { return protoimpl.X.MessageStringOf(x) } func (*BuildLanguage) ProtoMessage() {} func (x *BuildLanguage) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BuildLanguage.ProtoReflect.Descriptor instead. func (*BuildLanguage) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{21} } func (x *BuildLanguage) GetRule() []*RuleDefinition { if x != nil { return x.Rule } return nil } type Attribute_SelectorEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Label *string `protobuf:"bytes,1,opt,name=label" json:"label,omitempty"` IsDefaultValue *bool `protobuf:"varint,16,opt,name=is_default_value,json=isDefaultValue" json:"is_default_value,omitempty"` IntValue *int32 `protobuf:"varint,2,opt,name=int_value,json=intValue" json:"int_value,omitempty"` StringValue *string `protobuf:"bytes,3,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` BooleanValue *bool `protobuf:"varint,4,opt,name=boolean_value,json=booleanValue" json:"boolean_value,omitempty"` TristateValue *Attribute_Tristate `protobuf:"varint,5,opt,name=tristate_value,json=tristateValue,enum=blaze_query.Attribute_Tristate" json:"tristate_value,omitempty"` StringListValue []string `protobuf:"bytes,6,rep,name=string_list_value,json=stringListValue" json:"string_list_value,omitempty"` License *License `protobuf:"bytes,7,opt,name=license" json:"license,omitempty"` StringDictValue []*StringDictEntry `protobuf:"bytes,8,rep,name=string_dict_value,json=stringDictValue" json:"string_dict_value,omitempty"` FilesetListValue []*FilesetEntry `protobuf:"bytes,9,rep,name=fileset_list_value,json=filesetListValue" json:"fileset_list_value,omitempty"` LabelListDictValue []*LabelListDictEntry `protobuf:"bytes,10,rep,name=label_list_dict_value,json=labelListDictValue" json:"label_list_dict_value,omitempty"` StringListDictValue []*StringListDictEntry `protobuf:"bytes,11,rep,name=string_list_dict_value,json=stringListDictValue" json:"string_list_dict_value,omitempty"` IntListValue []int32 `protobuf:"varint,13,rep,name=int_list_value,json=intListValue" json:"int_list_value,omitempty"` LabelDictUnaryValue []*LabelDictUnaryEntry `protobuf:"bytes,15,rep,name=label_dict_unary_value,json=labelDictUnaryValue" json:"label_dict_unary_value,omitempty"` LabelKeyedStringDictValue []*LabelKeyedStringDictEntry `protobuf:"bytes,17,rep,name=label_keyed_string_dict_value,json=labelKeyedStringDictValue" json:"label_keyed_string_dict_value,omitempty"` DEPRECATEDStringDictUnaryValue [][]byte `protobuf:"bytes,14,rep,name=DEPRECATED_string_dict_unary_value,json=DEPRECATEDStringDictUnaryValue" json:"DEPRECATED_string_dict_unary_value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Attribute_SelectorEntry) Reset() { *x = Attribute_SelectorEntry{} mi := &file_build_proto_build_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Attribute_SelectorEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*Attribute_SelectorEntry) ProtoMessage() {} func (x *Attribute_SelectorEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Attribute_SelectorEntry.ProtoReflect.Descriptor instead. func (*Attribute_SelectorEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{7, 0} } func (x *Attribute_SelectorEntry) GetLabel() string { if x != nil && x.Label != nil { return *x.Label } return "" } func (x *Attribute_SelectorEntry) GetIsDefaultValue() bool { if x != nil && x.IsDefaultValue != nil { return *x.IsDefaultValue } return false } func (x *Attribute_SelectorEntry) GetIntValue() int32 { if x != nil && x.IntValue != nil { return *x.IntValue } return 0 } func (x *Attribute_SelectorEntry) GetStringValue() string { if x != nil && x.StringValue != nil { return *x.StringValue } return "" } func (x *Attribute_SelectorEntry) GetBooleanValue() bool { if x != nil && x.BooleanValue != nil { return *x.BooleanValue } return false } func (x *Attribute_SelectorEntry) GetTristateValue() Attribute_Tristate { if x != nil && x.TristateValue != nil { return *x.TristateValue } return Attribute_NO } func (x *Attribute_SelectorEntry) GetStringListValue() []string { if x != nil { return x.StringListValue } return nil } func (x *Attribute_SelectorEntry) GetLicense() *License { if x != nil { return x.License } return nil } func (x *Attribute_SelectorEntry) GetStringDictValue() []*StringDictEntry { if x != nil { return x.StringDictValue } return nil } func (x *Attribute_SelectorEntry) GetFilesetListValue() []*FilesetEntry { if x != nil { return x.FilesetListValue } return nil } func (x *Attribute_SelectorEntry) GetLabelListDictValue() []*LabelListDictEntry { if x != nil { return x.LabelListDictValue } return nil } func (x *Attribute_SelectorEntry) GetStringListDictValue() []*StringListDictEntry { if x != nil { return x.StringListDictValue } return nil } func (x *Attribute_SelectorEntry) GetIntListValue() []int32 { if x != nil { return x.IntListValue } return nil } func (x *Attribute_SelectorEntry) GetLabelDictUnaryValue() []*LabelDictUnaryEntry { if x != nil { return x.LabelDictUnaryValue } return nil } func (x *Attribute_SelectorEntry) GetLabelKeyedStringDictValue() []*LabelKeyedStringDictEntry { if x != nil { return x.LabelKeyedStringDictValue } return nil } func (x *Attribute_SelectorEntry) GetDEPRECATEDStringDictUnaryValue() [][]byte { if x != nil { return x.DEPRECATEDStringDictUnaryValue } return nil } type Attribute_Selector struct { state protoimpl.MessageState `protogen:"open.v1"` Entries []*Attribute_SelectorEntry `protobuf:"bytes,1,rep,name=entries" json:"entries,omitempty"` HasDefaultValue *bool `protobuf:"varint,2,opt,name=has_default_value,json=hasDefaultValue" json:"has_default_value,omitempty"` NoMatchError *string `protobuf:"bytes,3,opt,name=no_match_error,json=noMatchError" json:"no_match_error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Attribute_Selector) Reset() { *x = Attribute_Selector{} mi := &file_build_proto_build_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Attribute_Selector) String() string { return protoimpl.X.MessageStringOf(x) } func (*Attribute_Selector) ProtoMessage() {} func (x *Attribute_Selector) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Attribute_Selector.ProtoReflect.Descriptor instead. func (*Attribute_Selector) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{7, 1} } func (x *Attribute_Selector) GetEntries() []*Attribute_SelectorEntry { if x != nil { return x.Entries } return nil } func (x *Attribute_Selector) GetHasDefaultValue() bool { if x != nil && x.HasDefaultValue != nil { return *x.HasDefaultValue } return false } func (x *Attribute_Selector) GetNoMatchError() string { if x != nil && x.NoMatchError != nil { return *x.NoMatchError } return "" } type Attribute_SelectorList struct { state protoimpl.MessageState `protogen:"open.v1"` Type *Attribute_Discriminator `protobuf:"varint,1,opt,name=type,enum=blaze_query.Attribute_Discriminator" json:"type,omitempty"` Elements []*Attribute_Selector `protobuf:"bytes,2,rep,name=elements" json:"elements,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Attribute_SelectorList) Reset() { *x = Attribute_SelectorList{} mi := &file_build_proto_build_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Attribute_SelectorList) String() string { return protoimpl.X.MessageStringOf(x) } func (*Attribute_SelectorList) ProtoMessage() {} func (x *Attribute_SelectorList) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Attribute_SelectorList.ProtoReflect.Descriptor instead. func (*Attribute_SelectorList) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{7, 2} } func (x *Attribute_SelectorList) GetType() Attribute_Discriminator { if x != nil && x.Type != nil { return *x.Type } return Attribute_INTEGER } func (x *Attribute_SelectorList) GetElements() []*Attribute_Selector { if x != nil { return x.Elements } return nil } type AttributeValue_DictEntry struct { state protoimpl.MessageState `protogen:"open.v1"` Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` Value *AttributeValue `protobuf:"bytes,2,req,name=value" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AttributeValue_DictEntry) Reset() { *x = AttributeValue_DictEntry{} mi := &file_build_proto_build_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AttributeValue_DictEntry) String() string { return protoimpl.X.MessageStringOf(x) } func (*AttributeValue_DictEntry) ProtoMessage() {} func (x *AttributeValue_DictEntry) ProtoReflect() protoreflect.Message { mi := &file_build_proto_build_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AttributeValue_DictEntry.ProtoReflect.Descriptor instead. func (*AttributeValue_DictEntry) Descriptor() ([]byte, []int) { return file_build_proto_build_proto_rawDescGZIP(), []int{19, 0} } func (x *AttributeValue_DictEntry) GetKey() string { if x != nil && x.Key != nil { return *x.Key } return "" } func (x *AttributeValue_DictEntry) GetValue() *AttributeValue { if x != nil { return x.Value } return nil } var File_build_proto_build_proto protoreflect.FileDescriptor const file_build_proto_build_proto_rawDesc = "" + "\n" + "\x17build_proto/build.proto\x12\vblaze_query\"J\n" + "\aLicense\x12!\n" + "\flicense_type\x18\x01 \x03(\tR\vlicenseType\x12\x1c\n" + "\texception\x18\x02 \x03(\tR\texception\"9\n" + "\x0fStringDictEntry\x12\x10\n" + "\x03key\x18\x01 \x02(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x02(\tR\x05value\"=\n" + "\x13LabelDictUnaryEntry\x12\x10\n" + "\x03key\x18\x01 \x02(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x02(\tR\x05value\"<\n" + "\x12LabelListDictEntry\x12\x10\n" + "\x03key\x18\x01 \x02(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x03(\tR\x05value\"C\n" + "\x19LabelKeyedStringDictEntry\x12\x10\n" + "\x03key\x18\x01 \x02(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x02(\tR\x05value\"=\n" + "\x13StringListDictEntry\x12\x10\n" + "\x03key\x18\x01 \x02(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x03(\tR\x05value\"\xdb\x02\n" + "\fFilesetEntry\x12\x16\n" + "\x06source\x18\x01 \x02(\tR\x06source\x123\n" + "\x15destination_directory\x18\x02 \x02(\tR\x14destinationDirectory\x12#\n" + "\rfiles_present\x18\a \x01(\bR\ffilesPresent\x12\x12\n" + "\x04file\x18\x03 \x03(\tR\x04file\x12\x18\n" + "\aexclude\x18\x04 \x03(\tR\aexclude\x12Z\n" + "\x10symlink_behavior\x18\x05 \x01(\x0e2).blaze_query.FilesetEntry.SymlinkBehavior:\x04COPYR\x0fsymlinkBehavior\x12!\n" + "\fstrip_prefix\x18\x06 \x01(\tR\vstripPrefix\",\n" + "\x0fSymlinkBehavior\x12\b\n" + "\x04COPY\x10\x01\x12\x0f\n" + "\vDEREFERENCE\x10\x02\"\x9f\x16\n" + "\tAttribute\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x121\n" + "\x14explicitly_specified\x18\r \x01(\bR\x13explicitlySpecified\x12\x14\n" + "\x05nodep\x18\x14 \x01(\bR\x05nodep\x128\n" + "\x04type\x18\x02 \x02(\x0e2$.blaze_query.Attribute.DiscriminatorR\x04type\x12\x1b\n" + "\tint_value\x18\x03 \x01(\x05R\bintValue\x12!\n" + "\fstring_value\x18\x05 \x01(\tR\vstringValue\x12#\n" + "\rboolean_value\x18\x0e \x01(\bR\fbooleanValue\x12F\n" + "\x0etristate_value\x18\x0f \x01(\x0e2\x1f.blaze_query.Attribute.TristateR\rtristateValue\x12*\n" + "\x11string_list_value\x18\x06 \x03(\tR\x0fstringListValue\x12.\n" + "\alicense\x18\a \x01(\v2\x14.blaze_query.LicenseR\alicense\x12H\n" + "\x11string_dict_value\x18\b \x03(\v2\x1c.blaze_query.StringDictEntryR\x0fstringDictValue\x12G\n" + "\x12fileset_list_value\x18\t \x03(\v2\x19.blaze_query.FilesetEntryR\x10filesetListValue\x12R\n" + "\x15label_list_dict_value\x18\n" + " \x03(\v2\x1f.blaze_query.LabelListDictEntryR\x12labelListDictValue\x12U\n" + "\x16string_list_dict_value\x18\v \x03(\v2 .blaze_query.StringListDictEntryR\x13stringListDictValue\x12$\n" + "\x0eint_list_value\x18\x11 \x03(\x05R\fintListValue\x12U\n" + "\x16label_dict_unary_value\x18\x13 \x03(\v2 .blaze_query.LabelDictUnaryEntryR\x13labelDictUnaryValue\x12h\n" + "\x1dlabel_keyed_string_dict_value\x18\x16 \x03(\v2&.blaze_query.LabelKeyedStringDictEntryR\x19labelKeyedStringDictValue\x12H\n" + "\rselector_list\x18\x15 \x01(\v2#.blaze_query.Attribute.SelectorListR\fselectorList\x12J\n" + "\"DEPRECATED_string_dict_unary_value\x18\x12 \x03(\fR\x1eDEPRECATEDStringDictUnaryValue\x1a\xcf\a\n" + "\rSelectorEntry\x12\x14\n" + "\x05label\x18\x01 \x01(\tR\x05label\x12(\n" + "\x10is_default_value\x18\x10 \x01(\bR\x0eisDefaultValue\x12\x1b\n" + "\tint_value\x18\x02 \x01(\x05R\bintValue\x12!\n" + "\fstring_value\x18\x03 \x01(\tR\vstringValue\x12#\n" + "\rboolean_value\x18\x04 \x01(\bR\fbooleanValue\x12F\n" + "\x0etristate_value\x18\x05 \x01(\x0e2\x1f.blaze_query.Attribute.TristateR\rtristateValue\x12*\n" + "\x11string_list_value\x18\x06 \x03(\tR\x0fstringListValue\x12.\n" + "\alicense\x18\a \x01(\v2\x14.blaze_query.LicenseR\alicense\x12H\n" + "\x11string_dict_value\x18\b \x03(\v2\x1c.blaze_query.StringDictEntryR\x0fstringDictValue\x12G\n" + "\x12fileset_list_value\x18\t \x03(\v2\x19.blaze_query.FilesetEntryR\x10filesetListValue\x12R\n" + "\x15label_list_dict_value\x18\n" + " \x03(\v2\x1f.blaze_query.LabelListDictEntryR\x12labelListDictValue\x12U\n" + "\x16string_list_dict_value\x18\v \x03(\v2 .blaze_query.StringListDictEntryR\x13stringListDictValue\x12$\n" + "\x0eint_list_value\x18\r \x03(\x05R\fintListValue\x12U\n" + "\x16label_dict_unary_value\x18\x0f \x03(\v2 .blaze_query.LabelDictUnaryEntryR\x13labelDictUnaryValue\x12h\n" + "\x1dlabel_keyed_string_dict_value\x18\x11 \x03(\v2&.blaze_query.LabelKeyedStringDictEntryR\x19labelKeyedStringDictValue\x12J\n" + "\"DEPRECATED_string_dict_unary_value\x18\x0e \x03(\fR\x1eDEPRECATEDStringDictUnaryValueJ\x04\b\f\x10\r\x1a\x9c\x01\n" + "\bSelector\x12>\n" + "\aentries\x18\x01 \x03(\v2$.blaze_query.Attribute.SelectorEntryR\aentries\x12*\n" + "\x11has_default_value\x18\x02 \x01(\bR\x0fhasDefaultValue\x12$\n" + "\x0eno_match_error\x18\x03 \x01(\tR\fnoMatchError\x1a\x85\x01\n" + "\fSelectorList\x128\n" + "\x04type\x18\x01 \x01(\x0e2$.blaze_query.Attribute.DiscriminatorR\x04type\x12;\n" + "\belements\x18\x02 \x03(\v2\x1f.blaze_query.Attribute.SelectorR\belements\"\x8a\x03\n" + "\rDiscriminator\x12\v\n" + "\aINTEGER\x10\x01\x12\n" + "\n" + "\x06STRING\x10\x02\x12\t\n" + "\x05LABEL\x10\x03\x12\n" + "\n" + "\x06OUTPUT\x10\x04\x12\x0f\n" + "\vSTRING_LIST\x10\x05\x12\x0e\n" + "\n" + "LABEL_LIST\x10\x06\x12\x0f\n" + "\vOUTPUT_LIST\x10\a\x12\x14\n" + "\x10DISTRIBUTION_SET\x10\b\x12\v\n" + "\aLICENSE\x10\t\x12\x0f\n" + "\vSTRING_DICT\x10\n" + "\x12\x16\n" + "\x12FILESET_ENTRY_LIST\x10\v\x12\x13\n" + "\x0fLABEL_LIST_DICT\x10\f\x12\x14\n" + "\x10STRING_LIST_DICT\x10\r\x12\v\n" + "\aBOOLEAN\x10\x0e\x12\f\n" + "\bTRISTATE\x10\x0f\x12\x10\n" + "\fINTEGER_LIST\x10\x10\x12\v\n" + "\aUNKNOWN\x10\x12\x12\x14\n" + "\x10LABEL_DICT_UNARY\x10\x13\x12\x11\n" + "\rSELECTOR_LIST\x10\x14\x12\x1b\n" + "\x17LABEL_KEYED_STRING_DICT\x10\x15\x12 \n" + "\x1cDEPRECATED_STRING_DICT_UNARY\x10\x11\"%\n" + "\bTristate\x12\x06\n" + "\x02NO\x10\x00\x12\a\n" + "\x03YES\x10\x01\x12\b\n" + "\x04AUTO\x10\x02J\x04\b\f\x10\rJ\x04\b\x10\x10\x11\"\xea\x04\n" + "\x04Rule\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x12\x1d\n" + "\n" + "rule_class\x18\x02 \x02(\tR\truleClass\x12\x1a\n" + "\blocation\x18\x03 \x01(\tR\blocation\x124\n" + "\tattribute\x18\x04 \x03(\v2\x16.blaze_query.AttributeR\tattribute\x12\x1d\n" + "\n" + "rule_input\x18\x05 \x03(\tR\truleInput\x12T\n" + "\x15configured_rule_input\x18\x0f \x03(\v2 .blaze_query.ConfiguredRuleInputR\x13configuredRuleInput\x12\x1f\n" + "\vrule_output\x18\x06 \x03(\tR\n" + "ruleOutput\x12'\n" + "\x0fdefault_setting\x18\a \x03(\tR\x0edefaultSetting\x12?\n" + "\x1cDEPRECATED_public_by_default\x18\t \x01(\bR\x19DEPRECATEDPublicByDefault\x122\n" + "\x15DEPRECATED_is_skylark\x18\n" + " \x01(\bR\x13DEPRECATEDIsSkylark\x12A\n" + "\x1dskylark_environment_hash_code\x18\f \x01(\tR\x1askylarkEnvironmentHashCode\x12/\n" + "\x13instantiation_stack\x18\r \x03(\tR\x12instantiationStack\x12)\n" + "\x10definition_stack\x18\x0e \x03(\tR\x0fdefinitionStackJ\x04\b\b\x10\tJ\x04\b\v\x10\f\"\x8d\x01\n" + "\x13ConfiguredRuleInput\x12\x14\n" + "\x05label\x18\x01 \x01(\tR\x05label\x125\n" + "\x16configuration_checksum\x18\x02 \x01(\tR\x15configurationChecksum\x12)\n" + "\x10configuration_id\x18\x03 \x01(\rR\x0fconfigurationId\"\x83\x01\n" + "\vRuleSummary\x12%\n" + "\x04rule\x18\x01 \x02(\v2\x11.blaze_query.RuleR\x04rule\x121\n" + "\n" + "dependency\x18\x02 \x03(\v2\x11.blaze_query.RuleR\n" + "dependency\x12\x1a\n" + "\blocation\x18\x03 \x01(\tR\blocation\"\x8b\x01\n" + "\fPackageGroup\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x12+\n" + "\x11contained_package\x18\x02 \x03(\tR\x10containedPackage\x124\n" + "\x16included_package_group\x18\x03 \x03(\tR\x14includedPackageGroupJ\x04\b\x04\x10\x05\"b\n" + "\x10EnvironmentGroup\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x12 \n" + "\venvironment\x18\x02 \x03(\tR\venvironment\x12\x18\n" + "\adefault\x18\x03 \x03(\tR\adefault\"\xb4\x02\n" + "\n" + "SourceFile\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x12\x1a\n" + "\blocation\x18\x02 \x01(\tR\blocation\x12\x1e\n" + "\n" + "subinclude\x18\x03 \x03(\tR\n" + "subinclude\x12#\n" + "\rpackage_group\x18\x04 \x03(\tR\fpackageGroup\x12)\n" + "\x10visibility_label\x18\x05 \x03(\tR\x0fvisibilityLabel\x12\x18\n" + "\afeature\x18\x06 \x03(\tR\afeature\x12.\n" + "\alicense\x18\b \x01(\v2\x14.blaze_query.LicenseR\alicense\x126\n" + "\x17package_contains_errors\x18\t \x01(\bR\x15packageContainsErrorsJ\x04\b\a\x10\b\"h\n" + "\rGeneratedFile\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x12'\n" + "\x0fgenerating_rule\x18\x02 \x02(\tR\x0egeneratingRule\x12\x1a\n" + "\blocation\x18\x03 \x01(\tR\blocation\"\xd9\x03\n" + "\x06Target\x125\n" + "\x04type\x18\x01 \x02(\x0e2!.blaze_query.Target.DiscriminatorR\x04type\x12%\n" + "\x04rule\x18\x02 \x01(\v2\x11.blaze_query.RuleR\x04rule\x128\n" + "\vsource_file\x18\x03 \x01(\v2\x17.blaze_query.SourceFileR\n" + "sourceFile\x12A\n" + "\x0egenerated_file\x18\x04 \x01(\v2\x1a.blaze_query.GeneratedFileR\rgeneratedFile\x12>\n" + "\rpackage_group\x18\x05 \x01(\v2\x19.blaze_query.PackageGroupR\fpackageGroup\x12J\n" + "\x11environment_group\x18\x06 \x01(\v2\x1d.blaze_query.EnvironmentGroupR\x10environmentGroup\"h\n" + "\rDiscriminator\x12\b\n" + "\x04RULE\x10\x01\x12\x0f\n" + "\vSOURCE_FILE\x10\x02\x12\x12\n" + "\x0eGENERATED_FILE\x10\x03\x12\x11\n" + "\rPACKAGE_GROUP\x10\x04\x12\x15\n" + "\x11ENVIRONMENT_GROUP\x10\x05\":\n" + "\vQueryResult\x12+\n" + "\x06target\x18\x01 \x03(\v2\x13.blaze_query.TargetR\x06target\"\xc0\x01\n" + "\x14AllowedRuleClassInfo\x12L\n" + "\x06policy\x18\x01 \x02(\x0e24.blaze_query.AllowedRuleClassInfo.AllowedRuleClassesR\x06policy\x12,\n" + "\x12allowed_rule_class\x18\x02 \x03(\tR\x10allowedRuleClass\",\n" + "\x12AllowedRuleClasses\x12\a\n" + "\x03ANY\x10\x01\x12\r\n" + "\tSPECIFIED\x10\x02\"\xfa\x03\n" + "\x13AttributeDefinition\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x128\n" + "\x04type\x18\x02 \x02(\x0e2$.blaze_query.Attribute.DiscriminatorR\x04type\x12\x1c\n" + "\tmandatory\x18\x03 \x01(\bR\tmandatory\x12S\n" + "\x14allowed_rule_classes\x18\x04 \x01(\v2!.blaze_query.AllowedRuleClassInfoR\x12allowedRuleClasses\x12$\n" + "\rdocumentation\x18\x05 \x01(\tR\rdocumentation\x12\x1f\n" + "\vallow_empty\x18\x06 \x01(\bR\n" + "allowEmpty\x12*\n" + "\x11allow_single_file\x18\a \x01(\bR\x0fallowSingleFile\x125\n" + "\adefault\x18\t \x01(\v2\x1b.blaze_query.AttributeValueR\adefault\x12\x1e\n" + "\n" + "executable\x18\n" + " \x01(\bR\n" + "executable\x12\"\n" + "\fconfigurable\x18\v \x01(\bR\fconfigurable\x12\x14\n" + "\x05nodep\x18\f \x01(\bR\x05nodep\x12\x1e\n" + "\vcfg_is_host\x18\r \x01(\bR\tcfgIsHost\"\x8c\x02\n" + "\x0eAttributeValue\x12\x10\n" + "\x03int\x18\x01 \x01(\x05R\x03int\x12\x16\n" + "\x06string\x18\x02 \x01(\tR\x06string\x12\x12\n" + "\x04bool\x18\x03 \x01(\bR\x04bool\x12/\n" + "\x04list\x18\x04 \x03(\v2\x1b.blaze_query.AttributeValueR\x04list\x129\n" + "\x04dict\x18\x05 \x03(\v2%.blaze_query.AttributeValue.DictEntryR\x04dict\x1aP\n" + "\tDictEntry\x12\x10\n" + "\x03key\x18\x01 \x02(\tR\x03key\x121\n" + "\x05value\x18\x02 \x02(\v2\x1b.blaze_query.AttributeValueR\x05value\"\xa0\x01\n" + "\x0eRuleDefinition\x12\x12\n" + "\x04name\x18\x01 \x02(\tR\x04name\x12>\n" + "\tattribute\x18\x02 \x03(\v2 .blaze_query.AttributeDefinitionR\tattribute\x12$\n" + "\rdocumentation\x18\x03 \x01(\tR\rdocumentation\x12\x14\n" + "\x05label\x18\x04 \x01(\tR\x05label\"@\n" + "\rBuildLanguage\x12/\n" + "\x04rule\x18\x01 \x03(\v2\x1b.blaze_query.RuleDefinitionR\x04ruleB6\n" + "4com.google.devtools.build.lib.query2.proto.proto2api" var ( file_build_proto_build_proto_rawDescOnce sync.Once file_build_proto_build_proto_rawDescData []byte ) func file_build_proto_build_proto_rawDescGZIP() []byte { file_build_proto_build_proto_rawDescOnce.Do(func() { file_build_proto_build_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_build_proto_build_proto_rawDesc), len(file_build_proto_build_proto_rawDesc))) }) return file_build_proto_build_proto_rawDescData } var file_build_proto_build_proto_enumTypes = make([]protoimpl.EnumInfo, 5) var file_build_proto_build_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_build_proto_build_proto_goTypes = []any{ (FilesetEntry_SymlinkBehavior)(0), // 0: blaze_query.FilesetEntry.SymlinkBehavior (Attribute_Discriminator)(0), // 1: blaze_query.Attribute.Discriminator (Attribute_Tristate)(0), // 2: blaze_query.Attribute.Tristate (Target_Discriminator)(0), // 3: blaze_query.Target.Discriminator (AllowedRuleClassInfo_AllowedRuleClasses)(0), // 4: blaze_query.AllowedRuleClassInfo.AllowedRuleClasses (*License)(nil), // 5: blaze_query.License (*StringDictEntry)(nil), // 6: blaze_query.StringDictEntry (*LabelDictUnaryEntry)(nil), // 7: blaze_query.LabelDictUnaryEntry (*LabelListDictEntry)(nil), // 8: blaze_query.LabelListDictEntry (*LabelKeyedStringDictEntry)(nil), // 9: blaze_query.LabelKeyedStringDictEntry (*StringListDictEntry)(nil), // 10: blaze_query.StringListDictEntry (*FilesetEntry)(nil), // 11: blaze_query.FilesetEntry (*Attribute)(nil), // 12: blaze_query.Attribute (*Rule)(nil), // 13: blaze_query.Rule (*ConfiguredRuleInput)(nil), // 14: blaze_query.ConfiguredRuleInput (*RuleSummary)(nil), // 15: blaze_query.RuleSummary (*PackageGroup)(nil), // 16: blaze_query.PackageGroup (*EnvironmentGroup)(nil), // 17: blaze_query.EnvironmentGroup (*SourceFile)(nil), // 18: blaze_query.SourceFile (*GeneratedFile)(nil), // 19: blaze_query.GeneratedFile (*Target)(nil), // 20: blaze_query.Target (*QueryResult)(nil), // 21: blaze_query.QueryResult (*AllowedRuleClassInfo)(nil), // 22: blaze_query.AllowedRuleClassInfo (*AttributeDefinition)(nil), // 23: blaze_query.AttributeDefinition (*AttributeValue)(nil), // 24: blaze_query.AttributeValue (*RuleDefinition)(nil), // 25: blaze_query.RuleDefinition (*BuildLanguage)(nil), // 26: blaze_query.BuildLanguage (*Attribute_SelectorEntry)(nil), // 27: blaze_query.Attribute.SelectorEntry (*Attribute_Selector)(nil), // 28: blaze_query.Attribute.Selector (*Attribute_SelectorList)(nil), // 29: blaze_query.Attribute.SelectorList (*AttributeValue_DictEntry)(nil), // 30: blaze_query.AttributeValue.DictEntry } var file_build_proto_build_proto_depIdxs = []int32{ 0, // 0: blaze_query.FilesetEntry.symlink_behavior:type_name -> blaze_query.FilesetEntry.SymlinkBehavior 1, // 1: blaze_query.Attribute.type:type_name -> blaze_query.Attribute.Discriminator 2, // 2: blaze_query.Attribute.tristate_value:type_name -> blaze_query.Attribute.Tristate 5, // 3: blaze_query.Attribute.license:type_name -> blaze_query.License 6, // 4: blaze_query.Attribute.string_dict_value:type_name -> blaze_query.StringDictEntry 11, // 5: blaze_query.Attribute.fileset_list_value:type_name -> blaze_query.FilesetEntry 8, // 6: blaze_query.Attribute.label_list_dict_value:type_name -> blaze_query.LabelListDictEntry 10, // 7: blaze_query.Attribute.string_list_dict_value:type_name -> blaze_query.StringListDictEntry 7, // 8: blaze_query.Attribute.label_dict_unary_value:type_name -> blaze_query.LabelDictUnaryEntry 9, // 9: blaze_query.Attribute.label_keyed_string_dict_value:type_name -> blaze_query.LabelKeyedStringDictEntry 29, // 10: blaze_query.Attribute.selector_list:type_name -> blaze_query.Attribute.SelectorList 12, // 11: blaze_query.Rule.attribute:type_name -> blaze_query.Attribute 14, // 12: blaze_query.Rule.configured_rule_input:type_name -> blaze_query.ConfiguredRuleInput 13, // 13: blaze_query.RuleSummary.rule:type_name -> blaze_query.Rule 13, // 14: blaze_query.RuleSummary.dependency:type_name -> blaze_query.Rule 5, // 15: blaze_query.SourceFile.license:type_name -> blaze_query.License 3, // 16: blaze_query.Target.type:type_name -> blaze_query.Target.Discriminator 13, // 17: blaze_query.Target.rule:type_name -> blaze_query.Rule 18, // 18: blaze_query.Target.source_file:type_name -> blaze_query.SourceFile 19, // 19: blaze_query.Target.generated_file:type_name -> blaze_query.GeneratedFile 16, // 20: blaze_query.Target.package_group:type_name -> blaze_query.PackageGroup 17, // 21: blaze_query.Target.environment_group:type_name -> blaze_query.EnvironmentGroup 20, // 22: blaze_query.QueryResult.target:type_name -> blaze_query.Target 4, // 23: blaze_query.AllowedRuleClassInfo.policy:type_name -> blaze_query.AllowedRuleClassInfo.AllowedRuleClasses 1, // 24: blaze_query.AttributeDefinition.type:type_name -> blaze_query.Attribute.Discriminator 22, // 25: blaze_query.AttributeDefinition.allowed_rule_classes:type_name -> blaze_query.AllowedRuleClassInfo 24, // 26: blaze_query.AttributeDefinition.default:type_name -> blaze_query.AttributeValue 24, // 27: blaze_query.AttributeValue.list:type_name -> blaze_query.AttributeValue 30, // 28: blaze_query.AttributeValue.dict:type_name -> blaze_query.AttributeValue.DictEntry 23, // 29: blaze_query.RuleDefinition.attribute:type_name -> blaze_query.AttributeDefinition 25, // 30: blaze_query.BuildLanguage.rule:type_name -> blaze_query.RuleDefinition 2, // 31: blaze_query.Attribute.SelectorEntry.tristate_value:type_name -> blaze_query.Attribute.Tristate 5, // 32: blaze_query.Attribute.SelectorEntry.license:type_name -> blaze_query.License 6, // 33: blaze_query.Attribute.SelectorEntry.string_dict_value:type_name -> blaze_query.StringDictEntry 11, // 34: blaze_query.Attribute.SelectorEntry.fileset_list_value:type_name -> blaze_query.FilesetEntry 8, // 35: blaze_query.Attribute.SelectorEntry.label_list_dict_value:type_name -> blaze_query.LabelListDictEntry 10, // 36: blaze_query.Attribute.SelectorEntry.string_list_dict_value:type_name -> blaze_query.StringListDictEntry 7, // 37: blaze_query.Attribute.SelectorEntry.label_dict_unary_value:type_name -> blaze_query.LabelDictUnaryEntry 9, // 38: blaze_query.Attribute.SelectorEntry.label_keyed_string_dict_value:type_name -> blaze_query.LabelKeyedStringDictEntry 27, // 39: blaze_query.Attribute.Selector.entries:type_name -> blaze_query.Attribute.SelectorEntry 1, // 40: blaze_query.Attribute.SelectorList.type:type_name -> blaze_query.Attribute.Discriminator 28, // 41: blaze_query.Attribute.SelectorList.elements:type_name -> blaze_query.Attribute.Selector 24, // 42: blaze_query.AttributeValue.DictEntry.value:type_name -> blaze_query.AttributeValue 43, // [43:43] is the sub-list for method output_type 43, // [43:43] is the sub-list for method input_type 43, // [43:43] is the sub-list for extension type_name 43, // [43:43] is the sub-list for extension extendee 0, // [0:43] is the sub-list for field type_name } func init() { file_build_proto_build_proto_init() } func file_build_proto_build_proto_init() { if File_build_proto_build_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_build_proto_build_proto_rawDesc), len(file_build_proto_build_proto_rawDesc)), NumEnums: 5, NumMessages: 26, NumExtensions: 0, NumServices: 0, }, GoTypes: file_build_proto_build_proto_goTypes, DependencyIndexes: file_build_proto_build_proto_depIdxs, EnumInfos: file_build_proto_build_proto_enumTypes, MessageInfos: file_build_proto_build_proto_msgTypes, }.Build() File_build_proto_build_proto = out.File file_build_proto_build_proto_goTypes = nil file_build_proto_build_proto_depIdxs = nil } buildtools-10.1.0/build_proto/build.proto0000664000175000017500000004647615254573013020073 0ustar kenhyskenhys// Copyright 2014 The Bazel Authors. 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. // // This file contains the protocol buffer representation of a build // file or 'blaze query --output=proto' call. syntax = "proto2"; package blaze_query; // option cc_api_version = 2; // option java_api_version = 1; option java_package = "com.google.devtools.build.lib.query2.proto.proto2api"; message License { repeated string license_type = 1; repeated string exception = 2; } message StringDictEntry { required string key = 1; required string value = 2; } message LabelDictUnaryEntry { required string key = 1; required string value = 2; } message LabelListDictEntry { required string key = 1; repeated string value = 2; } message LabelKeyedStringDictEntry { required string key = 1; required string value = 2; } message StringListDictEntry { required string key = 1; repeated string value = 2; } // Represents an entry attribute of a Fileset rule in a build file. message FilesetEntry { // Indicates what to do when a source file is actually a symlink. enum SymlinkBehavior { COPY = 1; DEREFERENCE = 2; } // The label pointing to the source target where files are copied from. required string source = 1; // The relative path within the fileset rule where files will be mapped. required string destination_directory = 2; // Whether the files= attribute was specified. This is necessary because // no files= attribute and files=[] mean different things. optional bool files_present = 7; // A list of file labels to include from the source directory. repeated string file = 3; // If this is a fileset entry representing files within the rule // package, this lists relative paths to files that should be excluded from // the set. This cannot contain values if 'file' also has values. repeated string exclude = 4; // This field is optional because there will be some time when the new // PB is used by tools depending on blaze query, but the new blaze version // is not yet released. // TODO(bazel-team): Make this field required once a version of Blaze is // released that outputs this field. optional SymlinkBehavior symlink_behavior = 5 [default = COPY]; // The prefix to strip from the path of the files in this FilesetEntry. Note // that no value and the empty string as the value mean different things here. optional string strip_prefix = 6; } // A rule attribute. Each attribute must have a type and one of the various // value fields populated - for the most part. // // Attributes of BOOLEAN and TRISTATE type may set all of the int, bool, and // string values for backwards compatibility with clients that expect them to // be set. // // Attributes of INTEGER, STRING, LABEL, LICENSE, BOOLEAN, and TRISTATE type // may set *none* of the values. This can happen if the Attribute message is // prepared for a client that doesn't support SELECTOR_LIST, but the rule has // a selector list value for the attribute. (Selector lists for attributes of // other types--the collection types--are handled differently when prepared // for such a client. The possible collection values are gathered together // and flattened.) // // By checking the type, the appropriate value can be extracted - see the // comments on each type for the associated value. The order of lists comes // from the blaze parsing. If an attribute is of a list type, the associated // list should never be empty. message Attribute { reserved 12, 16; // Indicates the type of attribute. enum Discriminator { INTEGER = 1; // int_value STRING = 2; // string_value LABEL = 3; // string_value OUTPUT = 4; // string_value STRING_LIST = 5; // string_list_value LABEL_LIST = 6; // string_list_value OUTPUT_LIST = 7; // string_list_value DISTRIBUTION_SET = 8; // string_list_value - order is unimportant LICENSE = 9; // license STRING_DICT = 10; // string_dict_value FILESET_ENTRY_LIST = 11; // fileset_list_value LABEL_LIST_DICT = 12; // label_list_dict_value STRING_LIST_DICT = 13; // string_list_dict_value BOOLEAN = 14; // int, bool and string value TRISTATE = 15; // tristate, int and string value INTEGER_LIST = 16; // int_list_value UNKNOWN = 18; // unknown type, use only for build extensions LABEL_DICT_UNARY = 19; // label_dict_unary_value SELECTOR_LIST = 20; // selector_list LABEL_KEYED_STRING_DICT = 21; // label_keyed_string_dict DEPRECATED_STRING_DICT_UNARY = 17; } // Values for the TriState field type. enum Tristate { NO = 0; YES = 1; AUTO = 2; } message SelectorEntry { reserved 12; // The key of the selector entry. At this time, this is the label of a // config_setting rule, or the pseudo-label "//conditions:default". optional string label = 1; // True if the entry's value is the default value for the type as a // result of the condition value being specified as None (ie: // {"//condition": None}). optional bool is_default_value = 16; // Exactly one of the following fields (except for glob_criteria) must be // populated - note that the BOOLEAN and TRISTATE caveat in Attribute's // comment does not apply here. The type field in the SelectorList // containing this entry indicates which of these fields is populated, // in accordance with the comments on Discriminator enum values above. // (To be explicit: BOOLEAN populates the boolean_value field and TRISTATE // populates the tristate_value field.) optional int32 int_value = 2; optional string string_value = 3; optional bool boolean_value = 4; optional Tristate tristate_value = 5; repeated string string_list_value = 6; optional License license = 7; repeated StringDictEntry string_dict_value = 8; repeated FilesetEntry fileset_list_value = 9; repeated LabelListDictEntry label_list_dict_value = 10; repeated StringListDictEntry string_list_dict_value = 11; repeated int32 int_list_value = 13; repeated LabelDictUnaryEntry label_dict_unary_value = 15; repeated LabelKeyedStringDictEntry label_keyed_string_dict_value = 17; repeated bytes DEPRECATED_string_dict_unary_value = 14; } message Selector { // The list of (label, value) pairs in the map that defines the selector. // At this time, this cannot be empty, i.e. a selector has at least one // entry. repeated SelectorEntry entries = 1; // Whether or not this has any default values. optional bool has_default_value = 2; // The error message when no condition matches. optional string no_match_error = 3; } message SelectorList { // The type that this selector list evaluates to, and the type that each // selector in the list evaluates to. At this time, this cannot be // SELECTOR_LIST, i.e. selector lists do not nest. optional Discriminator type = 1; // The list of selector elements in this selector list. At this time, this // cannot be empty, i.e. a selector list is never empty. repeated Selector elements = 2; } // The name of the attribute required string name = 1; // Whether the attribute was explicitly specified optional bool explicitly_specified = 13; // If this attribute has a string value or a string list value, then this // may be set to indicate that the value may be treated as a label that // isn't a dependency of this attribute's rule. optional bool nodep = 20; // The type of attribute. This message is used for all of the different // attribute types so the discriminator helps for figuring out what is // stored in the message. required Discriminator type = 2; // If this attribute has an integer value this will be populated. // Boolean and TriState also use this field as [0,1] and [-1,0,1] // for [false, true] and [auto, no, yes] respectively. optional int32 int_value = 3; // If the attribute has a string value this will be populated. Label and // path attributes use this field as the value even though the type may // be LABEL or something else other than STRING. optional string string_value = 5; // If the attribute has a boolean value this will be populated. optional bool boolean_value = 14; // If the attribute is a Tristate value, this will be populated. optional Tristate tristate_value = 15; // The value of the attribute has a list of string values (label and path // note from STRING applies here as well). repeated string string_list_value = 6; // If this is a license attribute, the license information is stored here. optional License license = 7; // If this is a string dict, each entry will be stored here. repeated StringDictEntry string_dict_value = 8; // If the attribute is part of a Fileset, the fileset entries are stored in // this field. repeated FilesetEntry fileset_list_value = 9; // If this is a label list dict, each entry will be stored here. repeated LabelListDictEntry label_list_dict_value = 10; // If this is a string list dict, each entry will be stored here. repeated StringListDictEntry string_list_dict_value = 11; // The value of the attribute has a list of int32 values repeated int32 int_list_value = 17; // If this is a label dict unary, each entry will be stored here. repeated LabelDictUnaryEntry label_dict_unary_value = 19; // If this is a label-keyed string dict, each entry will be stored here. repeated LabelKeyedStringDictEntry label_keyed_string_dict_value = 22; // If this attribute's value is an expression containing one or more select // expressions, then its type is SELECTOR_LIST and a SelectorList will be // stored here. optional SelectorList selector_list = 21; repeated bytes DEPRECATED_string_dict_unary_value = 18; } // A rule instance (e.g., cc_library foo, java_binary bar). message Rule { reserved 8, 11; // The name of the rule (formatted as an absolute label, e.g. //foo/bar:baz). required string name = 1; // The rule class (e.g., java_library) required string rule_class = 2; // The BUILD file and line number of the location (formatted as // ::) in the rule's package's // BUILD file where the rule instance was instantiated. The line number will // be that of a rule invocation or macro call (that in turn invoked a // rule). See // https://bazel.build/rules/macros#macro-creation optional string location = 3; // All of the attributes that describe the rule. repeated Attribute attribute = 4; // All of the inputs to the rule (formatted as absolute labels). These are // predecessors in the dependency graph. repeated string rule_input = 5; repeated ConfiguredRuleInput configured_rule_input = 15; // All of the outputs of the rule (formatted as absolute labels). These are // successors in the dependency graph. repeated string rule_output = 6; // The set of all "features" inherited from the rule's package declaration. repeated string default_setting = 7; // The rule's class's public by default value. optional bool DEPRECATED_public_by_default = 9; optional bool DEPRECATED_is_skylark = 10; // Hash encapsulating the behavior of this Starlark rule. Any change to this // rule's definition that could change its behavior will be reflected here. optional string skylark_environment_hash_code = 12; // The Starlark call stack at the moment the rule was instantiated. // Each entry has the form "file:line:col: function". // The outermost stack frame ("", the BUILD file) appears first; // the frame for the rule function itself is omitted. // The file name may be relative to package's source root directory. // // Requires --proto:instantiation_stack=true. repeated string instantiation_stack = 13; // The Starlark call stack for the definition of the rule class of this // particular rule instance. If empty, either populating the field was not // enabled on the command line with the --proto:definition_stack flag or the // rule is a native one. repeated string definition_stack = 14; } message ConfiguredRuleInput { optional string label = 1; optional string configuration_checksum = 2; optional uint32 configuration_id = 3; } // Summary of all transitive dependencies of 'rule,' where each dependent // rule is included only once in the 'dependency' field. Gives complete // information to analyze the single build target labeled rule.name, // including optional location of target in BUILD file. message RuleSummary { required Rule rule = 1; repeated Rule dependency = 2; optional string location = 3; } // A package group. Aside from the name, it contains the list of packages // present in the group (as specified in the BUILD file). message PackageGroup { reserved 4; // The name of the package group required string name = 1; // The list of packages as specified in the BUILD file. Currently this is // only a list of packages, but some time in the future, there might be // some type of wildcard mechanism. repeated string contained_package = 2; // The list of sub package groups included in this one. repeated string included_package_group = 3; } // An environment group. message EnvironmentGroup { // The name of the environment group. required string name = 1; // The environments that belong to this group (as labels). repeated string environment = 2; // The member environments that rules implicitly support if not otherwise // specified. repeated string default = 3; } // A file that is an input into the build system. // Next-Id: 10 message SourceFile { reserved 7; // The name of the source file (a label). required string name = 1; // The location of the source file. This is a path with a line number and a // column number not a label in the build system. optional string location = 2; // Labels of .bzl (Starlark) files that are transitively loaded in this BUILD // file. This is present only when the SourceFile represents a BUILD file that // loaded .bzl files. // TODO(bazel-team): Rename this field. repeated string subinclude = 3; // Labels of package groups that are mentioned in the visibility declaration // for this source file. repeated string package_group = 4; // Labels mentioned in the visibility declaration (including :__pkg__ and // //visibility: ones) repeated string visibility_label = 5; // The package-level features enabled for this package. Only present if the // SourceFile represents a BUILD file. repeated string feature = 6; // License attribute for the file. optional License license = 8; // True if the package contains an error. Only present if the SourceFile // represents a BUILD file. optional bool package_contains_errors = 9; } // A file that is the output of a build rule. message GeneratedFile { // The name of the generated file (a label). required string name = 1; // The label of the target that generates the file. required string generating_rule = 2; // The path, line number, and column number of the output file (not a label). optional string location = 3; } // A target from a blaze query execution. Similar to the Attribute message, // the Discriminator is used to determine which field contains information. // For any given type, only one of these can be populated in a single Target. message Target { enum Discriminator { RULE = 1; SOURCE_FILE = 2; GENERATED_FILE = 3; PACKAGE_GROUP = 4; ENVIRONMENT_GROUP = 5; } // The type of target contained in the message. required Discriminator type = 1; // If this target represents a rule, the rule is stored here. optional Rule rule = 2; // A file that is not generated by the build system (version controlled // or created by the test harness). optional SourceFile source_file = 3; // A generated file that is the output of a rule. optional GeneratedFile generated_file = 4; // A package group. optional PackageGroup package_group = 5; // An environment group. optional EnvironmentGroup environment_group = 6; } // Container for all of the blaze query results. message QueryResult { // All of the targets returned by the blaze query. repeated Target target = 1; } //////////////////////////////////////////////////////////////////////////// // Messages dealing with querying the BUILD language itself. For now, this is // quite simplistic: Blaze can only tell the names of the rule classes, their // attributes with their type. // Information about allowed rule classes for a specific attribute of a rule. message AllowedRuleClassInfo { enum AllowedRuleClasses { ANY = 1; // Any rule is allowed to be in this attribute SPECIFIED = 2; // Only the explicitly listed rules are allowed } required AllowedRuleClasses policy = 1; // Rule class names of rules allowed in this attribute, e.g "cc_library", // "py_binary". Only present if the allowed_rule_classes field is set to // SPECIFIED. repeated string allowed_rule_class = 2; } // This message represents a single attribute of a single rule. // See https://bazel.build/rules/lib/attr. message AttributeDefinition { required string name = 1; // e.g. "name", "srcs" required Attribute.Discriminator type = 2; optional bool mandatory = 3; optional AllowedRuleClassInfo allowed_rule_classes = 4; // type=label* optional string documentation = 5; optional bool allow_empty = 6; // type=*_list|*_dict optional bool allow_single_file = 7; // type=label optional AttributeValue default = 9; // simple (not computed/late-bound) values only optional bool executable = 10; // type=label optional bool configurable = 11; optional bool nodep = 12; // label-valued edge does not establish a dependency optional bool cfg_is_host = 13; // edge entails a transition to "host" configuration } // An AttributeValue represents the value of an attribute. // A single field, determined by the attribute type, is populated. // // It is used only for AttributeDefinition.default. Attribute and // SelectorEntry do their own thing for unfortunate historical reasons. message AttributeValue { optional int32 int = 1; // type=int|tristate optional string string = 2; // type=string|label|output optional bool bool = 3; // type=bool repeated AttributeValue list = 4; // type=*_list|distrib repeated DictEntry dict = 5; // type=*_dict message DictEntry { required string key = 1; required AttributeValue value = 2; } } message RuleDefinition { required string name = 1; // Only contains documented attributes repeated AttributeDefinition attribute = 2; optional string documentation = 3; // Only for build extensions: label to file that defines the extension optional string label = 4; } message BuildLanguage { // Only contains documented rule definitions repeated RuleDefinition rule = 1; } buildtools-10.1.0/build_proto/BUILD.bazel0000664000175000017500000000214115254573013017542 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") load("//build:build_defs.bzl", "go_proto_checkedin_test") # gazelle:exclude build.gen.pb.go # build.proto is copied from Bazel 6.0.0 # https://github.com/bazelbuild/bazel/blob/6.0.0/src/main/protobuf/build.proto go_proto_checkedin_test( src = "build.gen.pb.go", allowed_diffs = ["^[[:space:]]*//.*protoc"], ) proto_library( name = "blaze_query_proto", srcs = ["build.proto"], visibility = ["//visibility:public"], ) go_proto_library( name = "build_proto_go_proto", importpath = "github.com/bazel-contrib/buildtools/v10/build_proto", proto = ":blaze_query_proto", visibility = ["//visibility:public"], ) go_library( name = "build_proto", embed = [":build_proto_go_proto"], importpath = "github.com/bazel-contrib/buildtools/v10/build_proto", visibility = ["//visibility:public"], ) alias( name = "go_default_library", actual = ":build_proto", visibility = ["//visibility:public"], ) buildtools-10.1.0/.bazelrc0000664000175000017500000000101715254573013014766 0ustar kenhyskenhysbuild:release -c opt --stamp --workspace_status_command="$PWD/status.py" ################################################################################ # User bazel configuration ################################################################################ # Load from the local configuration file, if it exists. This needs to be the # *last* statement in the root configuration file, as the local configuration # file should be able to overwrite flags from the root configuration. try-import %workspace%/.bazelrc.user buildtools-10.1.0/.github/0000775000175000017500000000000015254573013014704 5ustar kenhyskenhysbuildtools-10.1.0/.github/workflows/0000775000175000017500000000000015254573013016741 5ustar kenhyskenhysbuildtools-10.1.0/.github/workflows/go.yaml0000664000175000017500000000233215254573013020232 0ustar kenhyskenhysname: go on: push: branches: - main pull_request: workflow_dispatch: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: tidy: name: go build and go mod tidy runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 with: egress-policy: audit - name: Checkout uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 - name: Set up Bazel uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0 with: bazelisk-cache: true repository-cache: true - name: Run go mod tidy run: bazel run @io_bazel_rules_go//go -- mod tidy - name: Run go build run: bazel run @io_bazel_rules_go//go -- build ./... - name: Check for changes run: | if ! git diff --exit-code -- go.mod go.sum MODULE.bazel; then echo "::error::go.mod, go.sum or MODULE.bazel is out of date. Run 'bazel run @io_bazel_rules_go//go -- mod tidy' and commit the result." exit 1 fi buildtools-10.1.0/.github/workflows/release.yml0000664000175000017500000000472515254573013021114 0ustar kenhyskenhys# Cuts a GitHub release with prebuilt binaries whenever a v* tag is pushed. # # Build provenance attestations are generated for all binaries. Users can verify # a downloaded binary with: # gh attestation verify buildifier-linux-amd64 --repo bazel-contrib/buildtools name: Release on: push: tags: - "v*" # Never let two runs race on the release for the same tag. concurrency: group: release-${{ github.ref_name }} jobs: release: runs-on: ubuntu-latest timeout-minutes: 60 permissions: # Create the release and upload its assets. contents: write # Generate build provenance attestations, see # https://github.com/actions/attest#usage id-token: write attestations: write artifact-metadata: write env: TAG: ${{ github.ref_name }} steps: - name: Check out ${{ env.TAG }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # status.py stamps the binaries with the output of `git describe --tags`. fetch-depth: 0 - name: Build release binaries run: release/build_binaries.sh dist - name: Attest build provenance uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2 with: subject-path: dist/* - name: Create or update the release shell: bash env: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} run: | if release="$(gh release view "${TAG}" --json isDraft,isImmutable 2> /dev/null)"; then is_draft="$(jq -r .isDraft <<< "${release}")" if [[ "${is_draft}" == false && "$(jq -r .isImmutable <<< "${release}")" == true ]]; then echo "::error::The release for ${TAG} has already been published as immutable," \ "so its assets can no longer be changed. Cut a new release with a new tag instead." exit 1 fi echo "Attaching binaries to the existing release for ${TAG}" else echo "Creating a draft release for ${TAG}" gh release create "${TAG}" --draft --verify-tag --generate-notes \ --title "Release ${TAG#v} ($(date +%Y-%m-%d))" is_draft=true fi gh release upload "${TAG}" --clobber dist/* if [[ "${is_draft}" == true ]]; then echo "Publishing the release for ${TAG}" gh release edit "${TAG}" --draft=false fi buildtools-10.1.0/.github/workflows/stale.yml0000664000175000017500000000574015254573013020602 0ustar kenhyskenhys# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. # Details in this config are based on Bazel defaults, and should be somewhat kept in sync going forwards. # See: https://github.com/bazelbuild/bazel/blob/master/.github/workflows/stale.yml # # For more informatio about the Stale workflow, see: # https://github.com/actions/stale name: Close stale issues and pull requests on: workflow_dispatch: schedule: - cron: '0 10 * * *' permissions: contents: read jobs: stale: permissions: issues: write pull-requests: write name: Track and close stale issues/PRs runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 with: egress-policy: audit - name: Track and close stale issues/PRs uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-issue-stale: 430 days-before-issue-close: 90 stale-issue-message: > Thank you for contributing to the Bazel Buildtools repository! This issue has been marked as stale since it has not had any activity in the last 1+ years. It will be closed in the next 90 days unless any other activity occurs. If you think this issue is still relevant and should stay open, please post any comment here and the issue will no longer be marked as stale. close-issue-message: > This issue has been automatically closed due to inactivity. If you're still interested in pursuing this, please post `@bazelbuild/triage` in a comment here and we'll take a look. Thanks! days-before-pr-stale: 430 days-before-pr-close: 90 stale-pr-message: > Thank you for contributing to the Bazel Buildtools repository! This pull request has been marked as stale since it has not had any activity in the last 1+ years. It will be closed in the next 90 days unless any other activity occurs. If you think this pull request is still relevant and should stay open, please make sure it is up to date with the "main" branch and that a reviewer is assigned from the @bazelbuild/buildtools-team. close-pr-message: > This pull request has been automatically closed due to inactivity. If you think this pull request is still relevant and should stay open, please make sure it is up to date with the "main" branch and that a reviewer is assigned from the @bazelbuild/buildtools-team. stale-issue-label: 'stale' exempt-issue-labels: 'not stale,awaiting-bazeler,untriaged,P0,P1,P2,good first issue,help wanted' close-issue-reason: "not_planned" stale-pr-label: 'stale' exempt-pr-labels: 'not stale,awaiting-review,awaiting-PR-merge,P0,P1,P2' exempt-draft-pr: false operations-per-run: 500 ascending: true buildtools-10.1.0/unused_deps/0000775000175000017500000000000015254573013015662 5ustar kenhyskenhysbuildtools-10.1.0/unused_deps/README.md0000664000175000017500000000132515254573013017142 0ustar kenhyskenhys# Unused Deps unused_deps is a command line tool to determine any unused dependencies in [java_library](https://docs.bazel.build/versions/main/be/java.html#java_library) rules. targets. It outputs `buildozer` commands to apply the suggested prunings. ## Installation Build a binary and put it into your $GOPATH/bin: ```bash go install github.com/bazel-contrib/buildtools/v10/unused_deps@latest ``` ## Usage ```shell unused_deps TARGET... ``` Here, `TARGET` is a space-separated list of Bazel labels, with support for `:all` and `...` ## Options * `-disable_symlink_safety`: By default unused_deps will not write to symlinks pointing outside of the Bazel workspace. Setting this to true will disable this behavior. buildtools-10.1.0/unused_deps/jar_manifest.go0000664000175000017500000000472415254573013020662 0ustar kenhyskenhys/* Copyright 2018 Google LLC 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 https://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 main import ( "archive/zip" "fmt" "io" "strings" ) // manifestIndexNewline scans manifest and returns the index of the first // newline and the index of the byte following the newline. func manifestIndexNewline(manifest string) (int, int, error) { // A newline in a jar manifest is denoted with CR LF, CR, or LF. n := strings.IndexByte(manifest, '\n') r := strings.IndexByte(manifest, '\r') if n < 0 && r < 0 { return -1, -1, fmt.Errorf("no newline in '%s'", manifest) } if n < 0 { // Only CR. return r, r + 1, nil } if r < 0 || n < r { // Only LF or we have both but the LF comes first. return n, n + 1, nil } // We have both CR and LF and the CR comes before the LF. Check for the // special case of adjacent CR LF, which together denode a newline. if n == r+1 { return r, n + 1, nil } return r, r + 1, nil } // jarManifestValue returns the value associated with key in the manifest of // the jar file named jarFileName. func jarManifestValue(jarFileName string, key string) (string, error) { r, err := zip.OpenReader(jarFileName) if err != nil { return "", err } defer r.Close() for _, f := range r.File { if f.Name != "META-INF/MANIFEST.MF" { continue } rc, err := f.Open() if err != nil { return "", err } defer rc.Close() bytes, err := io.ReadAll(rc) if err != nil { return "", err } contents := string(bytes) found := strings.Index(contents, key+":") if found < 0 { return "", fmt.Errorf("manifest of jar %s contains no key %s", jarFileName, key) } label := "" rest := contents[found+len(key+":"):] for len(rest) > 0 && rest[0] == ' ' { newline, next, err := manifestIndexNewline(rest) if err != nil { return "", fmt.Errorf("bad value for key %s in manifest of jar %s", key, jarFileName) } label += rest[1:newline] rest = rest[next:] } return label, nil } return "", fmt.Errorf("jar file %s has no manifest", jarFileName) } buildtools-10.1.0/unused_deps/jar_manifest_test.go0000664000175000017500000000300515254573013021710 0ustar kenhyskenhys/* Copyright 2018 Google LLC 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 https://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 main import ( "errors" "testing" ) func TestManifestIndexNewline(t *testing.T) { foo := []struct { manifest string newline int next int err error }{ {"\n", 0, 1, nil}, {"\r", 0, 1, nil}, {"\n\r", 0, 1, nil}, {"\r\n", 0, 2, nil}, {"a\na", 1, 2, nil}, {"a\ra", 1, 2, nil}, {"a\nb\ra", 1, 2, nil}, {"a\rb\na", 1, 2, nil}, {"a\n\rb\ra", 1, 2, nil}, {"a\r\nb\na", 1, 3, nil}, {"", -1, -1, errors.New("no newline in ''")}, {"aaa", -1, -1, errors.New("no newline in 'aaa'")}, } errMismatch := func(e1, e2 error) bool { return (e1 != nil || e2 != nil) && (e1 == nil || e2 == nil || e1.Error() != e2.Error()) } for i, tt := range foo { newline, next, err := manifestIndexNewline(tt.manifest) if newline != tt.newline || next != tt.next || errMismatch(err, tt.err) { t.Errorf("%d. manifestIndexNewline(%q) => (%d, %d, %q), want (%d, %d, %q)", i, tt.manifest, newline, next, err, tt.newline, tt.next, tt.err) } } } buildtools-10.1.0/unused_deps/unused_deps.go0000664000175000017500000003262615254573013020540 0ustar kenhyskenhys/* Copyright 2017 Google LLC 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 https://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. */ // The unused_deps binary prints out buildozer commands for removing // unused Java dependencies from java_library Bazel rules. package main import ( "bufio" "bytes" "errors" "flag" "fmt" "log" "os" "os/exec" "path" "strings" "github.com/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/config" depspb "github.com/bazel-contrib/buildtools/v10/deps_proto" "github.com/bazel-contrib/buildtools/v10/edit" eapb "github.com/bazel-contrib/buildtools/v10/extra_actions_base_proto" "github.com/bazel-contrib/buildtools/v10/file" "github.com/bazel-contrib/buildtools/v10/labels" "github.com/golang/protobuf/proto" ) var ( buildVersion = "redacted" buildScmRevision = "redacted" version = flag.Bool("version", false, "Print the version of unused_deps") cQuery = flag.Bool("cquery", false, "Use 'cquery' command instead of 'query'") buildTool = flag.String("build_tool", config.DefaultBuildTool, config.BuildToolHelp) extraActionFileName = flag.String("extra_action_file", "", config.ExtraActionFileNameHelp) outputFileName = flag.String("output_file", "", "used only with extra_action_file") buildOptions = stringList("extra_build_flags", "Extra build flags to use when building the targets.") disableSymlinkSafety = flag.Bool("disable_symlink_safety", false, "per default unused_deps will not write to symlinks pointing outside of the Bazel workspace. Setting this to true will disable this behavior") blazeFlags = []string{"--tool_tag=unused_deps", "--keep_going", "--color=yes", "--curses=yes"} aspect = ` # Explicitly creates a params file for a Javac action. def _javac_params(target, ctx): params = [] for action in target.actions: if not action.mnemonic == "Javac" and not action.mnemonic == "KotlinCompile": continue output = ctx.actions.declare_file("%s.javac_params" % target.label.name) args = ctx.actions.args() args.add_all(action.argv) ctx.actions.write( output = output, content = args, ) params.append(output) break return [OutputGroupInfo(unused_deps_outputs = depset(params))] javac_params = aspect( implementation = _javac_params, ) ` ) 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 } } // getJarPath prints the path to the output jar file specified in the extra_action file at path. func getJarPath(path string) (string, error) { data, err := os.ReadFile(path) if err != nil { return "", err } i := &eapb.ExtraActionInfo{} if err := proto.Unmarshal(data, i); err != nil { return "", err } ext, err := proto.GetExtension(i, eapb.E_JavaCompileInfo_JavaCompileInfo) if err != nil { return "", err } jci, ok := ext.(*eapb.JavaCompileInfo) if !ok { return "", errors.New("no JavaCompileInfo in " + path) } return jci.GetOutputjar(), nil } // writeUnusedDeps writes the labels of unused direct deps, one per line, to outputFileName. func writeUnusedDeps(jarPath, outputFileName string) { depsPath := strings.Replace(jarPath, ".jar", ".jdeps", 1) paramsPath := jarPath + "-2.params" file, _ := os.Create(outputFileName) for dep := range unusedDeps(depsPath, directDepParams(paramsPath)) { file.WriteString(dep + "\n") } } func cmdWithStderr(name string, arg ...string) *exec.Cmd { cmd := exec.Command(name, arg...) cmd.Stderr = os.Stderr return cmd } // blazeInfo retrieves the blaze info value for a given key. func blazeInfo(key string) (value string) { out, err := cmdWithStderr(*buildTool, "info", key).Output() if err != nil { log.Printf("'%s info %s' failed: %s", *buildTool, key, err) } return strings.TrimSpace(bytes.NewBuffer(out).String()) } // inputFileName returns a blaze output file name from which to read input. func inputFileName(blazeBin, pkg, ruleName, extension string) string { name := fmt.Sprintf("%s/%s/lib%s.%s", blazeBin, pkg, ruleName, extension) // *_library if _, err := os.Stat(name); err == nil { return name } // lazily let the caller handle it if this doesn't exist return fmt.Sprintf("%s/%s/%s.%s", blazeBin, pkg, ruleName, extension) // *_{binary,test} } // directDepParams reads the jar-2.params files, looking for a // "--direct_dependencies" argument. When found, the direct dependencies are // returned as a map from jar file names to labels. func directDepParams(blazeOutputPath string, paramsFileNames ...string) (depsByJar map[string]string) { depsByJar = make(map[string]string) errs := make([]error, 0) for _, paramsFileName := range paramsFileNames { data, err := os.ReadFile(paramsFileName) if err != nil { errs = append(errs, err) continue } // The classpath param exceeds MaxScanTokenSize, so we scan just the // dependencies section. directDepsFlag := []byte("--direct_dependencies") arg := bytes.Index(data, directDepsFlag) if arg < 0 { continue } first := arg + len(directDepsFlag) + 1 scanner := bufio.NewScanner(bytes.NewReader(data[first:])) for scanner.Scan() { jar := scanner.Text() if strings.HasPrefix(jar, "--") { break } label, err := jarManifestValue(blazeOutputPath+strings.TrimPrefix(jar, "bazel-out"), "Target-Label") if err != nil { continue } if strings.HasPrefix(label, "@@") || strings.HasPrefix(label, "@/") { label = label[1:] } depsByJar[jar] = label } if err := scanner.Err(); err != nil { log.Printf("reading %s: %s", paramsFileName, err) } } if len(errs) == len(paramsFileNames) { for _, err := range errs { log.Println(err) } } return depsByJar } // unusedDeps returns a set of labels that are unused deps. // It reads Dependencies proto messages from depsFileName (a jdeps file), which indicate deps used // at compile time, and returns those values in the depsByJar map that aren't used at compile time. func unusedDeps(depsFileName string, depsByJar map[string]string) (unusedDeps map[string]bool) { unusedDeps = make(map[string]bool) data, err := os.ReadFile(depsFileName) if err != nil { log.Println(err) return unusedDeps } dependencies := &depspb.Dependencies{} if err := proto.Unmarshal(data, dependencies); err != nil { log.Println(err) return unusedDeps } for _, label := range depsByJar { unusedDeps[label] = true } for _, dependency := range dependencies.Dependency { if *dependency.Kind == depspb.Dependency_EXPLICIT { delete(unusedDeps, depsByJar[*dependency.Path]) } } return unusedDeps } // parseBuildFile tries to read and parse the contents of buildFileName. func parseBuildFile(buildFileName string) (buildFile *build.File, err error) { data, err := os.ReadFile(buildFileName) if err != nil { return nil, err } return build.Parse(buildFileName, data) } // getDepsExpr tries to parse the content of buildFileName and return the deps Expr for ruleName. func getDepsExpr(buildFileName string, ruleName string) build.Expr { buildFile, err := parseBuildFile(buildFileName) if buildFile == nil { log.Printf("%s when parsing %s", err, buildFileName) return nil } rule := edit.FindRuleByName(buildFile, ruleName) if rule == nil { log.Printf("%s not found in %s", ruleName, buildFileName) return nil } depsExpr := rule.Attr("deps") if depsExpr == nil { log.Printf("no deps attribute for %s in %s", ruleName, buildFileName) } return depsExpr } // hasRuntimeComment returns true if expr has an EOL comment containing the word "runtime". // TODO(bazel-team): delete when this comment convention is extinct func hasRuntimeComment(expr build.Expr) bool { for _, comment := range expr.Comment().Suffix { if strings.Contains(strings.ToLower(comment.Token), "runtime") { return true } } return false } // printCommands prints, for each key in the deps map, a buildozer command // to remove that entry from the deps attribute of the rule identified by label. // Returns true if at least one command was printed, or false otherwise. func printCommands(label string, deps map[string]bool) (anyCommandPrinted bool) { buildFileName, repo, pkg, ruleName := edit.InterpretLabelWithRepo(label) if repo != "" { outputBase := blazeInfo(config.DefaultOutputBase) buildFileName = fmt.Sprintf("%s/external/%s/%s", outputBase, repo, buildFileName) } depsExpr := getDepsExpr(buildFileName, ruleName) for _, li := range edit.AllLists(depsExpr) { for _, elem := range li.List { for dep := range deps { str, ok := elem.(*build.StringExpr) if !ok { continue } buildLabel := str.Value if repo != "" && buildLabel[:2] == "//" { buildLabel = fmt.Sprintf("@%s%s", repo, str.Value) } if !labels.Equal(buildLabel, dep, pkg) { continue } if hasRuntimeComment(str) { fmt.Printf("buildozer 'move deps runtime_deps %s' %s\n", str.Value, label) } else { // add dep's exported dependencies to label before removing dep fmt.Printf("buildozer \"add deps $(%s query 'labels(exports, %s)' | tr '\\n' ' ')\" %s\n", *buildTool, str.Value, label) fmt.Printf("buildozer 'remove deps %s' %s\n", str.Value, label) } anyCommandPrinted = true } } } return anyCommandPrinted } // setupAspect creates a workspace in a tmpdir and populates it with an aspect, // which is used with --override_repository below. func setupAspect() (string, error) { tmp, err := os.MkdirTemp(os.TempDir(), "unused_deps") if err != nil { return "", err } for _, f := range []string{"MODULE.bazel", "WORKSPACE", "BUILD"} { if err := file.WriteFileMode(path.Join(tmp, f), []byte{}, 0666); err != nil { return "", err } } if err := file.WriteFileMode(path.Join(tmp, "unused_deps.bzl"), []byte(aspect), 0666); err != nil { return "", err } return tmp, nil } func usage() { _, _ = fmt.Fprintf(flag.CommandLine.Output(), `usage: unused_deps TARGET... For Java rules in TARGETs, prints commands to delete deps unused at compile time. Note these may be used at run time; see documentation for more information. `) flag.PrintDefaults() os.Exit(2) } func main() { flag.Usage = usage flag.Parse() file.DisableSymlinkSafety = *disableSymlinkSafety if *version { fmt.Printf("unused_deps version: %s \n", buildVersion) fmt.Printf("unused_deps scm revision: %s \n", buildScmRevision) os.Exit(0) } if *extraActionFileName != "" { jarPath, err := getJarPath(*extraActionFileName) if err != nil { log.Fatal(err) } writeUnusedDeps(jarPath, *outputFileName) return } targetPatterns := flag.Args() if len(targetPatterns) == 0 { targetPatterns = []string{"//..."} } queryCmd := []string{} if *cQuery { queryCmd = append(queryCmd, "cquery") } else { queryCmd = append(queryCmd, "query") } queryCmd = append(queryCmd, blazeFlags...) queryCmd = append( queryCmd, fmt.Sprintf("kind('(kt|java|android)_*', %s)", strings.Join(targetPatterns, " + "))) log.Printf("running: %s %s", *buildTool, strings.Join(queryCmd, " ")) queryOut, err := cmdWithStderr(*buildTool, queryCmd...).Output() if err != nil { log.Print(err) } if len(queryOut) == 0 { fmt.Fprintln(os.Stderr, "found no targets of kind (kt|java|android)_*") usage() } aspectDir, err := setupAspect() if err != nil { log.Print(err) os.Exit(1) } defer func() { os.RemoveAll(aspectDir) }() buildCmd := []string{"build"} buildCmd = append(buildCmd, blazeFlags...) buildCmd = append(buildCmd, config.DefaultExtraBuildFlags...) buildCmd = append(buildCmd, "--output_groups=+unused_deps_outputs") buildCmd = append(buildCmd, "--inject_repository=unused_deps="+aspectDir) buildCmd = append(buildCmd, "--aspects=@unused_deps//:unused_deps.bzl%javac_params") buildCmd = append(buildCmd, buildOptions()...) blazeArgs := append(buildCmd, targetPatterns...) log.Printf("running: %s %s", *buildTool, strings.Join(blazeArgs, " ")) cmdWithStderr(*buildTool, blazeArgs...).Run() binDir := blazeInfo(config.DefaultBinDir) blazeOutputPath := blazeInfo(config.DefaultOutputPath) fmt.Fprintf(os.Stderr, "\n") // vertical space between build output and unused_deps output anyCommandPrinted := false for _, label := range strings.Fields(string(queryOut)) { if *cQuery && strings.HasPrefix(label, "(") { // cquery output includes the target's configuration ID. Skip it. // https://docs.bazel.build/versions/main/cquery.html#configurations continue } _, repo, pkg, ruleName := edit.InterpretLabelWithRepo(label) blazeBin := binDir if repo != "" { blazeBin = fmt.Sprintf("%s/external/%s", binDir, repo) } depsByJar := directDepParams(blazeOutputPath, inputFileName(blazeBin, pkg, ruleName, "javac_params")) depsToRemove := unusedDeps(inputFileName(blazeBin, pkg, ruleName, "jdeps"), depsByJar) // TODO(bazel-team): instead of printing, have buildifier-like modes? anyCommandPrinted = printCommands(label, depsToRemove) || anyCommandPrinted } if !anyCommandPrinted { fmt.Fprintln(os.Stderr, "No unused deps found.") } } buildtools-10.1.0/unused_deps/BUILD.bazel0000664000175000017500000000547715254573013017555 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") go_library( name = "unused_deps_lib", srcs = [ "jar_manifest.go", "unused_deps.go", ], importpath = "github.com/bazel-contrib/buildtools/v10/unused_deps", visibility = ["//visibility:private"], x_defs = { "main.buildVersion": "{STABLE_buildVersion}", "main.buildScmRevision": "{STABLE_buildScmRevision}", }, deps = [ "//build", "//config", "//deps_proto", "//edit", "//extra_actions_base_proto", "//file", "//labels", "@com_github_golang_protobuf//proto:go_default_library", ], ) go_binary( name = "unused_deps", embed = [":unused_deps_lib"], visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-darwin-amd64", out = "unused_deps-darwin_amd64", embed = [":unused_deps_lib"], goarch = "amd64", goos = "darwin", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-linux-amd64", out = "unused_deps-linux_amd64", embed = [":unused_deps_lib"], goarch = "amd64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-windows-amd64", out = "unused_deps-windows_amd64.exe", embed = [":unused_deps_lib"], goarch = "amd64", goos = "windows", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-linux-arm64", out = "unused_deps-linux_arm64", embed = [":unused_deps_lib"], goarch = "arm64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-darwin-arm64", out = "unused_deps-darwin_arm64", embed = [":unused_deps_lib"], goarch = "arm64", goos = "darwin", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-windows-arm64", out = "unused_deps-windows_arm64.exe", embed = [":unused_deps_lib"], goarch = "arm64", goos = "windows", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-linux-s390x", out = "unused_deps-linux_s390x", embed = [":unused_deps_lib"], goarch = "s390x", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "unused_deps-linux-riscv64", out = "unused_deps-linux_riscv64", embed = [":unused_deps_lib"], goarch = "riscv64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_test( name = "jar_manifest_test", size = "small", srcs = ["jar_manifest_test.go"], embed = [":unused_deps_lib"], ) go_test( name = "unused_deps_test", srcs = ["jar_manifest_test.go"], embed = [":unused_deps_lib"], ) buildtools-10.1.0/go.sum0000664000175000017500000001772515254573013014513 0ustar kenhyskenhyscloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/safeopen v0.0.0-20260327150837-43626d6f4685 h1:NcJjfIYRDHuboRtptwjtdQflVDKgXSqTIfwu9PpE9uo= github.com/google/safeopen v0.0.0-20260327150837-43626d6f4685/go.mod h1:D59KewtQCiD2Avi8N/v2zb/xTYaefwJl+ux2ejB58GQ= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= go.starlark.net v0.0.0-20210223155950-e043a3d3c984 h1:xwwDQW5We85NaTk2APgoN9202w/l0DVGp+GZMfsrh7s= go.starlark.net v0.0.0-20210223155950-e043a3d3c984/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU= golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= buildtools-10.1.0/CONTRIBUTING.md0000664000175000017500000000604415254573013015601 0ustar kenhyskenhys# Contributing to buildtools Want to contribute? Great! First, read this page. ## Before you contribute Before you start working on a larger contribution, you should get in touch with us first through the issue tracker with your idea so that we can help out and possibly guide you. Coordinating up front makes it much easier to avoid frustration later on. ## Building and testing The repository is built with Bazel. Use [Bazelisk](https://github.com/bazelbuild/bazelisk) to get the version pinned in `.bazelversion`. The Go toolchain is managed by Bazel, so you don't need a local Go installation. Run the full test suite, which is what CI runs on Linux, macOS and Windows: ```sh bazel test //... ``` Other commands you may need: * After adding, removing or renaming Go files or imports, regenerate the `BUILD.bazel` files: ```sh bazel run //:gazelle ``` * After changing Go dependencies, tidy `go.mod`, `go.sum` and `MODULE.bazel`. CI fails if they are out of date: ```sh bazel run @io_bazel_rules_go//go -- mod tidy ``` * Go code must be formatted with `gofmt`. Starlark files in this repository must be formatted with buildifier itself, either via [pre-commit](https://pre-commit.com/) (`pre-commit install`) or by running: ```sh bazel run //:buildifier ``` ### Generated files Some generated files are checked in, and tests fail if they are stale: * Protobuf bindings (`*.gen.pb.go`) and `lang/tables.gen.go`: ```sh ./update_generated.sh ``` * `WARNINGS.md`, after editing `warn/docs/warnings.textproto`: ```sh bazel build //warn/docs:warnings_docs && cp bazel-bin/warn/docs/WARNINGS.md . ``` * `build/parse.y.go`, after editing the grammar in `build/parse.y`: ```sh bazel build //build:parse.y.go_yacc && tail -n +3 bazel-bin/build/parse.y.baz.go > build/parse.y.go ``` ### Adding a linter warning Register the warning in one of the warning maps in `warn/warn.go`, add tests next to the implementation, document it in `warn/docs/warnings.textproto` and regenerate `WARNINGS.md` as described above. A test checks that every warning is documented. ## Pull requests All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Pull requests are squash-merged, so the pull request title and description become the commit message. Before you open a pull request, please make sure that: * The change is small and focused. Read Google's engineering practices on [small changes](https://google.github.io/eng-practices/review/developer/small-cls.html); if your change can't follow them, explain why in the description. * The description explains what the change does and why, following Google's guidance on [writing good descriptions](https://google.github.io/eng-practices/review/developer/cl-descriptions.html). * The code is covered by unit or integration tests. * If you tested the change in ways that aren't captured by automated tests, the description includes the steps to reproduce that testing. * `bazel test //...` passes and all generated files are up to date. buildtools-10.1.0/CODEOWNERS0000664000175000017500000000035415254573013014741 0ustar kenhyskenhys# Codeowners added as reviewers at PR creation time # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax * @bazelbuild/buildtools-team buildtools-10.1.0/LICENSE0000664000175000017500000002613615254573013014361 0ustar kenhyskenhys Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. buildtools-10.1.0/buildozer/0000775000175000017500000000000015254573013015343 5ustar kenhyskenhysbuildtools-10.1.0/buildozer/buildozer_test.sh0000775000175000017500000015701715254573013020753 0ustar kenhyskenhys#!/bin/bash # --- begin runfiles.bash initialization --- # Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash). set -euo pipefail if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then if [[ -f "$0.runfiles_manifest" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" elif [[ -f "$0.runfiles/MANIFEST" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then export RUNFILES_DIR="$0.runfiles" fi fi if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" else echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" exit 1 fi # --- end runfiles.bash initialization --- die () { echo "$1" 1>&2 exit 1 } [[ "$1" =~ external/* ]] && buildozer="${{1#external/}}" || buildozer="$TEST_WORKSPACE/$1" buildozer="$(rlocation "$buildozer")" source $TEST_SRCDIR/_main/buildozer/test_common.sh ## TEST INPUTS no_deps='go_library( name = "edit", )' empty_deps='go_library( name = "edit", deps = [], )' one_dep='go_library( name = "edit", deps = ["//buildifier:build"], )' two_deps='go_library( name = "edit", deps = [ ":local", "//buildifier:build", ], )' two_deps_with_select='go_library( name = "edit", deps = [ ":local", "//buildifier:build", ] + select({ "//tools/some:condition": [ "//some:value", "//some/other:value", ], "//tools/other:condition": [ "//yet/another:value", ], }), )' quoted_deps='go_library( name = "edit", deps = [ "//buildifier:build", "//foo", "//bar", "\"//baz\"", ], )' ## TESTS function test_add_one_dep() { run "$one_dep" 'add deps //foo' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ "//buildifier:build", "//foo", ], )' } function test_add_dep_no_deps() { run "$no_deps" 'add deps //foo' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//foo"], )' } function test_add_dep_quotes() { run "$no_deps" 'add deps "//foo"' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//foo"], )' } function test_add_dep_empty_deps() { run "$empty_deps" 'add deps //foo' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//foo"], )' } function test_add_dep_two_deps() { run "$two_deps" 'add deps :local2' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ ":local", ":local2", "//buildifier:build", ], )' } function test_add_existing_dep() { in='go_library( name = "edit", deps = [":local"], )' ERROR=3 run "$in" 'add deps //pkg:local' '//pkg:edit' assert_equals "$in" } function test_add_existing_dep2() { in='go_library( name = "edit", deps = ["//pkg:local"], )' ERROR=3 run "$in" 'add deps //pkg:local' '//pkg:edit' assert_equals "$in" } function test_add_shortened_dep() { in='go_library( name = "edit", )' run "$in" 'add deps //pkg:local' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":local"], )' } function test_sorted_deps() { in='go_library( name = "edit", deps = [ ":c", # comment that prevents buildifier reordering ":x", "//foo", ], )' run "$in" 'add deps :z :a :e //last' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ ":a", ":c", ":e", # comment that prevents buildifier reordering ":x", ":z", "//foo", "//last", ], )' } function test_noshorten_labels_flag() { in='go_library( name = "edit", )' run "$in" --shorten_labels=false 'add deps //pkg:local' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//pkg:local"], )' } function test_add_duplicate_label() { # "build" and ":build" labels are equivalent in='go_library( name = "edit", deps = ["build"], )' ERROR=3 run "$in" 'add deps :build' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["build"], )' } function test_add_duplicate_label2() { # "build" and ":build" labels are equivalent in='go_library( name = "edit", deps = [":build"], )' ERROR=3 run "$in" 'add deps build' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":build"], )' } function test_add_override_expr() { in='go_library( name = "edit", deps = ["build"], )' run "$in" 'add deps:expr build' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ build, "build", ], )' } function test_add_override_expr_with_dot_and_capital_e() { in='go_library( name = "edit", deps = ["build"], )' run "$in" 'add deps:expr SYSTEMS.SYS1' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ SYSTEMS.SYS1, "build", ], )' } function test_add_override_unknown_type() { in='go_library( name = "edit", deps = ["build"], )' ERROR=2 run "$in" 'add deps:foo build' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["build"], )' } function test_remove_last_dep() { run "$one_dep" 'remove deps //buildifier:build' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_all_attrs() { run "$one_dep" 'remove *' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_all_attrs_none() { ERROR=3 run "$no_deps" 'remove *' '//pkg:edit' assert_equals 'go_library( name = "edit", )' } function test_remove_dep() { run "$two_deps" 'remove deps //buildifier:build' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":local"], )' } function test_remove_dep_outside_of_select() { run "$two_deps_with_select" 'remove deps //buildifier:build' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":local"] + select({ "//tools/some:condition": [ "//some:value", "//some/other:value", ], "//tools/other:condition": [ "//yet/another:value", ], }), )' } function test_remove_all_deps_outside_of_select() { run "$two_deps_with_select" 'remove deps //buildifier:build :local' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = select({ "//tools/some:condition": [ "//some:value", "//some/other:value", ], "//tools/other:condition": [ "//yet/another:value", ], }), )' } function test_remove_dep_in_select() { run "$two_deps_with_select" 'remove deps //some:value' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ ":local", "//buildifier:build", ] + select({ "//tools/some:condition": ["//some/other:value"], "//tools/other:condition": [ "//yet/another:value", ], }), )' } function test_remove_deps_in_select() { run "$two_deps_with_select" 'remove deps //some:value //some/other:value' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ ":local", "//buildifier:build", ] + select({ "//tools/some:condition": [], "//tools/other:condition": [ "//yet/another:value", ], }), )' } function test_remove_all_deps_in_select() { run "$two_deps_with_select" 'remove deps //some:value //some/other:value //yet/another:value' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ ":local", "//buildifier:build", ], )' } function test_remove_all_deps() { run "$two_deps_with_select" 'remove deps //some:value //some/other:value //yet/another:value :local //buildifier:build' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_dep_quotes() { run "$two_deps" 'remove deps "//buildifier:build"' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":local"], )' } function test_remove_local_dep() { run "$two_deps" 'remove deps local' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//buildifier:build"], )' } function test_remove_two_deps() { run "$two_deps" 'remove deps //buildifier:build :local' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_dep_using_long_label() { run "$two_deps" 'remove deps //pkg:local' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//buildifier:build"], )' } function test_remove_nonexistent_item() { run "$two_deps" 'remove deps //foo:bar :local' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//buildifier:build"], )' } function test_remove_item_with_comment() { commented_deps='go_library( name = "edit", deps = [ # Do not remove!!!!11111!!! ":local", "//buildifier:build", #fixdeps: keep "//remove:me", ], )' run "$commented_deps" 'remove deps //buildifier:build //remove:me :local' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_src() { in='go_library( name = "edit", srcs = ["file" + ".go", "other.go"], )' run "$in" 'remove srcs other.go' '//pkg:edit' assert_equals 'go_library( name = "edit", srcs = ["file" + ".go"], )' } function test_remove_dep_without_colon() { in='go_library( name = "edit", deps = ["local", "//base"], )' run "$in" 'remove deps //pkg:local' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = ["//base"], )' } function test_remove_attribute() { run "$two_deps" 'remove deps' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_concatenated_attribute() { in='cc_library(name = "a", deps = ["//my/"] + ["foo"])' run "$in" 'remove deps :foo' '//pkg:a' assert_equals 'cc_library( name = "a", deps = ["//my/"], )' } function test_remove_from_all_attributes() { in='java_library(name = "a", data = ["b","c"], exports = ["c"], deps = ["c","d"])' run "$in" 'remove * c' '//pkg:a' assert_equals 'java_library( name = "a", data = ["b"], deps = ["d"], )' } function test_remove_from_all_rules() { in='cc_library(name = "a", visibility = ["//visibility:private"]) cc_library(name = "b") exports_files(["a.cc"], visibility = ["//visibility:private"])' run "$in" 'remove visibility' '//pkg:all' assert_equals 'cc_library(name = "a") cc_library(name = "b") exports_files(["a.cc"])' } function test_remove_package_attribute() { in='package(default_visibility = ["//visibility:public"])' run "$in" 'remove default_visibility' '//pkg:__pkg__' [ $(wc -c < "$root/pkg/BUILD") -eq 0 ] || fail "Expected empty file" } function test_remove_if_equal_label() { in='go_library( name = "edit", shared_library = ":local", # Suffix comment. )' run "$in" 'remove_if_equal shared_library :local' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_if_equal_label_does_not_match() { in='go_library( name = "edit", shared_library = ":local", # Suffix comment. )' ERROR=3 run "$in" 'remove_if_equal shared_library :global' '//pkg:edit' assert_equals 'go_library( name = "edit", shared_library = ":local", # Suffix comment. )' } function test_remove_if_equal_label_full_path() { in='go_library( name = "edit", shared_library = ":local", # Suffix comment. )' run "$in" 'remove_if_equal shared_library //pkg:local' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_if_equal_ident() { in='go_library( name = "edit", flag = True, )' run "$in" 'remove_if_equal flag True' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_if_equal_ident_does_not_match() { in='go_library( name = "edit", flag = True, )' ERROR=3 run "$in" 'remove_if_equal flag False' '//pkg:edit' assert_equals 'go_library( name = "edit", flag = True, )' } function test_remove_if_equal_string() { in='go_library( name = "edit", flag = "True", )' run "$in" 'remove_if_equal flag True' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_remove_if_equal_string_does_not_match() { in='go_library( name = "edit", flag = "False", )' ERROR=3 run "$in" 'remove_if_equal flag True' '//pkg:edit' assert_equals 'go_library( name = "edit", flag = "False", )' } function test_remove_if_equal_string_attr_string() { in='go_library( name = "edit", toolchain = "something", )' run "$in" 'remove_if_equal toolchain something' '//pkg:edit' assert_equals 'go_library(name = "edit")' } function test_move_last_dep() { run "$one_dep" 'move deps runtime_deps //buildifier:build' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = ["//buildifier:build"], )' } function test_move_dep() { run "$two_deps" 'move deps runtime_deps //buildifier:build' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = ["//buildifier:build"], deps = [":local"], )' } function test_move_two_deps() { run "$two_deps" 'move deps runtime_deps //buildifier:build :local' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = [ ":local", "//buildifier:build", ], )' } function test_move_dep_using_long_label() { run "$two_deps" 'move deps runtime_deps //pkg:local' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = [":local"], deps = ["//buildifier:build"], )' } function test_move_dep_with_comment() { local input='go_library( name = "edit", deps = [ ":local", # needed at runtime for some obscure reason "//buildifier:build", ], )' run "$input" 'move deps runtime_deps :local' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = [ ":local", # needed at runtime for some obscure reason ], deps = ["//buildifier:build"], )' } function test_move_nonexistent_item() { run "$two_deps" 'move deps runtime_deps //foo:bar :local' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = [":local"], deps = ["//buildifier:build"], )' } function test_move_all_deps_to_new_attribute() { run "$two_deps" 'move deps runtime_deps *' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = [ ":local", "//buildifier:build", ], )' } function test_move_all_deps_to_existing_attribute() { local input='go_library( name = "edit", runtime_deps = [":remote"], deps = [ ":local", "//buildifier:build", ], )' run "$input" 'move deps runtime_deps *' '//pkg:edit' assert_equals 'go_library( name = "edit", runtime_deps = [ ":local", ":remote", "//buildifier:build", ], )' } function test_rename_attribute() { run "$two_deps" 'rename deps data' '//pkg:edit' assert_equals 'go_library( name = "edit", data = [ ":local", "//buildifier:build", ], )' } function test_rename_attribute_already_exist() { in='cc_library( name = "edit", srcs = ["b.h"], hdrs = ["a.h"], )' ERROR=2 run "$in" 'rename srcs hdrs' '//pkg:edit' assert_equals "$in" assert_err "attribute hdrs already exists in rule edit" } function test_rename_attribute_does_not_exist() { ERROR=2 run "$two_deps" 'rename data resources' '//pkg:edit' assert_equals "$two_deps" assert_err "no attribute data found in rule edit" } function test_with_python_code() { in='# test that Python code is preserved and does not crash top_files = [ "lgpl.txt", "README", ] boom_files = top_files boom_files.extend(glob(["**/*.hpp"])) boom_files.remove("src/math/special_functions.hpp") # comment for foo def foo(x): x += [1, 2] foo(boom_files)' run "$in" 'add default_visibility //visibility:public' '//pkg:__pkg__' assert_equals "package(default_visibility = [\"//visibility:public\"]) $in" } function test_replace_string_attr() { in='go_library( name = "edit", shared_library = ":local", # Suffix comment. )' run "$in" 'replace shared_library :local :new' '//pkg:edit' assert_equals 'go_library( name = "edit", shared_library = ":new", # Suffix comment. )' } function test_replace_string_attr_quotes() { in='go_library( name = "edit", shared_library = ":local", # Suffix comment. )' run "$in" 'replace shared_library ":local" ":new"' '//pkg:edit' assert_equals 'go_library( name = "edit", shared_library = ":new", # Suffix comment. )' } function test_replace_string_attr_no_match() { in='go_library( name = "edit", library = ":no_match", # Suffix comment. )' ERROR=3 run "$in" 'replace library :local :new' '//pkg:edit' assert_equals 'go_library( name = "edit", library = ":no_match", # Suffix comment. )' } function test_replace_concatenated_lists() { in='go_library( name = "edit", deps = [":local"] + CONSTANT, )' run "$in" 'replace deps :local :new' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":new"] + CONSTANT, )' } function test_replace_dep() { in='go_library( name = "edit", deps = [ # Before-comment. ":local", # Suffix comment. "//buildifier:build", ] + select({ "//tools/some:condition": [ "//some:value", ], }), )' run "$in" 'replace deps :local :new' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ # Before-comment. ":new", # Suffix comment. "//buildifier:build", ] + select({ "//tools/some:condition": [ "//some:value", ], }), )' } function test_replace_dep_select() { # Replace a dep inside a select statement in='go_library( name = "edit", deps = [":dep"] + select({ "//tools/some:condition": [ "//some/other:value", ], "//tools/other:condition": [ "//yet/another:value", ], "//conditions:default": SOME_CONSTANT, }), )' run "$in" 'replace deps //some/other:value :new' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":dep"] + select({ "//tools/some:condition": [ ":new", ], "//tools/other:condition": [ "//yet/another:value", ], "//conditions:default": SOME_CONSTANT, }), )' } function test_replace_dep_using_long_label() { run "$two_deps" 'replace deps //pkg:local //pkg:new' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ ":new", "//buildifier:build", ], )' } function test_replace_in_all_attributes() { in='java_library(name = "a", data = ["b","c"], exports = ["c"], deps = ["c","d"])' run "$in" 'replace * c e' '//pkg:a' assert_equals 'java_library( name = "a", data = [ "b", "e", ], exports = ["e"], deps = [ "d", "e", ], )' } function test_substitute_dep() { in='go_library( name = "edit", deps = [ # Before-comment. "//some:value", # Suffix comment. "//some:value2", # Suffix comment. "//buildifier:build", ] )' run "$in" 'substitute deps //some:(.*) //new:${1}' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [ # Before-comment. "//new:value", # Suffix comment. "//new:value2", # Suffix comment. "//buildifier:build", ], )' } function test_substitute_dep_select() { # Replace a dep inside a select statement in='go_library( name = "edit", deps = [":dep"] + select({ "//tools/some:condition": [ "//some/other:value", "//some/other:value2", ], "//tools/other:condition": [ "//yet/another:value", "//yet/another:value2", ], "//conditions:default": SOME_CONSTANT, }), )' run "$in" 'substitute deps //some/other:(.*) :${1}' '//pkg:edit' assert_equals 'go_library( name = "edit", deps = [":dep"] + select({ "//tools/some:condition": [ ":value", ":value2", ], "//tools/other:condition": [ "//yet/another:value", "//yet/another:value2", ], "//conditions:default": SOME_CONSTANT, }), )' } function test_delete_rule_all() { in='cc_library(name = "all") cc_library(name = "b")' run "$in" 'delete' '//pkg:all' assert_equals 'cc_library(name = "b")' } function test_delete_rule_star() { in='cc_library(name = "all") cc_library(name = "b")' run "$in" 'delete' '//pkg:*' [ $(wc -c < "$root/pkg/BUILD") -eq 0 ] || fail "Expected empty file" } function test_delete_rule() { in='cc_library(name = "a") cc_library(name = "b") # Comment to make sure it is preserved a = 42 cc_library(name = "c")' run "$in" 'delete' '//pkg:b' assert_equals 'cc_library(name = "a") # Comment to make sure it is preserved a = 42 cc_library(name = "c")' } function test_delete_package() { in='package(default_visibility = ["//visibility:public"]) cc_library(name = "a")' run "$in" 'delete' '//pkg:__pkg__' assert_equals 'cc_library(name = "a")' } function test_delete_missing_package() { in='cc_library(name = "a")' ERROR=3 run "$in" 'delete' '//pkg:__pkg__' assert_equals "$in" } function test_delete_target_without_name() { in='load("/path/f", "a") a(arg1 = "foo")' run "$in" 'delete' '//pkg:%a' assert_equals 'load("/path/f", "a")' } function test_delete_using_line_number() { in='load("/path/f", "a") a(arg1 = "foo")' run "$in" 'delete' '//pkg:%3' assert_equals 'load("/path/f", "a")' } function test_copy() { in='proto_library(name = "from", visibility = ["://foo"] + CONST) cc_binary(name = "to")' run "$in" 'copy visibility from' '//pkg:to' assert_equals 'proto_library( name = "from", visibility = ["://foo"] + CONST, ) cc_binary( name = "to", visibility = ["://foo"] + CONST, )' } function test_copy_overwrite() { in='proto_library(name = "from", testonly = 1) cc_binary(name = "to", testonly = 2)' run "$in" 'copy testonly from' '//pkg:to' assert_equals 'proto_library( name = "from", testonly = 1, ) cc_binary( name = "to", testonly = 1, )' } function test_copy_no_overwrite() { in='proto_library(name = "from", visibility = ["://foo"] + CONST) cc_binary(name = "to")' run "$in" 'copy_no_overwrite visibility from' '//pkg:to' assert_equals 'proto_library( name = "from", visibility = ["://foo"] + CONST, ) cc_binary( name = "to", visibility = ["://foo"] + CONST, )' } function test_copy_no_overwrite_no_overwrite() { in='proto_library(name = "from", testonly = 1) cc_binary(name = "to", testonly = 2)' run "$in" 'copy_no_overwrite testonly from' '//pkg:to' assert_equals 'proto_library( name = "from", testonly = 1, ) cc_binary( name = "to", testonly = 2, )' } function test_copy_no_from_rule() { in='go_binary(name = "to")' ERROR=2 run "$in" 'copy visibility from' '//pkg:to' assert_err "could not find rule 'from'" } function test_copy_no_attribute() { in='go_binary(name = "from") go_binary(name = "to")' ERROR=2 run "$in" 'copy visibility from' '//pkg:to' assert_err "rule 'from' does not have attribute 'visibility'" } function test_set_kind() { in='cc_library(name = "a")' run "$in" 'set kind java_library' '//pkg:a' assert_equals 'java_library(name = "a")' } function test_set_list() { in='cc_library(name = "a")' run "$in" 'set copts foo' '//pkg:a' assert_equals 'cc_library( name = "a", copts = ["foo"], )' } function test_set_list() { in='cc_library(name = "a")' run "$in" 'set copts foo bar baz' '//pkg:a' assert_equals 'cc_library( name = "a", copts = [ "foo", "bar", "baz", ], )' } function test_set_string() { in='cc_library(name = "a")' run "$in" 'set name b' '//pkg:a' assert_equals 'cc_library(name = "b")' } function test_set_int() { in='cc_test(name = "a")' run "$in" 'set shard_count 8' '//pkg:a' assert_equals 'cc_test( name = "a", shard_count = 8, )' } function test_set_if_absent_absent() { in='soy_js(name = "a")' run "$in" 'set_if_absent allowv1syntax 1' '//pkg:a' assert_equals 'soy_js( name = "a", allowv1syntax = 1, )' } function test_set_if_absent_present() { in='soy_js( name = "a", allowv1syntax = 0 )' run "$in" 'set_if_absent allowv1syntax 1' '//pkg:a' assert_equals 'soy_js( name = "a", allowv1syntax = 0, )' } function test_set_if_absent_override_expr() { in='soy_js(name = "a")' run "$in" 'set_if_absent copts:expr foo()' '//pkg:a' assert_equals 'soy_js( name = "a", copts = foo(), )' } function test_set_custom_code() { in='cc_test(name = "a")' run "$in" 'set attr foo(a=1,b=2)' '//pkg:a' assert_equals 'cc_test( name = "a", attr = foo( a = 1, b = 2, ), )' } function test_set_override_expr() { in='cc_library(name = "a")' run "$in" 'set copts:expr foo()' '//pkg:a' assert_equals 'cc_library( name = "a", copts = foo(), )' } function assert_output() { echo "$1" > "expected" diff -u "expected" "$log" || fail "Output didn't match" } function assert_output_any_order() { echo "$1" | sort > "expected" sort < "$log" > "log_sorted" diff -u "expected" "log_sorted" || fail "Output didn't match" } function test_print_all_functions() { in='package(default_visibility = ["//visibility:public"]) cc_test(name = "a") java_binary(name = "b") exports_files(["a.cc"])' run "$in" 'print kind' '//pkg:all' assert_output 'package cc_test java_binary exports_files' } function test_print_java_libraries() { in='cc_test(name = "a") java_library(name = "b") java_library(name = "c")' run "$in" 'print' '//pkg:%java_library' assert_output 'b java_library c java_library' } function test_refer_to_rule_by_location() { in='cc_test(name = "a") java_library(name = "b") java_library(name = "c")' run "$in" 'print label' '//pkg:%2' assert_output '//pkg:b' } function test_refer_to_rule_by_location_no_such_rule() { in='cc_test(name = "a") java_library(name = "b") java_library(name = "c")' ERROR=2 run "$in" 'print label' '//pkg:%999' assert_err "rule '%999' not found" } function test_visibility_exported_file() { in='exports_files(["a.txt", "b.txt"])' run "$in" 'set visibility //foo:__pkg__' '//pkg:b.txt' assert_equals 'exports_files( [ "a.txt", "b.txt", ], visibility = ["//foo:__pkg__"], )' } function test_print_srcs() { in='cc_test(name = "a", srcs = ["foo.cc"]) java_library(name = "b") java_library(name = "c", srcs = ["foo.java", "bar.java"])' run "$in" 'print name kind srcs' '//pkg:*' assert_output 'a cc_test [foo.cc] b java_library (missing) c java_library [foo.java bar.java]' assert_err 'rule "//pkg:b" has no attribute "srcs"' } function test_print_empty_list() { in='package() java_library(name = "b", deps = [])' run "$in" 'print deps' '//pkg:b' assert_output '[]' } function test_print_label() { in='package() java_library(name = "b")' run "$in" 'print label kind' '//pkg:*' assert_output '//pkg:b java_library' } function test_print_label_json() { in='package() java_library(name = "b")' run --output_json "$in" 'print label kind' '//pkg:*' assert_output '{"records":[{"fields":[{"text":"//pkg:b"},{"text":"java_library"}]}]}' } function test_print_label_ellipsis() { mkdir -p "ellipsis_test/foo/bar" echo 'java_library(name = "test")' > "ellipsis_test/BUILD" echo 'java_library(name = "foo")' > "ellipsis_test/foo/BUILD" echo 'java_library(name = "foobar"); java_library(name = "bar");' > "ellipsis_test/foo/bar/BUILD" in='package() java_library(name = "b")' run "$in" 'print label' '//ellipsis_test/...:*' assert_output_any_order '//ellipsis_test:test //ellipsis_test/foo //ellipsis_test/foo/bar:foobar //ellipsis_test/foo/bar' } function test_print_startline() { in='package() java_library(name = "b")' run "$in" 'print startline label' '//pkg:*' assert_output '2 //pkg:b' } function test_print_endline() { in='package() java_library( name = "b" )' run "$in" 'print endline label' '//pkg:*' assert_output '4 //pkg:b' } function test_print_rule() { in='cc_library(name = "a") # Comment before cc_test( name = "b", copts = [ # comment before "foo", # comment after ], ) cc_library(name = "c")' run "$in" 'print rule' '//pkg:b' assert_output '# Comment before cc_test( name = "b", copts = [ # comment before "foo", # comment after ], )' } function test_print_version() { in='gendeb(name = "foobar", version = "12345")' run "$in" 'print version' '//pkg:*' assert_output '12345' } function test_print_attrs() { in='package() cc_library( name = "a", srcs = ["a.cc"], deps = ["//foo"], )' run "$in" 'print attrs' '//pkg:*' assert_output '[] [name srcs deps]' } function test_new_cc_library() { in='cc_test(name = "a") # end of file comment' run "$in" 'new cc_library foo' '//pkg:__pkg__' assert_equals 'cc_test(name = "a") cc_library(name = "foo") # end of file comment' } function test_new_cc_library_after_other_libraries() { in='cc_library(name = "l") cc_test(name = "a")' run "$in" 'new cc_library foo' '//pkg:__pkg__' assert_equals 'cc_library(name = "l") cc_library(name = "foo") cc_test(name = "a")' } function test_new_cc_library_empty_file() { in='' run "$in" 'new cc_library foo' '//pkg:__pkg__' assert_equals 'cc_library(name = "foo")' } function test_new_java_library() { in='cc_test(name = "a")' run "$in" 'new java_library foo' 'pkg/BUILD' assert_equals 'cc_test(name = "a") java_library(name = "foo")' } function test_new_already_exists() { in='cc_test(name = "a")' ERROR=2 run "$in" 'new cc_library a' '//pkg:__pkg__' assert_err "rule 'a' already exists" } function test_new_before_first() { in='cc_test(name = "a")' run "$in" 'new java_library foo before a' 'pkg/BUILD' assert_equals 'java_library(name = "foo") cc_test(name = "a")' } function test_new_before_last() { in='cc_test(name = "a") cc_test(name = "b")' run "$in" 'new java_library foo before b' 'pkg/BUILD' assert_equals 'cc_test(name = "a") java_library(name = "foo") cc_test(name = "b")' } function test_new_before_nonexistent_rule() { in='cc_test(name = "a") cc_test(name = "b")' run "$in" 'new java_library foo before bar' 'pkg/BUILD' assert_equals 'cc_test(name = "a") cc_test(name = "b") java_library(name = "foo")' } function test_new_before_already_exists() { in='cc_test(name = "foo") cc_test(name = "new_rule")' ERROR=2 run "$in" 'new java_library new_rule before foo' 'pkg/BUILD' assert_err "rule 'new_rule' already exists" } function test_new_after_first() { in='cc_test(name = "a")' run "$in" 'new java_library foo after a' 'pkg/BUILD' assert_equals 'cc_test(name = "a") java_library(name = "foo")' } function test_new_after_last() { in='cc_test(name = "a") cc_test(name = "b")' run "$in" 'new java_library foo after b' 'pkg/BUILD' assert_equals 'cc_test(name = "a") cc_test(name = "b") java_library(name = "foo")' } function test_new_after_by_location() { in=' cc_test(name = "a") cc_test(name = "b")' run "$in" 'new java_library foo after %2' 'pkg/BUILD' assert_equals 'cc_test(name = "a") java_library(name = "foo") cc_test(name = "b")' } function test_new_after_package() { in=' load("/foo/bar", "x", "y", "z") package(default_visibility = "//visibility:public") cc_test(name = "a") cc_test(name = "b")' run "$in" 'new java_library foo after __pkg__' 'pkg/BUILD' assert_equals 'load("/foo/bar", "x", "y", "z") package(default_visibility = "//visibility:public") java_library(name = "foo") cc_test(name = "a") cc_test(name = "b")' } function test_not_enough_arguments() { ERROR=1 run "$one_dep" 'add foo' '//pkg:edit' assert_err "Too few arguments for command 'add', expected at least 2." } function test_too_many_arguments() { ERROR=1 run "$one_dep" 'delete foo' '//pkg:edit' assert_err "Too many arguments for command 'delete', expected at most 0." } function test_package_name_missing() { ERROR=2 run "$one_dep" 'add deps //dep' ':edit' assert_err "file not found" } function test_nonexistent_package_name() { ERROR=2 run "$one_dep" 'add deps //dep' '//doesnt_exist:edit' assert_err "file not found" } function test_nonexistent_rule_name() { ERROR=2 run "$one_dep" 'add deps //dep' '//pkg:doesnt_exist' assert_err "rule 'doesnt_exist' not found" } function test_keep_going() { ERROR=2 run "$one_dep" -k 'add deps //dep' 'new cc_library edit' '//pkg:doesnt_exist' '//pkg:edit' assert_err "rule 'doesnt_exist' not found" assert_err "rule 'edit' already exists" # Make sure the commands are in the error message. assert_err "add deps //dep" assert_err "new cc_library edit" # Make sure the full targets are in the error message. assert_err "//pkg:doesnt_exist" assert_equals 'go_library( name = "edit", deps = [ "//buildifier:build", "//dep", ], )' } function test_buildifier_missing() { ERROR=2 run "$one_dep" '--buildifier=doesnt_exist' 'add deps //dep' '//pkg:edit' assert_err "executable file not found in \$PATH" } function test_buildifier_return_error() { ERROR=2 run "$one_dep" '--buildifier=false' 'add deps //dep' '//pkg:edit' assert_err "running buildifier: exit status 1" } function test_add_deps_to_targets_in_same_package() { in='cc_library(name = "smurf") cc_library(name = "smorf")' run "$in" 'add deps //foo:bar' //pkg:smurf //pkg:smorf assert_equals 'cc_library( name = "smurf", deps = ["//foo:bar"], ) cc_library( name = "smorf", deps = ["//foo:bar"], )' } function test_rule_comment() { in='cc_library(name = "a")' run "$in" 'comment Hello' //pkg:a assert_equals '# Hello cc_library(name = "a")' } function test_attribute_comment() { in='cc_library( name = "a", srcs = ["a.cc"], )' run "$in" 'comment srcs Hello\ World' //pkg:a assert_equals 'cc_library( name = "a", srcs = ["a.cc"], # Hello World )' } function test_attribute_comment_no_eol() { in='cc_library( name = "a", srcs = ["a.cc"], )' run "$in" --eol-comments=false 'comment srcs Hello\ World' //pkg:a assert_equals 'cc_library( name = "a", # Hello World srcs = ["a.cc"], )' } function test_value_comment() { in='cc_library( name = "a", srcs = [ "a.cc", "b.cc", # Old ], )' run "$in" 'comment srcs a.cc Hello' 'comment srcs b.cc New' //pkg:a assert_equals 'cc_library( name = "a", srcs = [ "a.cc", # Hello "b.cc", # New ], )' } function test_value_multiline_comment() { in='cc_library( name = "a", srcs = [ "a.cc", "b.cc", ], )' run "$in" 'comment srcs b.cc Just\ a\ multiline\ comment' //pkg:a assert_equals 'cc_library( name = "a", srcs = [ "a.cc", # Just a # multiline comment "b.cc", ], )' run "$in" 'comment srcs b.cc Another\nmultiline\ comment' //pkg:a assert_equals 'cc_library( name = "a", srcs = [ "a.cc", # Another # multiline comment "b.cc", ], )' } function test_rule_print_comment() { in='# Hello cc_library(name = "a")' run "$in" 'print_comment' //pkg:a assert_output 'Hello' } function test_rule_print_comment_with_suffix_and_after() { in='# Hello Before cc_library(name = "a") # Hello Suffix # Hello After' run "$in" 'print_comment' //pkg:a assert_output 'Hello Before Hello Suffix Hello After' } function test_attribute_print_comment() { in='cc_library( name = "a", srcs = ["a.cc"], # Hello World )' run "$in" 'print_comment srcs' //pkg:a assert_output 'Hello World' } function test_attribute_print_comment_no_eol() { in='cc_library( name = "a", # Hello World srcs = ["a.cc"], )' run "$in" --eol-comments=false 'print_comment srcs' //pkg:a assert_output 'Hello World' } function test_value_print_comment() { in='cc_library( name = "a", srcs = [ "a.cc", # World "b.cc", # Hello ], )' run "$in" 'print_comment srcs b.cc' 'print_comment srcs a.cc' //pkg:a assert_output 'Hello World' } function test_value_multiline_print_comment() { in='cc_library( name = "a", srcs = [ "a.cc", # Just a # multiline comment "b.cc", ], )' run "$in" 'print_comment srcs b.cc' //pkg:a assert_output 'Just a multiline comment' } function test_value_inside_select_print_comment() { in='cc_library( name = "a", srcs = [ "a.cc", # World "b.cc", # Hello ] + select({ "foo": [ "c.cc", # hello "d.cc", # world ], }), )' run "$in" 'print_comment srcs c.cc' 'print_comment srcs d.cc' //pkg:a assert_output 'hello world' } # Test both absolute and relative package names function test_path() { mkdir -p "java/com/foo/myproject" echo 'java_library(name = "foo")' > "java/com/foo/myproject/BUILD" $buildozer --buildifier= 'add deps a' "java/com/foo/myproject:foo" cd java $buildozer --buildifier= 'add deps b' "com/foo/myproject:foo" cd com $buildozer --buildifier= 'add deps c' "//java/com/foo/myproject:foo" cd foo $buildozer --buildifier= 'add deps d' "myproject:foo" cd myproject $buildozer --buildifier= 'add deps e' ":foo" # Check that all dependencies have been added echo -n 'java_library(name="foo",deps=["a","b","c","d","e",],)' > expected tr -d ' \n' < "BUILD" > result diff -u "expected" "result" || fail "Output didn't match" } function setup_file_test() { mkdir -p "a/pkg1" mkdir -p "a/pkg2" cat > a/pkg1/BUILD < a/pkg2/BUILD << EOF cc_library(name = "foo") cc_library(name = "bar") EOF echo -n " new cc_library baz|//a/pkg1:__pkg__ add deps a|//a/pkg1:baz add deps a|a/pkg1:foo add deps x#|a/pkg2:foo # add deps y|a/pkg1:foo # add deps y|a/pkg2:foo add deps y|a/pkg2:bar|add deps c|a/pkg1:foo add deps z|a/pkg2:bar add deps a|//a/pkg1:bar add deps b|a/pkg1:foo" > commands } function check_file_test() { # Check that all dependencies have been added cat > expected_pkg_1 < expected_pkg_2 < foo/BUILD < foo/bar/BUILD < foo/bar/baz/BUILD < foo/abc/BUILD < pkg1/BUILD < pkg2/BUILD < commands < MODULE.bazel < MODULE.bazel.expected < go.MODULE.bazel < go.MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel < MODULE.bazel.expected < MODULE.bazel.expected.stdout < MODULE.bazel.expected.stderr < stdout 2> stderr diff -u MODULE.bazel.expected MODULE.bazel || fail "File was changed" diff -u MODULE.bazel.expected.stdout stdout || fail "Output didn't match" diff -u MODULE.bazel.expected.stderr stderr || fail "Error output didn't match" } function test_disable_symlink_safety() { if [[ "$(uname -s)" != "Linux" ]]; then return fi outside="$TEST_TMPDIR/outside_$$" mkdir -p "$outside" cat > "$outside/BUILD" < /dev/null; then fail "Target file was not modified when -disable_symlink_safety was used" fi rm -f "$PKG/BUILD" rm -rf "$outside" } run_suite "buildozer tests" buildtools-10.1.0/buildozer/README.md0000664000175000017500000004365015254573013016632 0ustar kenhyskenhys# Buildozer Buildozer is a command line tool to rewrite multiple [Bazel](https://github.com/bazelbuild/bazel) BUILD files using standard commands. ## Installation 1. Build a binary and put it into your $GOPATH/bin: ```bash go install github.com/bazel-contrib/buildtools/v10/buildozer@latest ``` ## Usage ```shell buildozer [OPTIONS] ['command arg...'...|-f FILE] [label]... ``` Here, `label...` is a (space-separated, possibly empty) list of Bazel labels, for example `//path/to/pkg1:rule1 relative/path/to/pkg2:rule2`. In addition to the Bazel label syntax for specifying a package, Buildozer also allows the package part to refer to a BUILD-like file, for example `//WORKSPACE:all` or `toolchains/BUILD.tpl:host_toolchain`. Buildozer commands are passed as single positional arguments, and thus have to be quoted (or otherwise escaped). Multiple commands and multiple labels can be passed. Buildozer will execute all commands on all targets. (So if you do not specify at least one command and one target, nothing will happen.) Commands are executed in order, files are processed in parallel. When `-f FILE` is used instead of literal commands, buildozer reads commands from `FILE`. `FILE` can be `-`, in which case commands are read from the standard input. The format of the command file is as follows: Empty lines and lines beginning with `#` are ignored (including leading whitespace). Non-ignored lines consist of `|`-separated sets of commands and labels: ```shell command arg arg...|command arg arg...|...|label|label|... ``` (In fact, commands and labels can appear interleaved in arbitrary order.) `|` characters in commands can be escaped like `\|`, but double null bytes (`\x00\x00`) are not valid in command files. See below for special handling of labels to allow reading from the standard input. When a line in a command file uses the single label '*', then the command(s) will be applied to all elements of the list `label...` from the command line. ### 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 the special package name `-` to read the BUILD file from the standard input instead of from a local file in the package directory: `-:all_tests`. (It is presumably not useful to both use a `-` package name and use the `-f -` flag to read commands from the standard input.) ### 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. * `-disable_symlink_safety`: By default Buildozer will not write to symlinks pointing outside of the Bazel workspace. Setting this to true will disable this behavior. See `buildozer -help` for the full list. ### Edit commands Buildozer supports the following commands(`'command args'`): * `add [:] `: Adds value(s) to a list attribute of a rule. If a value is already present in the list, it is not added. `type` specifies the type of values being added. See [supported types](#supported-types) for the available types. * `new_load <[to=]from(s)>`: Add a load statement for the given path, importing the symbols. Afterwards, consider running `buildozer 'fix unusedLoads'`. * `replace_load <[to=]from(s)>`: Similar to `new_load`, but removes existing load statements for the requested symbols before adding new loads. * `substitute_load ` Replaces modules of loads which match `old_regexp` according to `new_template`. The regular expression must follow [RE2 syntax](https://github.com/google/re2/wiki/Syntax). `new_template` may be a simple replacement string, but it may also expand numbered or named groups using `$0` or `$x`. * `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 ? ?` * `delete`: Delete a rule. * `fix ?`: Apply a fix. * `move `: Moves `value(s)` from the list `old_attr` to the list `new_attr`. The wildcard `*` matches all values. * `new [(before|after) ]`: Add a new rule at the end of the BUILD file (before/after ``). The identifier `__pkg__` can be used to position rules relative to package(). * `print ` * `remove `: Removes attribute `attr`. The wildcard `*` matches all attributes except `name`. * `remove `: Removes `value(s)` from the list `attr`. The wildcard `*` matches all attributes. Lists containing none of the `value(s)` are not modified. * `remove_comment ? ?`: Removes the comment attached to the rule, an attribute, or a specific value in a list. * `remove_if_equal `: Removes the attribute `attr` if its value is equal to `value`. * `rename `: Rename the `old_attr` to `new_attr` which must not yet exist. * `replace `: Replaces `old_value` with `new_value` in the list `attr`. Wildcard `*` matches all attributes. Lists not containing `old_value` are not modified. * `substitute `: Replaces strings which match `old_regexp` in the list `attr` according to `new_template`. Wildcard `*` matches all attributes. The regular expression must follow [RE2 syntax](https://github.com/google/re2/wiki/Syntax). `new_template` may be a simple replacement string, but it may also expand numbered or named groups using `$0` or `$x`. Lists without strings that match `old_regexp` are not modified. * `set [:] `: Sets the value of an attribute. If the attribute was already present, its old value is replaced. `type` specifies the type of values being added. See [supported types](#supported-types) for the available types. * `set_if_absent [:] `: Sets the value of an attribute. If the attribute was already present, no action is taken. `type` specifies the type of values being added. See [supported types](#supported-types) for * the available types. * `set kind `: Set the target type to value. * `set_select ` * `copy `: Copies the value of `attr` between rules. If it exists in the `to_rule`, it will be overwritten. * `copy_no_overwrite `: Copies the value of `attr` between rules. If it exists in the `to_rule`, no action is taken. * `dict_add <(key:value)(s)>`: Sets the value of a key for the dict attribute `attr`. If the key was already present, it will _not_ be overwritten. Colon characters can be included in key and value escaped as "\\:". * `dict_set <(key:value)(s)>`: Sets the value of a key for the dict attribute `attr`. If the key was already present, its old value is replaced. Colon characters can be included in key and value escaped as "\\:". * `dict_remove `: Deletes the key for the dict attribute `attr`. * `dict_replace_if_equal `: Replaces `old_value` with `new_value` for key `key` in dictionary attribute `attr`. If the key is not present in the dictionary, or does not have value `old_value`, it will _not_ be updated. Wildcard `*` matches all keys. * `dict_list_add `: Adds value(s) to the list in the dict attribute `attr`. * `format`: Force formatting of all files, even if they were not changed by other commands. Here, `` represents an attribute (being `add`ed/`rename`d/`delete`d etc.), e.g.: `srcs`, `` 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`. 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). The following commands only apply to `MODULE.bazel` files (e.g. the target `//MODULE.bazel:all`): * `use_repo_add `: Ensures that the given repositories are imported via `use_repo` for the extension for which the given top-level variable contains the return value of a `use_extension` call. * `use_repo_remove `: Ensures that the given repositories are *not* imported via `use_repo` for the extension for which the given top-level variable contains the return value of a `use_extension` call. * `use_repo_add [dev] `: Ensures that the given repositories generated by the given extension are imported via `use_repo`. If the `dev` argument is given, extension usages with `dev_dependency = True` will be considered instead. Extension usages with `isolated = True` are ignored. * `use_repo_remove [dev] `: Ensures that the given repositories generated by the given extension are *not* imported via `use_repo`. If the `dev` argument is given, extension usages with `dev_dependency = True` will be considered instead. Extension usages with `isolated = True` are ignored. #### Supported types For the commands that support explicitly providing argument types, the following types are available: * `expr`: arbitrary expression, won't be quoted as a string unless already quoted. Buildozer doesn't check that the expression is syntactically correct, however the subsequent formatting step will fail if it's not. #### 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.bzl build_test' //pkg:__pkg__ # Replaces existing loads for build_test in //pkg buildozer 'replace_load @rules_build//build:defs.bzl build_test' //pkg:__pkg__ # Replaces modules of loads using regular expressions. # # In this example # load("@rules_foo//foo:defs.bzl", "foo_library", "foo_test") # will be replaced with # load("//third_party/build_defs/rules_foo/foo:defs.bzl", "foo_library", "foo_test") buildozer 'substitute_load ^@([^/]*)//([^:].*)$ //third_party/build_defs/${1}/${2}' //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 # Replace all dependencies using regular expressions. buildozer 'substitute deps //old/(.*) //new/${1}' //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 # Specifying an argument type to prevent automatic quoting buildozer 'add tags:expr FOO' //pkg:rule # adds tags = [FOO] ``` ### Print commands These commands are not modifying files, Buildifier returns 0 after a successful execution. * `print `: For each target, prints the value of the attributes (see below). * `print_comment ? ?`: 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 * `attrs`: displays the attribute names of the rule * `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 * `path`: the absolute path to the BUILD file that contains the rules #### 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 ` to load a list of commands and labels from a file (see [Usage](#usage) above). ```shell $ cat /tmp/cmds # a comment 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. Alternatively, BUILD files can be read from the standard input and written to the standard output, by using the `-` package name: ```shell $ cat /tmp/cmds add deps //base //strings|-:foo|-:bar $ cat some/path/BUILD | buildozer -f /tmp/cmds ``` This writes the result of updating the `:foo` and `:bar` targets in the input BUILD file to the standard output. Buildozer commands can be made executable by means of a shebang line, too: ```shell #!/usr/bin/env -S buildozer -f # # Adds //base and //string dependencies to :foo and :bar. add deps //base //strings|-:foo|-:bar ``` ## Using Buildozer in-memory Some clients of Buildozer have the need to execute buildozer actions in memory (due to a service environment which does not have access to their file system). This can be done using, `edit.ExecuteCommandsOnInlineFile`, which accepts commands and BUILD file content as bytes, applies the changes and returns the raw file content. For more details and implementation, see [`/edit/buildozer.go`](../edit/buildozer.go) Some caveats of running Buildozer in-memory: * The function assumes (and validates to some extent) that all commands apply to the same file and will return errors if there are commands affecting different paths. * When referencing targets, the function will not reliably determine if targets are local or remote. Hence redundant path references may be included in output. (e.g. `add dep //package/path:bar|//package/path:foo` would add the dep `//package/path:bar` instead of just `:bar`). ## Error code The return code is: * `0` on success, if changes were made or only readonly commands were executed * `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 '` command, like cleaning unused loads, changing labels to canonical notation, etc. * `edit/types.go`: Type information for attributes buildtools-10.1.0/buildozer/npm/0000775000175000017500000000000015254573013016135 5ustar kenhyskenhysbuildtools-10.1.0/buildozer/npm/package.json0000664000175000017500000000076315254573013020431 0ustar kenhyskenhys{ "name": "@bazel/buildozer", "description": "A command line tool to rewrite multiple BUILD files using standard commands", "version": "0.0.0-PLACEHOLDER", "license": "Apache-2.0", "bin": { "buildozer": "buildozer.js" }, "keywords": [ "bazel" ], "repository": { "type": "git", "url": "https://github.com/bazel-contrib/buildtools.git" }, "bugs": { "url": "https://github.com/bazel-contrib/buildtools/issues" } } buildtools-10.1.0/buildozer/npm/index.js0000664000175000017500000000340615254573013017605 0ustar kenhyskenhys/** * @fileoverview NodeJS binding to run the buildozer command through a convenient API */ const {spawnSync} = require('node:child_process'); // bazel will copy the /launcher.js in this repo while building the package const {getNativeBinary} = require('./buildozer'); /** * run buildozer with a list of commands * @see https://github.com/bazel-contrib/buildtools/tree/main/buildozer#do-multiple-changes-at-once * @returns The standard out of the buildozer command, split by lines */ function run(...commands) { return runWithOptions(commands, {}); } /** * run buildozer with a list of commands * @param commands a list of CommandBatch to pass to buildozer * @param options Options to pass to spawn * @param flags any buildozer flags to pass */ function runWithOptions(commands, options, flags = []) { // From https://github.com/bazel-contrib/buildtools/tree/main/buildozer#usage: // Here, label-list is a space-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)) const input = commands.map(c => [...c.commands, c.targets.join(',')].join('|')).join('\n'); const {stdout, stderr, status, error} = spawnSync(getNativeBinary(), flags.concat([ '-f', '-' /* read commands from stdin */ ]), { ...options, input, encoding: 'utf-8', }); // https://github.com/bazel-contrib/buildtools/tree/main/buildozer#error-code if (status == 0 || status == 3) return stdout.trim().split('\n').filter(l => !!l); console.error(`buildozer exited with status ${status}\n${stderr}`); throw error; } module.exports = { runWithOptions, run, }; buildtools-10.1.0/buildozer/npm/.gitignore0000664000175000017500000000001415254573013020120 0ustar kenhyskenhysnode_modulesbuildtools-10.1.0/buildozer/npm/test.js0000664000175000017500000000153115254573013017452 0ustar kenhyskenhysconst dir = require('path').join( process.env['TEST_SRCDIR'], process.env['JS_BINARY__WORKSPACE'], 'buildozer/npm/buildozer'); process.chdir(dir); const {stderr} = require('child_process').spawnSync( process.argv0, ['./buildozer.js', '--help'], {encoding: 'utf-8'}); if (!/Usage of .*buildozer/.test(stderr)) { throw new Error('buildozer --help should include usage: buildifier'); } process.chdir(process.env['TEST_TMPDIR']); const buildozer = require(dir); const fs = require('fs'); fs.mkdirSync('foo'); fs.writeFileSync('foo/BUILD', ''); buildozer.run({commands: ['new_load //:some.bzl some_rule'], targets: ['//foo:__pkg__']}); const content = fs.readFileSync('foo/BUILD', 'utf-8'); if (!content.includes('load("//:some.bzl", "some_rule")')) { throw new Error('buildozer generated file should include load statement'); } buildtools-10.1.0/buildozer/npm/BUILD.bazel0000664000175000017500000000320615254573013020014 0ustar kenhyskenhysload("@aspect_rules_js//js:defs.bzl", "js_test") load("@aspect_rules_js//npm:defs.bzl", "npm_package", "stamped_package_json") load("@bazel_skylib//rules:copy_file.bzl", "copy_file") copy_file( name = "copy_LICENSE", src = "//:LICENSE", out = "LICENSE", ) genrule( name = "buildozer_launcher", srcs = ["//:launcher.js"], outs = ["buildozer.js"], cmd = "sed s/_TOOL_/buildozer/ $< > $@", ) # npm rules live in this subdirectory to avoid a load() statement # in the parent package leaking to users. This means we need # to copy the output files so the pkg_npm will find them in the # output directory for this package. _PARENT_PACKAGE_FILES = [ "README.md", "buildozer-darwin_amd64", "buildozer-darwin_arm64", "buildozer-linux_amd64", "buildozer-linux_arm64", "buildozer-linux_riscv64", "buildozer-windows_amd64.exe", "buildozer-windows_arm64.exe", ] [ copy_file( name = "copy_%s" % s, # go_binary doesn't give a predeclared output for # the file in "out" so we have to construct a # label to reference the go_binary rule itself. src = "//buildozer:%s" % s.replace(".exe", "").replace("_", "-"), out = s, ) for s in _PARENT_PACKAGE_FILES ] stamped_package_json( name = "package", stamp_var = "BUILD_SCM_VERSION", ) npm_package( name = "buildozer", srcs = [ "LICENSE", "buildozer.js", "index.js", ":package", ] + _PARENT_PACKAGE_FILES, package = "@bazel/buildozer", publishable = True, ) js_test( name = "integration_test", data = [":buildozer"], entry_point = "test.js", ) buildtools-10.1.0/buildozer/main.go0000664000175000017500000001232115254573013016615 0ustar kenhyskenhys/* Copyright 2016 Google LLC 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 https://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/bazel-contrib/buildtools/v10/build" "github.com/bazel-contrib/buildtools/v10/edit" "github.com/bazel-contrib/buildtools/v10/tables" ) type flagArray []string func (i *flagArray) String() string { return strings.Join(*i, ",") } func (i *flagArray) Set(value string) error { *i = append(*i, value) return nil } var ( buildVersion = "redacted" buildScmRevision = "redacted" commandsFiles flagArray version = flag.Bool("version", false, "Print the version of buildozer") 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") 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.") isPrintingJSON = flag.Bool("output_json", false, "output serialized devtools.buildozer.Output json instead of human-readable strings.") tablesPath = flag.String("tables", "", "path to JSON file with custom table definitions which will replace the built-in tables") addTablesPath = flag.String("add_tables", "", "path to JSON file with custom table definitions which will be merged with 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") respectBazelignore = flag.Bool("respect_bazelignore", true, "use .bazelignore file for ignoring paths") disableSymlinkSafety = flag.Bool("disable_symlink_safety", false, "per default Buildozer will not write to symlinks pointing outside of the Bazel workspace. Setting this to true will disable this behavior") ) 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.Var(&commandsFiles, "f", "file name(s) to read commands from, use '-' for stdin (format:|-separated command line arguments to buildozer, excluding flags)") flag.Parse() if *version { fmt.Printf("buildozer version: %s \n", buildVersion) fmt.Printf("buildozer scm revision: %s \n", buildScmRevision) os.Exit(0) } 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) } } if *addTablesPath != "" { if err := tables.ParseAndUpdateJSONDefinitions(*addTablesPath, true); err != nil { fmt.Fprintf(os.Stderr, "buildifier: failed to parse %s for -add_tables: %s\n", *addTablesPath, err) os.Exit(2) } } if !(*shortenLabelsFlag) { build.DisableRewrites = []string{"label"} } edit.ShortenLabelsFlag = *shortenLabelsFlag edit.DeleteWithComments = *deleteWithComments opts := &edit.Options{ Stdout: *stdout, Buildifier: *buildifier, Parallelism: *parallelism, NumIO: *numio, CommandsFiles: commandsFiles, KeepGoing: *keepGoing, FilterRuleTypes: filterRuleTypes(), PreferEOLComments: *preferEOLComments, RootDir: *rootDir, Quiet: *quiet, EditVariables: *editVariables, IsPrintingProto: *isPrintingProto, IsPrintingJSON: *isPrintingJSON, RespectBazelignore: *respectBazelignore, DisableSymlinkSafety: *disableSymlinkSafety, } os.Exit(edit.Buildozer(opts, flag.Args())) } buildtools-10.1.0/buildozer/test_common.sh0000664000175000017500000000741015254573013020230 0ustar kenhyskenhys#!/bin/bash root="$TEST_TMPDIR/root" TEST_log="$TEST_TMPDIR/log" PKG="pkg" function set_up() { ERROR=0 mkdir -p "$root" cd "$root" touch WORKSPACE } # Runs buildozer, including saving the log and error messages, by sending the # BUILD file contents ($1) to STDIN. function run() { options=(--buildifier=) while [[ "$1" == --* ]]; do options+=("$1") shift done input="$1" shift mkdir -p "$PKG" pwd echo "$input" > "$PKG/BUILD" run_with_current_workspace "$buildozer ${options[*]}" "$@" } # Runs buildozer, including saving the log and error messages. function run_with_current_workspace() { log="$TEST_TMPDIR/log" log_err="$TEST_TMPDIR/log_err" cmd="$1" shift echo "$cmd $*" ret=0 $cmd "$@" > "$log" 2> "$log_err" || ret=$? if [ "$ret" -ne "$ERROR" ]; then cat "$log" cat "$log_err" fail "Expected error code $ERROR, got $ret" fi # There must be an error message if error code is 1 or 2. if [ "$ret" -eq "1" -o "$ret" -eq "2" ]; then [ -s "$log_err" ] || fail "No error message, despite error code $ret" fi } function assert_equals() { expected="$1" pkg="${2-}" if [ -z "$pkg" ]; then pkg="$PKG" fi echo "$expected" > "$pkg/expected" diff -u "$root/$pkg/expected" "$root/$pkg/BUILD" || fail "Output didn't match" } function assert_err() { if ! grep "$1" "$log_err"; then cat "$log_err" fail "Error log doesn't contain '$1'" fi } function assert_no_err() { if grep "$1" "$log_err"; then cat "$log_err" fail "Error log contains '$1'" fi } function fail() { __show_log >&2 echo "$TEST_name FAILED:" "$@" "." >&2 echo "$@" >$TEST_TMPDIR/__fail TEST_passed="false" # Cleanup as we are leaving the subshell now exit 1 } function __show_log() { echo "-- Test log: -----------------------------------------------------------" [[ -e $TEST_log ]] && cat $TEST_log || echo "(Log file did not exist.)" echo "------------------------------------------------------------------------" } function __pad() { local title=$1 local pad=$2 { echo -n "$pad$pad $title " printf "%80s" " " | tr ' ' "$pad" } | head -c 80 echo } function __trap_with_arg() { func="$1" ; shift for sig ; do trap "$func $sig" "$sig" done } function run_suite() { local message="$1" echo >&2 echo "$message" >&2 echo >&2 local total=0 local passed=0 TESTS=$(declare -F | awk '{print $3}' | grep ^test_ || true) for TEST_name in ${TESTS[@]}; do >$TEST_log # Reset the log. TEST_passed="true" total=$(($total + 1)) __pad $TEST_name '*' >&2 if [ "$(type -t $TEST_name)" = function ]; then # Run test in a subshell. rm -f $TEST_TMPDIR/__err_handled __trap_with_arg __test_terminated INT KILL PIPE TERM ABRT FPE ILL QUIT SEGV ( set_up eval $TEST_name test $TEST_passed == "true" ) 2>&1 | tee $TEST_TMPDIR/__log # Note that tee will prevent the control flow continuing if the test # spawned any processes which are still running and have not closed # their stdout. test_subshell_status=${PIPESTATUS[0]} if [ "$test_subshell_status" != 0 ]; then TEST_passed="false" fi else # Bad test explicitly specified in $TESTS. fail "Not a function: '$TEST_name'" fi local red='\033[0;31m' local green='\033[0;32m' local no_color='\033[0m' if [[ "$TEST_passed" == "true" ]]; then echo -e "${green}PASSED${no_color}: $TEST_name" >&2 passed=$(($passed + 1)) else echo -e "${red}FAILED${no_color}: $TEST_name" >&2 # end marker in CDATA cannot be escaped, we need to split the CDATA sections log=$(cat $TEST_TMPDIR/__log | sed 's/]]>/]]>]]>&2 done }buildtools-10.1.0/buildozer/BUILD.bazel0000664000175000017500000000531515254573013017225 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") load("@rules_shell//shell:sh_test.bzl", "sh_test") go_library( name = "buildozer_lib", srcs = ["main.go"], importpath = "github.com/bazel-contrib/buildtools/v10/buildozer", visibility = ["//visibility:private"], x_defs = { "main.buildVersion": "{STABLE_buildVersion}", "main.buildScmRevision": "{STABLE_buildScmRevision}", }, deps = [ "//build", "//edit", "//tables", ], ) go_binary( name = "buildozer", embed = [":buildozer_lib"], visibility = ["//visibility:public"], ) sh_test( name = "buildozer_test", size = "small", srcs = ["buildozer_test.sh"], args = [ "$(rootpath :buildozer)", ], data = [ "test_common.sh", ":buildozer", ], deps = ["@bazel_tools//tools/bash/runfiles"], ) go_binary( name = "buildozer-darwin-amd64", out = "buildozer-darwin_amd64", embed = [":buildozer_lib"], goarch = "amd64", goos = "darwin", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildozer-linux-amd64", out = "buildozer-linux_amd64", embed = [":buildozer_lib"], goarch = "amd64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildozer-windows-amd64", out = "buildozer-windows_amd64.exe", embed = [":buildozer_lib"], goarch = "amd64", goos = "windows", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildozer-darwin-arm64", out = "buildozer-darwin_arm64", embed = [":buildozer_lib"], goarch = "arm64", goos = "darwin", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildozer-linux-arm64", out = "buildozer-linux_arm64", embed = [":buildozer_lib"], goarch = "arm64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildozer-windows-arm64", out = "buildozer-windows_arm64.exe", embed = [":buildozer_lib"], goarch = "arm64", goos = "windows", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildozer-linux-riscv64", out = "buildozer-linux_riscv64", embed = [":buildozer_lib"], goarch = "riscv64", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) go_binary( name = "buildozer-linux-s390x", out = "buildozer-linux_s390x", embed = [":buildozer_lib"], goarch = "s390x", goos = "linux", pure = "on", visibility = ["//visibility:public"], ) exports_files( [ "README.md", ], visibility = ["//buildozer/npm:__pkg__"], ) buildtools-10.1.0/.gitignore0000664000175000017500000000007615254573013015337 0ustar kenhyskenhys/bazel-* *~ .idea .ijwb *.iml /.bazelrc.user MODULE.bazel.lockbuildtools-10.1.0/bzlenv/0000775000175000017500000000000015254573013014644 5ustar kenhyskenhysbuildtools-10.1.0/bzlenv/bzlenv_test.go0000664000175000017500000000371115254573013017534 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 bzlenv import ( "strconv" "strings" "testing" "github.com/bazel-contrib/buildtools/v10/build" ) func TestWalkEnvironment(t *testing.T) { input := ` a, b = 2, 3 def bar(x, y = a): b = 4 c = a [a for a in [b, c]] if True: return foo() def foo(): pass type T[U] = list[U] def baz[U](x: T) -> U: y: int y = 0 return x[y] ` expected := ` a0, b1 = 2, 3 def bar2(x6, y7 = a0): b8 = 4 c9 = a0 [a10 for a10 in [b8, c9]] if True: return foo3() def foo3(): pass type T4[U11] = list[U11] def baz5[U12](x13: T4) -> U12: y15: int y15 = 0 return x13[y15] ` var buildFile build.Expr buildFile, err := build.Parse("test_file.bzl", []byte(input)) if err != nil { t.Fatalf("Bad test input: %v", err) } var walk func(e *build.Expr, env *Environment) walk = func(e *build.Expr, env *Environment) { switch e := (*e).(type) { case *build.DefStmt: binding := env.Get(e.Name) if binding != nil { e.Name += strconv.Itoa(binding.ID) } case *build.Ident: binding := env.Get(e.Name) if binding != nil { e.Name += strconv.Itoa(binding.ID) } } WalkOnceWithEnvironment(*e, env, walk) } walk(&buildFile, NewEnvironment()) output := strings.Trim(build.FormatString(buildFile), "\n") expected = strings.Trim(expected, "\n") if output != expected { t.Errorf("\nexpected:\n%s\ngot:\n%s", expected, output) } } buildtools-10.1.0/bzlenv/bzlenv.go0000664000175000017500000001617215254573013016502 0ustar kenhyskenhys/* Copyright 2020 Google LLC 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 https://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 bzlenv provides function to create and update a static environment. package bzlenv import ( "github.com/bazel-contrib/buildtools/v10/build" ) // ValueKind describes how a binding was declared. type ValueKind int // List of ValueKind values. const ( Builtin ValueKind = iota // language builtin Imported // declared with load() Global // declared with assignment on top-level Function // declared with a def Parameter // function parameter Local // local variable, defined with assignment or as a loop variable TypeParameter // type parameter of a type alias or a generic function ) func (k ValueKind) String() string { switch k { case Builtin: return "builtin" case Imported: return "imported" case Global: return "global" case Function: return "function" case Parameter: return "parameter" case Local: return "local" case TypeParameter: return "type parameter" default: panic(k) } } // NameInfo represents information about a symbol name. type NameInfo struct { ID int // unique identifier Name string // name of the variable (not unique) Kind ValueKind Definition build.Expr // node that defines the value } type block map[string]NameInfo // Environment represents a static environment (e.g. information about all available symbols). type Environment struct { Blocks []block Function *build.DefStmt // enclosing function (or nil on top-level) nextID int // used to create unique identifiers Stack []build.Expr // parents of the current node } // NewEnvironment creates a new empty Environment. func NewEnvironment() *Environment { sc := block{} return &Environment{[]block{sc}, nil, 0, []build.Expr{}} } func (e *Environment) enterBlock() { e.Blocks = append(e.Blocks, block{}) } func (e *Environment) exitBlock() { if len(e.Blocks) < 1 { panic("no block to close") } e.Blocks = e.Blocks[:len(e.Blocks)-1] } func (e *Environment) currentBlock() block { return e.Blocks[len(e.Blocks)-1] } func (sc *block) declare(name string, kind ValueKind, definition build.Expr, id int) { (*sc)[name] = NameInfo{ ID: id, Name: name, Definition: definition, Kind: kind} } // Get resolves the name and resolves information about the binding (or nil if it's not defined). func (e *Environment) Get(name string) *NameInfo { for i := len(e.Blocks) - 1; i >= 0; i-- { if ret, ok := e.Blocks[i][name]; ok { return &ret } } return nil } func (e *Environment) declare(name string, kind ValueKind, node build.Expr) { sc := e.currentBlock() sc.declare(name, kind, node, e.nextID) e.nextID++ } func declareGlobals(stmts []build.Expr, env *Environment) { for _, node := range stmts { switch node := node.(type) { case *build.LoadStmt: for _, ident := range node.To { env.declare(ident.Name, Imported, ident) } case *build.TypeAliasStmt: // Type aliases allowed only at top level. env.declare(node.GetIdent().Name, Global, node) case *build.AssignExpr: kind := Local if env.Function == nil { kind = Global } for _, id := range CollectLValues(node.LHS) { env.declare(id.Name, kind, node) } case *build.TypedIdent: // var statement kind := Local if env.Function == nil { kind = Global } env.declare(node.GetIdent().Name, kind, node) case *build.DefStmt: env.declare(node.Name, Function, node) } } } // CollectLValues returns the list of identifiers that are assigned (assuming that node is a valid // LValue). For example, it returns `a`, `b` and `c` for the input `a, (b, c)`. func CollectLValues(node build.Expr) []*build.Ident { var result []*build.Ident switch node := node.(type) { case *build.Ident: result = append(result, node) case *build.TypedIdent: result = CollectLValues(node.Ident) case *build.TupleExpr: for _, item := range node.List { result = append(result, CollectLValues(item)...) } case *build.ListExpr: for _, item := range node.List { result = append(result, CollectLValues(item)...) } } return result } func declareParams(fct *build.DefStmt, env *Environment) { for _, node := range fct.Params { name, _ := build.GetParamName(node) env.declare(name, Parameter, node) } } func declareLocalVariables(stmts []build.Expr, env *Environment) { for _, stmt := range stmts { switch node := stmt.(type) { case *build.AssignExpr: kind := Local if env.Function == nil { kind = Global } for _, id := range CollectLValues(node.LHS) { env.declare(id.Name, kind, node) } case *build.TypedIdent: // var statement kind := Local if env.Function == nil { kind = Global } env.declare(node.GetIdent().Name, kind, node) case *build.IfStmt: declareLocalVariables(node.True, env) declareLocalVariables(node.False, env) case *build.ForStmt: for _, id := range CollectLValues(node.Vars) { env.declare(id.Name, Local, node) } declareLocalVariables(node.Body, env) } } } // WalkOnceWithEnvironment calls fct on every child of node, while maintaining the Environment of all available symbols. func WalkOnceWithEnvironment(node build.Expr, env *Environment, fct func(e *build.Expr, env *Environment)) { env.Stack = append(env.Stack, node) switch node := node.(type) { case *build.File: declareGlobals(node.Stmt, env) build.WalkOnce(node, func(e *build.Expr) { fct(e, env) }) case *build.DefStmt: if node.TypeParams != nil { env.enterBlock() for _, id := range CollectLValues(node.TypeParams) { env.declare(id.Name, TypeParameter, node) } } env.enterBlock() env.Function = node declareParams(node, env) declareLocalVariables(node.Body, env) build.WalkOnce(node, func(e *build.Expr) { fct(e, env) }) env.Function = nil env.exitBlock() if node.TypeParams != nil { env.exitBlock() } case *build.TypeAliasStmt: if node.TypeParams != nil { env.enterBlock() for _, id := range CollectLValues(node.TypeParams) { env.declare(id.Name, TypeParameter, node) } } build.WalkOnce(node, func(e *build.Expr) { fct(e, env) }) if node.TypeParams != nil { env.exitBlock() } case *build.Comprehension: env.enterBlock() for _, clause := range node.Clauses { switch clause := clause.(type) { case *build.ForClause: for _, id := range CollectLValues(clause.Vars) { env.declare(id.Name, Local, node) } } } build.WalkOnce(node, func(e *build.Expr) { fct(e, env) }) env.exitBlock() default: build.WalkOnce(node, func(e *build.Expr) { fct(e, env) }) } env.Stack = env.Stack[:len(env.Stack)-1] } buildtools-10.1.0/bzlenv/BUILD.bazel0000664000175000017500000000077215254573013016530 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "bzlenv", srcs = ["bzlenv.go"], importpath = "github.com/bazel-contrib/buildtools/v10/bzlenv", visibility = ["//visibility:public"], deps = ["//build"], ) go_test( name = "bzlenv_test", size = "small", srcs = ["bzlenv_test.go"], embed = [":bzlenv"], deps = ["//build"], ) alias( name = "go_default_library", actual = ":bzlenv", visibility = ["//visibility:public"], ) buildtools-10.1.0/config/0000775000175000017500000000000015254573013014611 5ustar kenhyskenhysbuildtools-10.1.0/config/config.go0000664000175000017500000000253515254573013016412 0ustar kenhyskenhys/* Copyright 2017 Google LLC 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 https://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 config provides environment specific configuration elements to unused_deps package config var ( // DefaultBuildTool should be used to build and extract deps DefaultBuildTool = "bazel" // BuildToolHelp message for above BuildToolHelp = "the build executable (like bazel)" // ExtraActionFileNameHelp help for extra actions ExtraActionFileNameHelp = "When specified, just prints suspected unused deps." // DefaultBinDir is used to query for Bazel's bazel-bin directory. DefaultBinDir = "bazel-bin" // DefaultOutputBase is used to query for Bazel's output base directory. DefaultOutputBase = "output_base" // DefaultOutputPath is used to query for Bazel's bazel-out directory. DefaultOutputPath = "output_path" // DefaultExtraBuildFlags is internal-only DefaultExtraBuildFlags = []string{} ) buildtools-10.1.0/config/BUILD.bazel0000664000175000017500000000051515254573013016470 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "config", srcs = ["config.go"], importpath = "github.com/bazel-contrib/buildtools/v10/config", visibility = ["//visibility:public"], ) alias( name = "go_default_library", actual = ":config", visibility = ["//visibility:public"], ) buildtools-10.1.0/.pre-commit-config.yaml0000664000175000017500000000063315254573013017627 0ustar kenhyskenhys# First [install pre-commit](https://pre-commit.com/#installation), # then run # pre-commit install # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: # Check formatting and lint for starlark code - repo: https://github.com/keith/pre-commit-buildifier rev: 6.3.3 hooks: - id: buildifier - id: buildifier-lint buildtools-10.1.0/.bazelversion0000664000175000017500000000000615254573013016044 0ustar kenhyskenhys9.2.0 buildtools-10.1.0/launcher.js0000664000175000017500000000411015254573013015477 0ustar kenhyskenhys#!/usr/bin/env node // Copyright 2020 The Bazel Authors. 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. 'use strict'; // This package inspired by // https://github.com/angular/clang-format/blob/master/index.js const os = require('os'); const path = require('path'); const spawn = require('child_process').spawn; function getNativeBinary() { const arch = { 'arm64' : 'arm64', 'x64' : 'amd64', 's390x': 's390x', 'riscv64': 'riscv64', }[os.arch()]; // Filter the platform based on the platforms that are build/included. const platform = { 'darwin' : 'darwin', 'linux' : 'linux', 'win32' : 'windows', }[os.platform()]; const extension = { 'darwin' : '', 'linux' : '', 'win32' : '.exe', }[os.platform()]; if (arch == undefined || platform == undefined) { console.error(`FATAL: Your platform/architecture combination ${ os.platform()} - ${os.arch()} is not yet supported. See instructions at https://github.com/bazel-contrib/buildtools/blob/main/_TOOL_/README.md.`); return Promise.resolve(1); } const binary = path.join(__dirname, `_TOOL_-${platform}_${arch}${extension}`); return binary; } function main(args) { const binary = getNativeBinary(); const ps = spawn(binary, args, {stdio : 'inherit'}); function shutdown() { ps.kill("SIGTERM") process.exit(); } process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); ps.on('close', e => process.exitCode = e); } if (require.main === module) { main(process.argv.slice(2)); } module.exports = { getNativeBinary, }; buildtools-10.1.0/WARNINGS.md0000664000175000017500000017111315254573013015122 0ustar kenhyskenhys# Buildifier warnings Warning categories supported by buildifier's linter: * [`allowed-symbol-load-locations`](#allowed-symbol-load-locations) * [`attr-applicable_licenses`](#attr-applicable_licenses) * [`attr-cfg`](#attr-cfg) * [`attr-license`](#attr-license) * [`attr-licenses`](#attr-licenses) * [`attr-non-empty`](#attr-non-empty) * [`attr-output-default`](#attr-output-default) * [`attr-package-metadata`](#attr-package-metadata) * [`attr-single-file`](#attr-single-file) * [`build-args-kwargs`](#build-args-kwargs) * [`bzl-visibility`](#bzl-visibility) * [`confusing-name`](#confusing-name) * [`constant-glob`](#constant-glob) * [`ctx-actions`](#ctx-actions) * [`ctx-args`](#ctx-args) * [`deprecated-function`](#deprecated-function) * [`depset-items`](#depset-items) * [`depset-iteration`](#depset-iteration) * [`depset-union`](#depset-union) * [`dict-concatenation`](#dict-concatenation) * [`dict-method-named-arg`](#dict-method-named-arg) * [`duplicated-name`](#duplicated-name) * [`filetype`](#filetype) * [`function-docstring`](#function-docstring) * [`function-docstring-args`](#function-docstring-args) * [`function-docstring-header`](#function-docstring-header) * [`function-docstring-return`](#function-docstring-return) * [`git-repository`](#git-repository) * [`http-archive`](#http-archive) * [`integer-division`](#integer-division) * [`keyword-positional-params`](#keyword-positional-params) * [`list-append`](#list-append) * [`load`](#load) * [`load-on-top`](#load-on-top) * [`make-location`](#make-location) * [`module-docstring`](#module-docstring) * [`name-conventions`](#name-conventions) * [`native-android`](#native-android) * [`native-build`](#native-build) * [`native-cc-binary`](#native-cc-binary) * [`native-cc-common`](#native-cc-common) * [`native-cc-debug-package-info`](#native-cc-debug-package-info) * [`native-cc-fdo-prefetch-hints`](#native-cc-fdo-prefetch-hints) * [`native-cc-fdo-profile`](#native-cc-fdo-profile) * [`native-cc-import`](#native-cc-import) * [`native-cc-info`](#native-cc-info) * [`native-cc-library`](#native-cc-library) * [`native-cc-memprof-profile`](#native-cc-memprof-profile) * [`native-cc-objc-import`](#native-cc-objc-import) * [`native-cc-objc-library`](#native-cc-objc-library) * [`native-cc-propeller-optimize`](#native-cc-propeller-optimize) * [`native-cc-proto`](#native-cc-proto) * [`native-cc-shared-library`](#native-cc-shared-library) * [`native-cc-shared-library-hint-info`](#native-cc-shared-library-hint-info) * [`native-cc-shared-library-info`](#native-cc-shared-library-info) * [`native-cc-test`](#native-cc-test) * [`native-cc-toolchain`](#native-cc-toolchain) * [`native-cc-toolchain-suite`](#native-cc-toolchain-suite) * [`native-java-binary`](#native-java-binary) * [`native-java-common`](#native-java-common) * [`native-java-import`](#native-java-import) * [`native-java-info`](#native-java-info) * [`native-java-library`](#native-java-library) * [`native-java-lite-proto`](#native-java-lite-proto) * [`native-java-package-config`](#native-java-package-config) * [`native-java-plugin`](#native-java-plugin) * [`native-java-plugin-info`](#native-java-plugin-info) * [`native-java-proto`](#native-java-proto) * [`native-java-runtime`](#native-java-runtime) * [`native-java-test`](#native-java-test) * [`native-java-toolchain`](#native-java-toolchain) * [`native-package`](#native-package) * [`native-proto`](#native-proto) * [`native-proto-common`](#native-proto-common) * [`native-proto-info`](#native-proto-info) * [`native-proto-lang-toolchain`](#native-proto-lang-toolchain) * [`native-proto-lang-toolchain-info`](#native-proto-lang-toolchain-info) * [`native-py`](#native-py) * [`native-sh-binary`](#native-sh-binary) * [`native-sh-library`](#native-sh-library) * [`native-sh-test`](#native-sh-test) * [`no-effect`](#no-effect) * [`out-of-order-load`](#out-of-order-load) * [`output-group`](#output-group) * [`overly-nested-depset`](#overly-nested-depset) * [`package-name`](#package-name) * [`package-on-top`](#package-on-top) * [`positional-args`](#positional-args) * [`print`](#print) * [`provider-params`](#provider-params) * [`redefined-variable`](#redefined-variable) * [`repository-name`](#repository-name) * [`return-value`](#return-value) * [`rule-impl-return`](#rule-impl-return) * [`same-origin-load`](#same-origin-load) * [`skylark-comment`](#skylark-comment) * [`skylark-docstring`](#skylark-docstring) * [`string-iteration`](#string-iteration) * [`uninitialized`](#uninitialized) * [`unnamed-macro`](#unnamed-macro) * [`unreachable`](#unreachable) * [`unsorted-dict-items`](#unsorted-dict-items) * [`unused-variable`](#unused-variable) ### How to disable warnings All warnings can be disabled / suppressed / ignored by adding a special comment `# buildifier: disable=` to the expression that causes the warning. Historically comments with `buildozer` instead of `buildifier` are also supported, they are equivalent. To disable multiple warnings, you can specify them as a comma-separated list on a single line: ```python # buildifier: disable=warning1,warning2 ``` Or place them on separate lines: ```python # buildifier: disable=warning1 # buildifier: disable=warning2 ``` #### Examples ```python # buildifier: disable=no-effect """ A multiline comment as a string literal. Docstrings don't trigger the warning if they are first statements of a file or a function. """ if debug: print("Debug information:", foo) # buildifier: disable=print ``` -------------------------------------------------------------------------------- ## Symbol must be loaded from a specific location * Category name: `allowed-symbol-load-locations` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=allowed-symbol-load-locations` Warns when a symbol is loaded from a location other than the expected ones. Expected locations are specified in the tables file: ```json { "AllowedSymbolLoadLocations": { "genrule": [ "//tools/bazel:genrule.bzl" ] } } ``` -------------------------------------------------------------------------------- ## Do not use `applicable_licenses` as an attribute name. * Category name: `attr-applicable_licenses` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=attr-applicable_licenses` Using `applicable_licenses` as an attribute name may cause unexpected behavior. Its use may be prohibited in future Bazel releases. -------------------------------------------------------------------------------- ## `cfg = "data"` for attr definitions has no effect * Category name: `attr-cfg` * Flag in Bazel: [`--incompatible_disallow_data_transition`](https://github.com/bazelbuild/bazel/issues/6153) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=attr-cfg` The [Configuration](https://docs.bazel.build/versions/main/skylark/rules.html#configurations) `cfg = "data"` is deprecated and has no effect. Consider removing it. The [Configuration](https://docs.bazel.build/versions/main/skylark/rules.html#configurations) `cfg = "host"` is deprecated. Consider replacing it with `cfg = "exec"`. -------------------------------------------------------------------------------- ## `attr.license()` is deprecated and shouldn't be used * Category name: `attr-license` * Flag in Bazel: `--incompatible_no_attr_license` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=attr-license` The `attr.license()` method is almost never used and being deprecated. -------------------------------------------------------------------------------- ## Do not use `licenses` as an attribute name. * Category name: `attr-licenses` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=attr-licenses` Using licenses as an attribute name may cause unexpected behavior. -------------------------------------------------------------------------------- ## `non_empty` attribute for attr definitions is deprecated * Category name: `attr-non-empty` * Flag in Bazel: `--incompatible_disable_deprecated_attr_params` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=attr-non-empty` The `non_empty` [attribute](https://docs.bazel.build/versions/main/skylark/lib/attr.html) for attr definitions is deprecated, please use `allow_empty` with an opposite value instead. -------------------------------------------------------------------------------- ## The `default` parameter for `attr.output()`is deprecated * Category name: `attr-output-default` * Flag in Bazel: [`--incompatible_no_output_attr_default`](https://github.com/bazelbuild/bazel/issues/7950) * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=attr-output-default` The `default` parameter of `attr.output()` is bug-prone, as two targets of the same rule would be unable to exist in the same package under default behavior. Use Starlark macros to specify defaults for these attributes instead. -------------------------------------------------------------------------------- ## Do not use `package_metadata` as an attribute name. * Category name: `attr-package-metadata` * Automatic fix: no * Not supported by the latest version of Buildifier * [Suppress the warning](#suppress): `# buildifier: disable=attr-package-metadata` Using `package_metadata` as an attribute name may cause unexpected behavior. Its use may be prohibited in future Bazel releases. -------------------------------------------------------------------------------- ## `single_file` is deprecated * Category name: `attr-single-file` * Flag in Bazel: `--incompatible_disable_deprecated_attr_params` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=attr-single-file` The `single_file` [attribute](https://docs.bazel.build/versions/main/skylark/lib/attr.html) is deprecated, please use `allow_single_file` instead. -------------------------------------------------------------------------------- ## `*args` and `**kwargs` are not allowed in BUILD files * Category name: `build-args-kwargs` * Flag in Bazel: `--incompatible_no_kwargs_in_build_files` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=build-args-kwargs` Having `*args` or `**kwargs` makes BUILD files hard to read and manipulate. The list of arguments should be explicit. -------------------------------------------------------------------------------- ## Module shouldn't be used directly * Category name: `bzl-visibility` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=bzl-visibility` If a directory `foo` contains a subdirectory `internal` or `private`, only files located under `foo` can access it. For example, `dir/rules_mockascript/private/foo.bzl` can be loaded from `dir/rules_mockascript/private/bar.bzl` or `dir/rules_mockascript/sub/public.bzl`, but not from `dir/other_rule/file.bzl`. -------------------------------------------------------------------------------- ## Never use `l`, `I`, or `O` as names * Category name: `confusing-name` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=confusing-name` The names `l`, `I`, or `O` can be easily confused with `I`, `l`, or `0` correspondingly. -------------------------------------------------------------------------------- ## Glob pattern has no wildcard ('*') * Category name: `constant-glob` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=constant-glob` [Glob function](https://docs.bazel.build/versions/main/be/functions.html#glob) is used to get a list of files from the depot. The patterns (the first argument) typically include a wildcard (* character). A pattern without a wildcard is often useless and sometimes harmful. To fix the warning, move the string out of the glob: ```diff - glob(["*.cc", "test.cpp"]) + glob(["*.cc"]) + ["test.cpp"] ``` **There’s one important difference**: before the change, Bazel would silently ignore test.cpp if file is missing; after the change, Bazel will throw an error if file is missing. If `test.cpp` doesn’t exist, the fix becomes: ```diff - glob(["*.cc", "test.cpp"]) + glob(["*.cc"]) ``` which improves maintenance and readability. If no pattern has a wildcard, just remove the glob. It will also improve build performance (glob can be relatively slow): ```diff - glob(["test.cpp"]) + ["test.cpp"] ``` -------------------------------------------------------------------------------- ## `ctx.{action_name}` is deprecated * Category name: `ctx-actions` * Flag in Bazel: [`--incompatible_new_actions_api`](https://github.com/bazelbuild/bazel/issues/5825) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=ctx-actions` The following [actions](https://docs.bazel.build/versions/main/skylark/lib/actions.html) are deprecated, please use the new API: * [`ctx.new_file`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#new_file) → [`ctx.actions.declare_file`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#declare_file) * `ctx.experimental_new_directory` → [`ctx.actions.declare_directory`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#declare_directory) * [`ctx.file_action`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#file_action) → [`ctx.actions.write`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#write) * [`ctx.action(command = "...")`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#action) → [`ctx.actions.run_shell`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#run_shell) * [`ctx.action(executable = "...")`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#action) → [`ctx.actions.run`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#run) * [`ctx.empty_action`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#empty_action) → [`ctx.actions.do_nothing`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#do_nothing) * [`ctx.template_action`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#template_action) → [`ctx.actions.expand_template`](https://docs.bazel.build/versions/main/skylark/lib/actions.html#expand_template) -------------------------------------------------------------------------------- ## `ctx.actions.args().add()` for multiple arguments is deprecated * Category name: `ctx-args` * Flag in Bazel: [`--incompatible_disallow_old_style_args_add`](https://github.com/bazelbuild/bazel/issues/5822) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=ctx-args` It's deprecated to use the [`add`](https://docs.bazel.build/versions/main/skylark/lib/Args.html#add) method of `ctx.actions.args()` to add a list (or a depset) of variables. Please use either [`add_all`](https://docs.bazel.build/versions/main/skylark/lib/Args.html#add_all) or [`add_joined`](https://docs.bazel.build/versions/main/skylark/lib/Args.html#add_joined), depending on the desired behavior. -------------------------------------------------------------------------------- ## The function is deprecated * Category name: `deprecated-function` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=deprecated-function` The function defined in another .bzl file has a docstring stating that it's deprecated, i.e. it contains a `Deprecated:` section. The convention for function docstrings is described by the [`function-docstring`](#function-docstring) warning. -------------------------------------------------------------------------------- ## Depset's "items" parameter is deprecated * Category name: `depset-items` * Flag in Bazel: [`--incompatible_disable_depset_items`](https://github.com/bazelbuild/bazel/issues/9017) * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=depset-items` The `items` parameter for [`depset`](https://docs.bazel.build/versions/main/skylark/lib/globals.html#depset) is deprecated. In its old form it's either a list of direct elements to be added (use the `direct` or unnamed first parameter instead) or a depset that becomes a transitive element of the new depset (use the `transitive` parameter instead). -------------------------------------------------------------------------------- ## Depset iteration is deprecated * Category name: `depset-iteration` * Flag in Bazel: [`--incompatible_depset_is_not_iterable`](https://github.com/bazelbuild/bazel/issues/5816) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=depset-iteration` Depsets are complex structures, iterations over them and lookups require flattening them to a list which may be a heavy operation. To make it more obvious it's now required to call the `.to_list()` method on them in order to be able to iterate their items: ```python deps = depset() [x.path for x in deps] # deprecated [x.path for x in deps.to_list()] # recommended ``` -------------------------------------------------------------------------------- ## Depsets should be joined using the depset constructor * Category name: `depset-union` * Flag in Bazel: [`--incompatible_depset_union`](https://github.com/bazelbuild/bazel/issues/5817) * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=depset-union` The following ways to merge two depsets are deprecated: ```python depset1 + depset2 depset1 | depset2 depset1.union(depset2) ``` Please use the [depset](https://docs.bazel.build/versions/main/skylark/lib/depset.html) constructor instead: ```python depset(transitive = [depset1, depset2]) ``` When fixing this issue, make sure you [understand depsets](https://docs.bazel.build/versions/main/skylark/depsets.html) and try to [reduce the number of calls to depset](https://docs.bazel.build/versions/main/skylark/performance.html#reduce-the-number-of-calls-to-depset). See this [explanation](https://github.com/bazelbuild/bazel/issues/5817#issuecomment-496910826) for more detail. -------------------------------------------------------------------------------- ## Dictionary concatenation is deprecated * Category name: `dict-concatenation` * Flag in Bazel: [`--incompatible_disallow_dict_plus`](https://github.com/bazelbuild/bazel/issues/6461) * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=dict-concatenation` The `+` operator to concatenate dicts is deprecated. The operator used to create a new dict and copy the data to it. There are several ways to avoid it, for example, instead of `d = d1 + d2 + d3` you can use one of the following: * Use [Skylib](https://github.com/bazelbuild/bazel-skylib): ```python load("@bazel_skylib//lib:dicts.bzl", "dicts") d = dicts.add(d1, d2, d3) ``` * The same if you don't want to use Skylib: ```python d = dict(d1.items() + d2.items() + d3.items()) ``` * The same in several steps: ```python d = dict(d1) # If you don't want `d1` to be mutated d.update(d2) d.update(d3) ``` -------------------------------------------------------------------------------- ## Dict methods do not have a named argument `default` * Category name: `dict-method-named-arg` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=dict-method-named-arg` Dict methods `get`, `pop` and `setdefault` do not accept a named argument called `default`. Due to a bug, Bazel currently accepts that named argument. It is better to use a positional argument instead: ```diff - mydict.get(5, default = 0) + mydict.get(5, 0) ``` -------------------------------------------------------------------------------- ## A rule with name `foo` was already found on line * Category name: `duplicated-name` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=duplicated-name` Each label in Bazel has a unique name, and Bazel doesn’t allow two rules to have the same name. With macros, this may be accepted by Bazel (if each macro generates different rules): ```python my_first_macro(name = "foo") my_other_macro(name = "foo") ``` Although the build may work, this code can be very confusing. It can confuse users reading a BUILD file (if they look for the rule “fooâ€�, they may read see only one of the macros). It will also confuse tools that edit BUILD files. To fix the issue just change the name attribute of one rule/macro. -------------------------------------------------------------------------------- ## The `FileType` function is deprecated * Category name: `filetype` * Flag in Bazel: [`--incompatible_disallow_filetype`](https://github.com/bazelbuild/bazel/issues/5831) * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=filetype` The function `FileType` is deprecated. Instead of using it as an argument to the [`rule` function](https://docs.bazel.build/versions/main/skylark/lib/globals.html#rule) just use a list of strings. -------------------------------------------------------------------------------- ## Function docstring * Category names: * `function-docstring` * `function-docstring-header` * `function-docstring-args` * `function-docstring-return` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=function-docstring`, `# buildifier: disable=function-docstring-header`, `# buildifier: disable=function-docstring-args`, `# buildifier: disable=function-docstring-return` Public functions should have docstrings describing functions and their signatures. A docstring is a string literal (not a comment) which should be the first statement of a function (it may follow comment lines). Function docstrings are expected to be formatted in the following way: ```python """One-line summary: must be followed and may be preceded by a blank line. Optional additional description like this. If it's a function docstring and the function has more than one argument, the docstring has to document these parameters as follows: Args: parameter1: description of the first parameter. Each parameter line should be indented by one, preferably two, spaces (as here). parameter2: description of the second parameter that spans two lines. Each additional line should have a hanging indentation of at least one, preferably two, additional spaces (as here). another_parameter (unused, mutable): a parameter may be followed by additional attributes in parentheses Returns: Description of the return value. Should be indented by at least one, preferably two spaces (as here) Can span multiple lines. Deprecated: Optional, description of why the function is deprecated and what should be used instead. """ ``` Docstrings are required for all public functions with at least 5 statements. If a docstring exists it should start with a one-line summary line followed by an empty line. If a docstring is required or it describes some arguments, it should describe all of them. If a docstring is required and the function returns a value, it should be described. -------------------------------------------------------------------------------- ## Function `git_repository` is not global anymore * Category name: `git-repository` * Flag in Bazel: [`--incompatible_remove_native_git_repository`](https://github.com/bazelbuild/bazel/issues/6569) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=git-repository` Native `git_repository` and `new_git_repository` functions are removed. Please use the Starlark version instead: ```python load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") ``` -------------------------------------------------------------------------------- ## Function `http_archive` is not global anymore * Category name: `http-archive` * Flag in Bazel: [`--incompatible_remove_native_http_archive`](https://github.com/bazelbuild/bazel/issues/6570) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=http-archive` Native `http_archive` function is removed. Please use the Starlark version instead: ```python load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") ``` -------------------------------------------------------------------------------- ## The `/` operator for integer division is deprecated * Category name: `integer-division` * Flag in Bazel: [`--incompatible_disallow_slash_operator`](https://github.com/bazelbuild/bazel/issues/5823) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=integer-division` The `/` operator is deprecated in favor of `//`, please use the latter for integer division: ```python a = b // c d //= e ``` -------------------------------------------------------------------------------- ## Keyword parameter should be positional * Category name: `keyword-positional-params` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=keyword-positional-params` Some parameters for builtin functions in Starlark are keyword for legacy reasons; their names are not meaningful (e.g. `x`). Making them positional-only will improve the readability. -------------------------------------------------------------------------------- ## Prefer using `.append()` to adding a single element list * Category name: `list-append` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=list-append` Transforming `x += [expr]` to `x.append(expr)` avoids a list allocation. NOTE: .append() does not work if the target is a select, hence this warning can safely be ignored or suppressed. -------------------------------------------------------------------------------- ## Loaded symbol is unused * Category name: `load` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=load` ### Background [load](https://docs.bazel.build/versions/main/skylark/concepts.html#loading-an-extension) is used to import definitions in a BUILD file. If the definition is not used in the file, the load can be safely removed. If a symbol is loaded two times, you will get a warning on the second occurrence. ### How to fix it Delete the line. When load is used to import multiple symbols, you can remove the unused symbols from the list. To fix your BUILD files automatically, try this command: ```bash $ buildozer 'fix unusedLoads' path/to/BUILD ``` If you want to keep the load, you can disable the warning by adding a comment `# @unused`. -------------------------------------------------------------------------------- ## Load statements should be at the top of the file * Category name: `load-on-top` * Flag in Bazel: [`--incompatible_bzl_disallow_load_after_statement`](https://github.com/bazelbuild/bazel/issues/5815) * Automatic fix: yes * Not supported by the latest version of Buildifier * [Suppress the warning](#suppress): `# buildifier: disable=load-on-top` Obsolete; the warning has been implemented in the formatter and the fix is now automatically applied to all files except `WORKSPACE` files (unless suppressed). Load statements should be first statements (with the exception of `WORKSPACE` files), they can follow only comments and docstrings. -------------------------------------------------------------------------------- ## The `$(location)` make variable is deprecated * Category name: `make-location` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=make-location` The `$(location)` and `$(locations)` make variables are legacy synonyms for `$(execpath)` and `$(rootpath)` whose behavior depends on the attribute being expanded. Use `$(execpath ...)` when you need the execution path, or `$(rootpath ...)` when you need the runfiles path. Examples that trigger this warning: ```python genrule( name = "example", srcs = [":input"], outs = ["output"], cmd = "cp $(location :input) $@", ) ``` Instead, use an explicit make variable: ```python cmd = "cp $(execpath :input) $@", ``` -------------------------------------------------------------------------------- ## The file has no module docstring * Category name: `module-docstring` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=module-docstring` `.bzl` files should have docstrings on top of them. A docstring is a string literal (not a comment) which should be the first statement of the file (it may follow comment lines). For example: ```python """ This module contains build rules for my project. """ ... ``` -------------------------------------------------------------------------------- ## Name conventions * Category name: `name-conventions` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=name-conventions` By convention, all variables should be lower_snake_case, constant should be UPPER_SNAKE_CASE, and providers should be UpperCamelCase ending with `Info`. -------------------------------------------------------------------------------- ## All Android build rules should be loaded from Starlark * Category name: `native-android` * Flag in Bazel: [`--incompatible_disable_native_android_rules`](https://github.com/bazelbuild/bazel/issues/8391) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-android` The Android build rules should be loaded from Starlark. Update: the plans for disabling native rules [have been postponed](https://groups.google.com/g/bazel-discuss/c/XNvpWcge4AE/m/aJ-aQzszAwAJ), at the moment it's not required to load Starlark rules. -------------------------------------------------------------------------------- ## The `native` module shouldn't be used in BUILD files * Category name: `native-build` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-build` There's no need in using `native.` in BUILD files, its members are available as global symbols there. -------------------------------------------------------------------------------- ## All C++ build rules should be loaded from Starlark * Category names: * `native-cc-binary` * `native-cc-common` * `native-cc-debug-package-info` * `native-cc-fdo-prefetch-hints` * `native-cc-fdo-profile` * `native-cc-import` * `native-cc-info` * `native-cc-library` * `native-cc-memprof-profile` * `native-cc-objc-import` * `native-cc-objc-library` * `native-cc-propeller-optimize` * `native-cc-shared-library` * `native-cc-shared-library-hint-info` * `native-cc-shared-library-info` * `native-cc-test` * `native-cc-toolchain` * `native-cc-toolchain-suite` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-cc-binary`, `# buildifier: disable=native-cc-common`, `# buildifier: disable=native-cc-debug-package-info`, `# buildifier: disable=native-cc-fdo-prefetch-hints`, `# buildifier: disable=native-cc-fdo-profile`, `# buildifier: disable=native-cc-import`, `# buildifier: disable=native-cc-info`, `# buildifier: disable=native-cc-library`, `# buildifier: disable=native-cc-memprof-profile`, `# buildifier: disable=native-cc-objc-import`, `# buildifier: disable=native-cc-objc-library`, `# buildifier: disable=native-cc-propeller-optimize`, `# buildifier: disable=native-cc-shared-library`, `# buildifier: disable=native-cc-shared-library-hint-info`, `# buildifier: disable=native-cc-shared-library-info`, `# buildifier: disable=native-cc-test`, `# buildifier: disable=native-cc-toolchain`, `# buildifier: disable=native-cc-toolchain-suite` The C++ build rules should be loaded from @rules_cc. -------------------------------------------------------------------------------- ## cc_proto_library rule should be loaded from Starlark * Category name: `native-cc-proto` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-cc-proto` The cc_proto_library rule should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-binary` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-binary` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-common` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-common` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-import` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-import` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-info` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-info` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-library` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-library` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## java_lite_proto_library rule should be loaded from Starlark * Category name: `native-java-lite-proto` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-lite-proto` The java_lite_proto_library rule should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-package-config` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-package-config` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-plugin` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-plugin` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-plugin-info` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-plugin-info` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## java_proto_library rule should be loaded from Starlark * Category name: `native-java-proto` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-proto` The java_proto_library rule should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-runtime` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-runtime` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-test` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-test` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Java build rules should be loaded from Starlark * Category name: `native-java-toolchain` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-java-toolchain` The Java build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## `native.package()` shouldn't be used in .bzl files * Category name: `native-package` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=native-package` It's discouraged and will be disallowed to use `native.package()` in .bzl files. It can silently modify the semantics of a BUILD file and makes it hard to maintain. -------------------------------------------------------------------------------- ## proto_library rule should be loaded from Starlark * Category name: `native-proto` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-proto` The proto_library rule should be loaded from Starlark. -------------------------------------------------------------------------------- ## proto_common module should be loaded from Starlark * Category name: `native-proto-common` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-proto-common` The proto_common module should be loaded from Starlark. -------------------------------------------------------------------------------- ## ProtoInfo provider should be loaded from Starlark * Category name: `native-proto-info` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-proto-info` The ProtoInfo provider should be loaded from Starlark. -------------------------------------------------------------------------------- ## proto_lang_toolchain rule should be loaded from Starlark * Category name: `native-proto-lang-toolchain` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-proto-lang-toolchain` The proto_lang_toolchain rule should be loaded from Starlark. -------------------------------------------------------------------------------- ## ProtoLangToolchainInfo provider should be loaded from Starlark * Category name: `native-proto-lang-toolchain-info` * Flag in Bazel: [`--incompatible_autoload_externally`](https://github.com/bazelbuild/bazel/issues/23043) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-proto-lang-toolchain-info` The ProtoLangToolchainInfo provider should be loaded from Starlark. -------------------------------------------------------------------------------- ## All Python build rules should be loaded from Starlark * Category name: `native-py` * Flag in Bazel: [`--incompatible_load_python_rules_from_bzl`](https://github.com/bazelbuild/bazel/issues/9006) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-py` The Python build rules should be loaded from Starlark. Update: the plans for disabling native rules [have been postponed](https://groups.google.com/g/bazel-discuss/c/XNvpWcge4AE/m/aJ-aQzszAwAJ), at the moment it's not required to load Starlark rules. -------------------------------------------------------------------------------- ## sh_binary build rules should be loaded from Starlark * Category name: `native-sh-binary` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-sh-binary` The sh_binary build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## sh_library build rules should be loaded from Starlark * Category name: `native-sh-library` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-sh-library` The sh_library build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## sh_test build rules should be loaded from Starlark * Category name: `native-sh-test` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=native-sh-test` The sh_test build rules should be loaded from Starlark. -------------------------------------------------------------------------------- ## Expression result is not used * Category name: `no-effect` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=no-effect` The statement has no effect. Consider removing it or storing its result in a variable. -------------------------------------------------------------------------------- ## Load statements should be ordered by their labels * Category name: `out-of-order-load` * Automatic fix: yes * Not supported by the latest version of Buildifier * [Suppress the warning](#suppress): `# buildifier: disable=out-of-order-load` Obsolete; the warning has been implemented in the formatter and the fix is now automatically applied to all files (unless suppressed). Load statements should be ordered by their first argument - extension file label. This makes it easier to developers to locate loads of interest and reduces chances for conflicts when performing large-scale automated refactoring. When applying automated fixes, it's highly recommended to also use [`load-on-top`](#load-on-top) fixes, since otherwise the relative order of a symbol load and its usage can change resulting in runtime error. -------------------------------------------------------------------------------- ## `ctx.attr.dep.output_group` is deprecated * Category name: `output-group` * Flag in Bazel: [`--incompatible_no_target_output_group`](https://github.com/bazelbuild/bazel/issues/7949) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=output-group` The `output_group` field of a target is deprecated in favor of the [`OutputGroupInfo` provider](https://docs.bazel.build/versions/main/skylark/lib/OutputGroupInfo.html). -------------------------------------------------------------------------------- ## The depset is potentially overly nested * Category name: `overly-nested-depset` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=overly-nested-depset` If a depset is iteratively chained in a for loop, e.g. the following pattern is used: ```python for ...: x = depset(..., transitive = [..., x, ...]) ``` this can result in an overly nested depset with a long chain of transitive elements. Such patterns can lead to performance problems, consider refactoring the code to create a flat list of transitive elements and call the depset constructor just once: ```python transitive = [] for ...: transitive += ... x = depset(..., transitive = transitive) ``` Or in simple cases you can use list comprehensions instead: ```python x = depset(..., transitive = [y.deps for y in ...]) ``` For more information, read Bazel documentation about [depsets](https://docs.bazel.build/versions/main/skylark/depsets.html) and [reducing the number of calls to depset](https://docs.bazel.build/versions/main/skylark/performance.html#reduce-the-number-of-calls-to-depset). -------------------------------------------------------------------------------- ## Global variable `PACKAGE_NAME` is deprecated * Category name: `package-name` * Flag in Bazel: [`--incompatible_package_name_is_a_function`](https://github.com/bazelbuild/bazel/issues/5827) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=package-name` The global variable `PACKAGE_NAME` is deprecated, please use [`native.package_name()`](https://docs.bazel.build/versions/main/skylark/lib/native.html#package_name) instead. -------------------------------------------------------------------------------- ## Package declaration should be at the top of the file * Category name: `package-on-top` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=package-on-top` Here is a typical structure of a BUILD file: * `load()` statements * `package()` * calls to rules, macros Instantiating a rule and setting the package defaults later can be very confusing, and has been a source of bugs (tools and humans sometimes believe package applies to everything in a BUILD file). This might become an error in the future. ### What can be used before package()? The linter allows the following to be before `package()`: * comments * `load()` * variable declarations * `package_group()` * `licenses()` -------------------------------------------------------------------------------- ## Keyword arguments should be used over positional arguments * Category name: `positional-args` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=positional-args` All macro and rule calls should use keyword args over positional arguments. Positional arguments can cause subtle errors if the order is switched or if an argument is removed. Keyword args also greatly improve readability. Additionally, positional arguments prevent migration from [Legacy Macros](https://bazel.build/extending/legacy-macros) to [Symbolic Macros](https://bazel.build/extending/macros). ```diff - my_macro("foo", "bar") + my_macro(name = "foo", env = "bar") ``` The linter allows the following functions to be called with positional arguments: * `load()` * `vardef()` * `export_files()` * `licenses()` * `print()` -------------------------------------------------------------------------------- ## `print()` is a debug function and shouldn't be submitted * Category name: `print` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=print` Using the `print()` function for warnings is discouraged: they are often spammy and non actionable, the people who see the warning are usually not the people who can fix the code to make the warning disappear, and the actual maintainers of the code may never see the warning. -------------------------------------------------------------------------------- ## Calls to `provider` should specify a list of fields and a documentation * Category name: `provider-params` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=provider-params` Calls to `provider` should specify a documentation string and a list of fields: ```python ServerAddressInfo = provider( "The address of an HTTP server. Fields are host (string) and port (int).", fields = ["host", "port"] ) ``` Fields should also be documented when needed: ```python ServerAddressInfo = provider( "The address of an HTTP server.", fields = { "host": "string, e.g. 'example.com'", "port": "int, a TCP port number", } ) ``` Note that specifying a list of fields is a breaking change. It is an error if a call to the provider uses undeclared fields. If you cannot declare the list of fields, you may explicitly set it to None (and explain why in a comment). ```python AllInfo = provider("This provider accepts any field.", fields = None) NoneInfo = provider("This provider cannot have fields.", fields = []) ``` See the [documentation for providers](https://docs.bazel.build/versions/main/skylark/lib/globals.html#provider). -------------------------------------------------------------------------------- ## Variable has already been defined * Category name: `redefined-variable` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=redefined-variable` ### Background In .bzl files, redefining a global variable is already forbidden. This helps both humans and tools reason about the code. For consistency, we want to bring this restriction also to BUILD files. ### How to fix it Rename one of the variables. Note that the content of lists and dictionaries can still be modified. We will forbid reassignment, but not every side-effect. -------------------------------------------------------------------------------- ## Global variable `REPOSITORY_NAME` is deprecated * Category name: `repository-name` * Flag in Bazel: [`--incompatible_package_name_is_a_function`](https://github.com/bazelbuild/bazel/issues/5827) * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=repository-name` The global variable `REPOSITORY_NAME` is deprecated, please use [`native.repository_name()`](https://docs.bazel.build/versions/main/skylark/lib/native.html#repository_name) instead. -------------------------------------------------------------------------------- ## Some but not all execution paths of a function return a value * Category name: `return-value` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=return-value` Some but not all execution paths of a function return a value. Either there's an explicit empty `return` statement, or an implicit return in the end of a function. If it is intentional, make it explicit using `return None`. If you know certain parts of the code cannot be reached, add the statement `fail("unreachable")` to them. -------------------------------------------------------------------------------- ## Avoid using the legacy provider syntax * Category name: `rule-impl-return` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=rule-impl-return` Returning structs from rule implementation functions is [deprecated](https://docs.bazel.build/versions/main/skylark/rules.html#migrating-from-legacy-providers), consider using [providers](https://docs.bazel.build/versions/main/skylark/rules.html#providers) or lists of providers instead. -------------------------------------------------------------------------------- ## Same label is used for multiple loads * Category name: `same-origin-load` * Automatic fix: yes * Not supported by the latest version of Buildifier * [Suppress the warning](#suppress): `# buildifier: disable=same-origin-load` Obsolete; the warning has been implemented in the formatter and the fix is now automatically applied to all files except `WORKSPACE` files (unless suppressed). ### Background [load](https://docs.bazel.build/versions/main/skylark/concepts.html#loading-an-extension) is used to import definitions in a BUILD file. If the same label is used for loading symbols more the ones, all such loads can be merged into a single one. ### How to fix it Merge all loads into a single one. For example, ```python load(":f.bzl", "s1") load(":f.bzl", "s2") ``` can be written more compactly as ```python load(":f.bzl", "s1", "s2") ``` -------------------------------------------------------------------------------- ## "Skylark" is an outdated name of the language, please use "starlark" instead * Category names: * `skylark-comment` * `skylark-docstring` * Automatic fix: yes * [Suppress the warning](#suppress): `# buildifier: disable=skylark-comment`, `# buildifier: disable=skylark-docstring` The configuration language for Bazel is called "Starlark" now, the name "Skylark" is outdated and shouldn't be used. -------------------------------------------------------------------------------- ## String iteration is deprecated * Category name: `string-iteration` * Flag in Bazel: [`--incompatible_string_is_not_iterable`](https://github.com/bazelbuild/bazel/issues/5830) * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=string-iteration` Iteration over strings often leads to confusion with iteration over a sequence of strings, therefore strings won't be recognized as sequences of 1-element strings (like in Python). Use string indexing and `len` instead: ```python my_string = "hello world" for i in range(len(my_string)): char = my_string[i] # do something with char ``` -------------------------------------------------------------------------------- ## Variable may not have been initialized * Category name: `uninitialized` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=uninitialized` The local value can be not initialized at the time of execution. It may happen if it's initialized in one of the if-else clauses but not in all of them, or in a for-loop which can potentially be empty. -------------------------------------------------------------------------------- ## The macro should have a keyword argument called "name" * Category name: `unnamed-macro` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=unnamed-macro` By convention all macro functions should have a keyword argument called `name` (even if they don't use it). This is important for tooling and automation. A macro is a function that calls a rule (either directly or indirectly by calling other macros). If this function is a helper function that's not supposed to be used outside of its file, please make it private (rename it so that the name starts with `_`), this will prevent loading the function from BUILD files and suppress the warning. -------------------------------------------------------------------------------- ## The statement is unreachable * Category name: `unreachable` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=unreachable` The statement is unreachable because it follows a `return`, `break`, `continue`, or `fail()` statement. -------------------------------------------------------------------------------- ## Dictionary items should be ordered by their keys * Category name: `unsorted-dict-items` * Automatic fix: yes * [Disabled by default](buildifier/README.md#linter) * [Suppress the warning](#suppress): `# buildifier: disable=unsorted-dict-items` Dictionary items should be sorted lexicographically by their keys. This makes it easier to find the item of interest and reduces chances of conflicts when performing large-scale automated refactoring. The order is affected by `NamePriority` dictionary passed using `-tables` or `-add_tables` flags. If you want to preserve the original dictionary items order, you can disable the warning by adding a comment `# @unsorted-dict-items` to the dictionary expression or any of its enclosing expressions (binary, if etc). For example, ```python # @unsorted-dict-items d = { "b": "bvalue", "a": "avalue", } ``` will not be reported as an issue because the assignment operation that uses the dictionary with unsorted items has a comment disabling this warning. -------------------------------------------------------------------------------- ## Variable is unused * Category name: `unused-variable` * Automatic fix: no * [Suppress the warning](#suppress): `# buildifier: disable=unused-variable` This happens when a variable or function is set but not used in the file, e.g. ```python x = [1, 2] ``` The line can often be safely removed. If you want to keep the variable, you can disable the warning by adding a comment `# @unused`. For a function parameter, place the comment on the parameter itself. ```python x = [1, 2] # @unused # @unused def f( x, y # @unused ): pass ``` For backward compatibility, an `# @unused` comment at the end of a function header applies to its final parameter. New code should prefer attaching the comment directly to the parameter as shown above. ```python def f(x, y): # @unused applies to y pass ``` If an unused variable is used for partially unpacking tuples, just prefix its name with an underscore to suppress the warning: ```python x, _y = foo() for _, (a, _b) in iterable: print(a + x) ``` The same applies for function arguments that are not used by design: ```python def foo(a, _b, *_args): return bar(a) ``` If a tuple is unpacked not in a for-loop and all variables are unused, it'll still trigger a warning, even if all variables are underscored: ```python _a, _b = pair _unused = 3 ``` buildtools-10.1.0/api_proto/0000775000175000017500000000000015254573013015340 5ustar kenhyskenhysbuildtools-10.1.0/api_proto/api.proto0000664000175000017500000000237415254573013017204 0ustar kenhyskenhys/* 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; option go_package = "api_proto"; 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; MISSING_LIST_ITEM = 2; } } } } message RepeatedString { repeated string strings = 1; } buildtools-10.1.0/api_proto/api.gen.pb.go0000775000175000017500000003015615254573013017620 0ustar kenhyskenhys// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.10 // protoc v7.34.0 // source: api_proto/api.proto package api_proto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) 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 Output_Record_Field_MISSING_LIST_ITEM Output_Record_Field_ERROR = 2 ) // Enum value maps for Output_Record_Field_ERROR. var ( Output_Record_Field_ERROR_name = map[int32]string{ 0: "UNKNOWN", 1: "MISSING", 2: "MISSING_LIST_ITEM", } Output_Record_Field_ERROR_value = map[string]int32{ "UNKNOWN": 0, "MISSING": 1, "MISSING_LIST_ITEM": 2, } ) func (x Output_Record_Field_ERROR) Enum() *Output_Record_Field_ERROR { p := new(Output_Record_Field_ERROR) *p = x return p } func (x Output_Record_Field_ERROR) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Output_Record_Field_ERROR) Descriptor() protoreflect.EnumDescriptor { return file_api_proto_api_proto_enumTypes[0].Descriptor() } func (Output_Record_Field_ERROR) Type() protoreflect.EnumType { return &file_api_proto_api_proto_enumTypes[0] } func (x Output_Record_Field_ERROR) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Output_Record_Field_ERROR.Descriptor instead. func (Output_Record_Field_ERROR) EnumDescriptor() ([]byte, []int) { return file_api_proto_api_proto_rawDescGZIP(), []int{0, 0, 0, 0} } type Output struct { state protoimpl.MessageState `protogen:"open.v1"` Records []*Output_Record `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Output) Reset() { *x = Output{} mi := &file_api_proto_api_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Output) String() string { return protoimpl.X.MessageStringOf(x) } func (*Output) ProtoMessage() {} func (x *Output) ProtoReflect() protoreflect.Message { mi := &file_api_proto_api_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Output.ProtoReflect.Descriptor instead. func (*Output) Descriptor() ([]byte, []int) { return file_api_proto_api_proto_rawDescGZIP(), []int{0} } func (x *Output) GetRecords() []*Output_Record { if x != nil { return x.Records } return nil } type RepeatedString struct { state protoimpl.MessageState `protogen:"open.v1"` Strings []string `protobuf:"bytes,1,rep,name=strings,proto3" json:"strings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RepeatedString) Reset() { *x = RepeatedString{} mi := &file_api_proto_api_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RepeatedString) String() string { return protoimpl.X.MessageStringOf(x) } func (*RepeatedString) ProtoMessage() {} func (x *RepeatedString) ProtoReflect() protoreflect.Message { mi := &file_api_proto_api_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RepeatedString.ProtoReflect.Descriptor instead. func (*RepeatedString) Descriptor() ([]byte, []int) { return file_api_proto_api_proto_rawDescGZIP(), []int{1} } func (x *RepeatedString) GetStrings() []string { if x != nil { return x.Strings } return nil } type Output_Record struct { state protoimpl.MessageState `protogen:"open.v1"` Fields []*Output_Record_Field `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Output_Record) Reset() { *x = Output_Record{} mi := &file_api_proto_api_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Output_Record) String() string { return protoimpl.X.MessageStringOf(x) } func (*Output_Record) ProtoMessage() {} func (x *Output_Record) ProtoReflect() protoreflect.Message { mi := &file_api_proto_api_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Output_Record.ProtoReflect.Descriptor instead. func (*Output_Record) Descriptor() ([]byte, []int) { return file_api_proto_api_proto_rawDescGZIP(), []int{0, 0} } func (x *Output_Record) GetFields() []*Output_Record_Field { if x != nil { return x.Fields } return nil } type Output_Record_Field struct { state protoimpl.MessageState `protogen:"open.v1"` // 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"` QuoteWhenPrinting bool `protobuf:"varint,7,opt,name=quote_when_printing,json=quoteWhenPrinting,proto3" json:"quote_when_printing,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Output_Record_Field) Reset() { *x = Output_Record_Field{} mi := &file_api_proto_api_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Output_Record_Field) String() string { return protoimpl.X.MessageStringOf(x) } func (*Output_Record_Field) ProtoMessage() {} func (x *Output_Record_Field) ProtoReflect() protoreflect.Message { mi := &file_api_proto_api_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Output_Record_Field.ProtoReflect.Descriptor instead. func (*Output_Record_Field) Descriptor() ([]byte, []int) { return file_api_proto_api_proto_rawDescGZIP(), []int{0, 0, 0} } func (x *Output_Record_Field) GetValue() isOutput_Record_Field_Value { if x != nil { return x.Value } return nil } func (x *Output_Record_Field) GetText() string { if x != nil { if x, ok := x.Value.(*Output_Record_Field_Text); ok { return x.Text } } return "" } func (x *Output_Record_Field) GetNumber() int32 { if x != nil { if x, ok := x.Value.(*Output_Record_Field_Number); ok { return x.Number } } return 0 } func (x *Output_Record_Field) GetError() Output_Record_Field_ERROR { if x != nil { if x, ok := x.Value.(*Output_Record_Field_Error); ok { return x.Error } } return Output_Record_Field_UNKNOWN } func (x *Output_Record_Field) GetList() *RepeatedString { if x != nil { if x, ok := x.Value.(*Output_Record_Field_List); ok { return x.List } } return nil } func (x *Output_Record_Field) GetQuoteWhenPrinting() bool { if x != nil { return x.QuoteWhenPrinting } return false } type isOutput_Record_Field_Value interface { isOutput_Record_Field_Value() } type Output_Record_Field_Text struct { Text string `protobuf:"bytes,1,opt,name=text,proto3,oneof"` } type Output_Record_Field_Number struct { Number int32 `protobuf:"varint,2,opt,name=number,proto3,oneof"` } type Output_Record_Field_Error struct { Error Output_Record_Field_ERROR `protobuf:"varint,3,opt,name=error,proto3,enum=devtools.buildozer.Output_Record_Field_ERROR,oneof"` } type Output_Record_Field_List struct { List *RepeatedString `protobuf:"bytes,5,opt,name=list,proto3,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() {} var File_api_proto_api_proto protoreflect.FileDescriptor const file_api_proto_api_proto_rawDesc = "" + "\n" + "\x13api_proto/api.proto\x12\x12devtools.buildozer\"\xbf\x03\n" + "\x06Output\x12;\n" + "\arecords\x18\x01 \x03(\v2!.devtools.buildozer.Output.RecordR\arecords\x1a\xf7\x02\n" + "\x06Record\x12?\n" + "\x06fields\x18\x01 \x03(\v2'.devtools.buildozer.Output.Record.FieldR\x06fields\x1a\xab\x02\n" + "\x05Field\x12\x14\n" + "\x04text\x18\x01 \x01(\tH\x00R\x04text\x12\x18\n" + "\x06number\x18\x02 \x01(\x05H\x00R\x06number\x12E\n" + "\x05error\x18\x03 \x01(\x0e2-.devtools.buildozer.Output.Record.Field.ERRORH\x00R\x05error\x128\n" + "\x04list\x18\x05 \x01(\v2\".devtools.buildozer.RepeatedStringH\x00R\x04list\x12.\n" + "\x13quote_when_printing\x18\a \x01(\bR\x11quoteWhenPrinting\"8\n" + "\x05ERROR\x12\v\n" + "\aUNKNOWN\x10\x00\x12\v\n" + "\aMISSING\x10\x01\x12\x15\n" + "\x11MISSING_LIST_ITEM\x10\x02B\a\n" + "\x05value\"*\n" + "\x0eRepeatedString\x12\x18\n" + "\astrings\x18\x01 \x03(\tR\astringsB\vZ\tapi_protob\x06proto3" var ( file_api_proto_api_proto_rawDescOnce sync.Once file_api_proto_api_proto_rawDescData []byte ) func file_api_proto_api_proto_rawDescGZIP() []byte { file_api_proto_api_proto_rawDescOnce.Do(func() { file_api_proto_api_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_proto_api_proto_rawDesc), len(file_api_proto_api_proto_rawDesc))) }) return file_api_proto_api_proto_rawDescData } var file_api_proto_api_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_api_proto_api_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_api_proto_api_proto_goTypes = []any{ (Output_Record_Field_ERROR)(0), // 0: devtools.buildozer.Output.Record.Field.ERROR (*Output)(nil), // 1: devtools.buildozer.Output (*RepeatedString)(nil), // 2: devtools.buildozer.RepeatedString (*Output_Record)(nil), // 3: devtools.buildozer.Output.Record (*Output_Record_Field)(nil), // 4: devtools.buildozer.Output.Record.Field } var file_api_proto_api_proto_depIdxs = []int32{ 3, // 0: devtools.buildozer.Output.records:type_name -> devtools.buildozer.Output.Record 4, // 1: devtools.buildozer.Output.Record.fields:type_name -> devtools.buildozer.Output.Record.Field 0, // 2: devtools.buildozer.Output.Record.Field.error:type_name -> devtools.buildozer.Output.Record.Field.ERROR 2, // 3: devtools.buildozer.Output.Record.Field.list:type_name -> devtools.buildozer.RepeatedString 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_api_proto_api_proto_init() } func file_api_proto_api_proto_init() { if File_api_proto_api_proto != nil { return } file_api_proto_api_proto_msgTypes[3].OneofWrappers = []any{ (*Output_Record_Field_Text)(nil), (*Output_Record_Field_Number)(nil), (*Output_Record_Field_Error)(nil), (*Output_Record_Field_List)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_api_proto_rawDesc), len(file_api_proto_api_proto_rawDesc)), NumEnums: 1, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_api_proto_api_proto_goTypes, DependencyIndexes: file_api_proto_api_proto_depIdxs, EnumInfos: file_api_proto_api_proto_enumTypes, MessageInfos: file_api_proto_api_proto_msgTypes, }.Build() File_api_proto_api_proto = out.File file_api_proto_api_proto_goTypes = nil file_api_proto_api_proto_depIdxs = nil } buildtools-10.1.0/api_proto/BUILD.bazel0000664000175000017500000000172215254573013017220 0ustar kenhyskenhysload("@io_bazel_rules_go//go:def.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") load("//build:build_defs.bzl", "go_proto_checkedin_test") # gazelle:exclude api.gen.pb.go go_proto_checkedin_test( src = "api.gen.pb.go", allowed_diffs = ["^[[:space:]]*//.*protoc"], ) proto_library( name = "api_proto_proto", srcs = ["api.proto"], visibility = ["//visibility:public"], ) go_proto_library( name = "api_proto_go_proto", importpath = "github.com/bazel-contrib/buildtools/v10/api_proto", proto = ":api_proto_proto", visibility = ["//visibility:public"], ) go_library( name = "api_proto", embed = [":api_proto_go_proto"], importpath = "github.com/bazel-contrib/buildtools/v10/api_proto", visibility = ["//visibility:public"], ) alias( name = "go_default_library", actual = ":api_proto", visibility = ["//visibility:public"], ) buildtools-10.1.0/BUILD.bazel0000664000175000017500000000256215254573013015227 0ustar kenhyskenhysload("@bazel_gazelle//:def.bzl", "gazelle") load("//buildifier:def.bzl", "buildifier", "buildifier_test") exports_files([ "LICENSE", "launcher.js", "WORKSPACE", ]) config_setting( name = "windows", values = {"cpu": "x64_windows"}, ) filegroup( name = "warnings", srcs = ["WARNINGS.md"], visibility = ["//warn/docs:__pkg__"], ) test_suite( name = "tests", tests = [ ":make_location_lint", "//api_proto:api.gen.pb.go_checkshtest", "//build:build_test", "//build_proto:build.gen.pb.go_checkshtest", "//buildifier:buildifier_integration_test", "//deps_proto:deps.gen.pb.go_checkshtest", "//edit:edit_test", "//extra_actions_base_proto:extra_actions_base.gen.pb.go_checkshtest", "//labels:labels_test", "//lang:tables.gen.go_checkshtest", "//tables:tables_test", "//warn:warn_test", "//warn/docs:docs_test", "//warn/docs/proto:docs.gen.pb.go_checkshtest", "//wspace:wspace_test", ], ) # gazelle:go_naming_convention import_alias # gazelle:prefix github.com/bazel-contrib/buildtools/v10 gazelle( name = "gazelle", ) buildifier( name = "buildifier", ) buildifier_test( name = "make_location_lint", lint_mode = "warn", lint_warnings = ["make-location"], no_sandbox = True, workspace = "//:WORKSPACE", )