From 273012d469eef9d1a027810baafcfc47fdef307e Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 6 Nov 2024 08:19:38 +0100 Subject: [PATCH] WIP --- .../lib/bazel/BazelRepositoryModule.java | 20 +++++ .../devtools/build/lib/bazel/bzlmod/BUILD | 1 + .../lib/bazel/bzlmod/ModuleFileFunction.java | 35 +++++++++ .../lib/bazel/bzlmod/ModuleFileGlobals.java | 5 +- .../devtools/build/lib/bazel/repository/BUILD | 1 + .../bazel/repository/RepositoryOptions.java | 69 ++++++++++++------ .../skyframe/RepositoryMappingFunction.java | 47 ++++++------ src/test/py/bazel/bzlmod/bazel_module_test.py | 59 --------------- .../py/bazel/bzlmod/bazel_overrides_test.py | 73 +++++++++++++++++++ 9 files changed, 202 insertions(+), 108 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java index d20cd08f3ec0fb..a803bfd4836916 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java @@ -147,6 +147,7 @@ public class BazelRepositoryModule extends BlazeModule { private final MutableSupplier> clientEnvironmentSupplier = new MutableSupplier<>(); private ImmutableMap overrides = ImmutableMap.of(); + private ImmutableMap injections = ImmutableMap.of(); private ImmutableMap moduleOverrides = ImmutableMap.of(); private Optional resolvedFileReplacingWorkspace = Optional.empty(); private FileSystem filesystem; @@ -470,6 +471,24 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException { overrides = ImmutableMap.of(); } + if (repoOptions.repositoryInjections != null) { + Map injectionMap = new LinkedHashMap<>(); + for (RepositoryOptions.RepositoryInjection injection : repoOptions.repositoryInjections) { + if (injection.path().isEmpty()) { + injectionMap.remove(injection.apparentName()); + continue; + } + String repoPath = getAbsolutePath(injection.path(), env); + injectionMap.put(injection.apparentName(), PathFragment.create(repoPath)); + } + ImmutableMap newInjections = ImmutableMap.copyOf(injectionMap); + if (!Maps.difference(injections, newInjections).areEqual()) { + injections = newInjections; + } + } else { + injections = ImmutableMap.of(); + } + if (repoOptions.moduleOverrides != null) { Map moduleOverrideMap = new LinkedHashMap<>(); for (RepositoryOptions.ModuleOverride override : repoOptions.moduleOverrides) { @@ -605,6 +624,7 @@ public ImmutableList getPrecomputedValues() { } return ImmutableList.of( PrecomputedValue.injected(RepositoryMappingFunction.REPOSITORY_OVERRIDES, overrides), + PrecomputedValue.injected(ModuleFileFunction.INJECTED_REPOSITORIES, injections), PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, moduleOverrides), PrecomputedValue.injected( RepositoryDelegatorFunction.RESOLVED_FILE_INSTEAD_OF_WORKSPACE, diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD index 8edaa2957afcc6..76e74e17d63775 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD @@ -259,6 +259,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/skyframe:package_lookup_function", "//src/main/java/com/google/devtools/build/lib/skyframe:package_lookup_value", "//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value", + "//src/main/java/com/google/devtools/build/lib/skyframe:repository_mapping_function", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs", diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java index 0c7bd44a509a71..29425e742aff61 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java @@ -76,6 +76,7 @@ import java.util.Optional; import java.util.stream.Stream; import javax.annotation.Nullable; +import net.starlark.java.eval.Dict; import net.starlark.java.eval.EvalException; import net.starlark.java.eval.Mutability; import net.starlark.java.eval.StarlarkSemantics; @@ -95,6 +96,8 @@ public class ModuleFileFunction implements SkyFunction { public static final Precomputed> MODULE_OVERRIDES = new Precomputed<>("module_overrides"); + public static final Precomputed> INJECTED_REPOSITORIES = + new Precomputed<>("repository_injections"); private final BazelStarlarkEnvironment starlarkEnv; private final Path workspaceRoot; @@ -194,6 +197,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) // Dev dependencies should always be ignored if the current module isn't the root module /* ignoreDevDeps= */ true, builtinModules, + /* injectedRepositories= */ ImmutableMap.of(), // Disable printing for modules from registries. We don't want them to be able to spam // the console during resolution, but module files potentially edited by the user as // part of a non-registry override should permit printing to aid debugging. @@ -287,6 +291,7 @@ private SkyValue computeForRootModule( ImmutableMap.copyOf(state.includeLabelToCompiledModuleFile), builtinModules, MODULE_OVERRIDES.get(env), + INJECTED_REPOSITORIES.get(env), IGNORE_DEV_DEPS.get(env), starlarkSemantics, env.getListener(), @@ -420,6 +425,7 @@ public static RootModuleFileValue evaluateRootModuleFile( ImmutableMap includeLabelToCompiledModuleFile, ImmutableMap builtinModules, Map commandOverrides, + Map injectedRepositories, boolean ignoreDevDeps, StarlarkSemantics starlarkSemantics, ExtendedEventHandler eventHandler, @@ -432,6 +438,7 @@ public static RootModuleFileValue evaluateRootModuleFile( ModuleKey.ROOT, ignoreDevDeps, builtinModules, + injectedRepositories, /* printIsNoop= */ false, starlarkSemantics, eventHandler, @@ -495,6 +502,7 @@ private static ModuleThreadContext execModuleFile( ModuleKey moduleKey, boolean ignoreDevDeps, ImmutableMap builtinModules, + Map injectedRepositories, boolean printIsNoop, StarlarkSemantics starlarkSemantics, ExtendedEventHandler eventHandler, @@ -524,6 +532,8 @@ private static ModuleThreadContext execModuleFile( } } }); + + injectRepos(injectedRepositories, context, thread); compiledRootModuleFile.runOnThread(thread); } catch (EvalException e) { eventHandler.handle(Event.error(e.getInnermostLocation(), e.getMessageWithStack())); @@ -532,6 +542,31 @@ private static ModuleThreadContext execModuleFile( return context; } + // Adds a local_repository for each repository injected via --injected_repositories. + private static void injectRepos( + Map injectedRepositories, + ModuleThreadContext context, + StarlarkThread thread) + throws EvalException { + ModuleThreadContext.ModuleExtensionUsageBuilder newUsageBuilder = + new ModuleThreadContext.ModuleExtensionUsageBuilder( + context, + "//:MODULE.bazel", + "@bazel_tools//tools/build_defs/repo:local.bzl%local_repository", + /* isolate= */ false); + var localRepositoryProxy = new ModuleFileGlobals.RepoRuleProxy(newUsageBuilder); + for (var injectedRepository : injectedRepositories.entrySet()) { + localRepositoryProxy.call( + injectedRepository.getKey(), + /* devDependency= */ true, + Dict.copyOf( + thread.mutability(), + ImmutableMap.of("path", injectedRepository.getValue().getPathString())), + thread); + } + context.getExtensionUsageBuilders().add(newUsageBuilder); + } + /** * Result of a {@link #getModuleFile} call. * diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java index 9e17ab282482db..83711a78cf674a 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java @@ -780,7 +780,8 @@ public RepoRuleProxy useRepoRule(String bzlFile, String ruleName, StarlarkThread context.setNonModuleCalled(); // Not a valid Starlark identifier so that it can't collide with a real extension. String extensionName = bzlFile + '%' + ruleName; - // Find or create the builder for the singular "innate" extension of this module. + // Find or create the builder for the singular "innate" extension of this repo rule for this + // module. for (ModuleExtensionUsageBuilder usageBuilder : context.getExtensionUsageBuilders()) { if (usageBuilder.isForExtension("//:MODULE.bazel", extensionName)) { return new RepoRuleProxy(usageBuilder); @@ -797,7 +798,7 @@ public RepoRuleProxy useRepoRule(String bzlFile, String ruleName, StarlarkThread static class RepoRuleProxy implements StarlarkValue { private final ModuleExtensionUsageBuilder usageBuilder; - private RepoRuleProxy(ModuleExtensionUsageBuilder usageBuilder) { + RepoRuleProxy(ModuleExtensionUsageBuilder usageBuilder) { this.usageBuilder = usageBuilder; } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD index 30b5d051d1d556..d01d8826c1c1d5 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD @@ -72,6 +72,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/util", "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", "//src/main/java/com/google/devtools/common/options", + "//src/main/java/net/starlark/java/eval", "//third_party:auto_value", "//third_party:guava", ], diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java index f64dc4a722714a..24afa21bac7272 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java @@ -14,7 +14,6 @@ package com.google.devtools.build.lib.bazel.repository; -import com.google.auto.value.AutoValue; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.util.OptionsUtils; @@ -28,6 +27,7 @@ import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParsingException; import java.util.List; +import net.starlark.java.eval.EvalException; /** Command-line options for repositories. */ public class RepositoryOptions extends OptionsBase { @@ -149,6 +149,16 @@ public class RepositoryOptions extends OptionsBase { + " previous overrides.") public List repositoryOverrides; + @Option( + name = "inject_repository", + defaultValue = "null", + allowMultiple = true, + converter = RepositoryInjectionConverter.class, + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + help = "Foo") + public List repositoryInjections; + @Option( name = "override_module", defaultValue = "null", @@ -362,7 +372,7 @@ public RepositoryOverride convert(String input) throws OptionsParsingException { OptionsUtils.PathFragmentConverter pathConverter = new OptionsUtils.PathFragmentConverter(); String pathString = pathConverter.convert(pieces[1]).getPathString(); try { - return RepositoryOverride.create(RepositoryName.create(pieces[0]), pathString); + return new RepositoryOverride(RepositoryName.create(pieces[0]), pathString); } catch (LabelSyntaxException e) { throw new OptionsParsingException("Invalid repository name given to override", input, e); } @@ -374,6 +384,35 @@ public String getTypeDescription() { } } + /** + * Converts from an equals-separated pair of strings into RepositoryName->PathFragment mapping. + */ + public static class RepositoryInjectionConverter + extends Converter.Contextless { + + @Override + public RepositoryInjection convert(String input) throws OptionsParsingException { + String[] pieces = input.split("=", 2); + if (pieces.length != 2) { + throw new OptionsParsingException( + "Repository injections must be of the form 'repository-name=path'", input); + } + OptionsUtils.PathFragmentConverter pathConverter = new OptionsUtils.PathFragmentConverter(); + String pathString = pathConverter.convert(pieces[1]).getPathString(); + try { + RepositoryName.validateUserProvidedRepoName(pieces[0]); + return new RepositoryInjection(pieces[0], pathString); + } catch (EvalException e) { + throw new OptionsParsingException("Invalid repository name given to inject", input, e); + } + } + + @Override + public String getTypeDescription() { + return "an equals-separated mapping of repository name to path"; + } + } + /** Converts from an equals-separated pair of strings into ModuleName->PathFragment mapping. */ public static class ModuleOverrideConverter extends Converter.Contextless { @@ -396,7 +435,7 @@ public ModuleOverride convert(String input) throws OptionsParsingException { OptionsUtils.PathFragmentConverter pathConverter = new OptionsUtils.PathFragmentConverter(); String pathString = pathConverter.convert(pieces[1]).getPathString(); - return ModuleOverride.create(pieces[0], pathString); + return new ModuleOverride(pieces[0], pathString); } @Override @@ -406,28 +445,10 @@ public String getTypeDescription() { } /** A repository override, represented by a name and an absolute path to a repository. */ - @AutoValue - public abstract static class RepositoryOverride { + public record RepositoryOverride(RepositoryName repositoryName, String path) {} - private static RepositoryOverride create(RepositoryName repositoryName, String path) { - return new AutoValue_RepositoryOptions_RepositoryOverride(repositoryName, path); - } - - public abstract RepositoryName repositoryName(); - - public abstract String path(); - } + public record RepositoryInjection(String apparentName, String path) {} /** A module override, represented by a name and an absolute path to a module. */ - @AutoValue - public abstract static class ModuleOverride { - - private static ModuleOverride create(String moduleName, String path) { - return new AutoValue_RepositoryOptions_ModuleOverride(moduleName, path); - } - - public abstract String moduleName(); - - public abstract String path(); - } + public record ModuleOverride(String moduleName, String path) {} } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunction.java index c58945f18c6887..7765ce341ff2e1 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunction.java @@ -38,7 +38,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.Set; import javax.annotation.Nullable; import net.starlark.java.eval.StarlarkSemantics; @@ -56,14 +55,32 @@ public RepositoryMappingFunction(RuleClassProvider ruleClassProvider) { @Override public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException { + RepositoryMappingValue.Key key = (RepositoryMappingValue.Key) skyKey; + RepositoryMappingValue repositoryMappingValue = computeWithoutOverrides(key, env); + if (repositoryMappingValue == null) { + return null; + } + RepositoryName repoName = key.repoName(); + if (repositoryMappingValue == RepositoryMappingValue.NOT_FOUND_VALUE + && REPOSITORY_OVERRIDES.get(env).containsKey(repoName)) { + throw new RepositoryMappingFunctionException( + String.format( + "the repository %s does not exist, but has been specified as overridden with --override_repository. Use --inject_repository instead to add a new repository.", + repoName)); + } + return repositoryMappingValue; + } + + private RepositoryMappingValue computeWithoutOverrides( + RepositoryMappingValue.Key skyKey, Environment env) + throws SkyFunctionException, InterruptedException { StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env); if (starlarkSemantics == null) { return null; } - RepositoryName repositoryName = ((RepositoryMappingValue.Key) skyKey).repoName(); + RepositoryName repositoryName = skyKey.repoName(); boolean enableBzlmod = starlarkSemantics.getBool(BuildLanguageOptions.ENABLE_BZLMOD); boolean enableWorkspace = starlarkSemantics.getBool(BuildLanguageOptions.ENABLE_WORKSPACE); - Set repositoryOverrides = REPOSITORY_OVERRIDES.get(env).keySet(); if (!enableBzlmod && !enableWorkspace) { throw new RepositoryMappingFunctionException( @@ -115,7 +132,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) } if (repositoryName.isMain() - && ((RepositoryMappingValue.Key) skyKey).rootModuleShouldSeeWorkspaceRepos() + && skyKey.rootModuleShouldSeeWorkspaceRepos() && enableWorkspace) { // The root module should be able to see repos defined in WORKSPACE. Therefore, we find all // workspace repos and add them as extra visible repos in root module's repo mappings. @@ -134,7 +151,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) .collect( toMap( Entry::getKey, entry -> RepositoryName.createUnvalidated(entry.getKey()))); - return computeForBazelModuleRepo(repositoryName, bazelDepGraphValue, repositoryOverrides) + return computeForBazelModuleRepo(repositoryName, bazelDepGraphValue) .get() // For the transitional period, we need to map the workspace name to the main repo. .withAdditionalMappings( @@ -146,7 +163,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) // Try and see if this is a repo generated from a Bazel module. Optional mappingValue = - computeForBazelModuleRepo(repositoryName, bazelDepGraphValue, repositoryOverrides); + computeForBazelModuleRepo(repositoryName, bazelDepGraphValue); if (mappingValue.isPresent()) { return repositoryName.isMain() ? mappingValue.get().withCachedInverseMap() @@ -169,15 +186,6 @@ public SkyValue compute(SkyKey skyKey, Environment env) repoMappingEntriesValue.getModuleKey().name(), repoMappingEntriesValue.getModuleKey().version()); } - - if (!enableWorkspace) { - // If a repo is overridden without a previous definition, it uses the main repo's mapping. - if (REPOSITORY_OVERRIDES.get(env).containsKey(repositoryName)) { - return computeForBazelModuleRepo( - RepositoryName.MAIN, bazelDepGraphValue, repositoryOverrides) - .get(); - } - } } if (enableWorkspace) { @@ -211,19 +219,12 @@ public SkyValue compute(SkyKey skyKey, Environment env) * Optional.empty(). */ private Optional computeForBazelModuleRepo( - RepositoryName repositoryName, - BazelDepGraphValue bazelDepGraphValue, - Set repositoryOverrides) { + RepositoryName repositoryName, BazelDepGraphValue bazelDepGraphValue) { ModuleKey moduleKey = bazelDepGraphValue.getCanonicalRepoNameLookup().get(repositoryName); if (moduleKey == null) { return Optional.empty(); } RepositoryMapping repoMapping = bazelDepGraphValue.getFullRepoMapping(moduleKey); - if (repositoryName.isMain()) { - repoMapping = - repoMapping.withAdditionalMappings( - repositoryOverrides.stream().collect(toMap(RepositoryName::getName, name -> name))); - } Module module = bazelDepGraphValue.getDepGraph().get(moduleKey); return Optional.of( RepositoryMappingValue.createForBzlmodRepo( diff --git a/src/test/py/bazel/bzlmod/bazel_module_test.py b/src/test/py/bazel/bzlmod/bazel_module_test.py index 2bc76f095fdeb8..7f086ff3fa0fa4 100644 --- a/src/test/py/bazel/bzlmod/bazel_module_test.py +++ b/src/test/py/bazel/bzlmod/bazel_module_test.py @@ -1111,65 +1111,6 @@ def testRegression22754(self): self.ScratchFile('testdata/WORKSPACE') self.RunBazel(['build', ':all']) - def testOverrideRepositoryAddsRepo(self): - self.ScratchFile( - 'MODULE.bazel', - [ - 'bazel_dep(name="other_module", version="1.0")', - ( - 'local_path_override(module_name="other_module",' - ' path="other_module")' - ), - 'ext = use_extension("//:defs.bzl", "my_ext")', - 'use_repo(ext, "repo")', - ], - ) - - self.ScratchFile( - 'other_module/MODULE.bazel', - ['module(name="other_module", version="1.0")'], - ) - - self.ScratchFile('other_repo/REPO.bazel') - self.ScratchFile('other_repo/BUILD', ['filegroup(name="target")']) - - self.ScratchFile( - 'defs.bzl', - [ - 'def _repo_impl(ctx):', - ' ctx.file("BUILD")', - 'my_repo = repository_rule(implementation=_repo_impl)', - 'def _ext_impl(ctx):', - ' my_repo(name = "repo")', - 'my_ext = module_extension(implementation=_ext_impl)', - ], - ) - self.ScratchFile('BUILD') - - self.RunBazel([ - 'build', - '--override_repository=my_repo=other_repo', - '@my_repo//:target', - ]) - - _, stdout, stderr = self.RunBazel([ - 'mod', - 'dump_repo_mapping', - '--override_repository=my_repo=%workspace%/other_repo', - '', - ]) - main_repo_mapping = json.loads('\n'.join(stdout)) - self.assertEqual(main_repo_mapping["my_repo"], "my_repo") - - _, stdout, stderr = self.RunBazel([ - 'mod', - 'dump_repo_mapping', - '--override_repository=my_repo=%workspace%/other_repo', - 'my_repo', - ]) - my_repo_mapping = json.loads('\n'.join(stdout)) - self.assertEqual(main_repo_mapping, my_repo_mapping) - if __name__ == '__main__': absltest.main() diff --git a/src/test/py/bazel/bzlmod/bazel_overrides_test.py b/src/test/py/bazel/bzlmod/bazel_overrides_test.py index 0f6ebf8d728472..6b9d1ab1466747 100644 --- a/src/test/py/bazel/bzlmod/bazel_overrides_test.py +++ b/src/test/py/bazel/bzlmod/bazel_overrides_test.py @@ -14,10 +14,12 @@ # limitations under the License. # pylint: disable=g-long-ternary +import json import os import shutil import tempfile from absl.testing import absltest + from src.test.py.bazel import test_base from src.test.py.bazel.bzlmod.test_utils import BazelRegistry @@ -556,6 +558,77 @@ def testLocalPathOverrideErrorResolved(self): self.ScratchFile('module/MODULE.bazel', ["module(name = 'module')"]) _, _, _ = self.RunBazel(['build', '@module//:all']) + def testInjectRepository(self): + self.ScratchFile( + 'MODULE.bazel', + [ + 'bazel_dep(name="other_module", version="1.0")', + ( + 'local_path_override(module_name="other_module",' + ' path="other_module")' + ), + 'ext = use_extension("//:defs.bzl", "my_ext")', + 'use_repo(ext, "repo")', + ], + ) + + self.ScratchFile( + 'other_module/MODULE.bazel', + ['module(name="other_module", version="1.0")'], + ) + + self.ScratchFile('other_repo/REPO.bazel') + self.ScratchFile('other_repo/BUILD', ['filegroup(name="target")']) + + self.ScratchFile( + 'defs.bzl', + [ + 'def _repo_impl(ctx):', + ' ctx.file("BUILD")', + 'my_repo = repository_rule(implementation=_repo_impl)', + 'def _ext_impl(ctx):', + ' my_repo(name = "repo")', + 'my_ext = module_extension(implementation=_ext_impl)', + ], + ) + self.ScratchFile('BUILD') + + self.RunBazel([ + 'build', + '--inject_repository=my_repo=%workspace%/other_repo', + '@my_repo//:target', + ]) + + _, stdout, _ = self.RunBazel([ + 'mod', + 'dump_repo_mapping', + '--inject_repository=my_repo=%workspace%/other_repo', + '', + ]) + main_repo_mapping = json.loads('\n'.join(stdout)) + self.assertEqual(main_repo_mapping["my_repo"], "+_repo_rules+my_repo") + + _, stdout, _ = self.RunBazel([ + 'mod', + 'dump_repo_mapping', + '--inject_repository=my_repo=%workspace%/other_repo', + '+_repo_rules+my_repo', + ]) + my_repo_mapping = json.loads('\n'.join(stdout)) + self.assertEqual(main_repo_mapping, my_repo_mapping) + + def testOverrideRepositoryOnNonExistentRepo(self): + self.ScratchFile('other_repo/REPO.bazel') + self.ScratchFile('other_repo/BUILD', ['filegroup(name="target")']) + + exit_code, _, stderr = self.RunBazel([ + 'build', + '--override_repository=my_repo=%workspace%/other_repo', + '@my_repo//:target', + ], allow_failure=True) + self.AssertNotExitCode(exit_code, 0, stderr) + self.assertIn("ERROR: No repository visible as '@my_repo' from main repository", stderr) + if __name__ == '__main__': absltest.main()