Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions buildozer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ 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>`). The
identifier `__pkg__` can be used to position rules relative to package().
* `new_if_absent <rule_kind> <rule_name> [(before|after) <relative_rule_name>]`:
Same as `new`, but does nothing if a rule with the same name already exists.
* `print <attr(s)>`
* `remove <attr>`: Removes attribute `attr`. The wildcard `*` matches all
attributes except `name`.
Expand Down
14 changes: 14 additions & 0 deletions buildozer/buildozer_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,20 @@ function test_new_already_exists() {
assert_err "rule 'a' already exists"
}

function test_new_if_absent_cc_library() {
in='cc_test(name = "a")'
run "$in" 'new_if_absent cc_library foo' '//pkg:__pkg__'
assert_equals 'cc_test(name = "a")

cc_library(name = "foo")'
}

function test_new_if_absent_existing_rule() {
in='cc_library(name = "foo")'
ERROR=3 run "$in" 'new_if_absent cc_library foo' '//pkg:__pkg__'
assert_equals 'cc_library(name = "foo")'
}

function test_new_before_first() {
in='cc_test(name = "a")'
run "$in" 'new java_library foo before a' 'pkg/BUILD'
Expand Down
25 changes: 20 additions & 5 deletions edit/buildozer.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,18 +236,16 @@ func cmdMove(opts *Options, env CmdEnvironment) (*build.File, error) {
return nil, nil
}

func cmdNew(opts *Options, env CmdEnvironment) (*build.File, error) {
// createNewRule is a helper function for creating a new rule.
// It is used by cmdNew and cmdNewIfAbsent.
func createNewRule(env CmdEnvironment) (*build.File, error) {
kind := env.Args[0]
name := env.Args[1]
addAtEOF, insertionIndex, err := findInsertionIndex(env)
if err != nil {
return nil, err
}

if FindRuleByName(env.File, name) != nil {
return nil, fmt.Errorf("rule '%s' already exists", name)
}

call := &build.CallExpr{X: &build.Ident{Name: kind}}
rule := &build.Rule{Call: call, ImplicitName: ""}
rule.SetAttr("name", &build.StringExpr{Value: name})
Expand All @@ -260,6 +258,22 @@ func cmdNew(opts *Options, env CmdEnvironment) (*build.File, error) {
return env.File, nil
}

func cmdNew(opts *Options, env CmdEnvironment) (*build.File, error) {
name := env.Args[1]
if FindRuleByName(env.File, name) != nil {
return nil, fmt.Errorf("rule '%s' already exists", name)
}
return createNewRule(env)
}

func cmdNewIfAbsent(opts *Options, env CmdEnvironment) (*build.File, error) {
name := env.Args[1]
if FindRuleByName(env.File, name) != nil {
return nil, nil
}
return createNewRule(env)
}

// findInsertionIndex is used by cmdNew to find the place at which to insert the new rule.
func findInsertionIndex(env CmdEnvironment) (bool, int, error) {
if len(env.Args) < 4 {
Expand Down Expand Up @@ -928,6 +942,7 @@ var AllCommands = map[string]CommandInfo{
"fix": {cmdFix, true, 0, -1, "<fix(es)>?"},
"move": {cmdMove, true, 3, -1, "<old_attr> <new_attr> <value(s)>"},
"new": {cmdNew, false, 2, 4, "<rule_kind> <rule_name> [(before|after) <relative_rule_name>]"},
"new_if_absent": {cmdNewIfAbsent, false, 2, 4, "<rule_kind> <rule_name> [(before|after) <relative_rule_name>]"},
"print": {cmdPrint, true, 0, -1, "<attribute(s)>"},
"remove": {cmdRemove, true, 1, -1, "<attr> <value(s)>"},
"remove_comment": {cmdRemoveComment, true, 0, 2, "<attr>? <value>?"},
Expand Down
48 changes: 48 additions & 0 deletions edit/buildozer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,54 @@ func TestCmdSubstitute(t *testing.T) {
}
}

var newIfAbsentTests = []struct {
args []string
buildFile string
expected string
}{
{[]string{"go_library", "new_rule"},
``,
`go_library(name = "new_rule")`,
},
{[]string{"go_library", "new_rule"},
`go_library(name = "existing_rule")`,
`go_library(name = "existing_rule")

go_library(name = "new_rule")`,
},
{[]string{"go_library", "existing_rule"},
`go_library(name = "existing_rule")`,
`go_library(name = "existing_rule")`,
},
}

func TestCmdNewIfAbsent(t *testing.T) {
for i, tt := range newIfAbsentTests {
bld, err := build.Parse("BUILD", []byte(tt.buildFile))
if err != nil {
t.Error(err)
continue
}
expectedBld, err := build.Parse("BUILD", []byte(tt.expected))
if err != nil {
t.Error(err)
continue
}
expected := strings.TrimSpace(string(build.Format(expectedBld)))
env := CmdEnvironment{
File: bld,
Args: tt.args,
}
if result, _ := cmdNewIfAbsent(NewOpts(), env); result != nil {
bld = result
}
got := strings.TrimSpace(string(build.Format(bld)))
if got != expected {
t.Errorf("cmdNewIfAbsent(%d):\ngot:\n%s\nexpected:\n%s", i, got, expected)
}
}
}

func TestCmdDictAddSet_missingColon(t *testing.T) {
for _, tc := range []struct {
name string
Expand Down