mirror of https://github.com/k3s-io/k3s
github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e
Used only by github.com/bazelbuild/bazel-gazelle, expecting 80c7f0d45d7ek3s-v1.15.3
parent
2cbf496c8e
commit
4bd9dcb855
2
go.mod
2
go.mod
|
@ -234,7 +234,7 @@ replace (
|
|||
github.com/auth0/go-jwt-middleware => github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7
|
||||
github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.16.26
|
||||
github.com/bazelbuild/bazel-gazelle => github.com/bazelbuild/bazel-gazelle v0.0.0-20181012220611-c728ce9f663e
|
||||
github.com/bazelbuild/buildtools => github.com/bazelbuild/buildtools v0.0.0-20171220125010-1a9c38e0df93
|
||||
github.com/bazelbuild/buildtools => github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e
|
||||
github.com/beorn7/perks => github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1
|
||||
github.com/blang/semver => github.com/blang/semver v3.5.0+incompatible
|
||||
github.com/boltdb/bolt => github.com/boltdb/bolt v1.3.1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -44,8 +44,8 @@ github.com/aws/aws-sdk-go v1.16.26 h1:GWkl3rkRO/JGRTWoLLIqwf7AWC4/W/1hMOUZqmX0js
|
|||
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/bazelbuild/bazel-gazelle v0.0.0-20181012220611-c728ce9f663e h1:k7E/Rdb9kYVDDrdCX46/GLgHhbxBs4BQz7+FDRf8lcg=
|
||||
github.com/bazelbuild/bazel-gazelle v0.0.0-20181012220611-c728ce9f663e/go.mod h1:uHBSeeATKpVazAACZBDPL/Nk/UhQDDsJWDlqYJo8/Us=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20171220125010-1a9c38e0df93 h1:HOcA9ovaT+3lN0RGVE8NpLHLU2poukBPujkIQOGI40U=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20171220125010-1a9c38e0df93/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e h1:VuTBHPJNCQ88Okm9ld5SyLCvU50soWJYQYjQFdcDxew=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 h1:OnJHjoVbY69GG4gclp0ngXfywigLhR6rrgUxmxQRWO4=
|
||||
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Code generated by protoc-gen-go.
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: api_proto/api.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package devtools_buildozer is a generated protocol buffer package.
|
||||
|
@ -101,9 +100,7 @@ func (m *Output_Record_Field) String() string { return proto.CompactT
|
|||
func (*Output_Record_Field) ProtoMessage() {}
|
||||
func (*Output_Record_Field) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0, 0} }
|
||||
|
||||
type isOutput_Record_Field_Value interface {
|
||||
isOutput_Record_Field_Value()
|
||||
}
|
||||
type isOutput_Record_Field_Value interface{ isOutput_Record_Field_Value() }
|
||||
|
||||
type Output_Record_Field_Text struct {
|
||||
Text string `protobuf:"bytes,1,opt,name=text,oneof"`
|
||||
|
|
|
@ -49,4 +49,4 @@ def genfile_check_test(src, gen):
|
|||
data = [src, gen],
|
||||
args = ["$(location " + src + ")", "$(location " + gen + ")"],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/bazelbuild/buildtools/tables"
|
||||
)
|
||||
|
||||
// Parse parses the input data and returns the corresponding parse tree.
|
||||
|
@ -35,15 +37,19 @@ func Parse(filename string, data []byte) (*File, error) {
|
|||
// 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
|
||||
comments []Comment // accumulated comments
|
||||
endRule int // position of end of current rule
|
||||
depth int // nesting of [ ] { } ( )
|
||||
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
|
||||
endStmt int // position of the end of the current statement
|
||||
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
|
||||
|
@ -55,14 +61,26 @@ type input struct {
|
|||
}
|
||||
|
||||
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},
|
||||
endStmt: -1, // -1 denotes it's not inside a statement
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -169,29 +187,23 @@ 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() {
|
||||
// The parser does not track indentation, because for the most part
|
||||
// BUILD expressions don't care about how they are indented.
|
||||
// However, we do need to be able to distinguish
|
||||
// If a single statement is split into multiple lines, we don't need
|
||||
// to track indentations and unindentations within these lines. For example:
|
||||
//
|
||||
// x = y[0]
|
||||
// def f(
|
||||
// # This indentation should be ignored
|
||||
// x):
|
||||
// # This unindentation should be ignored
|
||||
// # Actual indentation is from 0 to 2 spaces here
|
||||
// return x
|
||||
//
|
||||
// from the occasional
|
||||
// To handle this case, when we reach the beginning of a statement we scan forward to see where
|
||||
// it should end and record the number of input bytes remaining at that endpoint.
|
||||
//
|
||||
// x = y
|
||||
// [0]
|
||||
//
|
||||
// To handle this one case, when we reach the beginning of a
|
||||
// top-level BUILD expression, we scan forward to see where
|
||||
// it should end and record the number of input bytes remaining
|
||||
// at that endpoint. When we reach that point in the input, we
|
||||
// insert an implicit semicolon to force the two expressions
|
||||
// to stay separate.
|
||||
//
|
||||
if in.endRule != 0 && len(in.remaining) == in.endRule {
|
||||
in.endRule = 0
|
||||
in.lastToken = "implicit ;"
|
||||
val.tok = ";"
|
||||
return ';'
|
||||
// If --format_bzl is set to false, top level blocks (e.g. an entire function definition)
|
||||
// is considered as a single statement.
|
||||
if in.endStmt != -1 && len(in.remaining) == in.endStmt {
|
||||
in.endStmt = -1
|
||||
}
|
||||
|
||||
// Skip over spaces. Count newlines so we can give the parser
|
||||
|
@ -199,15 +211,19 @@ func (in *input) Lex(val *yySymType) int {
|
|||
// for top-level comment assignment.
|
||||
c := in.peekRune()
|
||||
if c == ' ' || c == '\t' || c == '\r' || c == '\n' {
|
||||
if c == '\n' && in.endRule == 0 {
|
||||
// Not in a rule. Tell parser about top-level blank line.
|
||||
in.startToken(val)
|
||||
in.readRune()
|
||||
in.endToken(val)
|
||||
return '\n'
|
||||
}
|
||||
if c == '\n' {
|
||||
in.indent = 0
|
||||
in.cleanLine = true
|
||||
if in.endStmt == -1 {
|
||||
// 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
|
||||
|
@ -215,6 +231,11 @@ func (in *input) Lex(val *yySymType) int {
|
|||
|
||||
// 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
|
||||
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.
|
||||
|
@ -231,10 +252,12 @@ func (in *input) Lex(val *yySymType) int {
|
|||
isSuffix = false
|
||||
}
|
||||
|
||||
// Consume comment.
|
||||
// Consume comment without the \n it ends with.
|
||||
in.startToken(val)
|
||||
for len(in.remaining) > 0 && in.readRune() != '\n' {
|
||||
for len(in.remaining) > 0 && in.peekRune() != '\n' {
|
||||
in.readRune()
|
||||
}
|
||||
|
||||
in.endToken(val)
|
||||
|
||||
val.tok = strings.TrimRight(val.tok, "\n")
|
||||
|
@ -243,22 +266,27 @@ func (in *input) Lex(val *yySymType) int {
|
|||
// 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.endRule == 0 {
|
||||
// Not in a rule. Tell parser about top-level comment.
|
||||
if in.endStmt == -1 {
|
||||
// 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.comments = append(in.comments, Comment{val.pos, "", false})
|
||||
in.lineComments = append(in.lineComments, Comment{val.pos, ""})
|
||||
}
|
||||
in.comments = append(in.comments, Comment{val.pos, val.tok, isSuffix})
|
||||
countNL = 1
|
||||
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.
|
||||
// We can ignore a trailing \ at end of line together with the \n.
|
||||
in.readRune()
|
||||
in.readRune()
|
||||
continue
|
||||
}
|
||||
|
@ -267,6 +295,41 @@ func (in *input) Lex(val *yySymType) int {
|
|||
break
|
||||
}
|
||||
|
||||
// Check for changes in indentation
|
||||
// Skip if --format_bzl is set to false, if we're inside a statement, or if there were non-space
|
||||
// characters before in the current line.
|
||||
if tables.FormatBzlFiles && in.endStmt == -1 && 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)
|
||||
|
@ -277,16 +340,9 @@ func (in *input) Lex(val *yySymType) int {
|
|||
return _EOF
|
||||
}
|
||||
|
||||
// If endRule is 0, we need to recompute where the end
|
||||
// of the next rule (Python expression) is, so that we can
|
||||
// generate a virtual end-of-rule semicolon (see above).
|
||||
if in.endRule == 0 {
|
||||
in.endRule = len(in.skipPython(in.remaining))
|
||||
if in.endRule == 0 {
|
||||
// skipPython got confused.
|
||||
// No more virtual semicolons.
|
||||
in.endRule = -1
|
||||
}
|
||||
// If endStmt is 0, we need to recompute where the end of the next statement is.
|
||||
if in.endStmt == -1 {
|
||||
in.endStmt = len(in.skipStmt(in.remaining))
|
||||
}
|
||||
|
||||
// Punctuation tokens.
|
||||
|
@ -301,12 +357,17 @@ func (in *input) Lex(val *yySymType) int {
|
|||
in.readRune()
|
||||
return c
|
||||
|
||||
case '.', '-', '%', ':', ';', ',', '/', '*': // single-char tokens
|
||||
case '.', ':', ';', ',': // single-char tokens
|
||||
in.readRune()
|
||||
return c
|
||||
|
||||
case '<', '>', '=', '!', '+': // possibly followed by =
|
||||
case '<', '>', '=', '!', '+', '-', '*', '/', '%': // possibly followed by =
|
||||
in.readRune()
|
||||
if c == '/' && in.peekRune() == '/' {
|
||||
// integer division
|
||||
in.readRune()
|
||||
}
|
||||
|
||||
if in.peekRune() == '=' {
|
||||
in.readRune()
|
||||
switch c {
|
||||
|
@ -318,8 +379,8 @@ func (in *input) Lex(val *yySymType) int {
|
|||
return _EQ
|
||||
case '!':
|
||||
return _NE
|
||||
case '+':
|
||||
return _ADDEQ
|
||||
default:
|
||||
return _AUGM
|
||||
}
|
||||
}
|
||||
return c
|
||||
|
@ -395,15 +456,17 @@ func (in *input) Lex(val *yySymType) int {
|
|||
in.Error(fmt.Sprintf("unexpected input character %#q", c))
|
||||
}
|
||||
|
||||
// Look for raw Python block (class, def, if, etc at beginning of line) and pass through.
|
||||
if in.depth == 0 && in.pos.LineRune == 1 && hasPythonPrefix(in.remaining) {
|
||||
// Find end of Python block and advance input beyond it.
|
||||
// Have to loop calling readRune in order to maintain line number info.
|
||||
rest := in.skipPython(in.remaining)
|
||||
for len(in.remaining) > len(rest) {
|
||||
in.readRune()
|
||||
if !tables.FormatBzlFiles {
|
||||
// Look for raw Python block (class, def, if, etc at beginning of line) and pass through.
|
||||
if in.depth == 0 && in.pos.LineRune == 1 && hasPythonPrefix(in.remaining) {
|
||||
// Find end of Python block and advance input beyond it.
|
||||
// Have to loop calling readRune in order to maintain line number info.
|
||||
rest := in.skipStmt(in.remaining)
|
||||
for len(in.remaining) > len(rest) {
|
||||
in.readRune()
|
||||
}
|
||||
return _PYTHON
|
||||
}
|
||||
return _PYTHON
|
||||
}
|
||||
|
||||
// Scan over alphanumeric identifier.
|
||||
|
@ -442,11 +505,15 @@ var keywordToken = map[string]int{
|
|||
"for": _FOR,
|
||||
"if": _IF,
|
||||
"else": _ELSE,
|
||||
"elif": _ELIF,
|
||||
"in": _IN,
|
||||
"is": _IS,
|
||||
"lambda": _LAMBDA,
|
||||
"load": _LOAD,
|
||||
"not": _NOT,
|
||||
"or": _OR,
|
||||
"def": _DEF,
|
||||
"return": _RETURN,
|
||||
}
|
||||
|
||||
// Python scanning.
|
||||
|
@ -457,6 +524,10 @@ var keywordToken = map[string]int{
|
|||
// hasPythonPrefix reports whether p begins with a keyword that would
|
||||
// introduce an uninterpreted Python block.
|
||||
func hasPythonPrefix(p []byte) bool {
|
||||
if tables.FormatBzlFiles {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, pre := range prefixes {
|
||||
if hasPrefixSpace(p, pre) {
|
||||
return true
|
||||
|
@ -474,10 +545,14 @@ var prefixes = []string{
|
|||
"for",
|
||||
"if",
|
||||
"try",
|
||||
"else",
|
||||
"elif",
|
||||
"except",
|
||||
}
|
||||
|
||||
// hasPrefixSpace reports whether p begins with pre followed by a space or colon.
|
||||
func hasPrefixSpace(p []byte, pre string) bool {
|
||||
|
||||
if len(p) <= len(pre) || p[len(pre)] != ' ' && p[len(pre)] != '\t' && p[len(pre)] != ':' {
|
||||
return false
|
||||
}
|
||||
|
@ -489,44 +564,46 @@ func hasPrefixSpace(p []byte, pre string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func isBlankOrComment(b []byte) bool {
|
||||
// A utility function for the legacy formatter.
|
||||
// Returns whether a given code starts with a top-level statement (maybe with some preceeding
|
||||
// comments and blank lines)
|
||||
func isOutsideBlock(b []byte) bool {
|
||||
isBlankLine := true
|
||||
isComment := false
|
||||
for _, c := range b {
|
||||
if c == '#' || c == '\n' {
|
||||
return true
|
||||
}
|
||||
if c != ' ' && c != '\t' && c != '\r' {
|
||||
return false
|
||||
switch {
|
||||
case c == ' ' || c == '\t' || c == '\r':
|
||||
isBlankLine = false
|
||||
case c == '#':
|
||||
isBlankLine = false
|
||||
isComment = true
|
||||
case c == '\n':
|
||||
isBlankLine = true
|
||||
isComment = false
|
||||
default:
|
||||
if !isComment {
|
||||
return isBlankLine
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// hasPythonContinuation reports whether p begins with a keyword that
|
||||
// continues an uninterpreted Python block.
|
||||
func hasPythonContinuation(p []byte) bool {
|
||||
for _, pre := range continuations {
|
||||
if hasPrefixSpace(p, pre) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// These keywords continue uninterpreted Python blocks.
|
||||
var continuations = []string{
|
||||
"except",
|
||||
"else",
|
||||
}
|
||||
|
||||
// skipPython returns the data remaining after the uninterpreted
|
||||
// Python block beginning at p. It does not advance the input position.
|
||||
// skipStmt returns the data remaining after the statement beginning at p.
|
||||
// It does not advance the input position.
|
||||
// (The only reason for the input receiver is to be able to call in.Error.)
|
||||
func (in *input) skipPython(p []byte) []byte {
|
||||
func (in *input) skipStmt(p []byte) []byte {
|
||||
quote := byte(0) // if non-zero, the kind of quote we're in
|
||||
tripleQuote := false // if true, the quote is a triple quote
|
||||
depth := 0 // nesting depth for ( ) [ ] { }
|
||||
var rest []byte // data after the Python block
|
||||
|
||||
defer func() {
|
||||
if quote != 0 {
|
||||
in.Error("EOF scanning Python quoted string")
|
||||
}
|
||||
}()
|
||||
|
||||
// Scan over input one byte at a time until we find
|
||||
// an unindented, non-blank, non-comment line
|
||||
// outside quoted strings and brackets.
|
||||
|
@ -559,20 +636,23 @@ func (in *input) skipPython(p []byte) []byte {
|
|||
continue
|
||||
}
|
||||
|
||||
if depth == 0 && i > 0 && p[i-1] == '\n' && (i < 2 || p[i-2] != '\\') {
|
||||
if depth == 0 && i > 0 && p[i] == '\n' && p[i-1] != '\\' {
|
||||
// Possible stopping point. Save the earliest one we find.
|
||||
if rest == nil {
|
||||
rest = p[i:]
|
||||
}
|
||||
|
||||
if !isBlankOrComment(p[i:]) {
|
||||
if !hasPythonContinuation(p[i:]) && c != ' ' && c != '\t' {
|
||||
// Yes, stop here.
|
||||
break
|
||||
}
|
||||
// Not a stopping point after all.
|
||||
rest = nil
|
||||
if tables.FormatBzlFiles {
|
||||
// In the bzl files mode we only care about the end of the statement, we've found it.
|
||||
return rest
|
||||
}
|
||||
// In the legacy mode we need to find where the current block ends
|
||||
if isOutsideBlock(p[i+1:]) {
|
||||
return rest
|
||||
}
|
||||
// Not a stopping point after all.
|
||||
rest = nil
|
||||
|
||||
}
|
||||
|
||||
switch c {
|
||||
|
@ -581,6 +661,8 @@ func (in *input) skipPython(p []byte) []byte {
|
|||
for i < len(p) && p[i] != '\n' {
|
||||
i++
|
||||
}
|
||||
// Rewind 1 position back because \n should be handled at the next iteration
|
||||
i--
|
||||
|
||||
case '(', '[', '{':
|
||||
depth++
|
||||
|
@ -589,9 +671,6 @@ func (in *input) skipPython(p []byte) []byte {
|
|||
depth--
|
||||
}
|
||||
}
|
||||
if quote != 0 {
|
||||
in.Error("EOF scanning Python quoted string")
|
||||
}
|
||||
return rest
|
||||
}
|
||||
|
||||
|
@ -691,8 +770,9 @@ func (in *input) order(v Expr) {
|
|||
in.order(&v.End)
|
||||
case *SliceExpr:
|
||||
in.order(v.X)
|
||||
in.order(v.Y)
|
||||
in.order(v.Z)
|
||||
in.order(v.From)
|
||||
in.order(v.To)
|
||||
in.order(v.Step)
|
||||
case *IndexExpr:
|
||||
in.order(v.X)
|
||||
in.order(v.Y)
|
||||
|
@ -701,6 +781,32 @@ func (in *input) order(v Expr) {
|
|||
in.order(name)
|
||||
}
|
||||
in.order(v.Expr)
|
||||
case *ReturnExpr:
|
||||
if v.X != nil {
|
||||
in.order(v.X)
|
||||
}
|
||||
case *FuncDef:
|
||||
for _, x := range v.Args {
|
||||
in.order(x)
|
||||
}
|
||||
for _, x := range v.Body.Statements {
|
||||
in.order(x)
|
||||
}
|
||||
case *ForLoop:
|
||||
for _, x := range v.LoopVars {
|
||||
in.order(x)
|
||||
}
|
||||
in.order(v.Iterable)
|
||||
for _, x := range v.Body.Statements {
|
||||
in.order(x)
|
||||
}
|
||||
case *IfElse:
|
||||
for _, condition := range v.Conditions {
|
||||
in.order(condition.If)
|
||||
for _, x := range condition.Then.Statements {
|
||||
in.order(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
if v != nil {
|
||||
in.post = append(in.post, v)
|
||||
|
@ -712,17 +818,8 @@ func (in *input) assignComments() {
|
|||
// Generate preorder and postorder lists.
|
||||
in.order(in.file)
|
||||
|
||||
// Split into whole-line comments and suffix comments.
|
||||
var line, suffix []Comment
|
||||
for _, com := range in.comments {
|
||||
if com.Suffix {
|
||||
suffix = append(suffix, com)
|
||||
} else {
|
||||
line = append(line, com)
|
||||
}
|
||||
}
|
||||
|
||||
// Assign line comments to syntax immediately following.
|
||||
line := in.lineComments
|
||||
for _, x := range in.pre {
|
||||
start, _ := x.Span()
|
||||
xcom := x.Comment()
|
||||
|
@ -736,6 +833,7 @@ func (in *input) assignComments() {
|
|||
in.file.After = append(in.file.After, line...)
|
||||
|
||||
// Assign suffix comments to syntax immediately before.
|
||||
suffix := in.suffixComments
|
||||
for i := len(in.post) - 1; i >= 0; i-- {
|
||||
x := in.post[i]
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ package build
|
|||
forsifs []*ForClauseWithIfClausesOpt
|
||||
string *StringExpr
|
||||
strings []*StringExpr
|
||||
block CodeBlock
|
||||
|
||||
// supporting information
|
||||
comma Position // position of trailing comma in list, if present
|
||||
|
@ -69,7 +70,7 @@ package build
|
|||
// However, we do not want to export them from the Go package
|
||||
// we are creating, so prefix them all with underscores.
|
||||
|
||||
%token <pos> _ADDEQ // operator +=
|
||||
%token <pos> _AUGM // augmented assignment
|
||||
%token <pos> _AND // keyword and
|
||||
%token <pos> _COMMENT // top-level # comment
|
||||
%token <pos> _EOF // end of file
|
||||
|
@ -79,34 +80,47 @@ package build
|
|||
%token <pos> _IDENT // non-keyword identifier or number
|
||||
%token <pos> _IF // keyword if
|
||||
%token <pos> _ELSE // keyword else
|
||||
%token <pos> _ELIF // keyword elif
|
||||
%token <pos> _IN // keyword in
|
||||
%token <pos> _IS // keyword is
|
||||
%token <pos> _LAMBDA // keyword lambda
|
||||
%token <pos> _LOAD // keyword load
|
||||
%token <pos> _LE // operator <=
|
||||
%token <pos> _NE // operator !=
|
||||
%token <pos> _NOT // keyword not
|
||||
%token <pos> _OR // keyword or
|
||||
%token <pos> _PYTHON // uninterpreted Python block
|
||||
%token <pos> _STRING // quoted string
|
||||
%token <pos> _DEF // keyword def
|
||||
%token <pos> _RETURN // keyword return
|
||||
%token <pos> _INDENT // indentation
|
||||
%token <pos> _UNINDENT // unindentation
|
||||
|
||||
%type <pos> comma_opt
|
||||
%type <expr> expr
|
||||
%type <expr> expr_opt
|
||||
%type <expr> primary_expr
|
||||
%type <exprs> exprs
|
||||
%type <exprs> exprs_opt
|
||||
%type <exprs> primary_exprs
|
||||
%type <forc> for_clause
|
||||
%type <forifs> for_clause_with_if_clauses_opt
|
||||
%type <forsifs> for_clauses_with_if_clauses_opt
|
||||
%type <expr> ident
|
||||
%type <exprs> idents
|
||||
%type <ifs> if_clauses_opt
|
||||
%type <exprs> stmts
|
||||
%type <expr> stmt
|
||||
%type <exprs> stmt // a simple_stmt or a for/if/def block
|
||||
%type <expr> block_stmt // a single for/if/def statement
|
||||
%type <expr> if_else_block // a single if-else statement
|
||||
%type <exprs> simple_stmt // One or many small_stmts on one line, e.g. 'a = f(x); return str(a)'
|
||||
%type <expr> small_stmt // A single statement, e.g. 'a = f(x)'
|
||||
%type <exprs> small_stmts_continuation // A sequence of `';' small_stmt`
|
||||
%type <expr> keyvalue
|
||||
%type <exprs> keyvalues
|
||||
%type <exprs> keyvalues_no_comma
|
||||
%type <string> string
|
||||
%type <strings> strings
|
||||
%type <block> suite
|
||||
|
||||
// Operator precedence.
|
||||
// Operators listed lower in the table bind tighter.
|
||||
|
@ -120,25 +134,25 @@ package build
|
|||
|
||||
%left '\n'
|
||||
%left _ASSERT
|
||||
// '=' and '+=' have the lowest precedence
|
||||
// '=' 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 '=' _ADDEQ
|
||||
%left _IF _ELSE
|
||||
%left ','
|
||||
%left ':'
|
||||
%left _IN _NOT _IS
|
||||
%left _OR
|
||||
%left _AND
|
||||
%left '<' '>' _EQ _NE _LE _GE
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
%left '.' '[' '('
|
||||
%right _UNARY
|
||||
%left _STRING
|
||||
%left '=' _AUGM
|
||||
%left _IF _ELSE _ELIF
|
||||
%left ','
|
||||
%left ':'
|
||||
%left _IN _NOT _IS
|
||||
%left _OR
|
||||
%left _AND
|
||||
%left '<' '>' _EQ _NE _LE _GE
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
%left '.' '[' '('
|
||||
%right _UNARY
|
||||
%left _STRING
|
||||
|
||||
%%
|
||||
|
||||
|
@ -155,26 +169,46 @@ file:
|
|||
return 0
|
||||
}
|
||||
|
||||
suite:
|
||||
'\n' _INDENT stmts _UNINDENT
|
||||
{
|
||||
$$ = CodeBlock{
|
||||
Start: $2,
|
||||
Statements: $3,
|
||||
End: End{Pos: $4},
|
||||
}
|
||||
}
|
||||
| simple_stmt
|
||||
{
|
||||
// simple_stmt is never empty
|
||||
start, _ := $1[0].Span()
|
||||
_, end := $1[len($1)-1].Span()
|
||||
$$ = CodeBlock{
|
||||
Start: start,
|
||||
Statements: $1,
|
||||
End: End{Pos: end},
|
||||
}
|
||||
}
|
||||
|
||||
stmts:
|
||||
{
|
||||
$$ = nil
|
||||
$<lastRule>$ = nil
|
||||
}
|
||||
| stmts stmt comma_opt semi_opt
|
||||
| stmts stmt
|
||||
{
|
||||
// If this statement follows a comment block,
|
||||
// attach the comments to the statement.
|
||||
if cb, ok := $<lastRule>1.(*CommentBlock); ok {
|
||||
$$ = $1
|
||||
$$[len($1)-1] = $2
|
||||
$2.Comment().Before = cb.After
|
||||
$<lastRule>$ = $2
|
||||
$$ = append($1[:len($1)-1], $2...)
|
||||
$2[0].Comment().Before = cb.After
|
||||
$<lastRule>$ = $2[len($2)-1]
|
||||
break
|
||||
}
|
||||
|
||||
// Otherwise add to list.
|
||||
$$ = append($1, $2)
|
||||
$<lastRule>$ = $2
|
||||
$$ = append($1, $2...)
|
||||
$<lastRule>$ = $2[len($2)-1]
|
||||
|
||||
// Consider this input:
|
||||
//
|
||||
|
@ -187,7 +221,8 @@ stmts:
|
|||
// for baz() instead.
|
||||
if x := $<lastRule>1; x != nil {
|
||||
com := x.Comment()
|
||||
$2.Comment().Before = com.After
|
||||
// stmt is never empty
|
||||
$2[0].Comment().Before = com.After
|
||||
com.After = nil
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +232,7 @@ stmts:
|
|||
$$ = $1
|
||||
$<lastRule>$ = nil
|
||||
}
|
||||
| stmts _COMMENT
|
||||
| stmts _COMMENT '\n'
|
||||
{
|
||||
$$ = $1
|
||||
$<lastRule>$ = $<lastRule>1
|
||||
|
@ -211,24 +246,209 @@ stmts:
|
|||
}
|
||||
|
||||
stmt:
|
||||
simple_stmt
|
||||
{
|
||||
$$ = $1
|
||||
}
|
||||
| block_stmt
|
||||
{
|
||||
$$ = []Expr{$1}
|
||||
}
|
||||
|
||||
block_stmt:
|
||||
_DEF _IDENT '(' exprs_opt ')' ':' suite
|
||||
{
|
||||
$$ = &FuncDef{
|
||||
Start: $1,
|
||||
Name: $<tok>2,
|
||||
ListStart: $3,
|
||||
Args: $4,
|
||||
Body: $7,
|
||||
End: $7.End,
|
||||
ForceCompact: forceCompact($3, $4, $5),
|
||||
ForceMultiLine: forceMultiLine($3, $4, $5),
|
||||
}
|
||||
}
|
||||
| _FOR primary_exprs _IN expr ':' suite
|
||||
{
|
||||
$$ = &ForLoop{
|
||||
Start: $1,
|
||||
LoopVars: $2,
|
||||
Iterable: $4,
|
||||
Body: $6,
|
||||
End: $6.End,
|
||||
}
|
||||
}
|
||||
| if_else_block
|
||||
{
|
||||
$$ = $1
|
||||
}
|
||||
|
||||
if_else_block:
|
||||
_IF expr ':' suite
|
||||
{
|
||||
$$ = &IfElse{
|
||||
Start: $1,
|
||||
Conditions: []Condition{
|
||||
Condition{
|
||||
If: $2,
|
||||
Then: $4,
|
||||
},
|
||||
},
|
||||
End: $4.End,
|
||||
}
|
||||
}
|
||||
| if_else_block elif expr ':' suite
|
||||
{
|
||||
block := $1.(*IfElse)
|
||||
block.Conditions = append(block.Conditions, Condition{
|
||||
If: $3,
|
||||
Then: $5,
|
||||
})
|
||||
block.End = $5.End
|
||||
$$ = block
|
||||
}
|
||||
| if_else_block _ELSE ':' suite
|
||||
{
|
||||
block := $1.(*IfElse)
|
||||
block.Conditions = append(block.Conditions, Condition{
|
||||
Then: $4,
|
||||
})
|
||||
block.End = $4.End
|
||||
$$ = block
|
||||
}
|
||||
|
||||
elif:
|
||||
_ELSE _IF
|
||||
| _ELIF
|
||||
|
||||
simple_stmt:
|
||||
small_stmt small_stmts_continuation semi_opt '\n'
|
||||
{
|
||||
$$ = append([]Expr{$1}, $2...)
|
||||
$<lastRule>$ = $$[len($$)-1]
|
||||
}
|
||||
|
||||
small_stmts_continuation:
|
||||
{
|
||||
$$ = []Expr{}
|
||||
}
|
||||
| small_stmts_continuation ';' small_stmt
|
||||
{
|
||||
$$ = append($1, $3)
|
||||
}
|
||||
|
||||
small_stmt:
|
||||
expr %prec ShiftInstead
|
||||
| _RETURN expr
|
||||
{
|
||||
_, end := $2.Span()
|
||||
$$ = &ReturnExpr{
|
||||
X: $2,
|
||||
End: end,
|
||||
}
|
||||
}
|
||||
| _RETURN
|
||||
{
|
||||
$$ = &ReturnExpr{End: $1}
|
||||
}
|
||||
| _PYTHON
|
||||
{
|
||||
$$ = &PythonBlock{Start: $1, Token: $<tok>1}
|
||||
}
|
||||
|
||||
semi_opt:
|
||||
| semi_opt ';'
|
||||
| ';'
|
||||
|
||||
expr:
|
||||
primary_expr:
|
||||
ident
|
||||
| primary_expr '.' _IDENT
|
||||
{
|
||||
$$ = &DotExpr{
|
||||
X: $1,
|
||||
Dot: $2,
|
||||
NamePos: $3,
|
||||
Name: $<tok>3,
|
||||
}
|
||||
}
|
||||
| _LOAD '(' exprs_opt ')'
|
||||
{
|
||||
$$ = &CallExpr{
|
||||
X: &LiteralExpr{Start: $1, Token: "load"},
|
||||
ListStart: $2,
|
||||
List: $3,
|
||||
End: End{Pos: $4},
|
||||
ForceCompact: forceCompact($2, $3, $4),
|
||||
ForceMultiLine: forceMultiLine($2, $3, $4),
|
||||
}
|
||||
}
|
||||
| primary_expr '(' exprs_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 ':' expr_opt ']'
|
||||
{
|
||||
$$ = &SliceExpr{
|
||||
X: $1,
|
||||
SliceStart: $2,
|
||||
From: $3,
|
||||
FirstColon: $4,
|
||||
To: $5,
|
||||
End: $6,
|
||||
}
|
||||
}
|
||||
| primary_expr '[' expr_opt ':' expr_opt ':' expr_opt ']'
|
||||
{
|
||||
$$ = &SliceExpr{
|
||||
X: $1,
|
||||
SliceStart: $2,
|
||||
From: $3,
|
||||
FirstColon: $4,
|
||||
To: $5,
|
||||
SecondColon: $6,
|
||||
Step: $7,
|
||||
End: $8,
|
||||
}
|
||||
}
|
||||
| primary_expr '(' expr for_clauses_with_if_clauses_opt ')'
|
||||
{
|
||||
$$ = &CallExpr{
|
||||
X: $1,
|
||||
ListStart: $2,
|
||||
List: []Expr{
|
||||
&ListForExpr{
|
||||
Brack: "",
|
||||
Start: $2,
|
||||
X: $3,
|
||||
For: $4,
|
||||
End: End{Pos: $5},
|
||||
},
|
||||
},
|
||||
End: End{Pos: $5},
|
||||
}
|
||||
}
|
||||
| strings %prec ShiftInstead
|
||||
{
|
||||
if len($1) == 1 {
|
||||
$$ = $1[0]
|
||||
break
|
||||
}
|
||||
|
||||
$$ = $1[0]
|
||||
for _, x := range $1[1:] {
|
||||
_, end := $$.Span()
|
||||
|
@ -322,63 +542,10 @@ expr:
|
|||
}
|
||||
}
|
||||
}
|
||||
| expr '.' _IDENT
|
||||
{
|
||||
$$ = &DotExpr{
|
||||
X: $1,
|
||||
Dot: $2,
|
||||
NamePos: $3,
|
||||
Name: $<tok>3,
|
||||
}
|
||||
}
|
||||
| expr '(' exprs_opt ')'
|
||||
{
|
||||
$$ = &CallExpr{
|
||||
X: $1,
|
||||
ListStart: $2,
|
||||
List: $3,
|
||||
End: End{Pos: $4},
|
||||
ForceCompact: forceCompact($2, $3, $4),
|
||||
ForceMultiLine: forceMultiLine($2, $3, $4),
|
||||
}
|
||||
}
|
||||
| expr '(' expr for_clauses_with_if_clauses_opt ')'
|
||||
{
|
||||
$$ = &CallExpr{
|
||||
X: $1,
|
||||
ListStart: $2,
|
||||
List: []Expr{
|
||||
&ListForExpr{
|
||||
Brack: "",
|
||||
Start: $2,
|
||||
X: $3,
|
||||
For: $4,
|
||||
End: End{Pos: $5},
|
||||
},
|
||||
},
|
||||
End: End{Pos: $5},
|
||||
}
|
||||
}
|
||||
| expr '[' expr ']'
|
||||
{
|
||||
$$ = &IndexExpr{
|
||||
X: $1,
|
||||
IndexStart: $2,
|
||||
Y: $3,
|
||||
End: $4,
|
||||
}
|
||||
}
|
||||
| expr '[' expr_opt ':' expr_opt ']'
|
||||
{
|
||||
$$ = &SliceExpr{
|
||||
X: $1,
|
||||
SliceStart: $2,
|
||||
Y: $3,
|
||||
Colon: $4,
|
||||
Z: $5,
|
||||
End: $6,
|
||||
}
|
||||
}
|
||||
| '-' primary_expr %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
|
||||
|
||||
expr:
|
||||
primary_expr
|
||||
| _LAMBDA exprs ':' expr
|
||||
{
|
||||
$$ = &LambdaExpr{
|
||||
|
@ -388,7 +555,6 @@ expr:
|
|||
Expr: $4,
|
||||
}
|
||||
}
|
||||
| '-' expr %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
|
||||
| _NOT expr %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
|
||||
| '*' expr %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
|
||||
| expr '*' expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
|
@ -403,7 +569,7 @@ expr:
|
|||
| expr _NE expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
| expr _GE expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
| expr '=' expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
| expr _ADDEQ expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
| expr _AUGM expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
| expr _IN expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
| expr _NOT _IN expr { $$ = binary($1, $2, "not in", $4) }
|
||||
| expr _OR expr { $$ = binary($1, $2, $<tok>2, $3) }
|
||||
|
@ -416,15 +582,15 @@ expr:
|
|||
$$ = binary($1, $2, $<tok>2, $3)
|
||||
}
|
||||
}
|
||||
| expr _IF expr _ELSE expr
|
||||
| expr _IF expr _ELSE expr
|
||||
{
|
||||
$$ = &ConditionalExpr{
|
||||
Then: $1,
|
||||
IfStart: $2,
|
||||
Test: $3,
|
||||
ElseStart: $4,
|
||||
Else: $5,
|
||||
}
|
||||
$$ = &ConditionalExpr{
|
||||
Then: $1,
|
||||
IfStart: $2,
|
||||
Test: $3,
|
||||
ElseStart: $4,
|
||||
Else: $5,
|
||||
}
|
||||
}
|
||||
|
||||
expr_opt:
|
||||
|
@ -491,6 +657,16 @@ exprs_opt:
|
|||
$$, $<comma>$ = $1, $2
|
||||
}
|
||||
|
||||
primary_exprs:
|
||||
primary_expr
|
||||
{
|
||||
$$ = []Expr{$1}
|
||||
}
|
||||
| primary_exprs ',' primary_expr
|
||||
{
|
||||
$$ = append($1, $3)
|
||||
}
|
||||
|
||||
string:
|
||||
_STRING
|
||||
{
|
||||
|
@ -519,18 +695,8 @@ ident:
|
|||
$$ = &LiteralExpr{Start: $1, Token: $<tok>1}
|
||||
}
|
||||
|
||||
idents:
|
||||
ident
|
||||
{
|
||||
$$ = []Expr{$1}
|
||||
}
|
||||
| idents ',' ident
|
||||
{
|
||||
$$ = append($1, $3)
|
||||
}
|
||||
|
||||
for_clause:
|
||||
_FOR idents _IN expr
|
||||
_FOR primary_exprs _IN expr
|
||||
{
|
||||
$$ = &ForClause{
|
||||
For: $1,
|
||||
|
@ -539,15 +705,6 @@ for_clause:
|
|||
Expr: $4,
|
||||
}
|
||||
}
|
||||
| _FOR '(' idents ')' _IN expr
|
||||
{
|
||||
$$ = &ForClause{
|
||||
For: $1,
|
||||
Var: $3,
|
||||
In: $5,
|
||||
Expr: $6,
|
||||
}
|
||||
}
|
||||
|
||||
for_clause_with_if_clauses_opt:
|
||||
for_clause if_clauses_opt {
|
||||
|
@ -558,13 +715,13 @@ for_clause_with_if_clauses_opt:
|
|||
}
|
||||
|
||||
for_clauses_with_if_clauses_opt:
|
||||
for_clause_with_if_clauses_opt
|
||||
{
|
||||
$$ = []*ForClauseWithIfClausesOpt{$1}
|
||||
}
|
||||
for_clause_with_if_clauses_opt
|
||||
{
|
||||
$$ = []*ForClauseWithIfClausesOpt{$1}
|
||||
}
|
||||
| for_clauses_with_if_clauses_opt for_clause_with_if_clauses_opt {
|
||||
$$ = append($1, $2)
|
||||
}
|
||||
$$ = append($1, $2)
|
||||
}
|
||||
|
||||
if_clauses_opt:
|
||||
{
|
||||
|
@ -606,6 +763,30 @@ func binary(x Expr, pos Position, op string, y Expr) Expr {
|
|||
}
|
||||
}
|
||||
|
||||
// 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:
|
||||
return true
|
||||
case *UnaryExpr:
|
||||
_, ok := x.X.(*LiteralExpr)
|
||||
return ok
|
||||
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
|
||||
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,
|
||||
|
@ -654,10 +835,7 @@ func forceCompact(start Position, list []Expr, end Position) bool {
|
|||
return false
|
||||
}
|
||||
line = end.Line
|
||||
switch x.(type) {
|
||||
case *LiteralExpr, *StringExpr, *UnaryExpr:
|
||||
// ok
|
||||
default:
|
||||
if !isSimpleExpression(&x) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,9 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const nestedIndentation = 2 // Indentation of nested blocks
|
||||
const listIndentation = 4 // Indentation of multiline expressions
|
||||
|
||||
// Format returns the formatted form of the given BUILD file.
|
||||
func Format(f *File) []byte {
|
||||
pr := &printer{}
|
||||
|
@ -118,7 +121,19 @@ func (p *printer) file(f *File) {
|
|||
p.newline()
|
||||
}
|
||||
|
||||
for i, stmt := range f.Stmt {
|
||||
p.statements(f.Stmt)
|
||||
|
||||
for _, com := range f.After {
|
||||
p.printf("%s", strings.TrimSpace(com.Token))
|
||||
p.newline()
|
||||
}
|
||||
|
||||
// If the last expression is in an indented code block there can be spaces in the last line.
|
||||
p.trim()
|
||||
}
|
||||
|
||||
func (p *printer) statements(stmts []Expr) {
|
||||
for i, stmt := range stmts {
|
||||
switch stmt := stmt.(type) {
|
||||
case *CommentBlock:
|
||||
// comments already handled
|
||||
|
@ -128,11 +143,17 @@ func (p *printer) file(f *File) {
|
|||
p.printf("%s", strings.TrimSpace(com.Token))
|
||||
p.newline()
|
||||
}
|
||||
p.printf("%s", stmt.Token) // includes trailing newline
|
||||
p.printf("%s", stmt.Token)
|
||||
p.newline()
|
||||
|
||||
default:
|
||||
p.expr(stmt, precLow)
|
||||
p.newline()
|
||||
|
||||
// Print an empty line break after the expression unless it's a code block.
|
||||
// For a code block, the line break is generated by its last statement.
|
||||
if !isCodeBlock(stmt) {
|
||||
p.newline()
|
||||
}
|
||||
}
|
||||
|
||||
for _, com := range stmt.Comment().After {
|
||||
|
@ -140,28 +161,26 @@ func (p *printer) file(f *File) {
|
|||
p.newline()
|
||||
}
|
||||
|
||||
if i+1 < len(f.Stmt) && !compactStmt(stmt, f.Stmt[i+1]) {
|
||||
if i+1 < len(stmts) && !compactStmt(stmt, stmts[i+1], p.margin == 0) {
|
||||
p.newline()
|
||||
}
|
||||
}
|
||||
|
||||
for _, com := range f.After {
|
||||
p.printf("%s", strings.TrimSpace(com.Token))
|
||||
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 compactStmt(s1, s2 Expr) bool {
|
||||
func compactStmt(s1, s2 Expr, isTopLevel bool) bool {
|
||||
if len(s2.Comment().Before) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return (isCall(s1, "subinclude") || isCall(s1, "load")) &&
|
||||
(isCall(s2, "subinclude") || isCall(s2, "load"))
|
||||
if isTopLevel {
|
||||
return isCall(s1, "load") && isCall(s2, "load")
|
||||
} else {
|
||||
return !(isCodeBlock(s1) || isCodeBlock(s2))
|
||||
}
|
||||
}
|
||||
|
||||
// isCall reports whether x is a call to a function with the given name.
|
||||
|
@ -177,6 +196,20 @@ func isCall(x Expr, name string) bool {
|
|||
return nam.Token == name
|
||||
}
|
||||
|
||||
// isCodeBlock checks if the statement is a code block (def, if, for, etc.)
|
||||
func isCodeBlock(x Expr) bool {
|
||||
switch x.(type) {
|
||||
case *FuncDef:
|
||||
return true
|
||||
case *ForLoop:
|
||||
return true
|
||||
case *IfElse:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Expression formatting.
|
||||
|
||||
// The expression formatter must introduce parentheses to force the
|
||||
|
@ -220,6 +253,11 @@ const (
|
|||
var opPrec = map[string]int{
|
||||
"=": precAssign,
|
||||
"+=": precAssign,
|
||||
"-=": precAssign,
|
||||
"*=": precAssign,
|
||||
"/=": precAssign,
|
||||
"//=": precAssign,
|
||||
"%=": precAssign,
|
||||
"or": precOr,
|
||||
"and": precAnd,
|
||||
"<": precCmp,
|
||||
|
@ -232,6 +270,7 @@ var opPrec = map[string]int{
|
|||
"-": precAdd,
|
||||
"*": precMultiply,
|
||||
"/": precMultiply,
|
||||
"//": precMultiply,
|
||||
"%": precMultiply,
|
||||
}
|
||||
|
||||
|
@ -327,12 +366,18 @@ func (p *printer) expr(v Expr, outerPrec int) {
|
|||
addParen(precSuffix)
|
||||
p.expr(v.X, precSuffix)
|
||||
p.printf("[")
|
||||
if v.Y != nil {
|
||||
p.expr(v.Y, precLow)
|
||||
if v.From != nil {
|
||||
p.expr(v.From, precLow)
|
||||
}
|
||||
p.printf(":")
|
||||
if v.Z != nil {
|
||||
p.expr(v.Z, precLow)
|
||||
if v.To != nil {
|
||||
p.expr(v.To, precLow)
|
||||
}
|
||||
if v.SecondColon.Byte != 0 {
|
||||
p.printf(":")
|
||||
if v.Step != nil {
|
||||
p.expr(v.Step, precLow)
|
||||
}
|
||||
}
|
||||
p.printf("]")
|
||||
|
||||
|
@ -379,7 +424,7 @@ func (p *printer) expr(v Expr, outerPrec int) {
|
|||
if v.LineBreak {
|
||||
p.margin = p.indent()
|
||||
if v.Op == "=" {
|
||||
p.margin += 4
|
||||
p.margin += listIndentation
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,6 +472,61 @@ func (p *printer) expr(v Expr, outerPrec int) {
|
|||
p.expr(v.Test, precSuffix)
|
||||
p.printf(" else ")
|
||||
p.expr(v.Else, precSuffix)
|
||||
|
||||
case *ReturnExpr:
|
||||
p.printf("return")
|
||||
if v.X != nil {
|
||||
p.printf(" ")
|
||||
p.expr(v.X, precSuffix)
|
||||
}
|
||||
|
||||
case *FuncDef:
|
||||
p.printf("def ")
|
||||
p.printf(v.Name)
|
||||
p.seq("()", v.Args, &v.End, modeCall, v.ForceCompact, v.ForceMultiLine)
|
||||
p.printf(":")
|
||||
p.margin += nestedIndentation
|
||||
p.newline()
|
||||
p.statements(v.Body.Statements)
|
||||
p.margin -= nestedIndentation
|
||||
|
||||
case *ForLoop:
|
||||
p.printf("for ")
|
||||
for i, loopVar := range v.LoopVars {
|
||||
if i > 0 {
|
||||
p.printf(", ")
|
||||
}
|
||||
p.expr(loopVar, precLow)
|
||||
}
|
||||
p.printf(" in ")
|
||||
p.expr(v.Iterable, precLow)
|
||||
p.printf(":")
|
||||
p.margin += nestedIndentation
|
||||
p.newline()
|
||||
p.statements(v.Body.Statements)
|
||||
p.margin -= nestedIndentation
|
||||
|
||||
case *IfElse:
|
||||
for i, block := range v.Conditions {
|
||||
if i == 0 {
|
||||
p.printf("if ")
|
||||
} else if block.If == nil {
|
||||
p.newline()
|
||||
p.printf("else")
|
||||
} else {
|
||||
p.newline()
|
||||
p.printf("elif ")
|
||||
}
|
||||
|
||||
if block.If != nil {
|
||||
p.expr(block.If, precLow)
|
||||
}
|
||||
p.printf(":")
|
||||
p.margin += nestedIndentation
|
||||
p.newline()
|
||||
p.statements(block.Then.Statements)
|
||||
p.margin -= nestedIndentation
|
||||
}
|
||||
}
|
||||
|
||||
// Add closing parenthesis if needed.
|
||||
|
@ -452,6 +552,7 @@ const (
|
|||
modeTuple // (x,)
|
||||
modeParen // (x)
|
||||
modeDict // {x:y}
|
||||
modeSeq // x, y
|
||||
)
|
||||
|
||||
// seq formats a list of values inside a given bracket pair (brack = "()", "[]", "{}").
|
||||
|
@ -504,7 +605,7 @@ func (p *printer) seq(brack string, list []Expr, end *End, mode seqMode, forceCo
|
|||
|
||||
default:
|
||||
// Multi-line form.
|
||||
p.margin += 4
|
||||
p.margin += listIndentation
|
||||
for i, x := range list {
|
||||
// If we are about to break the line before the first
|
||||
// element and there are trailing end-of-line comments
|
||||
|
@ -528,7 +629,7 @@ func (p *printer) seq(brack string, list []Expr, end *End, mode seqMode, forceCo
|
|||
p.newline()
|
||||
p.printf("%s", strings.TrimSpace(com.Token))
|
||||
}
|
||||
p.margin -= 4
|
||||
p.margin -= listIndentation
|
||||
p.newline()
|
||||
}
|
||||
p.depth--
|
||||
|
@ -566,7 +667,7 @@ func (p *printer) listFor(v *ListForExpr) {
|
|||
|
||||
if multiLine {
|
||||
if v.Brack != "" {
|
||||
p.margin += 4
|
||||
p.margin += listIndentation
|
||||
}
|
||||
p.newline()
|
||||
}
|
||||
|
@ -602,7 +703,7 @@ func (p *printer) listFor(v *ListForExpr) {
|
|||
p.printf("%s", strings.TrimSpace(com.Token))
|
||||
}
|
||||
if v.Brack != "" {
|
||||
p.margin -= 4
|
||||
p.margin -= listIndentation
|
||||
}
|
||||
p.newline()
|
||||
}
|
||||
|
@ -612,3 +713,7 @@ func (p *printer) listFor(v *ListForExpr) {
|
|||
p.depth--
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) isTopLevel() bool {
|
||||
return p.margin == 0
|
||||
}
|
||||
|
|
|
@ -18,33 +18,48 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
|||
|
||||
package build
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// A Rule represents a single BUILD rule.
|
||||
type Rule struct {
|
||||
Call *CallExpr
|
||||
Call *CallExpr
|
||||
ImplicitName string // The name which should be used if the name attribute is not set. See the comment on File.implicitRuleName.
|
||||
}
|
||||
|
||||
func (f *File) Rule(call *CallExpr) *Rule {
|
||||
r := &Rule{call, ""}
|
||||
if r.AttrString("name") == "" {
|
||||
r.ImplicitName = f.implicitRuleName()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var all []*Rule
|
||||
|
||||
for _, stmt := range f.Stmt {
|
||||
call, ok := stmt.(*CallExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rule := &Rule{call}
|
||||
rule := f.Rule(call)
|
||||
if kind != "" && rule.Kind() != kind {
|
||||
continue
|
||||
}
|
||||
all = append(all, rule)
|
||||
}
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
for _, stmt := range f.Stmt {
|
||||
call, ok := stmt.(*CallExpr)
|
||||
if !ok {
|
||||
|
@ -52,7 +67,7 @@ func (f *File) RuleAt(linenum int) *Rule {
|
|||
}
|
||||
start, end := call.X.Span()
|
||||
if start.Line <= linenum && linenum <= end.Line {
|
||||
return &Rule{call}
|
||||
return f.Rule(call)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -65,9 +80,9 @@ func (f *File) DelRules(kind, name string) int {
|
|||
var i int
|
||||
for _, stmt := range f.Stmt {
|
||||
if call, ok := stmt.(*CallExpr); ok {
|
||||
r := &Rule{call}
|
||||
r := f.Rule(call)
|
||||
if (kind == "" || r.Kind() == kind) &&
|
||||
(name == "" || r.AttrString("name") == name) {
|
||||
(name == "" || r.Name() == name) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +94,42 @@ func (f *File) DelRules(kind, name string) int {
|
|||
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
|
||||
|
@ -118,9 +169,13 @@ func (r *Rule) SetKind(kind string) {
|
|||
}
|
||||
|
||||
// Name returns the rule's target name.
|
||||
// If the rule has no target name, Name returns the empty string.
|
||||
// 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 {
|
||||
return r.AttrString("name")
|
||||
explicitName := r.AttrString("name")
|
||||
if explicitName == "" {
|
||||
return r.ImplicitName
|
||||
}
|
||||
return explicitName
|
||||
}
|
||||
|
||||
// AttrKeys returns the keys of all the rule's attributes.
|
||||
|
|
|
@ -57,9 +57,8 @@ type Expr interface {
|
|||
|
||||
// A Comment represents a single # comment.
|
||||
type Comment struct {
|
||||
Start Position
|
||||
Token string // without trailing newline
|
||||
Suffix bool // an end of line (not whole line) comment
|
||||
Start Position
|
||||
Token string // without trailing newline
|
||||
}
|
||||
|
||||
// Comments collects the comments associated with an expression.
|
||||
|
@ -87,12 +86,12 @@ type File struct {
|
|||
Stmt []Expr
|
||||
}
|
||||
|
||||
func (x *File) Span() (start, end Position) {
|
||||
if len(x.Stmt) == 0 {
|
||||
func (f *File) Span() (start, end Position) {
|
||||
if len(f.Stmt) == 0 {
|
||||
return
|
||||
}
|
||||
start, _ = x.Stmt[0].Span()
|
||||
_, end = x.Stmt[len(x.Stmt)-1].Span()
|
||||
start, _ = f.Stmt[0].Span()
|
||||
_, end = f.Stmt[len(f.Stmt)-1].Span()
|
||||
return start, end
|
||||
}
|
||||
|
||||
|
@ -360,15 +359,17 @@ func (x *ParenExpr) Span() (start, end Position) {
|
|||
return x.Start, x.End.Pos.add(")")
|
||||
}
|
||||
|
||||
// A SliceExpr represents a slice expression: X[Y:Z].
|
||||
// A SliceExpr represents a slice expression: expr[from:to] or expr[from:to:step] .
|
||||
type SliceExpr struct {
|
||||
Comments
|
||||
X Expr
|
||||
SliceStart Position
|
||||
Y Expr
|
||||
Colon Position
|
||||
Z Expr
|
||||
End Position
|
||||
X Expr
|
||||
SliceStart Position
|
||||
From Expr
|
||||
FirstColon Position
|
||||
To Expr
|
||||
SecondColon Position
|
||||
Step Expr
|
||||
End Position
|
||||
}
|
||||
|
||||
func (x *SliceExpr) Span() (start, end Position) {
|
||||
|
@ -421,3 +422,74 @@ func (x *ConditionalExpr) Span() (start, end Position) {
|
|||
_, end = x.Else.Span()
|
||||
return start, end
|
||||
}
|
||||
|
||||
// A CodeBlock represents an indented code block.
|
||||
type CodeBlock struct {
|
||||
Statements []Expr
|
||||
Start Position
|
||||
End
|
||||
}
|
||||
|
||||
func (x *CodeBlock) Span() (start, end Position) {
|
||||
return x.Start, x.End.Pos
|
||||
}
|
||||
|
||||
// A FuncDef represents a function definition expression: def foo(List):.
|
||||
type FuncDef struct {
|
||||
Comments
|
||||
Start Position // position of def
|
||||
Name string
|
||||
ListStart Position // position of (
|
||||
Args []Expr
|
||||
Body CodeBlock
|
||||
End // position of the end
|
||||
ForceCompact bool // force compact (non-multiline) form when printing
|
||||
ForceMultiLine bool // force multiline form when printing
|
||||
}
|
||||
|
||||
func (x *FuncDef) Span() (start, end Position) {
|
||||
return x.Start, x.End.Pos
|
||||
}
|
||||
|
||||
// A ReturnExpr represents a return statement: return f(x).
|
||||
type ReturnExpr struct {
|
||||
Comments
|
||||
Start Position
|
||||
X Expr
|
||||
End Position
|
||||
}
|
||||
|
||||
func (x *ReturnExpr) Span() (start, end Position) {
|
||||
return x.Start, x.End
|
||||
}
|
||||
|
||||
// A ForLoop represents a for loop block: for x in range(10):.
|
||||
type ForLoop struct {
|
||||
Comments
|
||||
Start Position // position of for
|
||||
LoopVars []Expr
|
||||
Iterable Expr
|
||||
Body CodeBlock
|
||||
End // position of the end
|
||||
}
|
||||
|
||||
func (x *ForLoop) Span() (start, end Position) {
|
||||
return x.Start, x.End.Pos
|
||||
}
|
||||
|
||||
// An IfElse represents an if-else blocks sequence: if x: ... elif y: ... else: ... .
|
||||
type IfElse struct {
|
||||
Comments
|
||||
Start Position // position of if
|
||||
Conditions []Condition
|
||||
End // position of the end
|
||||
}
|
||||
|
||||
type Condition struct {
|
||||
If Expr
|
||||
Then CodeBlock
|
||||
}
|
||||
|
||||
func (x *IfElse) Span() (start, end Position) {
|
||||
return x.Start, x.End.Pos
|
||||
}
|
||||
|
|
|
@ -72,11 +72,14 @@ func walk1(v *Expr, stack *[]Expr, f func(x Expr, stk []Expr) Expr) Expr {
|
|||
walk1(&v.Value, stack, f)
|
||||
case *SliceExpr:
|
||||
walk1(&v.X, stack, f)
|
||||
if v.Y != nil {
|
||||
walk1(&v.Y, stack, f)
|
||||
if v.From != nil {
|
||||
walk1(&v.From, stack, f)
|
||||
}
|
||||
if v.Z != nil {
|
||||
walk1(&v.Z, stack, f)
|
||||
if v.To != nil {
|
||||
walk1(&v.To, stack, f)
|
||||
}
|
||||
if v.Step != nil {
|
||||
walk1(&v.Step, stack, f)
|
||||
}
|
||||
case *ParenExpr:
|
||||
walk1(&v.X, stack, f)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Code generated by protoc-gen-go.
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: build_proto/build.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package blaze_query is a generated protocol buffer package.
|
||||
|
|
|
@ -92,6 +92,7 @@ Buildozer supports the following commands(`'command args'`):
|
|||
* `new <rule_kind> <rule_name> [(before|after) <relative_rule_name>]`: Add a
|
||||
new rule at the end of the BUILD file (before/after `<relative_rule>`).
|
||||
* `print <attr(s)>`
|
||||
* `remove <attr>`: Removes attribute `attr`.
|
||||
* `remove <attr> <value(s)>`: Removes `value(s)` from the list `attr`. The
|
||||
wildcard `*` matches all attributes. Lists containing none of the `value(s)` are
|
||||
not modified.
|
||||
|
@ -100,6 +101,13 @@ Buildozer supports the following commands(`'command args'`):
|
|||
* `replace <attr> <old_value> <new_value>`: Replaces `old_value` with `new_value`
|
||||
in the list `attr`. Wildcard `*` matches all attributes. Lists not containing
|
||||
`old_value` are not modified.
|
||||
* `substitute <attr> <old_regexp> <new_template>`: 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 <attr> <value(s)>`: Sets the value of an attribute. If the attribute
|
||||
was already present, its old value is replaced.
|
||||
* `set_if_absent <attr> <value(s)>`: Sets the value of an attribute. If the
|
||||
|
@ -138,6 +146,9 @@ 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
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ var (
|
|||
editVariables = flag.Bool("edit-variables", false, "For attributes that simply assign a variable (e.g. hdrs = LIB_HDRS), edit the build variable instead of appending to the attribute.")
|
||||
isPrintingProto = flag.Bool("output_proto", false, "output serialized devtools.buildozer.Output protos instead of human-readable strings.")
|
||||
tablesPath = flag.String("tables", "", "path to JSON file with custom table definitions which will replace the built-in tables")
|
||||
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")
|
||||
|
@ -67,9 +68,16 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
edit.ShortenLabelsFlag = *shortenLabelsFlag
|
||||
edit.DeleteWithComments = *deleteWithComments
|
||||
edit.Opts = edit.Options{
|
||||
opts := &edit.Options{
|
||||
Stdout: *stdout,
|
||||
Buildifier: *buildifier,
|
||||
Parallelism: *parallelism,
|
||||
|
@ -83,5 +91,5 @@ func main() {
|
|||
EditVariables: *editVariables,
|
||||
IsPrintingProto: *isPrintingProto,
|
||||
}
|
||||
os.Exit(edit.Buildozer(flag.Args()))
|
||||
os.Exit(edit.Buildozer(opts, flag.Args()))
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ go_library(
|
|||
"//vendor/github.com/bazelbuild/buildtools/build_proto:go_default_library",
|
||||
"//vendor/github.com/bazelbuild/buildtools/file:go_default_library",
|
||||
"//vendor/github.com/bazelbuild/buildtools/lang:go_default_library",
|
||||
"//vendor/github.com/bazelbuild/buildtools/tables:go_default_library",
|
||||
"//vendor/github.com/bazelbuild/buildtools/wspace:go_default_library",
|
||||
"//vendor/github.com/golang/protobuf/proto:go_default_library",
|
||||
],
|
||||
|
|
|
@ -53,8 +53,10 @@ type Options struct {
|
|||
IsPrintingProto bool // output serialized devtools.buildozer.Output protos instead of human-readable strings
|
||||
}
|
||||
|
||||
// Opts represents the options to be used by buildozer, and can be overriden before calling Buildozer.
|
||||
var Opts = Options{NumIO: 200, PreferEOLComments: true}
|
||||
// NewOpts returns a new Options struct with some defaults set.
|
||||
func NewOpts() *Options {
|
||||
return &Options{NumIO: 200, PreferEOLComments: true}
|
||||
}
|
||||
|
||||
// Usage is a user-overriden func to print the program usage.
|
||||
var Usage = func() {}
|
||||
|
@ -75,7 +77,7 @@ type CmdEnvironment struct {
|
|||
|
||||
// The cmdXXX functions implement the various commands.
|
||||
|
||||
func cmdAdd(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdAdd(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
attr := env.Args[0]
|
||||
for _, val := range env.Args[1:] {
|
||||
if IsIntList(attr) {
|
||||
|
@ -88,14 +90,14 @@ func cmdAdd(env CmdEnvironment) (*build.File, error) {
|
|||
return env.File, nil
|
||||
}
|
||||
|
||||
func cmdComment(env CmdEnvironment) (*build.File, error) {
|
||||
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")
|
||||
fullLine := !opts.PreferEOLComments || strings.Contains(str, "\n")
|
||||
str = strings.Replace("# "+str, "\n", "\n# ", -1)
|
||||
comment := []build.Comment{build.Comment{Token: str}}
|
||||
comment := []build.Comment{{Token: str}}
|
||||
|
||||
// The comment might be attached to a rule, an attribute, or a value in a list,
|
||||
// depending on how many arguments are passed.
|
||||
|
@ -139,7 +141,7 @@ func commentsText(comments []build.Comment) string {
|
|||
return strings.Replace(strings.Join(segments, " "), "\n", " ", -1)
|
||||
}
|
||||
|
||||
func cmdPrintComment(env CmdEnvironment) (*build.File, error) {
|
||||
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])
|
||||
}
|
||||
|
@ -147,7 +149,7 @@ func cmdPrintComment(env CmdEnvironment) (*build.File, error) {
|
|||
switch len(env.Args) {
|
||||
case 0: // Print rule comment.
|
||||
env.output.Fields = []*apipb.Output_Record_Field{
|
||||
&apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{commentsText(env.Rule.Call.Comments.Before)}},
|
||||
{Value: &apipb.Output_Record_Field_Text{commentsText(env.Rule.Call.Comments.Before)}},
|
||||
}
|
||||
case 1: // Print attribute comment.
|
||||
attr := env.Rule.AttrDefn(env.Args[0])
|
||||
|
@ -156,7 +158,7 @@ func cmdPrintComment(env CmdEnvironment) (*build.File, error) {
|
|||
}
|
||||
comments := append(attr.Before, attr.Suffix...)
|
||||
env.output.Fields = []*apipb.Output_Record_Field{
|
||||
&apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{commentsText(comments)}},
|
||||
{Value: &apipb.Output_Record_Field_Text{commentsText(comments)}},
|
||||
}
|
||||
case 2: // Print comment of a specific value in a list.
|
||||
attr := env.Rule.Attr(env.Args[0])
|
||||
|
@ -170,7 +172,7 @@ func cmdPrintComment(env CmdEnvironment) (*build.File, error) {
|
|||
}
|
||||
comments := append(expr.Comments.Before, expr.Comments.Suffix...)
|
||||
env.output.Fields = []*apipb.Output_Record_Field{
|
||||
&apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{commentsText(comments)}},
|
||||
{Value: &apipb.Output_Record_Field_Text{commentsText(comments)}},
|
||||
}
|
||||
default:
|
||||
panic("cmdPrintComment")
|
||||
|
@ -178,11 +180,11 @@ func cmdPrintComment(env CmdEnvironment) (*build.File, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func cmdDelete(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdDelete(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
return DeleteRule(env.File, env.Rule), nil
|
||||
}
|
||||
|
||||
func cmdMove(env CmdEnvironment) (*build.File, error) {
|
||||
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] == "*" {
|
||||
|
@ -204,7 +206,7 @@ func cmdMove(env CmdEnvironment) (*build.File, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func cmdNew(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdNew(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
kind := env.Args[0]
|
||||
name := env.Args[1]
|
||||
addAtEOF, insertionIndex, err := findInsertionIndex(env)
|
||||
|
@ -217,7 +219,7 @@ func cmdNew(env CmdEnvironment) (*build.File, error) {
|
|||
}
|
||||
|
||||
call := &build.CallExpr{X: &build.LiteralExpr{Token: kind}}
|
||||
rule := &build.Rule{Call: call}
|
||||
rule := &build.Rule{call, ""}
|
||||
rule.SetAttr("name", &build.StringExpr{Value: name})
|
||||
|
||||
if addAtEOF {
|
||||
|
@ -235,7 +237,7 @@ func findInsertionIndex(env CmdEnvironment) (bool, int, error) {
|
|||
}
|
||||
|
||||
relativeToRuleName := env.Args[3]
|
||||
ruleIdx := IndexOfRuleByName(env.File, relativeToRuleName)
|
||||
ruleIdx, _ := IndexOfRuleByName(env.File, relativeToRuleName)
|
||||
if ruleIdx == -1 {
|
||||
return true, 0, nil
|
||||
}
|
||||
|
@ -250,12 +252,12 @@ func findInsertionIndex(env CmdEnvironment) (bool, int, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func cmdNewLoad(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdNewLoad(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
env.File.Stmt = InsertLoad(env.File.Stmt, env.Args)
|
||||
return env.File, nil
|
||||
}
|
||||
|
||||
func cmdPrint(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdPrint(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
format := env.Args
|
||||
if len(format) == 0 {
|
||||
format = []string{"name", "kind"}
|
||||
|
@ -266,8 +268,10 @@ func cmdPrint(env CmdEnvironment) (*build.File, error) {
|
|||
value := env.Rule.Attr(str)
|
||||
if str == "kind" {
|
||||
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{env.Rule.Kind()}}
|
||||
} else if str == "name" {
|
||||
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{env.Rule.Name()}}
|
||||
} else if str == "label" {
|
||||
if env.Rule.Attr("name") != nil {
|
||||
if env.Rule.Name() != "" {
|
||||
fields[i] = &apipb.Output_Record_Field{Value: &apipb.Output_Record_Field_Text{fmt.Sprintf("//%s:%s", env.Pkg, env.Rule.Name())}}
|
||||
} else {
|
||||
return nil, nil
|
||||
|
@ -310,7 +314,7 @@ func attrKeysForPattern(rule *build.Rule, pattern string) []string {
|
|||
return []string{pattern}
|
||||
}
|
||||
|
||||
func cmdRemove(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdRemove(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
if len(env.Args) == 1 { // Remove the attribute
|
||||
if env.Rule.DelAttr(env.Args[0]) != nil {
|
||||
return env.File, nil
|
||||
|
@ -330,7 +334,7 @@ func cmdRemove(env CmdEnvironment) (*build.File, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func cmdRename(env CmdEnvironment) (*build.File, error) {
|
||||
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 {
|
||||
|
@ -339,7 +343,7 @@ func cmdRename(env CmdEnvironment) (*build.File, error) {
|
|||
return env.File, nil
|
||||
}
|
||||
|
||||
func cmdReplace(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdReplace(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
oldV := env.Args[1]
|
||||
newV := env.Args[2]
|
||||
for _, key := range attrKeysForPattern(env.Rule, env.Args[0]) {
|
||||
|
@ -355,7 +359,27 @@ func cmdReplace(env CmdEnvironment) (*build.File, error) {
|
|||
return env.File, nil
|
||||
}
|
||||
|
||||
func cmdSet(env CmdEnvironment) (*build.File, error) {
|
||||
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, getAttrValueExpr(key, []string{newValue}))
|
||||
}
|
||||
}
|
||||
return env.File, nil
|
||||
}
|
||||
|
||||
func cmdSet(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
attr := env.Args[0]
|
||||
args := env.Args[1:]
|
||||
if attr == "kind" {
|
||||
|
@ -366,7 +390,7 @@ func cmdSet(env CmdEnvironment) (*build.File, error) {
|
|||
return env.File, nil
|
||||
}
|
||||
|
||||
func cmdSetIfAbsent(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdSetIfAbsent(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
attr := env.Args[0]
|
||||
args := env.Args[1:]
|
||||
if attr == "kind" {
|
||||
|
@ -401,14 +425,14 @@ func getAttrValueExpr(attr string, args []string) build.Expr {
|
|||
}
|
||||
}
|
||||
|
||||
func cmdCopy(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdCopy(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
attrName := env.Args[0]
|
||||
from := env.Args[1]
|
||||
|
||||
return copyAttributeBetweenRules(env, attrName, from)
|
||||
}
|
||||
|
||||
func cmdCopyNoOverwrite(env CmdEnvironment) (*build.File, error) {
|
||||
func cmdCopyNoOverwrite(opts *Options, env CmdEnvironment) (*build.File, error) {
|
||||
attrName := env.Args[0]
|
||||
from := env.Args[1]
|
||||
|
||||
|
@ -438,7 +462,7 @@ func copyAttributeBetweenRules(env CmdEnvironment, attrName string, from string)
|
|||
return env.File, nil
|
||||
}
|
||||
|
||||
func cmdFix(env CmdEnvironment) (*build.File, error) {
|
||||
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
|
||||
|
@ -449,7 +473,7 @@ func cmdFix(env CmdEnvironment) (*build.File, error) {
|
|||
|
||||
// CommandInfo provides a command function and info on incoming arguments.
|
||||
type CommandInfo struct {
|
||||
Fn func(CmdEnvironment) (*build.File, error)
|
||||
Fn func(*Options, CmdEnvironment) (*build.File, error)
|
||||
MinArg int
|
||||
MaxArg int
|
||||
Template string
|
||||
|
@ -470,6 +494,7 @@ var AllCommands = map[string]CommandInfo{
|
|||
"remove": {cmdRemove, 1, -1, "<attr> <value(s)>"},
|
||||
"rename": {cmdRename, 2, 2, "<old_attr> <new_attr>"},
|
||||
"replace": {cmdReplace, 3, 3, "<attr> <old_value> <new_value>"},
|
||||
"substitute": {cmdSubstitute, 3, 3, "<attr> <old_regexp> <new_template>"},
|
||||
"set": {cmdSet, 2, -1, "<attr> <value(s)>"},
|
||||
"set_if_absent": {cmdSetIfAbsent, 2, -1, "<attr> <value(s)>"},
|
||||
"copy": {cmdCopy, 2, 2, "<attr> <from_rule>"},
|
||||
|
@ -500,13 +525,13 @@ func expandTargets(f *build.File, rule string) ([]*build.Rule, error) {
|
|||
return nil, fmt.Errorf("rule '%s' not found", rule)
|
||||
}
|
||||
|
||||
func filterRules(rules []*build.Rule) (result []*build.Rule) {
|
||||
if len(Opts.FilterRuleTypes) == 0 {
|
||||
func filterRules(opts *Options, rules []*build.Rule) (result []*build.Rule) {
|
||||
if len(opts.FilterRuleTypes) == 0 {
|
||||
return rules
|
||||
}
|
||||
for _, rule := range rules {
|
||||
acceptableType := false
|
||||
for _, filterType := range Opts.FilterRuleTypes {
|
||||
for _, filterType := range opts.FilterRuleTypes {
|
||||
if rule.Kind() == filterType {
|
||||
acceptableType = true
|
||||
break
|
||||
|
@ -634,7 +659,7 @@ var buildFileNamesSet = map[string]bool{
|
|||
|
||||
// rewrite parses the BUILD file for the given file, transforms the AST,
|
||||
// and write the changes back in the file (or on stdout).
|
||||
func rewrite(commandsForFile commandsForFile) *rewriteResult {
|
||||
func rewrite(opts *Options, commandsForFile commandsForFile) *rewriteResult {
|
||||
name := commandsForFile.file
|
||||
var data []byte
|
||||
var err error
|
||||
|
@ -676,7 +701,7 @@ func rewrite(commandsForFile commandsForFile) *rewriteResult {
|
|||
}
|
||||
|
||||
vars := map[string]*build.BinaryExpr{}
|
||||
if Opts.EditVariables {
|
||||
if opts.EditVariables {
|
||||
vars = getGlobalVariables(f.Stmt)
|
||||
}
|
||||
var errs []error
|
||||
|
@ -684,7 +709,7 @@ func rewrite(commandsForFile commandsForFile) *rewriteResult {
|
|||
for _, commands := range commandsForFile.commands {
|
||||
target := commands.target
|
||||
commands := commands.commands
|
||||
_, absPkg, rule := InterpretLabelForWorkspaceLocation(Opts.RootDir, target)
|
||||
_, absPkg, rule := InterpretLabelForWorkspaceLocation(opts.RootDir, target)
|
||||
_, pkg, _ := ParseLabel(target)
|
||||
if pkg == stdinPackageName { // Special-case: This is already absolute
|
||||
absPkg = stdinPackageName
|
||||
|
@ -694,23 +719,23 @@ func rewrite(commandsForFile commandsForFile) *rewriteResult {
|
|||
if err != nil {
|
||||
cerr := commandError(commands, target, err)
|
||||
errs = append(errs, cerr)
|
||||
if !Opts.KeepGoing {
|
||||
if !opts.KeepGoing {
|
||||
return &rewriteResult{file: name, errs: errs, records: records}
|
||||
|
||||
}
|
||||
}
|
||||
targets = filterRules(targets)
|
||||
targets = filterRules(opts, targets)
|
||||
for _, cmd := range commands {
|
||||
for _, r := range targets {
|
||||
cmdInfo := AllCommands[cmd.tokens[0]]
|
||||
record := &apipb.Output_Record{}
|
||||
newf, err := cmdInfo.Fn(CmdEnvironment{f, r, vars, absPkg, cmd.tokens[1:], record})
|
||||
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}, target, err)
|
||||
if Opts.KeepGoing {
|
||||
if opts.KeepGoing {
|
||||
errs = append(errs, cerr)
|
||||
} else {
|
||||
return &rewriteResult{file: name, errs: []error{cerr}, records: records}
|
||||
|
@ -727,12 +752,12 @@ func rewrite(commandsForFile commandsForFile) *rewriteResult {
|
|||
return &rewriteResult{file: name, errs: errs, records: records}
|
||||
}
|
||||
f = RemoveEmptyPackage(f)
|
||||
ndata, err := runBuildifier(f)
|
||||
ndata, err := runBuildifier(opts, f)
|
||||
if err != nil {
|
||||
return &rewriteResult{file: name, errs: []error{fmt.Errorf("running buildifier: %v", err)}, records: records}
|
||||
}
|
||||
|
||||
if Opts.Stdout || name == stdinPackageName {
|
||||
if opts.Stdout || name == stdinPackageName {
|
||||
os.Stdout.Write(ndata)
|
||||
return &rewriteResult{file: name, errs: errs, records: records}
|
||||
}
|
||||
|
@ -760,15 +785,15 @@ var EditFile = func(fi os.FileInfo, name string) error {
|
|||
}
|
||||
|
||||
// runBuildifier formats the build file f.
|
||||
// Runs Opts.Buildifier if it's non-empty, otherwise uses built-in formatter.
|
||||
// Opts.Buildifier is useful to force consistency with other tools that call Buildifier.
|
||||
func runBuildifier(f *build.File) ([]byte, error) {
|
||||
if Opts.Buildifier == "" {
|
||||
// Runs opts.Buildifier if it's non-empty, otherwise uses built-in formatter.
|
||||
// opts.Buildifier is useful to force consistency with other tools that call Buildifier.
|
||||
func runBuildifier(opts *Options, f *build.File) ([]byte, error) {
|
||||
if opts.Buildifier == "" {
|
||||
build.Rewrite(f, nil)
|
||||
return build.Format(f), nil
|
||||
}
|
||||
|
||||
cmd := exec.Command(Opts.Buildifier)
|
||||
cmd := exec.Command(opts.Buildifier)
|
||||
data := build.Format(f)
|
||||
cmd.Stdin = bytes.NewBuffer(data)
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
|
@ -787,9 +812,9 @@ func runBuildifier(f *build.File) ([]byte, error) {
|
|||
|
||||
// Given a target, whose package may contain a trailing "/...", returns all
|
||||
// extisting BUILD file paths which match the package.
|
||||
func targetExpressionToBuildFiles(target string) []string {
|
||||
file, _, _ := InterpretLabelForWorkspaceLocation(Opts.RootDir, target)
|
||||
if Opts.RootDir == "" {
|
||||
func targetExpressionToBuildFiles(opts *Options, target string) []string {
|
||||
file, _, _ := InterpretLabelForWorkspaceLocation(opts.RootDir, target)
|
||||
if opts.RootDir == "" {
|
||||
var err error
|
||||
if file, err = filepath.Abs(file); err != nil {
|
||||
fmt.Printf("Cannot make path absolute: %s\n", err.Error())
|
||||
|
@ -827,7 +852,7 @@ func targetExpressionToBuildFiles(target string) []string {
|
|||
|
||||
// appendCommands adds the given commands to be applied to each of the given targets
|
||||
// via the commandMap.
|
||||
func appendCommands(commandMap map[string][]commandsForTarget, args []string) {
|
||||
func appendCommands(opts *Options, commandMap map[string][]commandsForTarget, args []string) {
|
||||
commands, targets := parseCommands(args)
|
||||
for _, target := range targets {
|
||||
if strings.HasSuffix(target, "/BUILD") {
|
||||
|
@ -838,7 +863,7 @@ func appendCommands(commandMap map[string][]commandsForTarget, args []string) {
|
|||
if pkg == stdinPackageName {
|
||||
buildFiles = []string{stdinPackageName}
|
||||
} else {
|
||||
buildFiles = targetExpressionToBuildFiles(target)
|
||||
buildFiles = targetExpressionToBuildFiles(opts, target)
|
||||
}
|
||||
|
||||
for _, file := range buildFiles {
|
||||
|
@ -847,12 +872,12 @@ func appendCommands(commandMap map[string][]commandsForTarget, args []string) {
|
|||
}
|
||||
}
|
||||
|
||||
func appendCommandsFromFile(commandsByFile map[string][]commandsForTarget, fileName string) {
|
||||
func appendCommandsFromFile(opts *Options, commandsByFile map[string][]commandsForTarget, fileName string) {
|
||||
var reader io.Reader
|
||||
if Opts.CommandsFile == stdinPackageName {
|
||||
if opts.CommandsFile == stdinPackageName {
|
||||
reader = os.Stdin
|
||||
} else {
|
||||
rc := file.OpenReadFile(Opts.CommandsFile)
|
||||
rc := file.OpenReadFile(opts.CommandsFile)
|
||||
reader = rc
|
||||
defer rc.Close()
|
||||
}
|
||||
|
@ -863,7 +888,7 @@ func appendCommandsFromFile(commandsByFile map[string][]commandsForTarget, fileN
|
|||
continue
|
||||
}
|
||||
args := strings.Split(line, "|")
|
||||
appendCommands(commandsByFile, args)
|
||||
appendCommands(opts, commandsByFile, args)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error while reading commands file: %v", scanner.Err())
|
||||
|
@ -905,28 +930,28 @@ func printRecord(writer io.Writer, record *apipb.Output_Record) {
|
|||
}
|
||||
|
||||
// Buildozer loops over all arguments on the command line fixing BUILD files.
|
||||
func Buildozer(args []string) int {
|
||||
func Buildozer(opts *Options, args []string) int {
|
||||
commandsByFile := make(map[string][]commandsForTarget)
|
||||
if Opts.CommandsFile != "" {
|
||||
appendCommandsFromFile(commandsByFile, Opts.CommandsFile)
|
||||
if opts.CommandsFile != "" {
|
||||
appendCommandsFromFile(opts, commandsByFile, opts.CommandsFile)
|
||||
} else {
|
||||
if len(args) == 0 {
|
||||
Usage()
|
||||
}
|
||||
appendCommands(commandsByFile, args)
|
||||
appendCommands(opts, commandsByFile, args)
|
||||
}
|
||||
|
||||
numFiles := len(commandsByFile)
|
||||
if Opts.Parallelism > 0 {
|
||||
runtime.GOMAXPROCS(Opts.Parallelism)
|
||||
if opts.Parallelism > 0 {
|
||||
runtime.GOMAXPROCS(opts.Parallelism)
|
||||
}
|
||||
results := make(chan *rewriteResult, numFiles)
|
||||
data := make(chan commandsForFile)
|
||||
|
||||
for i := 0; i < Opts.NumIO; i++ {
|
||||
for i := 0; i < opts.NumIO; i++ {
|
||||
go func(results chan *rewriteResult, data chan commandsForFile) {
|
||||
for commandsForFile := range data {
|
||||
results <- rewrite(commandsForFile)
|
||||
results <- rewrite(opts, commandsForFile)
|
||||
}
|
||||
}(results, data)
|
||||
}
|
||||
|
@ -946,7 +971,7 @@ func Buildozer(args []string) int {
|
|||
for _, err := range fileResults.errs {
|
||||
fmt.Fprintf(os.Stderr, "%s: %s\n", fileResults.file, err)
|
||||
}
|
||||
if fileResults.modified && !Opts.Quiet {
|
||||
if fileResults.modified && !opts.Quiet {
|
||||
fmt.Fprintf(os.Stderr, "fixed %s\n", fileResults.file)
|
||||
}
|
||||
if fileResults.records != nil {
|
||||
|
@ -954,7 +979,7 @@ func Buildozer(args []string) int {
|
|||
}
|
||||
}
|
||||
|
||||
if Opts.IsPrintingProto {
|
||||
if opts.IsPrintingProto {
|
||||
data, err := proto.Marshal(&apipb.Output{Records: records})
|
||||
if err != nil {
|
||||
log.Fatal("marshaling error: ", err)
|
||||
|
@ -969,7 +994,7 @@ func Buildozer(args []string) int {
|
|||
if hasErrors {
|
||||
return 2
|
||||
}
|
||||
if !fileModified && !Opts.Stdout {
|
||||
if !fileModified && !opts.Stdout {
|
||||
return 3
|
||||
}
|
||||
return 0
|
||||
|
|
|
@ -20,11 +20,13 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bazelbuild/buildtools/build"
|
||||
"github.com/bazelbuild/buildtools/tables"
|
||||
"github.com/bazelbuild/buildtools/wspace"
|
||||
)
|
||||
|
||||
|
@ -55,7 +57,7 @@ func ParseLabel(target string) (string, string, string) {
|
|||
parts := strings.SplitN(target, ":", 2)
|
||||
parts[0] = strings.TrimPrefix(parts[0], "//")
|
||||
if len(parts) == 1 {
|
||||
if strings.HasPrefix(target, "//") {
|
||||
if strings.HasPrefix(target, "//") || tables.StripLabelLeadingSlashes {
|
||||
// "//absolute/pkg" -> "absolute/pkg", "pkg"
|
||||
return repo, parts[0], path.Base(parts[0])
|
||||
}
|
||||
|
@ -164,7 +166,7 @@ func ExprToRule(expr build.Expr, kind string) (*build.Rule, bool) {
|
|||
if !ok || k.Token != kind {
|
||||
return nil, false
|
||||
}
|
||||
return &build.Rule{Call: call}, true
|
||||
return &build.Rule{call, ""}, true
|
||||
}
|
||||
|
||||
// ExistingPackageDeclaration returns the package declaration, or nil if there is none.
|
||||
|
@ -200,7 +202,7 @@ func PackageDeclaration(f *build.File) *build.Rule {
|
|||
all = append(all, call)
|
||||
}
|
||||
f.Stmt = all
|
||||
return &build.Rule{Call: call}
|
||||
return &build.Rule{call, ""}
|
||||
}
|
||||
|
||||
// RemoveEmptyPackage removes empty package declarations from the file, i.e.:
|
||||
|
@ -274,49 +276,12 @@ func FindRuleByName(f *build.File, name string) *build.Rule {
|
|||
if name == "__pkg__" {
|
||||
return PackageDeclaration(f)
|
||||
}
|
||||
i := IndexOfRuleByName(f, name)
|
||||
if i != -1 {
|
||||
return &build.Rule{Call: f.Stmt[i].(*build.CallExpr)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UseImplicitName returns the rule in the file if it meets these conditions:
|
||||
// - It is the only unnamed rule in the file.
|
||||
// - The file path's ending directory name and the passed rule name match.
|
||||
// In the Pants Build System, by pantsbuild, the use of an implicit name makes
|
||||
// creating targets easier. This function implements such names.
|
||||
func UseImplicitName(f *build.File, rule string) *build.Rule {
|
||||
// We disallow empty names
|
||||
if f.Path == "BUILD" {
|
||||
return nil
|
||||
}
|
||||
ruleCount := 0
|
||||
var temp, found *build.Rule
|
||||
pkg := filepath.Base(filepath.Dir(f.Path))
|
||||
|
||||
for _, stmt := range f.Stmt {
|
||||
call, ok := stmt.(*build.CallExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
temp = &build.Rule{Call: call}
|
||||
if temp.Kind() != "" && temp.Name() == "" {
|
||||
ruleCount++
|
||||
found = temp
|
||||
}
|
||||
}
|
||||
|
||||
if ruleCount == 1 {
|
||||
if rule == pkg {
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
_, 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 {
|
||||
func IndexOfRuleByName(f *build.File, name string) (int, *build.Rule) {
|
||||
linenum := -1
|
||||
if strings.HasPrefix(name, "%") {
|
||||
// "%<LINENUM>" will match the rule which begins at LINENUM.
|
||||
|
@ -331,13 +296,13 @@ func IndexOfRuleByName(f *build.File, name string) int {
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
r := &build.Rule{Call: call}
|
||||
r := f.Rule(call)
|
||||
start, _ := call.X.Span()
|
||||
if r.Name() == name || start.Line == linenum {
|
||||
return i
|
||||
return i, r
|
||||
}
|
||||
}
|
||||
return -1
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// FindExportedFile returns the first exports_files call which contains the
|
||||
|
@ -377,7 +342,7 @@ func DeleteRuleByName(f *build.File, name string) *build.File {
|
|||
all = append(all, stmt)
|
||||
continue
|
||||
}
|
||||
r := &build.Rule{Call: call}
|
||||
r := f.Rule(call)
|
||||
if r.Name() != name {
|
||||
all = append(all, stmt)
|
||||
}
|
||||
|
@ -537,6 +502,42 @@ func ListReplace(e build.Expr, old, value, pkg string) bool {
|
|||
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 AllLists(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)
|
||||
|
|
|
@ -17,12 +17,22 @@ package edit
|
|||
import (
|
||||
buildpb "github.com/bazelbuild/buildtools/build_proto"
|
||||
"github.com/bazelbuild/buildtools/lang"
|
||||
"github.com/bazelbuild/buildtools/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 ||
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
type Definitions struct {
|
||||
IsLabelArg map[string]bool
|
||||
LabelBlacklist map[string]bool
|
||||
IsListArg map[string]bool
|
||||
IsSortableListArg map[string]bool
|
||||
SortableBlacklist map[string]bool
|
||||
SortableWhitelist map[string]bool
|
||||
|
@ -54,9 +55,9 @@ func ParseAndUpdateJSONDefinitions(file string, merge bool) error {
|
|||
}
|
||||
|
||||
if merge {
|
||||
MergeTables(definitions.IsLabelArg, definitions.LabelBlacklist, definitions.IsSortableListArg, definitions.SortableBlacklist, definitions.SortableWhitelist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative)
|
||||
MergeTables(definitions.IsLabelArg, definitions.LabelBlacklist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableBlacklist, definitions.SortableWhitelist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative)
|
||||
} else {
|
||||
OverrideTables(definitions.IsLabelArg, definitions.LabelBlacklist, definitions.IsSortableListArg, definitions.SortableBlacklist, definitions.SortableWhitelist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative)
|
||||
OverrideTables(definitions.IsLabelArg, definitions.LabelBlacklist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableBlacklist, definitions.SortableWhitelist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -97,6 +97,11 @@ var LabelBlacklist = map[string]bool{
|
|||
"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: a named argument to a rule call is considered to be a sortable list
|
||||
// if the name is one of these names. There is a separate blacklist for
|
||||
// rule-specific exceptions.
|
||||
|
@ -200,10 +205,13 @@ var StripLabelLeadingSlashes = false
|
|||
|
||||
var ShortenAbsoluteLabelsToRelative = false
|
||||
|
||||
var FormatBzlFiles = false
|
||||
|
||||
// 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, blacklist, sortableListArg, sortBlacklist, sortWhitelist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
|
||||
func OverrideTables(labelArg, blacklist, listArg, sortableListArg, sortBlacklist, sortWhitelist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
|
||||
IsLabelArg = labelArg
|
||||
LabelBlacklist = blacklist
|
||||
IsListArg = listArg
|
||||
IsSortableListArg = sortableListArg
|
||||
SortableBlacklist = sortBlacklist
|
||||
SortableWhitelist = sortWhitelist
|
||||
|
@ -213,13 +221,16 @@ func OverrideTables(labelArg, blacklist, sortableListArg, sortBlacklist, sortWhi
|
|||
}
|
||||
|
||||
// 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, blacklist, sortableListArg, sortBlacklist, sortWhitelist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
|
||||
func MergeTables(labelArg, blacklist, listArg, sortableListArg, sortBlacklist, sortWhitelist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
|
||||
for k, v := range labelArg {
|
||||
IsLabelArg[k] = v
|
||||
}
|
||||
for k, v := range blacklist {
|
||||
LabelBlacklist[k] = v
|
||||
}
|
||||
for k, v := range listArg {
|
||||
IsListArg[k] = v
|
||||
}
|
||||
for k, v := range sortableListArg {
|
||||
IsSortableListArg[k] = v
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ github.com/bazelbuild/bazel-gazelle/internal/rule
|
|||
github.com/bazelbuild/bazel-gazelle/internal/version
|
||||
github.com/bazelbuild/bazel-gazelle/internal/walk
|
||||
github.com/bazelbuild/bazel-gazelle/internal/wspace
|
||||
# github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e => github.com/bazelbuild/buildtools v0.0.0-20171220125010-1a9c38e0df93
|
||||
# github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e => github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e
|
||||
github.com/bazelbuild/buildtools/api_proto
|
||||
github.com/bazelbuild/buildtools/build
|
||||
github.com/bazelbuild/buildtools/build_proto
|
||||
|
|
Loading…
Reference in New Issue