From 22137cc3e171d99f9a51df16761f8c8aa1c31359 Mon Sep 17 00:00:00 2001 From: Hugues Morisset Date: Thu, 15 Jun 2023 16:20:31 +0200 Subject: [PATCH] Retrieve named_outputs keys with get_named_outs (#2808) * Retrieve named_outputs keys with get_named_outs When working with named outputs it is convenient to be able to get the keys from the target after it was created. With this addition, one can do the following: ``` target = genrule( name = "target", outs = { "a": ["a"], "b": ["b"] }, ... ) target = get_named_outs(target) target2 = genrule( name = "target2", srcs = target.a, ... ) ``` This makes easy to re-export a target outs in a filegroup: ``` target = genrule( name = "target", outs = { "a": ["a"], "b": ["b"] }, ... ) fg = filegroup( name = "fg", srcs = get_named_outs(target) | { "c": [":another_rule"] } ) ``` * Output similar to get_outs --- docs/BUILD | 2 +- rules/builtins.build_defs | 2 + src/core/build_target.go | 23 ++++++++++ src/parse/asp/builtins.go | 29 +++++++++++++ test/get_outs/BUILD | 90 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 test/get_outs/BUILD diff --git a/docs/BUILD b/docs/BUILD index 3868a45430..aab2b25d38 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -59,7 +59,7 @@ genrule( plugins = { "python": "v1.3.0", "java": "v0.3.0", - "go": "v1.6.0", + "go": "v1.7.0", "cc": "v0.3.2", "shell": "v0.1.2", "go-proto": "v0.2.0", diff --git a/rules/builtins.build_defs b/rules/builtins.build_defs index 8b2fb62a77..a066a8916d 100644 --- a/rules/builtins.build_defs +++ b/rules/builtins.build_defs @@ -235,6 +235,8 @@ def add_out(target:str, name:str, out:str=''): pass def get_outs(target:str): pass +def get_named_outs(target:str) -> dict: + pass def add_licence(target:str, licence:str): pass def get_licences(target:str): diff --git a/src/core/build_target.go b/src/core/build_target.go index 1e4a5d5484..f756c7d441 100644 --- a/src/core/build_target.go +++ b/src/core/build_target.go @@ -718,6 +718,29 @@ func (target *BuildTarget) DeclaredOutputNames() []string { return ret } +// DeclaredNamedSources returns the named sources from this target's original declaration. +func (target *BuildTarget) DeclaredNamedSources() map[string][]string { + ret := make(map[string][]string, len(target.NamedSources)) + for k, v := range target.NamedSources { + ret[k] = make([]string, len(v)) + for i, bi := range v { + ret[k][i] = bi.String() + } + } + return ret +} + +// DeclaredSourceNames is a convenience function to return the names of the declared +// sources in a consistent order. +func (target *BuildTarget) DeclaredSourceNames() []string { + ret := make([]string, 0, len(target.NamedSources)) + for name := range target.NamedSources { + ret = append(ret, name) + } + sort.Strings(ret) + return ret +} + func (target *BuildTarget) filegroupOutputs(srcs []BuildInput) []string { ret := make([]string, 0, len(srcs)) // Filegroups just re-output their inputs. diff --git a/src/parse/asp/builtins.go b/src/parse/asp/builtins.go index 5775d473f5..27cda63a74 100644 --- a/src/parse/asp/builtins.go +++ b/src/parse/asp/builtins.go @@ -57,6 +57,7 @@ func registerBuiltins(s *scope) { setNativeCode(s, "add_data", addData) setNativeCode(s, "add_out", addOut) setNativeCode(s, "get_outs", getOuts) + setNativeCode(s, "get_named_outs", getNamedOuts) setNativeCode(s, "add_licence", addLicence) setNativeCode(s, "get_licences", getLicences) setNativeCode(s, "get_command", getCommand) @@ -1020,6 +1021,34 @@ func getOuts(s *scope, args []pyObject) pyObject { return ret } +// getNamedOuts gets the named outputs of a target +func getNamedOuts(s *scope, args []pyObject) pyObject { + var target *core.BuildTarget + if name := args[0].String(); core.LooksLikeABuildLabel(name) { + label := core.ParseBuildLabel(name, s.pkg.Name) + target = s.state.Graph.TargetOrDie(label) + } else { + target = getTargetPost(s, name) + } + + var outs map[string][]string + if target.IsFilegroup { + outs = target.DeclaredNamedSources() + } else { + outs = target.DeclaredNamedOutputs() + } + + ret := make(pyDict, len(outs)) + for k, v := range outs { + list := make(pyList, len(v)) + for i, out := range v { + list[i] = pyString(out) + } + ret[k] = list + } + return ret +} + // addLicence adds a licence to a target. func addLicence(s *scope, args []pyObject) pyObject { target := getTargetPost(s, string(args[0].(pyString))) diff --git a/test/get_outs/BUILD b/test/get_outs/BUILD new file mode 100644 index 0000000000..d46a9d016a --- /dev/null +++ b/test/get_outs/BUILD @@ -0,0 +1,90 @@ +nonamedouts = genrule( + name = "nonamedouts", + outs = ["x"], + cmd = """ + echo 'x' > "$OUTS" + """, +) + +gr_getouts = genrule( + name = "genrule_getouts", + outs = { + "wibble": ["wibble_file1"], + "wobble": ["wobble_file1"], + }, + cmd = """ + echo 'wibblewibblewibble' > "$OUTS_WIBBLE" + echo 'wobblewobblewobble' > "$OUTS_WOBBLE" + """, +) + +fg_getouts = filegroup( + name = "filegroup_getouts", + srcs = { + "wibble": [text_file(name = 'wibble_file2', content = 'wibblewibblewibble')], + "wobble": [text_file(name = 'wobble_file2', content = 'wobblewobblewobble')], + }, +) + +def assert_dict(l1, l2): + if l1 != l2: + fail(f"{l1} != {l2}") + +assert_dict({}, get_named_outs(nonamedouts)) + +assert_dict({ + "wibble": ["wibble_file1"], + "wobble": ["wobble_file1"], +}, get_named_outs(gr_getouts)) + +assert_dict({ + "wibble": ["//test/get_outs:wibble_file2"], + "wobble": ["//test/get_outs:wobble_file2"], +}, get_named_outs(fg_getouts)) + +gr_subtargets = { k: [f'{gr_getouts}|{k}'] for k, _ in get_named_outs(gr_getouts).items() } +fg_subtargets = { k: [f'{fg_getouts}|{k}'] for k, _ in get_named_outs(fg_getouts).items() } + +gentest( + name = "get_outs_gr_wibble_test", + data = gr_subtargets.wibble, + labels = ["get_outs"], + no_test_output = True, + test_cmd = """ + $TOOL "$DATA" "wibblewibblewibble" + """, + test_tools = ["//test/build_defs:content_checker"], +) + +gentest( + name = "get_outs_gr_wobble_test", + data = gr_subtargets.wobble, + labels = ["get_outs"], + no_test_output = True, + test_cmd = """ + $TOOL "$DATA" "wobblewobblewobble" + """, + test_tools = ["//test/build_defs:content_checker"], +) + +gentest( + name = "get_outs_fg_wibble_test", + data = fg_subtargets.wibble, + labels = ["get_outs"], + no_test_output = True, + test_cmd = """ + $TOOL "$DATA" "wibblewibblewibble" + """, + test_tools = ["//test/build_defs:content_checker"], +) + +gentest( + name = "get_outs_fg_wobble_test", + data = fg_subtargets.wobble, + labels = ["get_outs"], + no_test_output = True, + test_cmd = """ + $TOOL "$DATA" "wobblewobblewobble" + """, + test_tools = ["//test/build_defs:content_checker"], +)