Skip to content

Commit 640e850

Browse files
fmeumcopybara-github
authored andcommitted
Track dev/non-dev use_extension calls
By always tracking whether a given extension usage by a module has `use_extension` calls with and/or without `dev_dependency = True` instead of just for isolated extension usages, we obtain the following advantages: * Module extensions can use `module_ctx.is_dev_dependency` to learn whether the root module contains only `use_extension` calls with `dev_dependency = True` for them. This is necessary to decide whether repositories that do not directly correspond to tags (e.g. hub repos) should be marked as dev or non-dev dependencies in `module_ctx.extension_metadata`. * `ModuleExtensionMetadata` consistency checks of the type "no dev/non-dev imports without dev/non-dev usage" are generalized from isolated to all extensions. * Prepares for the removal of `isDevUsage` from `IsolationKey` in a follow-up change which will instead use the exported name of the (only) usage proxy of an isolated usage as the key. Closes #18829. PiperOrigin-RevId: 547517851 Change-Id: I1290e53adf735a16d62e2c103f3776ecbd5b1a18
1 parent 6d6bbdc commit 640e850

9 files changed

+154
-95
lines changed

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
public class ModuleExtensionContext extends StarlarkBaseExternalContext {
4747
private final ModuleExtensionId extensionId;
4848
private final StarlarkList<StarlarkBazelModule> modules;
49+
private final boolean rootModuleHasNonDevDependency;
4950

5051
protected ModuleExtensionContext(
5152
Path workingDirectory,
@@ -57,7 +58,8 @@ protected ModuleExtensionContext(
5758
StarlarkSemantics starlarkSemantics,
5859
@Nullable RepositoryRemoteExecutor remoteExecutor,
5960
ModuleExtensionId extensionId,
60-
StarlarkList<StarlarkBazelModule> modules) {
61+
StarlarkList<StarlarkBazelModule> modules,
62+
boolean rootModuleHasNonDevDependency) {
6163
super(
6264
workingDirectory,
6365
env,
@@ -69,6 +71,7 @@ protected ModuleExtensionContext(
6971
remoteExecutor);
7072
this.extensionId = extensionId;
7173
this.modules = modules;
74+
this.rootModuleHasNonDevDependency = rootModuleHasNonDevDependency;
7275
}
7376

7477
public Path getWorkingDirectory() {
@@ -96,11 +99,11 @@ protected ImmutableMap<String, String> getRemoteExecProperties() throws EvalExce
9699
name = "modules",
97100
structField = true,
98101
doc =
99-
"A list of all the Bazel modules in the external dependency graph, each of which is a <a"
100-
+ " href=\"../builtins/bazel_module.html\">bazel_module</a> object that exposes all"
101-
+ " the tags it specified for this module extension. The iteration order of this"
102-
+ " dictionary is guaranteed to be the same as breadth-first search starting from the"
103-
+ " root module.")
102+
"A list of all the Bazel modules in the external dependency graph that use this module "
103+
+ "extension, each of which is a <a href=\"../builtins/bazel_module.html\">"
104+
+ "bazel_module</a> object that exposes all the tags it specified for this extension."
105+
+ " The iteration order of this dictionary is guaranteed to be the same as"
106+
+ " breadth-first search starting from the root module.")
104107
public StarlarkList<StarlarkBazelModule> getModules() {
105108
return modules;
106109
}
@@ -133,6 +136,14 @@ public boolean isIsolated() {
133136
return extensionId.getIsolationKey().isPresent();
134137
}
135138

139+
@StarlarkMethod(
140+
name = "root_module_has_non_dev_dependency",
141+
doc = "Whether the root module uses this extension as a non-dev dependency.",
142+
structField = true)
143+
public boolean rootModuleHasNonDevDependency() {
144+
return rootModuleHasNonDevDependency;
145+
}
146+
136147
@StarlarkMethod(
137148
name = "extension_metadata",
138149
doc =
@@ -154,7 +165,7 @@ public boolean isIsolated() {
154165
+ " disjoint.<p>Exactly one of <code>root_module_direct_deps</code> and"
155166
+ " <code>root_module_direct_dev_deps</code> can be set to the special value"
156167
+ " <code>\"all\"</code>, which is treated as if a list with the names of"
157-
+ " allrepositories generated by the extension was specified as the value.",
168+
+ " all repositories generated by the extension was specified as the value.",
158169
positional = false,
159170
named = true,
160171
defaultValue = "None",
@@ -180,7 +191,7 @@ public boolean isIsolated() {
180191
+ " disjoint.<p>Exactly one of <code>root_module_direct_deps</code> and"
181192
+ " <code>root_module_direct_dev_deps</code> can be set to the special value"
182193
+ " <code>\"all\"</code>, which is treated as if a list with the names of"
183-
+ " allrepositories generated by the extension was specified as the value.",
194+
+ " all repositories generated by the extension was specified as the value.",
184195
positional = false,
185196
named = true,
186197
defaultValue = "None",

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

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
import com.google.common.base.Preconditions;
2121
import com.google.common.collect.ImmutableSet;
2222
import com.google.common.collect.ImmutableSortedSet;
23+
import com.google.common.collect.Iterables;
2324
import com.google.common.collect.Sets;
2425
import com.google.devtools.build.docgen.annot.DocCategory;
2526
import com.google.devtools.build.lib.cmdline.RepositoryName;
2627
import com.google.devtools.build.lib.events.Event;
2728
import com.google.devtools.build.lib.events.EventHandler;
2829
import java.util.Collection;
2930
import java.util.LinkedHashSet;
30-
import java.util.List;
3131
import java.util.Optional;
3232
import java.util.Set;
3333
import java.util.stream.Collectors;
@@ -82,23 +82,11 @@ static ModuleExtensionMetadata create(
8282
// root_module_direct_dev_deps = [], but not root_module_direct_dev_deps = ["some_repo"].
8383
if (rootModuleDirectDepsUnchecked.equals("all")
8484
&& rootModuleDirectDevDepsUnchecked.equals(StarlarkList.immutableOf())) {
85-
if (extensionId.getIsolationKey().isPresent()
86-
&& extensionId.getIsolationKey().get().isDevUsage()) {
87-
throw Starlark.errorf(
88-
"root_module_direct_deps must be empty for an isolated extension usage with "
89-
+ "dev_dependency = True");
90-
}
9185
return new ModuleExtensionMetadata(null, null, UseAllRepos.REGULAR);
9286
}
9387

9488
if (rootModuleDirectDevDepsUnchecked.equals("all")
9589
&& rootModuleDirectDepsUnchecked.equals(StarlarkList.immutableOf())) {
96-
if (extensionId.getIsolationKey().isPresent()
97-
&& !extensionId.getIsolationKey().get().isDevUsage()) {
98-
throw Starlark.errorf(
99-
"root_module_direct_dev_deps must be empty for an isolated extension usage with "
100-
+ "dev_dependency = False");
101-
}
10290
return new ModuleExtensionMetadata(null, null, UseAllRepos.DEV);
10391
}
10492

@@ -128,20 +116,6 @@ static ModuleExtensionMetadata create(
128116
Sequence.cast(
129117
rootModuleDirectDevDepsUnchecked, String.class, "root_module_direct_dev_deps");
130118

131-
if (extensionId.getIsolationKey().isPresent()) {
132-
ModuleExtensionId.IsolationKey isolationKey = extensionId.getIsolationKey().get();
133-
if (isolationKey.isDevUsage() && !rootModuleDirectDeps.isEmpty()) {
134-
throw Starlark.errorf(
135-
"root_module_direct_deps must be empty for an isolated extension usage with "
136-
+ "dev_dependency = True");
137-
}
138-
if (!isolationKey.isDevUsage() && !rootModuleDirectDevDeps.isEmpty()) {
139-
throw Starlark.errorf(
140-
"root_module_direct_dev_deps must be empty for an isolated extension usage with "
141-
+ "dev_dependency = False");
142-
}
143-
}
144-
145119
Set<String> explicitRootModuleDirectDeps = new LinkedHashSet<>();
146120
for (String dep : rootModuleDirectDeps) {
147121
try {
@@ -192,40 +166,46 @@ Optional<Event> generateFixupMessage(
192166
// expected to modify any other module's MODULE.bazel file.
193167
return Optional.empty();
194168
}
169+
// Every module only has at most a single usage of a given extension.
170+
ModuleExtensionUsage rootUsage = Iterables.getOnlyElement(rootUsages);
195171

196172
var rootModuleDirectDevDeps = getRootModuleDirectDevDeps(allRepos);
197173
var rootModuleDirectDeps = getRootModuleDirectDeps(allRepos);
198174
if (rootModuleDirectDevDeps.isEmpty() && rootModuleDirectDeps.isEmpty()) {
199175
return Optional.empty();
200176
}
201-
202177
Preconditions.checkState(
203178
rootModuleDirectDevDeps.isPresent() && rootModuleDirectDeps.isPresent());
179+
180+
if (!rootUsage.getHasNonDevUseExtension() && !rootModuleDirectDeps.get().isEmpty()) {
181+
throw Starlark.errorf(
182+
"root_module_direct_deps must be empty if the root module contains no "
183+
+ "usages with dev_dependency = False");
184+
}
185+
if (!rootUsage.getHasDevUseExtension() && !rootModuleDirectDevDeps.get().isEmpty()) {
186+
throw Starlark.errorf(
187+
"root_module_direct_dev_deps must be empty if the root module contains no "
188+
+ "usages with dev_dependency = True");
189+
}
190+
204191
return generateFixupMessage(
205-
rootUsages, allRepos, rootModuleDirectDeps.get(), rootModuleDirectDevDeps.get());
192+
rootUsage, allRepos, rootModuleDirectDeps.get(), rootModuleDirectDevDeps.get());
206193
}
207194

208195
private static Optional<Event> generateFixupMessage(
209-
List<ModuleExtensionUsage> rootUsages,
196+
ModuleExtensionUsage rootUsage,
210197
Set<String> allRepos,
211198
Set<String> expectedImports,
212199
Set<String> expectedDevImports) {
213-
var actualDevImports =
214-
rootUsages.stream()
215-
.flatMap(usage -> usage.getDevImports().stream())
216-
.collect(toImmutableSet());
200+
var actualDevImports = ImmutableSet.copyOf(rootUsage.getDevImports());
217201
var actualImports =
218-
rootUsages.stream()
219-
.flatMap(usage -> usage.getImports().values().stream())
202+
rootUsage.getImports().values().stream()
220203
.filter(repo -> !actualDevImports.contains(repo))
221204
.collect(toImmutableSet());
222205

223-
// All label strings that map to the same Label are equivalent for buildozer as it implements
224-
// the same normalization of label strings with no or empty repo name.
225-
ModuleExtensionUsage firstUsage = rootUsages.get(0);
226-
String extensionBzlFile = firstUsage.getExtensionBzlFile();
227-
String extensionName = firstUsage.getExtensionName();
228-
Location location = firstUsage.getLocation();
206+
String extensionBzlFile = rootUsage.getExtensionBzlFile();
207+
String extensionName = rootUsage.getExtensionName();
208+
Location location = rootUsage.getLocation();
229209

230210
var importsToAdd = ImmutableSortedSet.copyOf(Sets.difference(expectedImports, actualImports));
231211
var importsToRemove =
@@ -290,28 +270,28 @@ private static Optional<Event> generateFixupMessage(
290270
importsToAdd,
291271
extensionBzlFile,
292272
extensionName,
293-
firstUsage.getIsolationKey()),
273+
rootUsage.getIsolationKey()),
294274
makeUseRepoCommand(
295275
"use_repo_remove",
296276
false,
297277
importsToRemove,
298278
extensionBzlFile,
299279
extensionName,
300-
firstUsage.getIsolationKey()),
280+
rootUsage.getIsolationKey()),
301281
makeUseRepoCommand(
302282
"use_repo_add",
303283
true,
304284
devImportsToAdd,
305285
extensionBzlFile,
306286
extensionName,
307-
firstUsage.getIsolationKey()),
287+
rootUsage.getIsolationKey()),
308288
makeUseRepoCommand(
309289
"use_repo_remove",
310290
true,
311291
devImportsToRemove,
312292
extensionBzlFile,
313293
extensionName,
314-
firstUsage.getIsolationKey()))
294+
rootUsage.getIsolationKey()))
315295
.flatMap(Optional::stream);
316296

317297
return Optional.of(

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ public abstract class ModuleExtensionUsage {
6868
/** All the tags specified by this module for this extension. */
6969
public abstract ImmutableList<Tag> getTags();
7070

71+
/**
72+
* Whether any <code>use_extension</code> calls for this usage had <code>dev_dependency = True
73+
* </code> set.*
74+
*/
75+
public abstract boolean getHasDevUseExtension();
76+
77+
/**
78+
* Whether any <code>use_extension</code> calls for this usage had <code>dev_dependency = False
79+
* </code> set.*
80+
*/
81+
public abstract boolean getHasNonDevUseExtension();
82+
7183
public static Builder builder() {
7284
return new AutoValue_ModuleExtensionUsage.Builder();
7385
}
@@ -100,6 +112,10 @@ public ModuleExtensionUsage.Builder addTag(Tag value) {
100112
return this;
101113
}
102114

115+
public abstract Builder setHasDevUseExtension(boolean hasDevUseExtension);
116+
117+
public abstract Builder setHasNonDevUseExtension(boolean hasNonDevUseExtension);
118+
103119
public abstract ModuleExtensionUsage build();
104120
}
105121
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,9 @@ class ModuleExtensionUsageBuilder {
521521
private final ImmutableSet.Builder<String> devImports;
522522
private final ImmutableList.Builder<Tag> tags;
523523

524+
private boolean hasNonDevUseExtension;
525+
private boolean hasDevUseExtension;
526+
524527
ModuleExtensionUsageBuilder(
525528
String extensionBzlFile,
526529
String extensionName,
@@ -545,6 +548,8 @@ ModuleExtensionUsage buildUsage() {
545548
.setLocation(location)
546549
.setImports(ImmutableBiMap.copyOf(imports))
547550
.setDevImports(devImports.build())
551+
.setHasDevUseExtension(hasDevUseExtension)
552+
.setHasNonDevUseExtension(hasNonDevUseExtension)
548553
.setTags(tags.build());
549554
return builder.build();
550555
}
@@ -554,6 +559,11 @@ ModuleExtensionUsage buildUsage() {
554559
* tags with all other such proxies, thus preserving their order across dev/non-dev deps.
555560
*/
556561
ModuleExtensionProxy getProxy(boolean devDependency) {
562+
if (devDependency) {
563+
hasDevUseExtension = true;
564+
} else {
565+
hasNonDevUseExtension = true;
566+
}
557567
return new ModuleExtensionProxy(devDependency);
558568
}
559569

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,9 @@ private ModuleExtensionContext createContext(
400400
throw new SingleExtensionEvalFunctionException(e, Transience.PERSISTENT);
401401
}
402402
}
403+
ModuleExtensionUsage rootUsage = usagesValue.getExtensionUsages().get(ModuleKey.ROOT);
404+
boolean rootModuleHasNonDevDependency =
405+
rootUsage != null && rootUsage.getHasNonDevUseExtension();
403406
return new ModuleExtensionContext(
404407
workingDirectory,
405408
env,
@@ -410,7 +413,8 @@ private ModuleExtensionContext createContext(
410413
starlarkSemantics,
411414
repositoryRemoteExecutor,
412415
extensionId,
413-
StarlarkList.immutableCopyOf(modules));
416+
StarlarkList.immutableCopyOf(modules),
417+
rootModuleHasNonDevDependency);
414418
}
415419

416420
static final class SingleExtensionEvalFunctionException extends SkyFunctionException {

src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelDepGraphFunctionTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ private static ModuleExtensionUsage createModuleExtensionUsage(
219219
.setDevImports(ImmutableSet.of())
220220
.setUsingModule(ModuleKey.ROOT)
221221
.setLocation(Location.BUILTIN)
222+
.setHasDevUseExtension(false)
223+
.setHasNonDevUseExtension(true)
222224
.build();
223225
}
224226

0 commit comments

Comments
 (0)