Don't expose `null` option values to Starlark - replace with Runtime.Nonetype.
Starlark transitions can access the values of native options and expose them to Starlark code. In those cases, covert null to Runtime.Nonetype.
RELNOTES: None.
PiperOrigin-RevId: 240243755
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/FunctionTransitionUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/FunctionTransitionUtil.java
index 04444ee..0237af7 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/FunctionTransitionUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/FunctionTransitionUtil.java
@@ -30,6 +30,7 @@
import com.google.devtools.build.lib.packages.StructImpl;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Mutability;
+import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.syntax.Runtime.NoneType;
import com.google.devtools.build.lib.syntax.SkylarkDict;
import com.google.devtools.common.options.OptionDefinition;
@@ -174,7 +175,7 @@
FragmentOptions options = buildOptions.get(optionInfo.getOptionClass());
Object optionValue = field.get(options);
- dict.put(optionKey, optionValue, null, mutability);
+ dict.put(optionKey, optionValue == null ? Runtime.NONE : optionValue, null, mutability);
} catch (IllegalAccessException e) {
// These exceptions should not happen, but if they do, throw a RuntimeException.
throw new RuntimeException(e);
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java
index 59fde8e..5b1e701 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java
@@ -621,4 +621,42 @@
getConfiguredTarget("//test");
assertContainsEvent("Use of Starlark transition without whitelist");
}
+
+ @Test
+ public void testNoNullOptionValues() throws Exception {
+ writeWhitelistFile();
+ scratch.file(
+ "test/transitions.bzl",
+ "def _impl(settings, attr):",
+ " if settings['//command_line_option:android_crosstool_top'] == None:",
+ " return {'//command_line_option:test_arg': ['post-transition']}",
+ " else:",
+ " return {'//command_line_option:test_arg': settings['//command_line_option:test_arg']}",
+ "my_transition = transition(implementation = _impl,",
+ " inputs = [",
+ " '//command_line_option:test_arg',",
+ " '//command_line_option:android_crosstool_top'",
+ " ],",
+ " outputs = ['//command_line_option:test_arg'])");
+ scratch.file(
+ "test/rules.bzl",
+ "load('//test:transitions.bzl', 'my_transition')",
+ "def _impl(ctx):",
+ " return []",
+ "my_rule = rule(",
+ " implementation = _impl,",
+ " cfg = my_transition,",
+ " attrs = {",
+ " '_whitelist_function_transition': attr.label(",
+ " default = '//tools/whitelists/function_transition_whitelist',",
+ " ),",
+ " })");
+ scratch.file("test/BUILD", "load('//test:rules.bzl', 'my_rule')", "my_rule(name = 'test')");
+
+ useConfiguration("--android_crosstool_top=");
+
+ BuildConfiguration configuration = getConfiguration(getConfiguredTarget("//test"));
+ assertThat(configuration.getOptions().get(TestOptions.class).testArguments)
+ .containsExactly("post-transition");
+ }
}