Skip to content

Commit e2c2ddb

Browse files
committed
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.
1 parent 815b6c9 commit e2c2ddb

File tree

7 files changed

+165
-102
lines changed

7 files changed

+165
-102
lines changed

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

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,31 +96,44 @@ protected ImmutableMap<String, String> getRemoteExecProperties() throws EvalExce
9696
name = "modules",
9797
structField = true,
9898
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.")
99+
"A list of all the Bazel modules in the external dependency graph that use this module "
100+
+ "extension, each of which is a <a href=\"../builtins/bazel_module.html\">"
101+
+ "bazel_module</a> object that exposes all the tags it specified for this extension."
102+
+ " The iteration order of this dictionary is guaranteed to be the same as"
103+
+ " breadth-first search starting from the root module.")
104104
public StarlarkList<StarlarkBazelModule> getModules() {
105105
return modules;
106106
}
107107

108108
@StarlarkMethod(
109109
name = "is_dev_dependency",
110110
doc =
111-
"Returns whether the given tag was specified on the result of a <a "
111+
"When given a tag, returns whether the given tag was specified on the result of a <a "
112112
+ "href=\"../globals/module.html#use_extension\">use_extension</a> call with "
113-
+ "<code>devDependency = True</code>.",
113+
+ "<code>devDependency = True</code>.<p>When given a module, returns "
114+
+ "<code>True</code> if and only if all <a "
115+
+ "href=\"../globals/module.html#use_extension\">use_extension</a> calls made by that "
116+
+ "module for the current extension specify <code>devDependency = True</code>. In "
117+
+ "particular, it returns <code>False</code> for modules other than the root module.",
114118
parameters = {
115119
@Param(
116-
name = "tag",
120+
name = "tag_or_module",
117121
doc =
118-
"A tag obtained from <a"
119-
+ " href=\"../builtins/bazel_module.html#tags\">bazel_module.tags</a>.",
120-
allowedTypes = {@ParamType(type = TypeCheckedTag.class)})
122+
"A tag obtained from the <a"
123+
+ " href=\"../builtins/bazel_module.html#tags\">tags</a> property of a "
124+
+ "<a href=\"../builtins/bazel_module.html\">bazel_module</a> or a "
125+
+ "<a href=\"../builtins/bazel_module.html\">bazel_module</a>.",
126+
allowedTypes = {
127+
@ParamType(type = TypeCheckedTag.class),
128+
@ParamType(type = StarlarkBazelModule.class)
129+
})
121130
})
122-
public boolean isDevDependency(TypeCheckedTag tag) {
123-
return tag.isDevDependency();
131+
public boolean isDevDependency(Object tagOrModule) {
132+
if (tagOrModule instanceof TypeCheckedTag) {
133+
return ((TypeCheckedTag) tagOrModule).isDevDependency();
134+
} else {
135+
return ((StarlarkBazelModule) tagOrModule).hasDevUseExtensionOnly();
136+
}
124137
}
125138

126139
@StarlarkMethod(
@@ -154,7 +167,7 @@ public boolean isIsolated() {
154167
+ " disjoint.<p>Exactly one of <code>root_module_direct_deps</code> and"
155168
+ " <code>root_module_direct_dev_deps</code> can be set to the special value"
156169
+ " <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.",
170+
+ " all repositories generated by the extension was specified as the value.",
158171
positional = false,
159172
named = true,
160173
defaultValue = "None",
@@ -180,7 +193,7 @@ public boolean isIsolated() {
180193
+ " disjoint.<p>Exactly one of <code>root_module_direct_deps</code> and"
181194
+ " <code>root_module_direct_dev_deps</code> can be set to the special value"
182195
+ " <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.",
196+
+ " all repositories generated by the extension was specified as the value.",
184197
positional = false,
185198
named = true,
186199
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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ public abstract class ModuleExtensionUsage {
6868
/** All the tags specified by this module for this extension. */
6969
public abstract ImmutableList<Tag> getTags();
7070

71+
/** Whether any <code>use_extension</code> calls for this usage had <code>dev_dependency = True
72+
* </code> set.**/
73+
public abstract boolean getHasDevUseExtension();
74+
75+
/** Whether any <code>use_extension</code> calls for this usage had <code>dev_dependency = False
76+
* </code> set.**/
77+
public abstract boolean getHasNonDevUseExtension();
78+
7179
public static Builder builder() {
7280
return new AutoValue_ModuleExtensionUsage.Builder();
7381
}
@@ -100,6 +108,10 @@ public ModuleExtensionUsage.Builder addTag(Tag value) {
100108
return this;
101109
}
102110

111+
public abstract Builder setHasDevUseExtension(boolean hasDevUseExtension);
112+
113+
public abstract Builder setHasNonDevUseExtension(boolean hasNonDevUseExtension);
114+
103115
public abstract ModuleExtensionUsage build();
104116
}
105117
}

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/StarlarkBazelModule.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class StarlarkBazelModule implements StarlarkValue {
4646
private final String version;
4747
private final Tags tags;
4848
private final boolean isRootModule;
49+
private final boolean hasDevUseExtensionOnly;
4950

5051
@StarlarkBuiltin(
5152
name = "bazel_module_tags",
@@ -85,11 +86,17 @@ public String getErrorMessageForUnknownField(String field) {
8586
}
8687
}
8788

88-
private StarlarkBazelModule(String name, String version, Tags tags, boolean isRootModule) {
89+
private StarlarkBazelModule(
90+
String name,
91+
String version,
92+
Tags tags,
93+
boolean isRootModule,
94+
boolean hasDevUseExtensionOnly) {
8995
this.name = name;
9096
this.version = version;
9197
this.tags = tags;
9298
this.isRootModule = isRootModule;
99+
this.hasDevUseExtensionOnly = hasDevUseExtensionOnly;
93100
}
94101

95102
/**
@@ -132,11 +139,14 @@ public static StarlarkBazelModule create(
132139
.get(tag.getTagName())
133140
.add(TypeCheckedTag.create(tagClass, tag, labelConverter));
134141
}
142+
boolean hasDevUseExtensionOnly =
143+
usage != null && usage.getHasDevUseExtension() && !usage.getHasNonDevUseExtension();
135144
return new StarlarkBazelModule(
136145
module.getName(),
137146
module.getVersion().getOriginal(),
138147
new Tags(Maps.transformValues(typeCheckedTags, StarlarkList::immutableCopyOf)),
139-
module.getKey().equals(ModuleKey.ROOT));
148+
module.getKey().equals(ModuleKey.ROOT),
149+
hasDevUseExtensionOnly);
140150
}
141151

142152
@Override
@@ -169,4 +179,8 @@ public Tags getTags() {
169179
public boolean isRoot() {
170180
return isRootModule;
171181
}
182+
183+
public boolean hasDevUseExtensionOnly() {
184+
return hasDevUseExtensionOnly;
185+
}
172186
}

0 commit comments

Comments
 (0)