Allows using split_attr on Starlark split transitions.
RELNOTES: ctx.split_attr now includes attributes with Starlark split transitions.
PiperOrigin-RevId: 301580907
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
index 4cdbab6..9651024 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.analysis.skylark;
+import static com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition.PATCH_TRANSITION_KEY;
import static com.google.devtools.build.lib.packages.RuleClass.Builder.SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME;
import com.google.common.base.Function;
@@ -426,7 +427,7 @@
ImmutableMap.Builder<String, Object> splitAttrInfos = ImmutableMap.builder();
for (Attribute attr : attributes) {
- if (!attr.getTransitionFactory().isSplit() || attr.hasStarlarkDefinedTransition()) {
+ if (!attr.getTransitionFactory().isSplit()) {
continue;
}
Map<Optional<String>, ? extends List<? extends TransitiveInfoCollection>> splitPrereqs =
@@ -436,6 +437,13 @@
for (Map.Entry<Optional<String>, ? extends List<? extends TransitiveInfoCollection>>
splitPrereq : splitPrereqs.entrySet()) {
+ // Skip a split with an empty dependency list.
+ // TODO(jungjw): Figure out exactly which cases trigger this and see if this can be made
+ // more error-proof.
+ if (splitPrereq.getValue().isEmpty()) {
+ continue;
+ }
+
Object value;
if (attr.getType() == BuildType.LABEL) {
Preconditions.checkState(splitPrereq.getValue().size() == 1);
@@ -445,7 +453,8 @@
value = StarlarkList.immutableCopyOf(splitPrereq.getValue());
}
- if (splitPrereq.getKey().isPresent()) {
+ if (splitPrereq.getKey().isPresent()
+ && !splitPrereq.getKey().get().equals(PATCH_TRANSITION_KEY)) {
splitPrereqsMap.put(splitPrereq.getKey().get(), value);
} else {
// If the split transition is not in effect, then the key will be missing since there's
@@ -462,9 +471,8 @@
return StructProvider.STRUCT.create(
splitAttrInfos.build(),
- "No attribute '%s' in split_attr. This attribute is either not defined with a split"
- + " configuration OR is defined with a Starlark split transition, the results of which"
- + " cannot be accessed from split_attr.");
+ "No attribute '%s' in split_attr."
+ + " This attribute is not defined with a split configuration.");
}
@Override
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java
index 5e263a7..8716954 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java
@@ -25,16 +25,18 @@
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.analysis.StarlarkRuleTransitionProviderTest.DummyTestLoader;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
+import com.google.devtools.build.lib.analysis.test.TestConfiguration.TestOptions;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.Provider;
-import com.google.devtools.build.lib.packages.SkylarkInfo;
import com.google.devtools.build.lib.packages.SkylarkProvider;
import com.google.devtools.build.lib.packages.StructImpl;
import com.google.devtools.build.lib.packages.util.BazelMockAndroidSupport;
+import com.google.devtools.build.lib.syntax.Starlark;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.util.Fingerprint;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
@@ -115,53 +117,7 @@
}
@Test
- public void testSplitAttrDoesNotIncludeStarlarkSplit() throws Exception {
- setSkylarkSemanticsOptions("--experimental_starlark_config_transitions=true");
- writeWhitelistFile();
- scratch.file(
- "test/skylark/rules.bzl",
- "load('//myinfo:myinfo.bzl', 'MyInfo')",
- "def transition_func(settings, attr):",
- " return {",
- " 'amsterdam': {'//command_line_option:test_arg': ['stroopwafel']},",
- " 'paris': {'//command_line_option:test_arg': ['crepe']},",
- " }",
- "my_transition = transition(",
- " implementation = transition_func,",
- " inputs = [],",
- " outputs = ['//command_line_option:test_arg']",
- ")",
- "def _impl(ctx): ",
- " return MyInfo(split_attr = ctx.split_attr)",
- "my_rule = rule(",
- " implementation = _impl,",
- " attrs = {",
- " 'dep': attr.label(cfg = my_transition),",
- " '_whitelist_function_transition': attr.label(",
- " default = '//tools/whitelists/function_transition_whitelist',",
- " ),",
- " }",
- ")",
- "def _s_impl_e(ctx):",
- " return []",
- "simple_rule = rule(_s_impl_e)");
-
- scratch.file(
- "test/skylark/BUILD",
- "load('//test/skylark:rules.bzl', 'simple_rule', 'my_rule')",
- "my_rule(name = 'test', dep = ':dep')",
- "simple_rule(name = 'dep')");
-
- SkylarkInfo splitAttr =
- (SkylarkInfo)
- getMyInfoFromTarget(getConfiguredTarget("//test/skylark:test")).getValue("split_attr");
-
- assertThat(splitAttr.getValue("dep")).isNull();
- assertThat(splitAttr.getFieldNames()).isEmpty();
- }
-
- @Test
- public void testAccessStarlarkSplitThrowsError() throws Exception {
+ public void testStarlarkSplitTransitionSplitAttr() throws Exception {
setSkylarkSemanticsOptions("--experimental_starlark_config_transitions=true");
writeWhitelistFile();
scratch.file(
@@ -198,12 +154,125 @@
"my_rule(name = 'test', dep = ':dep')",
"simple_rule(name = 'dep')");
- reporter.removeHandler(failFastHandler);
- getConfiguredTarget("//test/skylark:test");
- assertContainsEvent(
- "No attribute 'dep' in split_attr. This attribute is either not defined with a split"
- + " configuration OR is defined with a Starlark split transition, the results of which"
- + " cannot be accessed from split_attr.");
+ @SuppressWarnings("unchecked")
+ Map<Object, ConfiguredTarget> splitAttr =
+ (Map<Object, ConfiguredTarget>)
+ getMyInfoFromTarget(getConfiguredTarget("//test/skylark:test"))
+ .getValue("split_attr_dep");
+ assertThat(splitAttr.keySet()).containsExactly("amsterdam", "paris");
+ assertThat(
+ getConfiguration(splitAttr.get("amsterdam"))
+ .getOptions()
+ .get(TestOptions.class)
+ .testArguments)
+ .containsExactly("stroopwafel");
+ assertThat(
+ getConfiguration(splitAttr.get("paris"))
+ .getOptions()
+ .get(TestOptions.class)
+ .testArguments)
+ .containsExactly("crepe");
+ }
+
+ @Test
+ public void testStarlarkListSplitTransitionSplitAttr() throws Exception {
+ setSkylarkSemanticsOptions("--experimental_starlark_config_transitions=true");
+ writeWhitelistFile();
+ scratch.file(
+ "test/skylark/rules.bzl",
+ "load('//myinfo:myinfo.bzl', 'MyInfo')",
+ "def transition_func(settings, attr):",
+ " return [",
+ " {'//command_line_option:test_arg': ['stroopwafel']},",
+ " {'//command_line_option:test_arg': ['crepe']},",
+ " ]",
+ "my_transition = transition(",
+ " implementation = transition_func,",
+ " inputs = [],",
+ " outputs = ['//command_line_option:test_arg']",
+ ")",
+ "def _impl(ctx): ",
+ " return MyInfo(split_attr_dep = ctx.split_attr.dep)",
+ "my_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'dep': attr.label(cfg = my_transition),",
+ " '_whitelist_function_transition': attr.label(",
+ " default = '//tools/whitelists/function_transition_whitelist',",
+ " ),",
+ " }",
+ ")",
+ "def _s_impl_e(ctx):",
+ " return []",
+ "simple_rule = rule(_s_impl_e)");
+
+ scratch.file(
+ "test/skylark/BUILD",
+ "load('//test/skylark:rules.bzl', 'simple_rule', 'my_rule')",
+ "my_rule(name = 'test', dep = ':dep')",
+ "simple_rule(name = 'dep')");
+
+ @SuppressWarnings("unchecked")
+ Map<Object, ConfiguredTarget> splitAttr =
+ (Map<Object, ConfiguredTarget>)
+ getMyInfoFromTarget(getConfiguredTarget("//test/skylark:test"))
+ .getValue("split_attr_dep");
+ assertThat(splitAttr.keySet()).containsExactly("0", "1");
+ assertThat(
+ getConfiguration(splitAttr.get("0")).getOptions().get(TestOptions.class).testArguments)
+ .containsExactly("stroopwafel");
+ assertThat(
+ getConfiguration(splitAttr.get("1")).getOptions().get(TestOptions.class).testArguments)
+ .containsExactly("crepe");
+ }
+
+ @Test
+ public void testStarlarkPatchTransitionSplitAttr() throws Exception {
+ setSkylarkSemanticsOptions("--experimental_starlark_config_transitions=true");
+ writeWhitelistFile();
+ scratch.file(
+ "test/skylark/rules.bzl",
+ "load('//myinfo:myinfo.bzl', 'MyInfo')",
+ "def transition_func(settings, attr):",
+ " return {'//command_line_option:test_arg': ['stroopwafel']}",
+ "my_transition = transition(",
+ " implementation = transition_func,",
+ " inputs = [],",
+ " outputs = ['//command_line_option:test_arg']",
+ ")",
+ "def _impl(ctx): ",
+ " return MyInfo(split_attr_dep = ctx.split_attr.dep)",
+ "my_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'dep': attr.label(cfg = my_transition),",
+ " '_whitelist_function_transition': attr.label(",
+ " default = '//tools/whitelists/function_transition_whitelist',",
+ " ),",
+ " }",
+ ")",
+ "def _s_impl_e(ctx):",
+ " return []",
+ "simple_rule = rule(_s_impl_e)");
+
+ scratch.file(
+ "test/skylark/BUILD",
+ "load('//test/skylark:rules.bzl', 'simple_rule', 'my_rule')",
+ "my_rule(name = 'test', dep = ':dep')",
+ "simple_rule(name = 'dep')");
+
+ @SuppressWarnings("unchecked")
+ Map<Object, ConfiguredTarget> splitAttr =
+ (Map<Object, ConfiguredTarget>)
+ getMyInfoFromTarget(getConfiguredTarget("//test/skylark:test"))
+ .getValue("split_attr_dep");
+ assertThat(splitAttr.keySet()).containsExactly(Starlark.NONE);
+ assertThat(
+ getConfiguration(splitAttr.get(Starlark.NONE))
+ .getOptions()
+ .get(TestOptions.class)
+ .testArguments)
+ .containsExactly("stroopwafel");
}
@Test