diff --git a/buildozer/README.md b/buildozer/README.md index 55038d465..f3c2ee179 100644 --- a/buildozer/README.md +++ b/buildozer/README.md @@ -117,6 +117,8 @@ Buildozer supports the following commands(`'command args'`): exists in the `to_rule`, it will be overwritten. * `copy_no_overwrite `: Copies the value of `attr` between rules. If it exists in the `to_rule`, no action is taken. + * `copy_rule `: Copies the value of `from_rule` to the `to_rule`. If + `to_rule` exists, it will be overwritten. * `dict_add <(key:value)(s)>`: Sets the value of a key for the dict attribute `attr`. If the key was already present, it will _not_ be overwritten * `dict_set <(key:value)(s)>`: Sets the value of a key for the dict @@ -185,6 +187,9 @@ buildozer 'new cc_binary new_bin before tests' //:__pkg__ # Copy an attribute from `protolib` to `py_protolib`. buildozer 'copy testonly protolib' //pkg:py_protolib +# Copy a rule from `protolib` to `py_protolib`. +buildozer 'copy_rule protolib' //pkg:py_protolib + # Set two attributes in the same rule buildozer 'set compile 1' 'set srcmap 1' //pkg:rule diff --git a/buildozer/buildozer_test.sh b/buildozer/buildozer_test.sh index 2f469c651..e5a9b7ddc 100755 --- a/buildozer/buildozer_test.sh +++ b/buildozer/buildozer_test.sh @@ -866,6 +866,46 @@ go_binary(name = "to")' assert_err "rule 'from' does not have attribute 'visibility'" } +function test_copy_rule() { + in='proto_library(name = "from", visibility = ["://foo"] + CONST) + +cc_binary(name = "to")' + + run "$in" 'copy_rule from' '//pkg:to' + assert_equals 'proto_library( + name = "from", + visibility = ["://foo"] + CONST, +) + +cc_binary( + name = "to", + visibility = ["://foo"] + CONST, +)' +} + +function test_copy_rule_overwrite() { + in='proto_library(name = "from", testonly = 1) + +cc_binary(name = "to", testonly = 2)' + + run "$in" 'copy_rule from' '//pkg:to' + assert_equals 'proto_library( + name = "from", + testonly = 1, +) + +cc_binary( + name = "to", + testonly = 1, +)' +} + +function test_copy_rule_no_from_rule() { + in='go_binary(name = "to")' + ERROR=2 run "$in" 'copy_rule from' '//pkg:to' + assert_err "could not find rule 'from'" +} + function test_set_kind() { in='cc_library(name = "a")' diff --git a/edit/buildozer.go b/edit/buildozer.go index 5026a73a0..36617deaa 100644 --- a/edit/buildozer.go +++ b/edit/buildozer.go @@ -563,6 +563,28 @@ func cmdCopyNoOverwrite(opts *Options, env CmdEnvironment) (*build.File, error) return copyAttributeBetweenRules(env, attrName, from) } +func cmdCopyRule(opts *Options, env CmdEnvironment) (*build.File, error) { + from := env.Args[0] + + fromRule := FindRuleByName(env.File, from) + if fromRule == nil { + return nil, fmt.Errorf("could not find rule '%s'", from) + } + for _, key := range env.Rule.AttrKeys() { + if key != "name" { + env.Rule.DelAttr(key) + } + } + for _, key := range fromRule.AttrKeys() { + if key != "name" { + copyAttributeBetweenRules(env, key, from) + } + } + + return env.File, nil + +} + // cmdDictAdd adds a key to a dict, if that key does _not_ exit already. func cmdDictAdd(opts *Options, env CmdEnvironment) (*build.File, error) { attr := env.Args[0] @@ -727,6 +749,7 @@ var AllCommands = map[string]CommandInfo{ "set_if_absent": {cmdSetIfAbsent, true, 1, -1, " "}, "copy": {cmdCopy, true, 2, 2, " "}, "copy_no_overwrite": {cmdCopyNoOverwrite, true, 2, 2, " "}, + "copy_rule": {cmdCopyRule, true, 1, 1, ""}, "dict_add": {cmdDictAdd, true, 2, -1, " <(key:value)(s)>"}, "dict_set": {cmdDictSet, true, 2, -1, " <(key:value)(s)>"}, "dict_remove": {cmdDictRemove, true, 2, -1, " "},