Skip to content

Commit 9fe80d3

Browse files
authored
[7.1.0] Add bazel mod tidy (#21265)
Implements https://docs.google.com/document/d/13LbK_1WhA4la0eH7yISjnMvXs2cKFXD-adKPu0i0RK0/edit RELNOTES: The new `bazel mod tidy` subcommand automatically updates `use_repo` calls in the `MODULE.bazel` file for extensions that use `module_ctx.extension_metadata`. Closes #20483. PiperOrigin-RevId: 605021386 Change-Id: Idb1f22c51e126328b9efd6a5a6d6f89d77b9308d Closes #21241
1 parent f9acae2 commit 9fe80d3

27 files changed

+1132
-208
lines changed

MODULE.bazel.lock

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/MODULE.tools

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ bazel_dep(name = "rules_license", version = "0.0.3")
1010
bazel_dep(name = "rules_proto", version = "4.0.0")
1111
bazel_dep(name = "rules_python", version = "0.22.0")
1212

13+
bazel_dep(name = "buildozer", version = "6.4.0.2")
1314
bazel_dep(name = "platforms", version = "0.0.7")
1415
bazel_dep(name = "protobuf", version = "3.19.6", repo_name = "com_google_protobuf")
1516
bazel_dep(name = "zlib", version = "1.3")
@@ -42,5 +43,9 @@ use_repo(remote_coverage_tools_extension, "remote_coverage_tools")
4243
remote_android_extensions = use_extension("//tools/android:android_extensions.bzl", "remote_android_tools_extensions")
4344
use_repo(remote_android_extensions, "android_gmaven_r8", "android_tools")
4445

46+
# Used by bazel mod tidy (see BazelModTidyFunction).
47+
buildozer_binary = use_extension("@buildozer//:buildozer_binary.bzl", "buildozer_binary")
48+
use_repo(buildozer_binary, "buildozer_binary")
49+
4550
# Platforms used by transitions in builtins
4651
bazel_dep(name = "apple_support", version = "1.5.0", repo_name = "build_bazel_apple_support")

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,11 @@ java_library(
2929
"//src/main/java/com/google/devtools/build/lib/authandtls",
3030
"//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper",
3131
"//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper:credential_module",
32-
"//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:common",
33-
"//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:inspection",
3432
"//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:inspection_impl",
3533
"//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:registry",
3634
"//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution",
3735
"//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution_impl",
36+
"//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:tidy_impl",
3837
"//src/main/java/com/google/devtools/build/lib/bazel/commands",
3938
"//src/main/java/com/google/devtools/build/lib/bazel/repository",
4039
"//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options",

src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java

+3-33
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,11 @@
3434
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment;
3535
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperProvider;
3636
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialModule;
37-
import com.google.devtools.build.lib.bazel.bzlmod.AttributeValues;
3837
import com.google.devtools.build.lib.bazel.bzlmod.BazelDepGraphFunction;
3938
import com.google.devtools.build.lib.bazel.bzlmod.BazelFetchAllFunction;
4039
import com.google.devtools.build.lib.bazel.bzlmod.BazelLockFileFunction;
40+
import com.google.devtools.build.lib.bazel.bzlmod.BazelModTidyFunction;
4141
import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleInspectorFunction;
42-
import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleInspectorValue.AugmentedModule.ResolutionReason;
4342
import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleResolutionFunction;
4443
import com.google.devtools.build.lib.bazel.bzlmod.LocalPathOverride;
4544
import com.google.devtools.build.lib.bazel.bzlmod.ModuleExtensionRepoMappingEntriesFunction;
@@ -48,7 +47,6 @@
4847
import com.google.devtools.build.lib.bazel.bzlmod.NonRegistryOverride;
4948
import com.google.devtools.build.lib.bazel.bzlmod.RegistryFactory;
5049
import com.google.devtools.build.lib.bazel.bzlmod.RegistryFactoryImpl;
51-
import com.google.devtools.build.lib.bazel.bzlmod.RepoSpec;
5250
import com.google.devtools.build.lib.bazel.bzlmod.RepoSpecFunction;
5351
import com.google.devtools.build.lib.bazel.bzlmod.SingleExtensionEvalFunction;
5452
import com.google.devtools.build.lib.bazel.bzlmod.SingleExtensionUsagesFunction;
@@ -245,36 +243,7 @@ public void workspaceInit(
245243
new SingleExtensionEvalFunction(directories, clientEnvironmentSupplier, downloadManager);
246244

247245
if (builtinModules == null) {
248-
builtinModules =
249-
ImmutableMap.of(
250-
// @bazel_tools is a special repo that we pull from the extracted install dir.
251-
"bazel_tools",
252-
LocalPathOverride.create(
253-
directories.getEmbeddedBinariesRoot().getChild("embedded_tools").getPathString()),
254-
// @local_config_platform is currently generated by the native repo rule
255-
// local_config_platform
256-
// It has to be a special repo for now because:
257-
// - It's embedded in local_config_platform.WORKSPACE and depended on by many
258-
// toolchains.
259-
// - The canonical name "local_config_platform" is hardcoded in Bazel code.
260-
// See {@link PlatformOptions}
261-
"local_config_platform",
262-
new NonRegistryOverride() {
263-
@Override
264-
public RepoSpec getRepoSpec() {
265-
return RepoSpec.builder()
266-
.setRuleClassName("local_config_platform")
267-
.setAttributes(AttributeValues.create(ImmutableMap.of()))
268-
.build();
269-
}
270-
271-
@Override
272-
public ResolutionReason getResolutionReason() {
273-
// NOTE: It is not exactly a LOCAL_PATH_OVERRIDE, but there is no inspection
274-
// ResolutionReason for builtin modules
275-
return ResolutionReason.LOCAL_PATH_OVERRIDE;
276-
}
277-
});
246+
builtinModules = ModuleFileFunction.getBuiltinModules(directories.getEmbeddedBinariesRoot());
278247
}
279248

280249
builder
@@ -286,6 +255,7 @@ public ResolutionReason getResolutionReason() {
286255
.addSkyFunction(
287256
SkyFunctions.BAZEL_LOCK_FILE, new BazelLockFileFunction(directories.getWorkspace()))
288257
.addSkyFunction(SkyFunctions.BAZEL_FETCH_ALL, new BazelFetchAllFunction())
258+
.addSkyFunction(SkyFunctions.BAZEL_MOD_TIDY, new BazelModTidyFunction())
289259
.addSkyFunction(SkyFunctions.BAZEL_MODULE_INSPECTION, new BazelModuleInspectorFunction())
290260
.addSkyFunction(SkyFunctions.BAZEL_MODULE_RESOLUTION, new BazelModuleResolutionFunction())
291261
.addSkyFunction(SkyFunctions.SINGLE_EXTENSION_EVAL, singleExtensionEvalFunction)

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

+57
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ java_library(
196196
deps = [
197197
":common",
198198
":exception",
199+
":inspection",
199200
":module_extension",
200201
":module_extension_metadata",
201202
":registry",
@@ -271,6 +272,49 @@ java_library(
271272
],
272273
)
273274

275+
java_library(
276+
name = "tidy",
277+
srcs = [
278+
"BazelModTidyValue.java",
279+
],
280+
deps = [
281+
":resolution",
282+
"//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options",
283+
"//src/main/java/com/google/devtools/build/lib/skyframe:sky_functions",
284+
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:serialization-constant",
285+
"//src/main/java/com/google/devtools/build/lib/vfs",
286+
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
287+
"//src/main/java/net/starlark/java/eval",
288+
"//third_party:auto_value",
289+
"//third_party:guava",
290+
],
291+
)
292+
293+
java_library(
294+
name = "tidy_impl",
295+
srcs = [
296+
"BazelModTidyFunction.java",
297+
],
298+
deps = [
299+
":common",
300+
":exception",
301+
":resolution",
302+
":resolution_impl",
303+
":tidy",
304+
"//src/main/java/com/google/devtools/build/lib/cmdline",
305+
"//src/main/java/com/google/devtools/build/lib/rules:repository/repository_function",
306+
"//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value",
307+
"//src/main/java/com/google/devtools/build/lib/skyframe:repository_mapping_value",
308+
"//src/main/java/com/google/devtools/build/lib/vfs",
309+
"//src/main/java/com/google/devtools/build/skyframe",
310+
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
311+
"//src/main/java/net/starlark/java/eval",
312+
"//src/main/protobuf:failure_details_java_proto",
313+
"//third_party:guava",
314+
"//third_party:jsr305",
315+
],
316+
)
317+
274318
java_library(
275319
name = "inspection",
276320
srcs = [
@@ -312,6 +356,7 @@ java_library(
312356
deps = [
313357
":common",
314358
":module_extension",
359+
":module_file_fixup_event",
315360
"//src/main/java/com/google/devtools/build/docgen/annot",
316361
"//src/main/java/com/google/devtools/build/lib/cmdline",
317362
"//src/main/java/com/google/devtools/build/lib/events",
@@ -324,3 +369,15 @@ java_library(
324369
"//third_party:jsr305",
325370
],
326371
)
372+
373+
java_library(
374+
name = "module_file_fixup_event",
375+
srcs = [
376+
"RootModuleFileFixupEvent.java",
377+
],
378+
deps = [
379+
":module_extension",
380+
"//src/main/java/com/google/devtools/build/lib/events",
381+
"//third_party:guava",
382+
],
383+
)

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

+2
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ public static void updateLockfile(Path workspaceRoot, BazelLockFileValue updated
213213

214214
@Subscribe
215215
public void bazelModuleResolved(BazelModuleResolutionEvent moduleResolutionEvent) {
216+
// Latest event wins, which is relevant in the case of `bazel mod tidy`, where a new event is
217+
// sent after the command has modified the module file.
216218
this.moduleResolutionEvent = moduleResolutionEvent;
217219
}
218220

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

+21
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

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

18+
import static com.google.devtools.build.lib.bazel.bzlmod.InterimModule.toModule;
1819

1920
import com.google.auto.value.AutoValue;
2021
import com.google.common.collect.ImmutableList;
@@ -117,4 +118,24 @@ public ImmutableList<String> getModuleAndFlagsDiff(
117118
}
118119
return moduleDiff.build();
119120
}
121+
122+
/**
123+
* Returns a new BazelLockFileValue in which all information about the root module has been
124+
* replaced by the given value.
125+
*
126+
* <p>This operation is shallow: If the new root module has different dependencies, the dep graph
127+
* will not be updated.
128+
*/
129+
public BazelLockFileValue withShallowlyReplacedRootModule(
130+
ModuleFileValue.RootModuleFileValue value) {
131+
ImmutableMap.Builder<ModuleKey, Module> newDepGraph = ImmutableMap.builder();
132+
newDepGraph.putAll(getModuleDepGraph());
133+
newDepGraph.put(
134+
ModuleKey.ROOT,
135+
toModule(value.getModule(), /* override= */ null, /* remoteRepoSpec= */ null));
136+
return toBuilder()
137+
.setModuleFileHash(value.getModuleFileHash())
138+
.setModuleDepGraph(newDepGraph.buildKeepingLast())
139+
.build();
140+
}
120141
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright 2022 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
package com.google.devtools.build.lib.bazel.bzlmod;
17+
18+
import static com.google.common.collect.ImmutableSet.toImmutableSet;
19+
import static com.google.devtools.build.lib.bazel.bzlmod.BazelLockFileFunction.LOCKFILE_MODE;
20+
import static com.google.devtools.build.lib.bazel.bzlmod.ModuleFileFunction.IGNORE_DEV_DEPS;
21+
import static com.google.devtools.build.lib.bazel.bzlmod.ModuleFileFunction.MODULE_OVERRIDES;
22+
import static com.google.devtools.build.lib.skyframe.PrecomputedValue.STARLARK_SEMANTICS;
23+
24+
import com.google.common.collect.ImmutableSet;
25+
import com.google.devtools.build.lib.cmdline.Label;
26+
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
27+
import com.google.devtools.build.lib.cmdline.RepositoryName;
28+
import com.google.devtools.build.lib.rules.repository.NeedsSkyframeRestartException;
29+
import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
30+
import com.google.devtools.build.lib.server.FailureDetails;
31+
import com.google.devtools.build.lib.skyframe.RepositoryMappingValue;
32+
import com.google.devtools.build.lib.vfs.RootedPath;
33+
import com.google.devtools.build.skyframe.SkyFunction;
34+
import com.google.devtools.build.skyframe.SkyFunctionException;
35+
import com.google.devtools.build.skyframe.SkyKey;
36+
import com.google.devtools.build.skyframe.SkyValue;
37+
import com.google.devtools.build.skyframe.SkyframeLookupResult;
38+
import javax.annotation.Nullable;
39+
import net.starlark.java.eval.EvalException;
40+
41+
/**
42+
* Computes all information required for the {@code bazel mod tidy} command. The evaluation of all
43+
* module extensions used by the root module is triggered to, as a side effect, emit any {@link
44+
* RootModuleFileFixupEvent}s.
45+
*/
46+
public class BazelModTidyFunction implements SkyFunction {
47+
48+
@Override
49+
@Nullable
50+
public SkyValue compute(SkyKey skyKey, Environment env)
51+
throws InterruptedException, SkyFunctionException {
52+
BazelDepGraphValue depGraphValue = (BazelDepGraphValue) env.getValue(BazelDepGraphValue.KEY);
53+
if (depGraphValue == null) {
54+
return null;
55+
}
56+
RepositoryMappingValue bazelToolsRepoMapping =
57+
(RepositoryMappingValue)
58+
env.getValue(RepositoryMappingValue.key(RepositoryName.BAZEL_TOOLS));
59+
if (bazelToolsRepoMapping == null) {
60+
return null;
61+
}
62+
Label buildozerLabel;
63+
try {
64+
buildozerLabel =
65+
Label.parseWithRepoContext(
66+
// This label always has the ".exe" extension, even on Unix, to get a single static
67+
// label that works on all platforms.
68+
"@buildozer_binary//:buildozer.exe",
69+
Label.RepoContext.of(
70+
RepositoryName.BAZEL_TOOLS, bazelToolsRepoMapping.getRepositoryMapping()));
71+
} catch (LabelSyntaxException e) {
72+
throw new IllegalStateException(e);
73+
}
74+
RootedPath buildozer;
75+
try {
76+
buildozer = RepositoryFunction.getRootedPathFromLabel(buildozerLabel, env);
77+
} catch (NeedsSkyframeRestartException e) {
78+
return null;
79+
} catch (EvalException e) {
80+
throw new IllegalStateException(e);
81+
}
82+
83+
ImmutableSet<SkyKey> extensionsUsedByRootModule =
84+
depGraphValue.getExtensionUsagesTable().columnMap().get(ModuleKey.ROOT).keySet().stream()
85+
.map(SingleExtensionEvalValue::key)
86+
.collect(toImmutableSet());
87+
SkyframeLookupResult result = env.getValuesAndExceptions(extensionsUsedByRootModule);
88+
if (env.valuesMissing()) {
89+
return null;
90+
}
91+
for (SkyKey extension : extensionsUsedByRootModule) {
92+
try {
93+
result.getOrThrow(extension, ExternalDepsException.class);
94+
} catch (ExternalDepsException e) {
95+
if (e.getDetailedExitCode().getFailureDetail() == null
96+
|| !e.getDetailedExitCode()
97+
.getFailureDetail()
98+
.getExternalDeps()
99+
.getCode()
100+
.equals(FailureDetails.ExternalDeps.Code.INVALID_EXTENSION_IMPORT)) {
101+
throw new BazelModTidyFunctionException(e, SkyFunctionException.Transience.PERSISTENT);
102+
}
103+
// This is an error bazel mod tidy can fix, so don't fail.
104+
}
105+
}
106+
107+
return BazelModTidyValue.create(
108+
buildozer.asPath(),
109+
MODULE_OVERRIDES.get(env),
110+
IGNORE_DEV_DEPS.get(env),
111+
LOCKFILE_MODE.get(env),
112+
STARLARK_SEMANTICS.get(env));
113+
}
114+
115+
static final class BazelModTidyFunctionException extends SkyFunctionException {
116+
117+
BazelModTidyFunctionException(ExternalDepsException cause, Transience transience) {
118+
super(cause, transience);
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)