diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigGlobalLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigGlobalLibrary.java index 19bb09104c6938..861504ed602906 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigGlobalLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigGlobalLibrary.java @@ -21,6 +21,7 @@ import com.google.devtools.build.lib.analysis.config.StarlarkDefinedConfigTransition.Settings; import com.google.devtools.build.lib.cmdline.BazelModuleContext; import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.starlarkbuildapi.config.ConfigGlobalLibraryApi; import com.google.devtools.build.lib.starlarkbuildapi.config.ConfigurationTransitionApi; import java.util.HashSet; @@ -53,10 +54,10 @@ public ConfigurationTransitionApi transition( StarlarkSemantics semantics = thread.getSemantics(); List inputsList = Sequence.cast(inputs, String.class, "inputs"); List outputsList = Sequence.cast(outputs, String.class, "outputs"); - validateBuildSettingKeys(inputsList, Settings.INPUTS); - validateBuildSettingKeys(outputsList, Settings.OUTPUTS); BazelModuleContext moduleContext = BazelModuleContext.of(Module.ofInnermostEnclosingStarlarkFunction(thread)); + validateBuildSettingKeys(inputsList, Settings.INPUTS, moduleContext.packageContext()); + validateBuildSettingKeys(outputsList, Settings.OUTPUTS, moduleContext.packageContext()); Location location = thread.getCallerLocation(); return StarlarkDefinedConfigTransition.newRegularTransition( implementation, @@ -76,15 +77,17 @@ public ConfigurationTransitionApi analysisTestTransition( throws EvalException { Map changedSettingsMap = Dict.cast(changedSettings, String.class, Object.class, "changed_settings dict"); - validateBuildSettingKeys(changedSettingsMap.keySet(), Settings.OUTPUTS); BazelModuleContext moduleContext = BazelModuleContext.of(Module.ofInnermostEnclosingStarlarkFunction(thread)); + validateBuildSettingKeys( + changedSettingsMap.keySet(), Settings.OUTPUTS, moduleContext.packageContext()); Location location = thread.getCallerLocation(); return StarlarkDefinedConfigTransition.newAnalysisTestTransition( changedSettingsMap, moduleContext.repoMapping(), moduleContext.label(), location); } - private void validateBuildSettingKeys(Iterable optionKeys, Settings keyErrorDescriptor) + private void validateBuildSettingKeys( + Iterable optionKeys, Settings keyErrorDescriptor, Label.PackageContext packageContext) throws EvalException { HashSet processedOptions = Sets.newHashSet(); @@ -93,8 +96,16 @@ private void validateBuildSettingKeys(Iterable optionKeys, Settings keyE for (String optionKey : optionKeys) { if (!optionKey.startsWith(COMMAND_LINE_OPTION_PREFIX)) { try { - Label.parseAbsoluteUnchecked(optionKey); - } catch (IllegalArgumentException e) { + Label label = Label.parseWithRepoContext(optionKey, packageContext); + if (!label.getRepository().isVisible()) { + throw Starlark.errorf( + "invalid transition %s '%s': no repo visible as @%s from %s", + singularErrorDescriptor, + label, + label.getRepository().getName(), + label.getRepository().getOwnerRepoDisplayString()); + } + } catch (LabelSyntaxException e) { throw Starlark.errorf( "invalid transition %s '%s'. If this is intended as a native option, " + "it must begin with //command_line_option: %s", diff --git a/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java b/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java index 24fcfd5962afa1..2b1caf109e6cca 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java @@ -339,4 +339,48 @@ public void transitionOutput_otherRepo() throws Exception { assertThat(getConfiguredTarget("//test:buildme")).isNotNull(); assertNoEvents(); } + + @Test + public void testInvisibleRepoInLabelResultsInEarlyError() throws Exception { + setBuildLanguageOptions("--enable_bzlmod"); + + scratch.file("MODULE.bazel"); + scratch.file( + "test/defs.bzl", + "def _setting_impl(ctx):", + " return []", + "string_flag = rule(", + " implementation = _setting_impl,", + " build_setting = config.string(flag=True),", + ")", + "def _transition_impl(settings, attr):", + " return {'//test:formation': 'mesa'}", + "formation_transition = transition(", + " implementation = _transition_impl,", + " inputs = ['@foobar//test:formation'],", // invalid repo name + " outputs = ['//test:formation'],", + ")", + "def _impl(ctx):", + " return []", + "state = rule(", + " implementation = _impl,", + " cfg = formation_transition,", + " attrs = {", + " '_allowlist_function_transition': attr.label(", + " default = '//tools/allowlists/function_transition_allowlist',", + " ),", + " })"); + scratch.file( + "test/BUILD", + "load('//test:defs.bzl', 'state', 'string_flag')", + "state(name = 'arizona')", + "string_flag(name = 'formation', build_setting_default = 'canyon')"); + + reporter.removeHandler(failFastHandler); + getConfiguredTarget("//test:arizona"); + + assertContainsEvent( + "Error in transition: invalid transition input '@[unknown repo 'foobar' requested from @]" + + "//test:formation': no repo visible as @foobar from main repository"); + } }