github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e

Used only by github.com/bazelbuild/bazel-gazelle, expecting 80c7f0d45d7e
This commit is contained in:
Jordan Liggitt
2019-04-05 10:33:56 -04:00
parent 2cbf496c8e
commit 4bd9dcb855
21 changed files with 1566 additions and 766 deletions

View File

@@ -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