diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java index def5643da5a187..cce73c1245756b 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java @@ -76,6 +76,7 @@ import com.google.devtools.build.lib.shell.ShellUtils; import com.google.devtools.build.lib.shell.ShellUtils.TokenizationException; import com.google.devtools.build.lib.starlarkbuildapi.StarlarkRuleContextApi; +import com.google.devtools.build.lib.starlarkbuildapi.platform.ToolchainContextApi; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.Collection; @@ -701,9 +702,24 @@ public Dict var() throws EvalException { } @Override - public ResolvedToolchainContext toolchains() throws EvalException { + public ToolchainContextApi toolchains() throws EvalException { checkMutable("toolchains"); - return ruleContext.getToolchainContext(); + ResolvedToolchainContext toolchainContext = ruleContext.getToolchainContext(); + if (toolchainContext == null) { + // Starlark rules are easier if this cannot be null, so return a no-op value instead. + return new ToolchainContextApi() { + @Override + public Object getIndex(StarlarkSemantics semantics, Object key) { + return Starlark.NONE; + } + + @Override + public boolean containsKey(StarlarkSemantics semantics, Object key) { + return false; + } + }; + } + return toolchainContext; } @Override diff --git a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java index 4a20ee9292b436..626a874e0bb585 100644 --- a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java +++ b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java @@ -3123,4 +3123,29 @@ public void testBuildFilePath() throws Exception { Object result = ev.eval("ruleContext.build_file_path"); assertThat(result).isEqualTo("bar/BUILD"); } + + @Test + public void testNoToolchainContext() throws Exception { + // Build setting rules do not have a toolchain context, as they are part of the configuration. + scratch.file( + "test/BUILD", + "load(':rule.bzl', 'sample_setting')", + "toolchain_type(name = 'toolchain_type')", + "sample_setting(", + " name = 'test',", + " build_setting_default = True,", + ")"); + scratch.file( + "test/rule.bzl", + "def _sample_impl(ctx):", + " info = ctx.toolchains['//:toolchain_type']", + " if info != None:", + " fail('Toolchain should be empty')", + "sample_setting = rule(", + " implementation = _sample_impl,", + " build_setting = config.bool(flag = True),", + ")"); + getConfiguredTarget("//test:test"); + assertNoEvents(); + } }