Skip to content

Commit 33ee3b5

Browse files
authored
[8.3.0] Repo contents cache (#26129)
Original commits: * fde1c9c * bc80679 * ad74aa5 * 8501bc5
1 parent 5e2dbca commit 33ee3b5

File tree

84 files changed

+1970
-1086
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+1970
-1086
lines changed

scripts/bootstrap/bootstrap.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ _BAZEL_ARGS="--spawn_strategy=standalone \
3838
--strategy=Javac=worker --worker_quit_after_build --ignore_unsupported_sandboxing \
3939
--compilation_mode=opt \
4040
--repository_cache=derived/repository_cache \
41+
--repo_contents_cache= \
4142
--repo_env=BAZEL_HTTP_RULES_URLS_AS_DEFAULT_CANONICAL_ID=0 \
4243
--extra_toolchains=//scripts/bootstrap:all \
4344
--extra_toolchains=@rules_python//python:autodetecting_toolchain \

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ java_library(
4747
"//src/main/java/com/google/devtools/build/lib/cmdline",
4848
"//src/main/java/com/google/devtools/build/lib/events",
4949
"//src/main/java/com/google/devtools/build/lib/pkgcache",
50+
"//src/main/java/com/google/devtools/build/lib/profiler",
5051
"//src/main/java/com/google/devtools/build/lib/rules:repository/local_repository_rule",
5152
"//src/main/java/com/google/devtools/build/lib/rules:repository/new_local_repository_function",
5253
"//src/main/java/com/google/devtools/build/lib/rules:repository/new_local_repository_rule",

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

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
import com.google.devtools.build.lib.cmdline.RepositoryName;
8282
import com.google.devtools.build.lib.events.Event;
8383
import com.google.devtools.build.lib.pkgcache.PackageOptions;
84+
import com.google.devtools.build.lib.profiler.Profiler;
85+
import com.google.devtools.build.lib.profiler.ProfilerTask;
86+
import com.google.devtools.build.lib.profiler.SilentCloseable;
8487
import com.google.devtools.build.lib.rules.repository.LocalRepositoryFunction;
8588
import com.google.devtools.build.lib.rules.repository.LocalRepositoryRule;
8689
import com.google.devtools.build.lib.rules.repository.NewLocalRepositoryFunction;
@@ -214,7 +217,7 @@ private static class RepositoryCacheInfoItem extends InfoItem {
214217
public byte[] get(
215218
Supplier<BuildConfigurationValue> configurationSupplier, CommandEnvironment env)
216219
throws AbruptExitException, InterruptedException {
217-
return print(repositoryCache.getRootPath());
220+
return print(repositoryCache.getPath());
218221
}
219222
}
220223

@@ -245,7 +248,8 @@ public void workspaceInit(
245248
isFetch,
246249
clientEnvironmentSupplier,
247250
directories,
248-
BazelSkyframeExecutorConstants.EXTERNAL_PACKAGE_HELPER);
251+
BazelSkyframeExecutorConstants.EXTERNAL_PACKAGE_HELPER,
252+
repositoryCache.getRepoContentsCache());
249253
singleExtensionEvalFunction =
250254
new SingleExtensionEvalFunction(directories, clientEnvironmentSupplier);
251255

@@ -311,7 +315,10 @@ public void initializeRuleClasses(ConfiguredRuleClassProvider.Builder builder) {
311315
@Override
312316
public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
313317
DownloadManager downloadManager =
314-
new DownloadManager(repositoryCache, env.getDownloaderDelegate(), env.getHttpDownloader());
318+
new DownloadManager(
319+
repositoryCache.getDownloadCache(),
320+
env.getDownloaderDelegate(),
321+
env.getHttpDownloader());
315322
this.starlarkRepositoryFunction.setDownloadManager(downloadManager);
316323
this.moduleFileFunction.setDownloadManager(downloadManager);
317324
this.repoSpecFunction.setDownloadManager(downloadManager);
@@ -339,7 +346,7 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
339346
}
340347
disableNativeRepoRules = repoOptions.disableNativeRepoRules;
341348

342-
repositoryCache.setHardlink(repoOptions.useHardlinks);
349+
repositoryCache.getDownloadCache().setHardlink(repoOptions.useHardlinks);
343350
if (repoOptions.experimentalScaleTimeouts > 0.0) {
344351
starlarkRepositoryFunction.setTimeoutScaling(repoOptions.experimentalScaleTimeouts);
345352
singleExtensionEvalFunction.setTimeoutScaling(repoOptions.experimentalScaleTimeouts);
@@ -352,34 +359,61 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
352359
starlarkRepositoryFunction.setTimeoutScaling(1.0);
353360
singleExtensionEvalFunction.setTimeoutScaling(1.0);
354361
}
355-
if (repoOptions.experimentalRepositoryCache != null) {
356-
Path repositoryCachePath;
357-
if (repoOptions.experimentalRepositoryCache.isEmpty()) {
358-
// A set but empty path indicates a request to disable the repository cache.
359-
repositoryCachePath = null;
360-
} else if (repoOptions.experimentalRepositoryCache.isAbsolute()) {
361-
repositoryCachePath = filesystem.getPath(repoOptions.experimentalRepositoryCache);
362-
} else {
363-
repositoryCachePath =
364-
env.getBlazeWorkspace()
365-
.getWorkspace()
366-
.getRelative(repoOptions.experimentalRepositoryCache);
367-
}
368-
repositoryCache.setRepositoryCachePath(repositoryCachePath);
362+
if (repoOptions.repositoryCache != null) {
363+
repositoryCache.setPath(toPath(repoOptions.repositoryCache, env));
369364
} else {
370-
Path repositoryCachePath =
365+
repositoryCache.setPath(
371366
env.getDirectories()
372367
.getServerDirectories()
373368
.getOutputUserRoot()
374-
.getRelative(DEFAULT_CACHE_LOCATION);
375-
try {
376-
repositoryCachePath.createDirectoryAndParents();
377-
repositoryCache.setRepositoryCachePath(repositoryCachePath);
369+
.getRelative(DEFAULT_CACHE_LOCATION));
370+
}
371+
// Note that the repo contents cache stuff has to happen _after_ the repo cache stuff, because
372+
// the specific settings about the repo contents cache might overwrite the repo cache
373+
// settings. In particular, if `--repo_contents_cache` is not set (it's null), we use whatever
374+
// default set by `repositoryCache.setPath(...)`.
375+
if (repoOptions.repoContentsCache != null) {
376+
repositoryCache.getRepoContentsCache().setPath(toPath(repoOptions.repoContentsCache, env));
377+
}
378+
Path repoContentsCachePath = repositoryCache.getRepoContentsCache().getPath();
379+
if (repoContentsCachePath != null
380+
&& env.getWorkspace() != null
381+
&& repoContentsCachePath.startsWith(env.getWorkspace())) {
382+
// Having the repo contents cache inside the workspace is very dangerous. During the
383+
// lifetime of a Bazel invocation, we treat files inside the workspace as immutable. This
384+
// can cause mysterious failures if we write files inside the workspace during the
385+
// invocation, as is often the case with the repo contents cache.
386+
// TODO: wyv@ - This is a crude check that disables some use cases (such as when the output
387+
// base itself is inside the main repo). Investigate a better check.
388+
repositoryCache.getRepoContentsCache().setPath(null);
389+
throw new AbruptExitException(
390+
detailedExitCode(
391+
"""
392+
The repo contents cache [%s] is inside the workspace [%s]. This can cause spurious \
393+
failures. Disable the repo contents cache with `--repo_contents_cache=`, or \
394+
specify `--repo_contents_cache=<path outside the workspace>`.
395+
"""
396+
.formatted(repoContentsCachePath, env.getWorkspace()),
397+
Code.BAD_REPO_CONTENTS_CACHE));
398+
}
399+
if (repositoryCache.getRepoContentsCache().isEnabled()) {
400+
try (SilentCloseable c =
401+
Profiler.instance()
402+
.profile(ProfilerTask.REPO_CACHE_GC_WAIT, "waiting to acquire repo cache lock")) {
403+
repositoryCache.getRepoContentsCache().acquireSharedLock();
378404
} catch (IOException e) {
379-
env.getReporter()
380-
.handle(
381-
Event.warn(
382-
"Failed to set up cache at " + repositoryCachePath + ": " + e.getMessage()));
405+
throw new AbruptExitException(
406+
detailedExitCode(
407+
"could not acquire lock on repo contents cache", Code.BAD_REPO_CONTENTS_CACHE),
408+
e);
409+
}
410+
if (!repoOptions.repoContentsCacheGcMaxAge.isZero()) {
411+
env.addIdleTask(
412+
repositoryCache
413+
.getRepoContentsCache()
414+
.createGcIdleTask(
415+
repoOptions.repoContentsCacheGcMaxAge,
416+
repoOptions.repoContentsCacheGcIdleDelay));
383417
}
384418
}
385419

@@ -626,6 +660,32 @@ private String getAbsolutePath(String path, CommandEnvironment env) {
626660
return path;
627661
}
628662

663+
/**
664+
* An empty path fragment is turned into {@code null}; otherwise, it's treated as relative to the
665+
* workspace root.
666+
*/
667+
@Nullable
668+
private Path toPath(PathFragment path, CommandEnvironment env) {
669+
if (path.isEmpty() || env.getBlazeWorkspace().getWorkspace() == null) {
670+
return null;
671+
}
672+
return env.getBlazeWorkspace().getWorkspace().getRelative(path);
673+
}
674+
675+
@Override
676+
public void afterCommand() throws AbruptExitException {
677+
if (repositoryCache.getRepoContentsCache().isEnabled()) {
678+
try {
679+
repositoryCache.getRepoContentsCache().releaseSharedLock();
680+
} catch (IOException e) {
681+
throw new AbruptExitException(
682+
detailedExitCode(
683+
"could not release lock on repo contents cache", Code.BAD_REPO_CONTENTS_CACHE),
684+
e);
685+
}
686+
}
687+
}
688+
629689
@Override
630690
public ImmutableList<Injected> getPrecomputedValues() {
631691
Instant now = clock.now();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ java_library(
5353
name = "vendor",
5454
srcs = ["VendorManager.java"],
5555
deps = [
56+
"//src/main/java/com/google/devtools/build/lib/bazel/repository/cache",
5657
"//src/main/java/com/google/devtools/build/lib/bazel/repository/downloader",
5758
"//src/main/java/com/google/devtools/build/lib/cmdline",
5859
"//src/main/java/com/google/devtools/build/lib/profiler",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedExcept
9898
if (repoDirValue == null) {
9999
return null;
100100
}
101-
if (!repoDirValue.excludeFromVendoring()) {
101+
if (repoDirValue instanceof RepositoryDirectoryValue.Success s && !s.excludeFromVendoring()) {
102102
shouldVendor.add((RepositoryName) repoDelegatorKey.argument());
103103
}
104104
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import com.google.common.collect.ImmutableTable;
2626
import com.google.common.collect.Table;
2727
import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException;
28-
import com.google.devtools.build.lib.bazel.repository.cache.RepositoryCache;
28+
import com.google.devtools.build.lib.bazel.repository.cache.DownloadCache;
2929
import com.google.devtools.build.lib.bazel.repository.downloader.Checksum;
3030
import com.google.devtools.build.lib.cmdline.Label;
3131
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
@@ -445,7 +445,7 @@ public Optional<Checksum> read(JsonReader jsonReader) throws IOException {
445445
return Optional.empty();
446446
}
447447
try {
448-
return Optional.of(Checksum.fromString(RepositoryCache.KeyType.SHA256, checksumString));
448+
return Optional.of(Checksum.fromString(DownloadCache.KeyType.SHA256, checksumString));
449449
} catch (Checksum.InvalidChecksumException e) {
450450
throw new JsonParseException(String.format("Invalid checksum: %s", checksumString), e);
451451
}

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@
2828
import com.google.common.collect.Maps;
2929
import com.google.devtools.build.lib.actions.FileValue;
3030
import com.google.devtools.build.lib.bazel.bzlmod.CompiledModuleFile.IncludeStatement;
31+
import com.google.devtools.build.lib.bazel.bzlmod.ModuleExtensionUsage.Proxy;
32+
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileGlobals.ModuleExtensionProxy;
3133
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.NonRootModuleFileValue;
3234
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue;
35+
import com.google.devtools.build.lib.bazel.bzlmod.ModuleThreadContext.ModuleExtensionUsageBuilder;
36+
import com.google.devtools.build.lib.bazel.bzlmod.Registry.NotFoundException;
3337
import com.google.devtools.build.lib.bazel.repository.PatchUtil;
3438
import com.google.devtools.build.lib.bazel.repository.downloader.Checksum.MissingChecksumException;
3539
import com.google.devtools.build.lib.bazel.repository.downloader.DownloadManager;
@@ -47,6 +51,7 @@
4751
import com.google.devtools.build.lib.profiler.ProfilerTask;
4852
import com.google.devtools.build.lib.profiler.SilentCloseable;
4953
import com.google.devtools.build.lib.rules.repository.RepositoryDirectoryValue;
54+
import com.google.devtools.build.lib.rules.repository.RepositoryDirectoryValue.Success;
5055
import com.google.devtools.build.lib.server.FailureDetails.ExternalDeps.Code;
5156
import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction;
5257
import com.google.devtools.build.lib.skyframe.ClientEnvironmentValue;
@@ -62,6 +67,7 @@
6267
import com.google.devtools.build.lib.vfs.RootedPath;
6368
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
6469
import com.google.devtools.build.skyframe.SkyFunction;
70+
import com.google.devtools.build.skyframe.SkyFunction.Environment.SkyKeyComputeState;
6571
import com.google.devtools.build.skyframe.SkyFunctionException;
6672
import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
6773
import com.google.devtools.build.skyframe.SkyKey;
@@ -132,7 +138,7 @@ public ModuleFileFunction(
132138
this.builtinModules = builtinModules;
133139
}
134140

135-
private static class State implements Environment.SkyKeyComputeState {
141+
private static class State implements SkyKeyComputeState {
136142
// The following fields are used during root module file evaluation. We try to compile the root
137143
// module file itself first, and then read, parse, and compile any included module files layer
138144
// by layer, in a BFS fashion (hence the `horizon` field). Finally, everything is collected into
@@ -480,7 +486,7 @@ public static RootModuleFileValue evaluateRootModuleFile(
480486
throw errorf(Code.BAD_MODULE, "error executing MODULE.bazel file for the root module");
481487
}
482488
for (ModuleExtensionUsage usage : module.getExtensionUsages()) {
483-
ModuleExtensionUsage.Proxy firstProxy = usage.getProxies().getFirst();
489+
Proxy firstProxy = usage.getProxies().getFirst();
484490
if (usage.getIsolationKey().isPresent() && firstProxy.getImports().isEmpty()) {
485491
throw errorf(
486492
Code.BAD_MODULE,
@@ -581,16 +587,16 @@ private static void injectRepos(
581587
return;
582588
}
583589
// Use the innate extension backing use_repo_rule.
584-
ModuleThreadContext.ModuleExtensionUsageBuilder usageBuilder =
585-
new ModuleThreadContext.ModuleExtensionUsageBuilder(
590+
ModuleExtensionUsageBuilder usageBuilder =
591+
new ModuleExtensionUsageBuilder(
586592
context,
587593
"//:MODULE.bazel",
588594
"@bazel_tools//tools/build_defs/repo:local.bzl local_repository",
589595
/* isolate= */ false);
590-
ModuleFileGlobals.ModuleExtensionProxy extensionProxy =
591-
new ModuleFileGlobals.ModuleExtensionProxy(
596+
ModuleExtensionProxy extensionProxy =
597+
new ModuleExtensionProxy(
592598
usageBuilder,
593-
ModuleExtensionUsage.Proxy.builder()
599+
Proxy.builder()
594600
.setDevDependency(true)
595601
.setLocation(Location.BUILTIN)
596602
.setContainingModuleFilePath(context.getCurrentModuleFilePath()));
@@ -638,9 +644,12 @@ private GetModuleFileResult getModuleFile(
638644
if (repoDir == null) {
639645
return null;
640646
}
647+
// This repo _definitely_ exists, since it has a non-registry override, which directly gets
648+
// "translated" into a repo spec. So we can cast `repoDir` to `Success`.
649+
Path repoDirPath = ((Success) repoDir).getPath();
641650
RootedPath moduleFilePath =
642651
RootedPath.toRootedPath(
643-
Root.fromPath(repoDir.getPath()), LabelConstants.MODULE_DOT_BAZEL_FILE_NAME);
652+
Root.fromPath(repoDirPath), LabelConstants.MODULE_DOT_BAZEL_FILE_NAME);
644653
if (env.getValue(FileValue.key(moduleFilePath)) == null) {
645654
return null;
646655
}
@@ -706,7 +715,7 @@ private GetModuleFileResult getModuleFile(
706715
try {
707716
originalModuleFile =
708717
registry.getModuleFile(key, downloadEventHandler, this.downloadManager);
709-
} catch (Registry.NotFoundException e) {
718+
} catch (NotFoundException e) {
710719
if (notFoundTrace == null) {
711720
notFoundTrace = new ArrayList<>();
712721
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import com.google.common.collect.ImmutableMap;
1919
import com.google.common.hash.Hashing;
20-
import com.google.devtools.build.lib.bazel.repository.cache.RepositoryCache;
20+
import com.google.devtools.build.lib.bazel.repository.cache.DownloadCache;
2121
import com.google.devtools.build.lib.bazel.repository.downloader.Checksum;
2222
import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
2323
import java.util.Collection;
@@ -44,7 +44,7 @@ static ImmutableMap<String, Optional<Checksum>> collectToMap(Collection<Postable
4444
private static Checksum computeHash(byte[] bytes) {
4545
try {
4646
return Checksum.fromString(
47-
RepositoryCache.KeyType.SHA256, Hashing.sha256().hashBytes(bytes).toString());
47+
DownloadCache.KeyType.SHA256, Hashing.sha256().hashBytes(bytes).toString());
4848
} catch (Checksum.InvalidChecksumException e) {
4949
// This can't happen since HashCode.toString() always returns a valid hash.
5050
throw new IllegalStateException(e);

0 commit comments

Comments
 (0)