diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java index 39eadbb4ac81d1..aae8a7a69f89d2 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java @@ -1208,7 +1208,7 @@ public Object call(StarlarkThread thread, Tuple args, Dict kwarg // The less magic the better. Do not give in those temptations! Dict.Builder initializerKwargs = Dict.builder(); for (var attr : currentRuleClass.getAttributes()) { - if (attr.isPublic() && attr.starlarkDefined()) { + if ((attr.isPublic() && attr.starlarkDefined()) || attr.getName().equals("name")) { if (kwargs.containsKey(attr.getName())) { Object value = kwargs.get(attr.getName()); if (value == Starlark.NONE) { @@ -1238,6 +1238,12 @@ public Object call(StarlarkThread thread, Tuple args, Dict kwarg : Dict.cast(ret, String.class, Object.class, "rule's initializer return value"); for (var arg : newKwargs.keySet()) { + if (arg.equals("name")) { + if (!kwargs.get("name").equals(newKwargs.get("name"))) { + throw Starlark.errorf("Initializer can't change the name of the target"); + } + continue; + } checkAttributeName(arg); if (arg.startsWith("_")) { // allow setting private attributes from initializers in builtins diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java index efad37499e5696..a16b51b66848fd 100644 --- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java @@ -482,8 +482,8 @@ StarlarkCallable macro(StarlarkFunction implementation, StarlarkThread thread) doc = "Experimental: the Stalark function initializing the attributes of the rule. " + "

The function is called at load time for each instance of the rule. It's " - + "called with values of public attributes defined by the rule (not with " - + "generic attributes, for example name or tags). " + + "called with name and the values of public attributes defined by" + + "the rule (not with generic attributes, for example tags). " + "

It has to return a dictionary from the attribute names to the desired " + "values. The attributes that are not returned are unaffected. Returning " + "None as value results in using the default value specified in " diff --git a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java index 309b7884ec9e29..67fab2c94af72c 100644 --- a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java +++ b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java @@ -2966,7 +2966,7 @@ public void initializer_basic() throws Exception { scratch.file( "initializer_testing/b.bzl", "MyInfo = provider()", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " return {'deps': deps + ['//:added']}", "def impl(ctx): ", " return [MyInfo(", @@ -2994,6 +2994,49 @@ public void initializer_basic() throws Exception { assertThat((List) info.getValue("deps")).containsExactly("@@//:initial", "@@//:added"); } + @Test + public void initializer_nameUnchanged() throws Exception { + scratch.file( + "initializer_testing/b.bzl", + "def initializer(name, **kwargs):", + " if name != 'my_target':", + " fail()", + " return {'name': name} | kwargs", + "MyInfo = provider()", + "def impl(ctx): ", + " pass", + "my_rule = rule(impl, initializer = initializer)"); + scratch.file( + "initializer_testing/BUILD", // + "load(':b.bzl','my_rule')", + "my_rule(name = 'my_target')"); + + getConfiguredTarget("//initializer_testing:my_target"); + + assertNoEvents(); + } + + @Test + public void initializer_nameChanged() throws Exception { + scratch.file( + "initializer_testing/b.bzl", + "def initializer(name, **kwargs):", + " return {'name': 'my_new_name'}", + "def impl(ctx): ", + " pass", + "my_rule = rule(impl, initializer = initializer)"); + scratch.file( + "initializer_testing/BUILD", // + "load(':b.bzl','my_rule')", + "my_rule(name = 'my_target')"); + + reporter.removeHandler(failFastHandler); + reporter.addHandler(ev.getEventCollector()); + getConfiguredTarget("//initializer_testing:my_target"); + + ev.assertContainsError("Error in my_rule: Initializer can't change the name of the target"); + } + @Test @SuppressWarnings("unchecked") public void initializer_stringListDict() throws Exception { @@ -3066,7 +3109,7 @@ public void initializer_legacyAnyType() throws Exception { scratch.file( "initializer_testing/b.bzl", "MyInfo = provider()", - "def initializer(tristate = -1):", + "def initializer(name, tristate = -1):", " return {'tristate': int(tristate)}", "def impl(ctx): ", " return [MyInfo(tristate = ctx.attr.tristate)]", @@ -3126,7 +3169,7 @@ public void initializer_withSelect() throws Exception { scratch.file( "initializer_testing/b.bzl", "MyInfo = provider()", - "def initializer(srcs = []):", + "def initializer(name, srcs = []):", " return {'srcs': srcs + ['b.ml']}", "def impl(ctx): ", " return [MyInfo(", @@ -3186,7 +3229,7 @@ public void initializer_overridesAttributeDefault() throws Exception { scratch.file( "initializer_testing/b.bzl", "MyInfo = provider()", - "def initializer(deps = ['//:initializer_default']):", + "def initializer(name, deps = ['//:initializer_default']):", " return {'deps': deps}", "def impl(ctx): ", " return [MyInfo(", @@ -3221,7 +3264,7 @@ public void initializer_returningNoneSetsDefault() throws Exception { scratch.file( "initializer_testing/b.bzl", "MyInfo = provider()", - "def initializer(deps = ['//:initializer_default']):", + "def initializer(name, deps = ['//:initializer_default']):", " return {'deps': None}", "def impl(ctx): ", " return [MyInfo(", @@ -3251,7 +3294,7 @@ public void initializer_omittedValueIsNotPassed() throws Exception { scratch.file( "initializer_testing/b.bzl", "MyInfo = provider()", - "def initializer(srcs):", + "def initializer(name, srcs):", " return {'srcs': srcs}", "def impl(ctx): ", " pass", @@ -3278,7 +3321,7 @@ public void initializer_noneValueIsNotPassed() throws Exception { scratch.file( "initializer_testing/b.bzl", "MyInfo = provider()", - "def initializer(srcs):", + "def initializer(name, srcs):", " return {'srcs': srcs}", "def impl(ctx): ", " pass", @@ -3303,7 +3346,7 @@ public void initializer_noneValueIsNotPassed() throws Exception { public void initializer_incorrectReturnType() throws Exception { scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = []):", + "def initializer(name, srcs = []):", " return [srcs]", "def impl(ctx): ", " pass", @@ -3328,7 +3371,7 @@ public void initializer_incorrectReturnType() throws Exception { public void initializer_incorrectReturnDicts() throws Exception { scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = []):", + "def initializer(name, srcs = []):", " return {True: srcs}", "def impl(ctx): ", " pass", @@ -3354,7 +3397,7 @@ public void initializer_failsSettingBaseAttribute() throws Exception { // 'args' is an attribute defined for all executable rules scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " return {'srcs': srcs, 'deps': deps, 'args': ['a']}", "def impl(ctx): ", " pass", @@ -3381,7 +3424,7 @@ public void initializer_failsSettingBaseAttribute() throws Exception { public void initializer_failsSettingPrivateAttribute_outsideBuiltins() throws Exception { scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " return {'srcs': srcs, '_tool': ':my_tool'}", "def impl(ctx): ", " pass", @@ -3412,7 +3455,7 @@ public void initializer_settingPrivateAttribute_insideBuiltins() throws Exceptio scratch.file("initializer_testing/builtins/BUILD", "filegroup(name='my_tool')"); scratch.file( "initializer_testing/builtins/b.bzl", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " return {'srcs': srcs, '_tool': ':my_tool'}", "MyInfo = provider()", "def impl(ctx): ", @@ -3443,7 +3486,7 @@ public void initializer_settingPrivateAttribute_insideBuiltins() throws Exceptio public void initializer_failsSettingUnknownAttr() throws Exception { scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " return {'srcs': srcs, 'my_deps': deps}", "def impl(ctx): ", " pass", @@ -3469,7 +3512,7 @@ public void initializer_failsSettingUnknownAttr() throws Exception { public void initializer_failsCreatingAnotherRule() throws Exception { scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " native.java_library(name = 'jl', srcs = ['a.java'])", " return {'srcs': srcs, 'deps': deps}", "def impl(ctx): ", @@ -3497,7 +3540,7 @@ public void initializer_failsCreatingAnotherRule() throws Exception { public void initializer_failsWithExistingRules() throws Exception { scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " native.existing_rules()", " return {'srcs': srcs, 'deps': deps}", "def impl(ctx): ", @@ -3524,7 +3567,7 @@ public void initializer_failsWithExistingRules() throws Exception { public void initializer_withFails() throws Exception { scratch.file( "initializer_testing/b.bzl", - "def initializer(srcs = [], deps = []):", + "def initializer(name, srcs = [], deps = []):", " fail('Fail called in initializer')", " return {'srcs': srcs, 'deps': deps}", "def impl(ctx): ", @@ -3653,7 +3696,7 @@ public void extendRule_withInitializers() throws Exception { scratch.file( "extend_rule_testing/parent/parent.bzl", "ParentInfo = provider()", - "def _parent_initializer(srcs, deps):", // only parents attributes + "def _parent_initializer(name, srcs, deps):", // only parents attributes " return {'deps': deps + ['//extend_rule_testing:parent_dep']}", "def _impl(ctx):", " return [ParentInfo()]", @@ -3670,7 +3713,7 @@ public void extendRule_withInitializers() throws Exception { "extend_rule_testing/child.bzl", "load('//extend_rule_testing/parent:parent.bzl', 'parent_library')", "ChildInfo = provider()", - "def _child_initializer(srcs, deps, runtime_deps = []):", + "def _child_initializer(name, srcs, deps, runtime_deps = []):", " return {'deps': deps + [':child_dep'], 'runtime_deps': runtime_deps + [':runtime_dep']}", "def _impl(ctx):", " return ctx.super() + [ChildInfo(",