Skip to content

Commit 46341b1

Browse files
fmeumcopybara-github
authored andcommitted
Add override_repo and inject_repo
Work towards bazelbuild#19301 Fixes bazelbuild#23580 RELNOTES: `override_repo` and `inject_repo` can be used to override and inject repos in module extensions. Closes bazelbuild#23534. PiperOrigin-RevId: 678139661 Change-Id: Iea7caca949c00e701f056c1037e273fee9740e93
1 parent 4371aa0 commit 46341b1

23 files changed

+914
-36
lines changed

site/en/external/extension.md

+57
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,63 @@ several repo visibility rules:
174174
the repo visible to the module instead of the extension-generated repo
175175
of the same name.
176176

177+
### Overriding and injecting module extension repos
178+
179+
The root module can use
180+
[`override_repo`](/rules/lib/globals/module#override_repo) and
181+
[`inject_repo`](/rules/lib/globals/module#inject_repo) to override or inject
182+
module extension repos.
183+
184+
#### Example: Replacing `rules_java`'s `java_tools` with a vendored copy
185+
186+
```python
187+
# MODULE.bazel
188+
local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
189+
local_repository(
190+
name = "my_java_tools",
191+
path = "vendor/java_tools",
192+
)
193+
194+
bazel_dep(name = "rules_java", version = "7.11.1")
195+
java_toolchains = use_extension("@rules_java//java:extension.bzl", "toolchains")
196+
197+
override_repo(java_toolchains, remote_java_tools = "my_java_tools")
198+
```
199+
200+
#### Example: Patch a Go dependency to depend on `@zlib` instead of the system zlib
201+
202+
```python
203+
# MODULE.bazel
204+
bazel_dep(name = "gazelle", version = "0.38.0")
205+
bazel_dep(name = "zlib", version = "1.3.1.bcr.3")
206+
207+
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
208+
go_deps.from_file(go_mod = "//:go.mod")
209+
go_deps.module_override(
210+
patches = [
211+
"//patches:my_module_zlib.patch",
212+
],
213+
path = "example.com/my_module",
214+
)
215+
use_repo(go_deps, ...)
216+
217+
inject_repo(go_deps, "zlib")
218+
```
219+
220+
```diff
221+
# patches/my_module_zlib.patch
222+
--- a/BUILD.bazel
223+
+++ b/BUILD.bazel
224+
@@ -1,6 +1,6 @@
225+
go_binary(
226+
name = "my_module",
227+
importpath = "example.com/my_module",
228+
srcs = ["my_module.go"],
229+
- copts = ["-lz"],
230+
+ cdeps = ["@zlib"],
231+
)
232+
```
233+
177234
## Best practices
178235

179236
This section describes best practices when writing extensions so they are

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelDepGraphFunction.java

+38-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@ public SkyValue compute(SkyKey skyKey, Environment env)
8080
canonicalRepoNameLookup,
8181
depGraph.values().stream().map(AbridgedModule::from).collect(toImmutableList()),
8282
extensionUsagesById,
83-
extensionUniqueNames.inverse());
83+
extensionUniqueNames.inverse(),
84+
resolveRepoOverrides(
85+
depGraph,
86+
extensionUsagesById,
87+
extensionUniqueNames.inverse(),
88+
canonicalRepoNameLookup));
8489
}
8590

8691
private static ImmutableTable<ModuleExtensionId, ModuleKey, ModuleExtensionUsage>
@@ -198,6 +203,38 @@ private static String makeUniqueNameCandidate(ModuleExtensionId id, int attempt)
198203
+ extensionNameDisambiguator);
199204
}
200205

206+
private static ImmutableTable<ModuleExtensionId, String, RepositoryName> resolveRepoOverrides(
207+
ImmutableMap<ModuleKey, Module> depGraph,
208+
ImmutableTable<ModuleExtensionId, ModuleKey, ModuleExtensionUsage> extensionUsagesTable,
209+
ImmutableMap<ModuleExtensionId, String> extensionUniqueNames,
210+
ImmutableBiMap<RepositoryName, ModuleKey> canonicalRepoNameLookup) {
211+
RepositoryMapping rootModuleMappingWithoutOverrides =
212+
BazelDepGraphValue.getRepositoryMapping(
213+
ModuleKey.ROOT,
214+
depGraph,
215+
extensionUsagesTable,
216+
extensionUniqueNames,
217+
canonicalRepoNameLookup,
218+
// ModuleFileFunction ensures that repos that override other repos are not themselves
219+
// overridden, so we can safely pass an empty table here instead of resolving chains
220+
// of overrides.
221+
ImmutableTable.of());
222+
ImmutableTable.Builder<ModuleExtensionId, String, RepositoryName> repoOverridesBuilder =
223+
ImmutableTable.builder();
224+
for (var extensionId : extensionUsagesTable.rowKeySet()) {
225+
var rootUsage = extensionUsagesTable.row(extensionId).get(ModuleKey.ROOT);
226+
if (rootUsage != null) {
227+
for (var override : rootUsage.getRepoOverrides().entrySet()) {
228+
repoOverridesBuilder.put(
229+
extensionId,
230+
override.getKey(),
231+
rootModuleMappingWithoutOverrides.get(override.getValue().overridingRepoName()));
232+
}
233+
}
234+
}
235+
return repoOverridesBuilder.buildOrThrow();
236+
}
237+
201238
static class BazelDepGraphFunctionException extends SkyFunctionException {
202239
BazelDepGraphFunctionException(ExternalDepsException e, Transience transience) {
203240
super(e, transience);

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelDepGraphValue.java

+39-9
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ public static BazelDepGraphValue create(
4141
ImmutableMap<RepositoryName, ModuleKey> canonicalRepoNameLookup,
4242
ImmutableList<AbridgedModule> abridgedModules,
4343
ImmutableTable<ModuleExtensionId, ModuleKey, ModuleExtensionUsage> extensionUsagesTable,
44-
ImmutableMap<ModuleExtensionId, String> extensionUniqueNames) {
44+
ImmutableMap<ModuleExtensionId, String> extensionUniqueNames,
45+
ImmutableTable<ModuleExtensionId, String, RepositoryName> repoOverrides) {
4546
return new AutoValue_BazelDepGraphValue(
4647
depGraph,
4748
ImmutableBiMap.copyOf(canonicalRepoNameLookup),
4849
abridgedModules,
4950
extensionUsagesTable,
50-
extensionUniqueNames);
51+
extensionUniqueNames,
52+
repoOverrides);
5153
}
5254

5355
public static BazelDepGraphValue createEmptyDepGraph() {
@@ -71,7 +73,8 @@ public static BazelDepGraphValue createEmptyDepGraph() {
7173
canonicalRepoNameLookup,
7274
ImmutableList.of(),
7375
ImmutableTable.of(),
74-
ImmutableMap.of());
76+
ImmutableMap.of(),
77+
ImmutableTable.of());
7578
}
7679

7780
/**
@@ -103,27 +106,54 @@ public static BazelDepGraphValue createEmptyDepGraph() {
103106
*/
104107
public abstract ImmutableMap<ModuleExtensionId, String> getExtensionUniqueNames();
105108

109+
/**
110+
* For each module extension, a mapping from the name of the repo exported by the extension to the
111+
* canonical name of the repo that should override it (if any).
112+
*/
113+
public abstract ImmutableTable<ModuleExtensionId, String, RepositoryName> getRepoOverrides();
114+
106115
/**
107116
* Returns the full {@link RepositoryMapping} for the given module, including repos from Bazel
108117
* module deps and module extensions.
109118
*/
110119
public final RepositoryMapping getFullRepoMapping(ModuleKey key) {
120+
return getRepositoryMapping(
121+
key,
122+
getDepGraph(),
123+
getExtensionUsagesTable(),
124+
getExtensionUniqueNames(),
125+
getCanonicalRepoNameLookup(),
126+
getRepoOverrides());
127+
}
128+
129+
static RepositoryMapping getRepositoryMapping(
130+
ModuleKey key,
131+
ImmutableMap<ModuleKey, Module> depGraph,
132+
ImmutableTable<ModuleExtensionId, ModuleKey, ModuleExtensionUsage> extensionUsagesTable,
133+
ImmutableMap<ModuleExtensionId, String> extensionUniqueNames,
134+
ImmutableBiMap<RepositoryName, ModuleKey> canonicalRepoNameLookup,
135+
ImmutableTable<ModuleExtensionId, String, RepositoryName> repoOverrides) {
111136
ImmutableMap.Builder<String, RepositoryName> mapping = ImmutableMap.builder();
112137
for (Map.Entry<ModuleExtensionId, ModuleExtensionUsage> extIdAndUsage :
113-
getExtensionUsagesTable().column(key).entrySet()) {
138+
extensionUsagesTable.column(key).entrySet()) {
114139
ModuleExtensionId extensionId = extIdAndUsage.getKey();
115140
ModuleExtensionUsage usage = extIdAndUsage.getValue();
116-
String repoNamePrefix = getExtensionUniqueNames().get(extensionId) + "+";
141+
String repoNamePrefix = extensionUniqueNames.get(extensionId) + "+";
117142
for (ModuleExtensionUsage.Proxy proxy : usage.getProxies()) {
118143
for (Map.Entry<String, String> entry : proxy.getImports().entrySet()) {
119-
String canonicalRepoName = repoNamePrefix + entry.getValue();
120-
mapping.put(entry.getKey(), RepositoryName.createUnvalidated(canonicalRepoName));
144+
RepositoryName defaultCanonicalRepoName =
145+
RepositoryName.createUnvalidated(repoNamePrefix + entry.getValue());
146+
mapping.put(
147+
entry.getKey(),
148+
repoOverrides
149+
.row(extensionId)
150+
.getOrDefault(entry.getValue(), defaultCanonicalRepoName));
121151
}
122152
}
123153
}
124-
return getDepGraph()
154+
return depGraph
125155
.get(key)
126-
.getRepoMappingWithBazelDepsOnly(getCanonicalRepoNameLookup().inverse())
156+
.getRepoMappingWithBazelDepsOnly(canonicalRepoNameLookup.inverse())
127157
.withAdditionalMappings(mapping.buildOrThrow());
128158
}
129159
}

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionEvalStarlarkThreadContext.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ record RepoRuleCall(
7171
private final String repoPrefix;
7272
private final PackageIdentifier basePackageId;
7373
private final RepositoryMapping baseRepoMapping;
74+
private final ImmutableMap<String, RepositoryName> repoOverrides;
7475
private final BlazeDirectories directories;
7576
private final ExtendedEventHandler eventHandler;
7677
private final Map<String, RepoRuleCall> deferredRepos = new LinkedHashMap<>();
@@ -80,6 +81,7 @@ public ModuleExtensionEvalStarlarkThreadContext(
8081
String repoPrefix,
8182
PackageIdentifier basePackageId,
8283
RepositoryMapping baseRepoMapping,
84+
ImmutableMap<String, RepositoryName> repoOverrides,
8385
RepositoryMapping mainRepoMapping,
8486
BlazeDirectories directories,
8587
ExtendedEventHandler eventHandler) {
@@ -88,6 +90,7 @@ public ModuleExtensionEvalStarlarkThreadContext(
8890
this.repoPrefix = repoPrefix;
8991
this.basePackageId = basePackageId;
9092
this.baseRepoMapping = baseRepoMapping;
93+
this.repoOverrides = repoOverrides;
9194
this.directories = directories;
9295
this.eventHandler = eventHandler;
9396
}
@@ -133,13 +136,15 @@ public ImmutableMap<String, RepoSpec> createRepos(StarlarkSemantics starlarkSema
133136
// Make it possible to refer to extension repos in the label attributes of another extension
134137
// repo. Wrapping a label in Label(...) ensures that it is evaluated with respect to the
135138
// containing module's repo mapping instead.
136-
var extensionRepos =
139+
ImmutableMap.Builder<String, RepositoryName> entries = ImmutableMap.builder();
140+
entries.putAll(baseRepoMapping.entries());
141+
entries.putAll(
137142
Maps.asMap(
138143
deferredRepos.keySet(),
139-
apparentName -> RepositoryName.createUnvalidated(repoPrefix + apparentName));
144+
apparentName -> RepositoryName.createUnvalidated(repoPrefix + apparentName)));
145+
entries.putAll(repoOverrides);
140146
RepositoryMapping fullRepoMapping =
141-
RepositoryMapping.create(extensionRepos, baseRepoMapping.ownerRepo())
142-
.withAdditionalMappings(baseRepoMapping);
147+
RepositoryMapping.create(entries.buildKeepingLast(), baseRepoMapping.ownerRepo());
143148
// LINT.ThenChange(//src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionRepoMappingEntriesFunction.java)
144149

145150
ImmutableMap.Builder<String, RepoSpec> repoSpecs = ImmutableMap.builder();

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionRepoMappingEntriesFunction.java

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ private ModuleExtensionRepoMappingEntriesValue computeRepoMappingEntries(
7474
ImmutableMap.Builder<String, RepositoryName> entries = ImmutableMap.builder();
7575
entries.putAll(bazelDepGraphValue.getFullRepoMapping(moduleKey).entries());
7676
entries.putAll(extensionEvalValue.getCanonicalRepoNameToInternalNames().inverse());
77+
entries.putAll(bazelDepGraphValue.getRepoOverrides().row(extensionId));
7778
return ModuleExtensionRepoMappingEntriesValue.create(entries.buildKeepingLast(), moduleKey);
7879
// LINT.ThenChange(//src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionEvalStarlarkThreadContext.java)
7980
}

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionUsage.java

+27
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.auto.value.AutoValue;
2020
import com.google.common.collect.ImmutableBiMap;
2121
import com.google.common.collect.ImmutableList;
22+
import com.google.common.collect.ImmutableMap;
2223
import com.google.devtools.build.lib.vfs.PathFragment;
2324
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2425
import com.ryanharter.auto.value.gson.GenerateTypeAdapter;
@@ -130,6 +131,24 @@ public final boolean getHasNonDevUseExtension() {
130131
return getProxies().stream().anyMatch(p -> !p.isDevDependency());
131132
}
132133

134+
/**
135+
* Represents a repo that overrides another repo within the scope of the extension.
136+
*
137+
* @param overridingRepoName The apparent name of the overriding repo in the root module.
138+
* @param mustExist Whether this override should apply to an existing repo.
139+
* @param location The location of the {@code override_repo} or {@code inject_repo} call.
140+
*/
141+
@GenerateTypeAdapter
142+
public record RepoOverride(String overridingRepoName, boolean mustExist, Location location) {}
143+
144+
/**
145+
* Contains information about overrides that apply to repos generated by this extension. Keyed by
146+
* the extension-local repo name.
147+
*
148+
* <p>This is only non-empty for root module usages.
149+
*/
150+
public abstract ImmutableMap<String, RepoOverride> getRepoOverrides();
151+
133152
public abstract Builder toBuilder();
134153

135154
public static Builder builder() {
@@ -152,6 +171,11 @@ ModuleExtensionUsage trimForEvaluation() {
152171
// Extension implementation functions do not see the imports, they are only validated
153172
// against the set of generated repos in a validation step that comes afterward.
154173
.setProxies(ImmutableList.of())
174+
// Tracked in SingleExtensionUsagesValue instead, using canonical instead of apparent names.
175+
// Whether this override must apply to an existing repo as well as its source location also
176+
// don't influence the evaluation of the extension as they are checked in
177+
// SingleExtensionFunction.
178+
.setRepoOverrides(ImmutableMap.of())
155179
.build();
156180
}
157181

@@ -185,6 +209,9 @@ public Builder addTag(Tag value) {
185209
return this;
186210
}
187211

212+
@CanIgnoreReturnValue
213+
public abstract Builder setRepoOverrides(ImmutableMap<String, RepoOverride> repoOverrides);
214+
188215
public abstract ModuleExtensionUsage build();
189216
}
190217
}

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ public static RootModuleFileValue evaluateRootModuleFile(
435435
try {
436436
module = moduleThreadContext.buildModule(/* registry= */ null);
437437
} catch (EvalException e) {
438-
eventHandler.handle(Event.error(e.getMessageWithStack()));
438+
eventHandler.handle(Event.error(e.getInnermostLocation(), e.getMessageWithStack()));
439439
throw errorf(Code.BAD_MODULE, "error executing MODULE.bazel file for the root module");
440440
}
441441
for (ModuleExtensionUsage usage : module.getExtensionUsages()) {
@@ -521,7 +521,7 @@ private static ModuleThreadContext execModuleFile(
521521
});
522522
compiledRootModuleFile.runOnThread(thread);
523523
} catch (EvalException e) {
524-
eventHandler.handle(Event.error(e.getMessageWithStack()));
524+
eventHandler.handle(Event.error(e.getInnermostLocation(), e.getMessageWithStack()));
525525
throw errorf(Code.BAD_MODULE, "error executing MODULE.bazel file for %s", moduleKey);
526526
}
527527
return context;

0 commit comments

Comments
 (0)