Skip to content

Commit 3b6069d

Browse files
authored
[7.2.0] Make bazel mod tidy work with include() (#22540)
We now generate fixup commands targeting `include()`d segments in addition to the root MODULE.bazel file itself. Fixup commands are grouped by file, and then passed to Buildozer using `-f -` (i.e. multiple commands for multiple files, passed in using stdin). - For repos to add, we find the first usage with the correct prod/dev type and add them there. - For repos to remove, we remove them from whichever usage had them. - At the end, all module files and included segments are formatted. Fixes #22063 Closes #22455. PiperOrigin-RevId: 636680730 Change-Id: Id894a449d8d1cdf39271ea3a724a91aebc51befb
1 parent faeb2d7 commit 3b6069d

17 files changed

+472
-156
lines changed

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ java_library(
6060
"//src/main/java/com/google/devtools/build/lib/cmdline",
6161
"//src/main/java/com/google/devtools/build/lib/packages",
6262
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/repository",
63+
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
6364
"//src/main/java/net/starlark/java/eval",
6465
"//src/main/java/net/starlark/java/syntax",
6566
"//third_party:auto_value",
@@ -300,6 +301,7 @@ java_library(
300301
"//src/main/java/com/google/devtools/build/lib/skyframe:sky_functions",
301302
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:serialization-constant",
302303
"//src/main/java/com/google/devtools/build/lib/vfs",
304+
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
303305
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
304306
"//third_party:auto_value",
305307
"//third_party:guava",
@@ -374,6 +376,7 @@ java_library(
374376
"//src/main/java/com/google/devtools/build/docgen/annot",
375377
"//src/main/java/com/google/devtools/build/lib/cmdline",
376378
"//src/main/java/com/google/devtools/build/lib/events",
379+
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
377380
"//src/main/java/net/starlark/java/annot",
378381
"//src/main/java/net/starlark/java/eval",
379382
"//src/main/java/net/starlark/java/syntax",
@@ -391,6 +394,7 @@ java_library(
391394
],
392395
deps = [
393396
":module_extension",
397+
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
394398
"//third_party:guava",
395399
],
396400
)

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import static com.google.common.collect.ImmutableSet.toImmutableSet;
1919

2020
import com.google.common.collect.ImmutableList;
21-
import com.google.common.collect.ImmutableMap;
2221
import com.google.common.collect.ImmutableSet;
2322
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue;
2423
import com.google.devtools.build.lib.cmdline.Label;
@@ -83,12 +82,7 @@ public SkyValue compute(SkyKey skyKey, Environment env)
8382
}
8483

8584
ImmutableSet<SkyKey> extensionsUsedByRootModule =
86-
depGraphValue
87-
.getExtensionUsagesTable()
88-
.columnMap()
89-
.getOrDefault(ModuleKey.ROOT, ImmutableMap.of())
90-
.keySet()
91-
.stream()
85+
depGraphValue.getExtensionUsagesTable().column(ModuleKey.ROOT).keySet().stream()
9286
// Use the eval-only key to avoid errors caused by incorrect imports - we can fix them.
9387
.map(SingleExtensionValue::evalKey)
9488
.collect(toImmutableSet());
@@ -108,8 +102,6 @@ public SkyValue compute(SkyKey skyKey, Environment env)
108102
}
109103

110104
return BazelModTidyValue.create(
111-
fixups.build(),
112-
buildozer.asPath(),
113-
rootModuleFileValue.getIncludeLabelToCompiledModuleFile());
105+
fixups.build(), buildozer.asPath(), rootModuleFileValue.getModuleFilePaths());
114106
}
115107
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717

1818
import com.google.auto.value.AutoValue;
1919
import com.google.common.collect.ImmutableList;
20-
import com.google.common.collect.ImmutableMap;
20+
import com.google.common.collect.ImmutableSet;
2121
import com.google.devtools.build.lib.skyframe.SkyFunctions;
2222
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
2323
import com.google.devtools.build.lib.vfs.Path;
24+
import com.google.devtools.build.lib.vfs.PathFragment;
2425
import com.google.devtools.build.skyframe.SkyKey;
2526
import com.google.devtools.build.skyframe.SkyValue;
2627
import java.util.List;
@@ -37,13 +38,14 @@ public abstract class BazelModTidyValue implements SkyValue {
3738
/** The path of the buildozer binary provided by the "buildozer" module. */
3839
public abstract Path buildozer();
3940

40-
public abstract ImmutableMap<String, CompiledModuleFile> includeLabelToCompiledModuleFile();
41+
/** The set of paths to the root MODULE.bazel file and all its includes. */
42+
public abstract ImmutableSet<PathFragment> moduleFilePaths();
4143

4244
static BazelModTidyValue create(
4345
List<RootModuleFileFixup> fixups,
4446
Path buildozer,
45-
ImmutableMap<String, CompiledModuleFile> includeLabelToCompiledModuleFile) {
47+
ImmutableSet<PathFragment> moduleFilePaths) {
4648
return new AutoValue_BazelModTidyValue(
47-
ImmutableList.copyOf(fixups), buildozer, includeLabelToCompiledModuleFile);
49+
ImmutableList.copyOf(fixups), buildozer, moduleFilePaths);
4850
}
4951
}

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

Lines changed: 44 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,26 @@
1414

1515
package com.google.devtools.build.lib.bazel.bzlmod;
1616

17-
import static com.google.common.collect.ImmutableList.toImmutableList;
1817
import static com.google.common.collect.ImmutableSet.toImmutableSet;
1918

2019
import com.google.auto.value.AutoValue;
2120
import com.google.common.base.Preconditions;
21+
import com.google.common.collect.ImmutableListMultimap;
2222
import com.google.common.collect.ImmutableSet;
2323
import com.google.common.collect.ImmutableSortedSet;
24-
import com.google.common.collect.Iterables;
2524
import com.google.common.collect.Sets;
2625
import com.google.devtools.build.docgen.annot.DocCategory;
26+
import com.google.devtools.build.lib.bazel.bzlmod.ModuleExtensionUsage.Proxy;
2727
import com.google.devtools.build.lib.cmdline.RepositoryName;
2828
import com.google.devtools.build.lib.events.Event;
2929
import com.google.devtools.build.lib.events.EventHandler;
30+
import com.google.devtools.build.lib.vfs.PathFragment;
3031
import com.ryanharter.auto.value.gson.GenerateTypeAdapter;
3132
import java.util.ArrayList;
3233
import java.util.Collection;
3334
import java.util.LinkedHashSet;
3435
import java.util.Optional;
3536
import java.util.Set;
36-
import java.util.stream.Stream;
3737
import javax.annotation.Nullable;
3838
import net.starlark.java.annot.StarlarkBuiltin;
3939
import net.starlark.java.eval.EvalException;
@@ -171,20 +171,8 @@ static ModuleExtensionMetadata create(
171171
}
172172

173173
public Optional<RootModuleFileFixup> generateFixup(
174-
Collection<ModuleExtensionUsage> usages, Set<String> allRepos, EventHandler eventHandler)
174+
ModuleExtensionUsage rootUsage, Set<String> allRepos, EventHandler eventHandler)
175175
throws EvalException {
176-
var rootUsages =
177-
usages.stream()
178-
.filter(usage -> usage.getUsingModule().equals(ModuleKey.ROOT))
179-
.collect(toImmutableList());
180-
if (rootUsages.isEmpty()) {
181-
// The root module doesn't use the current extension. Do not suggest fixes as the user isn't
182-
// expected to modify any other module's MODULE.bazel file.
183-
return Optional.empty();
184-
}
185-
// Every module only has at most a single usage of a given extension.
186-
ModuleExtensionUsage rootUsage = Iterables.getOnlyElement(rootUsages);
187-
188176
var rootModuleDirectDevDeps = getRootModuleDirectDevDeps(allRepos);
189177
var rootModuleDirectDeps = getRootModuleDirectDeps(allRepos);
190178
if (rootModuleDirectDevDeps.isEmpty() && rootModuleDirectDeps.isEmpty()) {
@@ -311,73 +299,52 @@ private static Optional<RootModuleFileFixup> generateFixup(
311299

312300
message += "Fix the use_repo calls by running 'bazel mod tidy'.";
313301

314-
var buildozerCommands =
315-
Stream.of(
316-
makeUseRepoCommand(
317-
"use_repo_add",
318-
false,
319-
importsToAdd,
320-
extensionBzlFile,
321-
extensionName,
322-
rootUsage.getIsolationKey()),
323-
makeUseRepoCommand(
324-
"use_repo_remove",
325-
false,
326-
importsToRemove,
327-
extensionBzlFile,
328-
extensionName,
329-
rootUsage.getIsolationKey()),
330-
makeUseRepoCommand(
331-
"use_repo_add",
332-
true,
333-
devImportsToAdd,
334-
extensionBzlFile,
335-
extensionName,
336-
rootUsage.getIsolationKey()),
337-
makeUseRepoCommand(
338-
"use_repo_remove",
339-
true,
340-
devImportsToRemove,
341-
extensionBzlFile,
342-
extensionName,
343-
rootUsage.getIsolationKey()))
344-
.flatMap(Optional::stream)
345-
.collect(toImmutableList());
302+
var moduleFilePathToCommandsBuilder = ImmutableListMultimap.<PathFragment, String>builder();
303+
// Repos to add are easy: always add them to the first proxy of the correct type.
304+
if (!importsToAdd.isEmpty()) {
305+
Proxy firstNonDevProxy =
306+
rootUsage.getProxies().stream().filter(p -> !p.isDevDependency()).findFirst().get();
307+
moduleFilePathToCommandsBuilder.put(
308+
firstNonDevProxy.getContainingModuleFilePath(),
309+
makeUseRepoCommand("use_repo_add", firstNonDevProxy.getProxyName(), importsToAdd));
310+
}
311+
if (!devImportsToAdd.isEmpty()) {
312+
Proxy firstDevProxy =
313+
rootUsage.getProxies().stream().filter(p -> p.isDevDependency()).findFirst().get();
314+
moduleFilePathToCommandsBuilder.put(
315+
firstDevProxy.getContainingModuleFilePath(),
316+
makeUseRepoCommand("use_repo_add", firstDevProxy.getProxyName(), devImportsToAdd));
317+
}
318+
// Repos to remove are a bit trickier: remove them from the proxy that actually imported them.
319+
for (Proxy proxy : rootUsage.getProxies()) {
320+
var toRemove =
321+
ImmutableSortedSet.copyOf(
322+
Sets.intersection(
323+
proxy.getImports().values(),
324+
proxy.isDevDependency() ? devImportsToRemove : importsToRemove));
325+
if (!toRemove.isEmpty()) {
326+
moduleFilePathToCommandsBuilder.put(
327+
proxy.getContainingModuleFilePath(),
328+
makeUseRepoCommand("use_repo_remove", proxy.getProxyName(), toRemove));
329+
}
330+
}
346331

347332
eventHandler.handle(Event.warn(rootUsage.getProxies().getFirst().getLocation(), message));
348-
return Optional.of(new RootModuleFileFixup(buildozerCommands, rootUsage));
333+
return Optional.of(new RootModuleFileFixup(moduleFilePathToCommandsBuilder.build(), rootUsage));
349334
}
350335

351-
private static Optional<String> makeUseRepoCommand(
352-
String cmd,
353-
boolean devDependency,
354-
Collection<String> repos,
355-
String extensionBzlFile,
356-
String extensionName,
357-
Optional<ModuleExtensionId.IsolationKey> isolationKey) {
358-
if (repos.isEmpty()) {
359-
return Optional.empty();
360-
}
361-
336+
private static String makeUseRepoCommand(String cmd, String proxyName, Collection<String> repos) {
362337
var commandParts = new ArrayList<String>();
363338
commandParts.add(cmd);
364-
if (isolationKey.isPresent()) {
365-
commandParts.add(isolationKey.get().getUsageExportedName());
366-
} else {
367-
if (devDependency) {
368-
commandParts.add("dev");
369-
}
370-
commandParts.add(extensionBzlFile);
371-
commandParts.add(extensionName);
372-
}
339+
commandParts.add(proxyName.isEmpty() ? "_unnamed_usage" : proxyName);
373340
commandParts.addAll(repos);
374-
return Optional.of(String.join(" ", commandParts));
341+
return String.join(" ", commandParts);
375342
}
376343

377344
private Optional<ImmutableSet<String>> getRootModuleDirectDeps(Set<String> allRepos)
378345
throws EvalException {
379-
switch (getUseAllRepos()) {
380-
case NO:
346+
return switch (getUseAllRepos()) {
347+
case NO -> {
381348
if (getExplicitRootModuleDirectDeps() != null) {
382349
Set<String> invalidRepos = Sets.difference(getExplicitRootModuleDirectDeps(), allRepos);
383350
if (!invalidRepos.isEmpty()) {
@@ -387,13 +354,11 @@ private Optional<ImmutableSet<String>> getRootModuleDirectDeps(Set<String> allRe
387354
String.join(", ", invalidRepos));
388355
}
389356
}
390-
return Optional.ofNullable(getExplicitRootModuleDirectDeps());
391-
case REGULAR:
392-
return Optional.of(ImmutableSet.copyOf(allRepos));
393-
case DEV:
394-
return Optional.of(ImmutableSet.of());
395-
}
396-
throw new IllegalStateException("not reached");
357+
yield Optional.ofNullable(getExplicitRootModuleDirectDeps());
358+
}
359+
case REGULAR -> Optional.of(ImmutableSet.copyOf(allRepos));
360+
case DEV -> Optional.of(ImmutableSet.of());
361+
};
397362
}
398363

399364
private Optional<ImmutableSet<String>> getRootModuleDirectDevDeps(Set<String> allRepos)

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
import static com.google.common.collect.ImmutableList.toImmutableList;
1818

1919
import com.google.auto.value.AutoValue;
20+
import com.google.common.collect.ImmutableBiMap;
2021
import com.google.common.collect.ImmutableList;
21-
import com.google.common.collect.ImmutableMap;
22+
import com.google.devtools.build.lib.vfs.PathFragment;
2223
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2324
import com.ryanharter.auto.value.gson.GenerateTypeAdapter;
2425
import java.util.Optional;
@@ -63,6 +64,12 @@ public abstract static class Proxy {
6364
*/
6465
public abstract String getProxyName();
6566

67+
/**
68+
* The path to the MODULE.bazel file (or one of its includes) that contains this proxy object.
69+
* This path should be relative to the workspace root.
70+
*/
71+
public abstract PathFragment getContainingModuleFilePath();
72+
6673
/** Whether {@code dev_dependency} is set to true. */
6774
public abstract boolean isDevDependency();
6875

@@ -71,7 +78,7 @@ public abstract static class Proxy {
7178
* current module. The key is the local repo name (in the scope of the current module), and the
7279
* value is the name exported by the module extension.
7380
*/
74-
public abstract ImmutableMap<String, String> getImports();
81+
public abstract ImmutableBiMap<String, String> getImports();
7582

7683
public static Builder builder() {
7784
return new AutoValue_ModuleExtensionUsage_Proxy.Builder().setProxyName("");
@@ -86,19 +93,21 @@ public abstract static class Builder {
8693

8794
public abstract Builder setProxyName(String value);
8895

96+
public abstract Builder setContainingModuleFilePath(PathFragment value);
97+
8998
public abstract boolean isDevDependency();
9099

91100
public abstract Builder setDevDependency(boolean value);
92101

93-
abstract ImmutableMap.Builder<String, String> importsBuilder();
102+
abstract ImmutableBiMap.Builder<String, String> importsBuilder();
94103

95104
@CanIgnoreReturnValue
96105
public final Builder addImport(String key, String value) {
97106
importsBuilder().put(key, value);
98107
return this;
99108
}
100109

101-
public abstract Builder setImports(ImmutableMap<String, String> value);
110+
public abstract Builder setImports(ImmutableBiMap<String, String> value);
102111

103112
public abstract Proxy build();
104113
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import java.util.Map;
7171
import java.util.Objects;
7272
import java.util.Optional;
73+
import java.util.stream.Stream;
7374
import javax.annotation.Nullable;
7475
import net.starlark.java.eval.EvalException;
7576
import net.starlark.java.eval.Mutability;
@@ -476,12 +477,18 @@ public static RootModuleFileValue evaluateRootModuleFile(
476477
name ->
477478
ModuleKey.create(name, Version.EMPTY).getCanonicalRepoNameWithoutVersion(),
478479
name -> name));
480+
ImmutableSet<PathFragment> moduleFilePaths =
481+
Stream.concat(
482+
Stream.of(LabelConstants.MODULE_DOT_BAZEL_FILE_NAME),
483+
includeLabelToCompiledModuleFile.keySet().stream()
484+
.map(label -> Label.parseCanonicalUnchecked(label).toPathFragment()))
485+
.collect(toImmutableSet());
479486
return RootModuleFileValue.create(
480487
module,
481488
moduleFileHash,
482489
overrides,
483490
nonRegistryOverrideCanonicalRepoNameLookup,
484-
includeLabelToCompiledModuleFile);
491+
moduleFilePaths);
485492
}
486493

487494
private static ModuleThreadContext execModuleFile(

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ public ModuleExtensionProxy useExtension(
430430
var proxyBuilder =
431431
ModuleExtensionUsage.Proxy.builder()
432432
.setLocation(thread.getCallerLocation())
433-
.setDevDependency(devDependency);
433+
.setDevDependency(devDependency)
434+
.setContainingModuleFilePath(context.getCurrentModuleFilePath());
434435

435436
String extensionBzlFile = normalizeLabelString(context.getModuleBuilder(), rawExtensionBzlFile);
436437
var newUsageBuilder =
@@ -692,7 +693,9 @@ public void call(
692693
usageBuilder,
693694
ModuleExtensionUsage.Proxy.builder()
694695
.setDevDependency(devDependency)
695-
.setLocation(thread.getCallerLocation()));
696+
.setLocation(thread.getCallerLocation())
697+
.setContainingModuleFilePath(
698+
usageBuilder.getContext().getCurrentModuleFilePath()));
696699
extensionProxy.getValue(tagName).call(kwargs, thread);
697700
extensionProxy.addImport(name, name, "by a repo rule", thread.getCallerLocation());
698701
}

0 commit comments

Comments
 (0)