Skip to content

Commit

Permalink
the rest
Browse files Browse the repository at this point in the history
  • Loading branch information
fmeum committed Dec 19, 2024
1 parent 9c03c55 commit 02e2e2c
Show file tree
Hide file tree
Showing 19 changed files with 170 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ public void workspaceInit(
.addSkyFunction(SkyFunctions.MODULE_FILE, moduleFileFunction)
.addSkyFunction(SkyFunctions.BAZEL_DEP_GRAPH, new BazelDepGraphFunction())
.addSkyFunction(
SkyFunctions.BAZEL_LOCK_FILE, new BazelLockFileFunction(directories.getWorkspace()))
SkyFunctions.BAZEL_LOCK_FILE,
new BazelLockFileFunction(directories.getWorkspace(), directories.getOutputBase()))
.addSkyFunction(SkyFunctions.BAZEL_FETCH_ALL, new BazelFetchAllFunction())
.addSkyFunction(SkyFunctions.BAZEL_MOD_TIDY, new BazelModTidyFunction())
.addSkyFunction(SkyFunctions.BAZEL_MODULE_INSPECTION, new BazelModuleInspectorFunction())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ java_library(
":resolution_impl",
"//src/main/java/com/google/devtools/build/lib:runtime",
"//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options",
"//src/main/java/com/google/devtools/build/lib/bazel/repository/downloader",
"//src/main/java/com/google/devtools/build/lib/cmdline",
"//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value",
"//src/main/java/com/google/devtools/build/lib/skyframe:skyframe_cluster",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,29 +55,42 @@ public class BazelLockFileFunction implements SkyFunction {
private static final BazelLockFileValue EMPTY_LOCKFILE = BazelLockFileValue.builder().build();

private final Path rootDirectory;
private final Path outputBase;

public BazelLockFileFunction(Path rootDirectory) {
public BazelLockFileFunction(Path rootDirectory, Path outputBase) {
this.rootDirectory = rootDirectory;
this.outputBase = outputBase;
}

@Override
@Nullable
public SkyValue compute(SkyKey skyKey, Environment env)
throws BazelLockfileFunctionException, InterruptedException {
boolean forPersistentLockfile = skyKey == BazelLockFileValue.PERSISTENT_KEY;
RootedPath lockfilePath =
RootedPath.toRootedPath(Root.fromPath(rootDirectory), LabelConstants.MODULE_LOCKFILE_NAME);
RootedPath.toRootedPath(
Root.fromPath(forPersistentLockfile ? outputBase : rootDirectory),
LabelConstants.MODULE_LOCKFILE_NAME);

// Add dependency on the lockfile to recognize changes to it
// Add dependency on the lockfiles to recognize changes to it
if (env.getValue(FileValue.key(lockfilePath)) == null) {
return null;
}

try (SilentCloseable c = Profiler.instance().profile(ProfilerTask.BZLMOD, "parse lockfile")) {
return getLockfileValue(lockfilePath, LOCKFILE_MODE.get(env));
try (SilentCloseable c =
Profiler.instance()
.profile(
ProfilerTask.BZLMOD,
forPersistentLockfile ? "parse persistent lockfile" : "parse lockfile")) {
return getLockfileValue(
lockfilePath, forPersistentLockfile ? LockfileMode.UPDATE : LOCKFILE_MODE.get(env));
} catch (IOException
| JsonSyntaxException
| NullPointerException
| IllegalArgumentException e) {
if (forPersistentLockfile) {
return EMPTY_LOCKFILE;
}
String actionSuffix;
if (POSSIBLE_MERGE_CONFLICT_PATTERN.matcher(e.getMessage()).find()) {
actionSuffix =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.common.flogger.GoogleLogger;
import com.google.devtools.build.lib.bazel.repository.RepositoryOptions;
import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.LockfileMode;
import com.google.devtools.build.lib.bazel.repository.downloader.Checksum;
import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.runtime.BlazeModule;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
Expand All @@ -39,6 +40,7 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;

Expand All @@ -50,6 +52,7 @@ public class BazelLockFileModule extends BlazeModule {

private SkyframeExecutor executor;
private Path workspaceRoot;
private Path outputBase;
private LockfileMode optionsLockfileMode;

private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
Expand All @@ -61,6 +64,7 @@ public class BazelLockFileModule extends BlazeModule {
public void beforeCommand(CommandEnvironment env) {
executor = env.getSkyframeExecutor();
workspaceRoot = env.getWorkspace();
outputBase = env.getOutputBase();
optionsLockfileMode = env.getOptions().getOptions(RepositoryOptions.class).lockfileMode;
}

Expand All @@ -70,6 +74,7 @@ public void afterCommand() {
BazelModuleResolutionValue moduleResolutionValue;
BazelDepGraphValue depGraphValue;
BazelLockFileValue oldLockfile;
BazelLockFileValue oldPersistentLockfile;
try {
PrecomputedValue lockfileModeValue =
(PrecomputedValue) evaluator.getExistingValue(LOCKFILE_MODE.getKey());
Expand All @@ -89,11 +94,16 @@ public void afterCommand() {
(BazelModuleResolutionValue) evaluator.getExistingValue(BazelModuleResolutionValue.KEY);
depGraphValue = (BazelDepGraphValue) evaluator.getExistingValue(BazelDepGraphValue.KEY);
oldLockfile = (BazelLockFileValue) evaluator.getExistingValue(BazelLockFileValue.KEY);
oldPersistentLockfile =
(BazelLockFileValue) evaluator.getExistingValue(BazelLockFileValue.PERSISTENT_KEY);
} catch (InterruptedException e) {
// Not thrown in Bazel.
throw new IllegalStateException(e);
}
if (moduleResolutionValue == null || depGraphValue == null || oldLockfile == null) {
if (moduleResolutionValue == null
|| depGraphValue == null
|| oldLockfile == null
|| oldPersistentLockfile == null) {
// An error during the actual build prevented the evaluation of these values and has already
// been reported at this point.
return;
Expand All @@ -120,28 +130,75 @@ public void afterCommand() {
newExtensionInfos.put(key.argument(), value.lockFileInfo().get());
}
});
var combinedExtensionInfos =
combineModuleExtensions(
oldLockfile.getModuleExtensions(),
newExtensionInfos,
/* hasUsages= */ depGraphValue.getExtensionUsagesTable()::containsRow);

// Create an updated version of the lockfile, keeping only the extension results from the old
// lockfile that are still up-to-date and adding the newly resolved extension results.
BazelLockFileValue newLockfile =
BazelLockFileValue.builder()
.setRegistryFileHashes(
ImmutableSortedMap.copyOf(moduleResolutionValue.getRegistryFileHashes()))
.setSelectedYankedVersions(moduleResolutionValue.getSelectedYankedVersions())
.setModuleExtensions(combinedExtensionInfos)
.build();

// Write the new value to the file, but only if needed. This is not just a performance
// optimization: whenever the lockfile is updated, most Skyframe nodes will be marked as dirty
// on the next build, which breaks commands such as `bazel config` that rely on
// com.google.devtools.build.skyframe.MemoizingEvaluator#getDoneValues.
if (!newLockfile.equals(oldLockfile)) {
updateLockfile(workspaceRoot, newLockfile);

Thread updateLockfile =
Thread.startVirtualThread(
() -> {
var nonReproducibleExtensionInfos =
combineModuleExtensions(
oldPersistentLockfile.getModuleExtensions(),
newExtensionInfos,
/* hasUsages= */ depGraphValue.getExtensionUsagesTable()::containsRow,
/* reproducible= */ false);

// Create an updated version of the lockfile, keeping only the extension results from
// the old
// lockfile that are still up-to-date and adding the newly resolved extension results.
BazelLockFileValue newLockfile =
BazelLockFileValue.builder()
.setRegistryFileHashes(
ImmutableSortedMap.copyOf(moduleResolutionValue.getRegistryFileHashes()))
.setSelectedYankedVersions(moduleResolutionValue.getSelectedYankedVersions())
.setModuleExtensions(nonReproducibleExtensionInfos)
.build();

// Write the new values to the files, but only if needed. This is not just a
// performance optimization: whenever the lockfile is updated, most Skyframe nodes
// will be marked as dirty on the next build, which breaks commands such as `bazel
// config` that rely on
// com.google.devtools.build.skyframe.MemoizingEvaluator#getDoneValues.
if (!newLockfile.equals(oldLockfile)) {
updateLockfile(workspaceRoot, newLockfile);
}
});

Thread updatePersistentLockfile =
Thread.startVirtualThread(
() -> {
var reproducibleExtensionInfos =
combineModuleExtensions(
oldLockfile.getModuleExtensions(),
newExtensionInfos,
/* hasUsages= */ depGraphValue.getExtensionUsagesTable()::containsRow,
/* reproducible= */ true);

var persistentRegistryFileHashes =
ImmutableMap.<String, Optional<Checksum>>builder()
.putAll(oldPersistentLockfile.getRegistryFileHashes())
.putAll(
Maps.filterValues(
moduleResolutionValue.getRegistryFileHashes(), Optional::isPresent))
.buildKeepingLast();
BazelLockFileValue newPersistentLockfile =
BazelLockFileValue.builder()
.setRegistryFileHashes(
ImmutableSortedMap.copyOf(persistentRegistryFileHashes))
.setSelectedYankedVersions(ImmutableMap.of())
.setModuleExtensions(reproducibleExtensionInfos)
.build();

if (!newPersistentLockfile.equals(oldPersistentLockfile)) {
updateLockfile(outputBase, newPersistentLockfile);
}
});

try {
updateLockfile.join();
updatePersistentLockfile.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.atSevere().withCause(e).log(
"Interrupted while updating MODULE.bazel.lock file: %s", e.getMessage());
}
}

Expand All @@ -156,12 +213,13 @@ public void afterCommand() {
Map<ModuleExtensionId, ImmutableMap<ModuleExtensionEvalFactors, LockFileModuleExtension>>
oldExtensionInfos,
Map<ModuleExtensionId, LockFileModuleExtension.WithFactors> newExtensionInfos,
Predicate<ModuleExtensionId> hasUsages) {
Predicate<ModuleExtensionId> hasUsages,
boolean reproducible) {
Map<ModuleExtensionId, ImmutableMap<ModuleExtensionEvalFactors, LockFileModuleExtension>>
updatedExtensionMap = new HashMap<>();

// Keep those per factor extension results that are still used according to the static
// information given in the extension declaration (dependence on os and arch, reproducibility).
// information given in the extension declaration (dependence on os and arch).
// Other information such as transitive .bzl hash and usages hash are *not* checked here.
for (var entry : oldExtensionInfos.entrySet()) {
var moduleExtensionId = entry.getKey();
Expand All @@ -176,7 +234,7 @@ public void afterCommand() {
continue;
}
var newFactors = newExtensionInfo.extensionFactors();
// Factor results can be individually marked as reproducible and should be dropped if so.
// Factor results can be individually marked as reproducible.
ImmutableSortedMap<ModuleExtensionEvalFactors, LockFileModuleExtension>
perFactorResultsToKeep =
ImmutableSortedMap.copyOf(
Expand All @@ -185,7 +243,8 @@ public void afterCommand() {
existingFactors ->
newFactors.hasSameDependenciesAs(existingFactors)
&& !(newFactors.equals(existingFactors)
&& newExtensionInfo.moduleExtension().isReproducible())));
&& newExtensionInfo.moduleExtension().isReproducible()
!= reproducible)));
if (perFactorResultsToKeep.isEmpty()) {
continue;
}
Expand All @@ -195,7 +254,7 @@ public void afterCommand() {
// Add the new resolved extensions
for (var extensionIdAndInfo : newExtensionInfos.entrySet()) {
LockFileModuleExtension extension = extensionIdAndInfo.getValue().moduleExtension();
if (extension.isReproducible()) {
if (extension.isReproducible() != reproducible) {
continue;
}

Expand Down Expand Up @@ -227,12 +286,12 @@ public void afterCommand() {
/**
* Updates the data stored in the lockfile (MODULE.bazel.lock)
*
* @param workspaceRoot Root of the workspace where the lockfile is located
* @param lockfileRoot Root under which the lockfile is located
* @param updatedLockfile The updated lockfile data to save
*/
private static void updateLockfile(Path workspaceRoot, BazelLockFileValue updatedLockfile) {
private static void updateLockfile(Path lockfileRoot, BazelLockFileValue updatedLockfile) {
RootedPath lockfilePath =
RootedPath.toRootedPath(Root.fromPath(workspaceRoot), LabelConstants.MODULE_LOCKFILE_NAME);
RootedPath.toRootedPath(Root.fromPath(lockfileRoot), LabelConstants.MODULE_LOCKFILE_NAME);
try {
FileSystemUtils.writeContent(
lockfilePath.asPath(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public abstract class BazelLockFileValue implements SkyValue {

@SerializationConstant public static final SkyKey KEY = () -> SkyFunctions.BAZEL_LOCK_FILE;

@SerializationConstant
public static final SkyKey PERSISTENT_KEY = () -> SkyFunctions.BAZEL_LOCK_FILE;

static Builder builder() {
return new AutoValue_BazelLockFileValue.Builder()
.setLockFileVersion(LOCK_FILE_VERSION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@

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

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.LockfileMode;
import com.google.devtools.build.lib.bazel.repository.downloader.Checksum;
import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction;
import com.google.devtools.build.lib.server.FailureDetails;
import com.google.devtools.build.lib.skyframe.PrecomputedValue.Precomputed;
Expand Down Expand Up @@ -64,8 +67,13 @@ public SkyValue compute(SkyKey skyKey, Environment env)
RegistryFunction.LAST_INVALIDATION.get(env);
}

BazelLockFileValue lockfile = (BazelLockFileValue) env.getValue(BazelLockFileValue.KEY);
if (lockfile == null) {
var lockfiles =
env.getValuesAndExceptions(
ImmutableList.of(BazelLockFileValue.KEY, BazelLockFileValue.PERSISTENT_KEY));
BazelLockFileValue lockfile = (BazelLockFileValue) lockfiles.get(BazelLockFileValue.KEY);
BazelLockFileValue persistentLockfile =
(BazelLockFileValue) lockfiles.get(BazelLockFileValue.PERSISTENT_KEY);
if (lockfile == null || persistentLockfile == null) {
return null;
}

Expand All @@ -74,7 +82,10 @@ public SkyValue compute(SkyKey skyKey, Environment env)
return registryFactory.createRegistry(
key.url().replace("%workspace%", workspaceRoot.getPathString()),
lockfileMode,
lockfile.getRegistryFileHashes(),
ImmutableMap.<String, Optional<Checksum>>builder()
.putAll(persistentLockfile.getRegistryFileHashes())
.putAll(lockfile.getRegistryFileHashes())
.buildKeepingLast(),
lockfile.getSelectedYankedVersions(),
vendorDir);
} catch (URISyntaxException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,23 @@ public SkyValue compute(SkyKey skyKey, Environment env)
LockFileModuleExtension lockedExtension = null;
LockfileMode lockfileMode = BazelLockFileFunction.LOCKFILE_MODE.get(env);
if (!lockfileMode.equals(LockfileMode.OFF)) {
BazelLockFileValue lockfile = (BazelLockFileValue) env.getValue(BazelLockFileValue.KEY);
if (lockfile == null) {
var lockfiles =
env.getValuesAndExceptions(
ImmutableList.of(BazelLockFileValue.KEY, BazelLockFileValue.PERSISTENT_KEY));
BazelLockFileValue lockfile = (BazelLockFileValue) lockfiles.get(BazelLockFileValue.KEY);
BazelLockFileValue persistentLockfile =
(BazelLockFileValue) lockfiles.get(BazelLockFileValue.PERSISTENT_KEY);
if (lockfile == null || persistentLockfile == null) {
return null;
}
var lockedExtensionMap = lockfile.getModuleExtensions().get(extensionId);
lockedExtension =
lockedExtensionMap == null ? null : lockedExtensionMap.get(extension.getEvalFactors());
if (lockedExtension == null) {
lockedExtensionMap = persistentLockfile.getModuleExtensions().get(extensionId);
lockedExtension =
lockedExtensionMap == null ? null : lockedExtensionMap.get(extension.getEvalFactors());
}
if (lockedExtension != null) {
try {
SingleExtensionValue singleExtensionValue =
Expand Down Expand Up @@ -335,7 +345,9 @@ private SingleExtensionValue tryGettingValueFromLockFile(
Optional.of(new LockFileModuleExtension.WithFactors(evalFactors, lockedExtension)),
env);
}
if (lockfileMode.equals(LockfileMode.ERROR)) {
// Reproducible extensions are always locked in the persistent lockfile to provide best-effort
// speedups, but should never result in an error if out-of-date.
if (lockfileMode.equals(LockfileMode.ERROR) && !lockedExtension.isReproducible()) {
throw new SingleExtensionEvalFunctionException(
ExternalDepsException.withMessage(
Code.BAD_LOCKFILE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class LabelConstants {
public static final PathFragment VENDOR_FILE_NAME = PathFragment.create("VENDOR.bazel");

public static final PathFragment MODULE_LOCKFILE_NAME = PathFragment.create("MODULE.bazel.lock");
public static final PathFragment MODULE_REPRODUCIBLE_LOCKFILE_NAME =
PathFragment.create("MODULE.bazel.reproducible_lock");

// With this prefix, non-main repositories are symlinked under
// $output_base/execution_root/__main__/external
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ public BazelPackageLoader buildImpl() {
EXTERNAL_PACKAGE_HELPER))
.put(
SkyFunctions.BAZEL_LOCK_FILE,
new BazelLockFileFunction(directories.getWorkspace()))
new BazelLockFileFunction(
directories.getWorkspace(), directories.getOutputBase()))
.put(SkyFunctions.BAZEL_DEP_GRAPH, new BazelDepGraphFunction())
.put(SkyFunctions.BAZEL_MODULE_RESOLUTION, new BazelModuleResolutionFunction())
.put(SkyFunctions.REPO_SPEC, repoSpecFunction)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ public ImmutableMap<SkyFunctionName, SkyFunction> getSkyFunctions(BlazeDirectori
directories.getWorkspace(),
getBuiltinModules(directories)))
.put(SkyFunctions.BAZEL_DEP_GRAPH, new BazelDepGraphFunction())
.put(SkyFunctions.BAZEL_LOCK_FILE, new BazelLockFileFunction(directories.getWorkspace()))
.put(
SkyFunctions.BAZEL_LOCK_FILE,
new BazelLockFileFunction(directories.getWorkspace(), directories.getOutputBase()))
.put(SkyFunctions.BAZEL_MODULE_RESOLUTION, new BazelModuleResolutionFunction())
.put(SkyFunctions.SINGLE_EXTENSION, new SingleExtensionFunction())
.put(
Expand Down
Loading

0 comments on commit 02e2e2c

Please sign in to comment.