blob: 3b393a9bacef9b1dc07a707df8cfbee580afc156 [file] [log] [blame]
// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.BuildView;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
import com.google.devtools.build.lib.analysis.WorkspaceStatusAction.Factory;
import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory;
import com.google.devtools.build.lib.analysis.config.BinTools;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.concurrent.Uninterruptibles;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.PackageFactory;
import com.google.devtools.build.lib.packages.Preprocessor;
import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
import com.google.devtools.build.lib.profiler.AutoProfiler;
import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.BasicFilesystemDirtinessChecker;
import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.ExternalDirtinessChecker;
import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.MissingDiffDirtinessChecker;
import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.UnionDirtinessChecker;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.ResourceUsage;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
import com.google.devtools.build.lib.vfs.ModifiedFileSet;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.skyframe.BuildDriver;
import com.google.devtools.build.skyframe.Differencer;
import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
import com.google.devtools.build.skyframe.Injectable;
import com.google.devtools.build.skyframe.MemoizingEvaluator.EvaluatorSupplier;
import com.google.devtools.build.skyframe.RecordingDifferencer;
import com.google.devtools.build.skyframe.SequentialBuildDriver;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.logging.Logger;
/**
* A SkyframeExecutor that implicitly assumes that builds can be done incrementally from the most
* recent build. In other words, builds are "sequenced".
*/
public final class SequencedSkyframeExecutor extends SkyframeExecutor {
private static final Logger LOG = Logger.getLogger(SequencedSkyframeExecutor.class.getName());
/** Lower limit for number of loaded packages to consider clearing CT values. */
private int valueCacheEvictionLimit = -1;
/** Union of labels of loaded packages since the last eviction of CT values. */
private Set<PackageIdentifier> allLoadedPackages = ImmutableSet.of();
private boolean lastAnalysisDiscarded = false;
// Can only be set once (to false) over the lifetime of this object. If false, the graph will not
// store edges, saving memory but making incremental builds impossible.
private boolean keepGraphEdges = true;
private RecordingDifferencer recordingDiffer;
private final DiffAwarenessManager diffAwarenessManager;
private final Iterable<SkyValueDirtinessChecker> customDirtinessCheckers;
private SequencedSkyframeExecutor(
EvaluatorSupplier evaluatorSupplier,
PackageFactory pkgFactory,
TimestampGranularityMonitor tsgm,
BlazeDirectories directories,
BinTools binTools,
Factory workspaceStatusActionFactory,
ImmutableList<BuildInfoFactory> buildInfoFactories,
Iterable<? extends DiffAwareness.Factory> diffAwarenessFactories,
Predicate<PathFragment> allowedMissingInputs,
Preprocessor.Factory.Supplier preprocessorFactorySupplier,
ImmutableMap<SkyFunctionName, SkyFunction> extraSkyFunctions,
ImmutableList<PrecomputedValue.Injected> extraPrecomputedValues,
Iterable<SkyValueDirtinessChecker> customDirtinessCheckers) {
super(
evaluatorSupplier,
pkgFactory,
tsgm,
directories,
binTools,
workspaceStatusActionFactory,
buildInfoFactories,
allowedMissingInputs,
preprocessorFactorySupplier,
extraSkyFunctions,
extraPrecomputedValues,
false);
this.diffAwarenessManager = new DiffAwarenessManager(diffAwarenessFactories);
this.customDirtinessCheckers = customDirtinessCheckers;
}
public static SequencedSkyframeExecutor create(
PackageFactory pkgFactory,
TimestampGranularityMonitor tsgm,
BlazeDirectories directories,
BinTools binTools,
Factory workspaceStatusActionFactory,
ImmutableList<BuildInfoFactory> buildInfoFactories,
Iterable<? extends DiffAwareness.Factory> diffAwarenessFactories,
Predicate<PathFragment> allowedMissingInputs,
Preprocessor.Factory.Supplier preprocessorFactorySupplier,
ImmutableMap<SkyFunctionName, SkyFunction> extraSkyFunctions,
ImmutableList<PrecomputedValue.Injected> extraPrecomputedValues,
Iterable<SkyValueDirtinessChecker> customDirtinessCheckers) {
SequencedSkyframeExecutor skyframeExecutor =
new SequencedSkyframeExecutor(
InMemoryMemoizingEvaluator.SUPPLIER,
pkgFactory,
tsgm,
directories,
binTools,
workspaceStatusActionFactory,
buildInfoFactories,
diffAwarenessFactories,
allowedMissingInputs,
preprocessorFactorySupplier,
extraSkyFunctions,
extraPrecomputedValues,
customDirtinessCheckers);
skyframeExecutor.init();
return skyframeExecutor;
}
@VisibleForTesting
public static SequencedSkyframeExecutor create(PackageFactory pkgFactory,
TimestampGranularityMonitor tsgm, BlazeDirectories directories, BinTools binTools,
WorkspaceStatusAction.Factory workspaceStatusActionFactory,
ImmutableList<BuildInfoFactory> buildInfoFactories,
Iterable<? extends DiffAwareness.Factory> diffAwarenessFactories) {
return create(
pkgFactory,
tsgm,
directories,
binTools,
workspaceStatusActionFactory,
buildInfoFactories,
diffAwarenessFactories,
Predicates.<PathFragment>alwaysFalse(),
Preprocessor.Factory.Supplier.NullSupplier.INSTANCE,
ImmutableMap.<SkyFunctionName, SkyFunction>of(),
ImmutableList.<PrecomputedValue.Injected>of(),
ImmutableList.<SkyValueDirtinessChecker>of());
}
@Override
protected BuildDriver newBuildDriver() {
return new SequentialBuildDriver(memoizingEvaluator);
}
@Override
protected void init() {
// Note that we need to set recordingDiffer first since SkyframeExecutor#init calls
// SkyframeExecutor#evaluatorDiffer.
recordingDiffer = new RecordingDifferencer();
super.init();
}
@Override
public void resetEvaluator() {
super.resetEvaluator();
diffAwarenessManager.reset();
}
@Override
protected Differencer evaluatorDiffer() {
return recordingDiffer;
}
@Override
protected Injectable injectable() {
return recordingDiffer;
}
@VisibleForTesting
public RecordingDifferencer getDifferencerForTesting() {
return recordingDiffer;
}
@Override
public void sync(EventHandler eventHandler, PackageCacheOptions packageCacheOptions,
Path outputBase, Path workingDirectory, String defaultsPackageContents, UUID commandId)
throws InterruptedException, AbruptExitException {
this.valueCacheEvictionLimit = packageCacheOptions.minLoadedPkgCountForCtNodeEviction;
super.sync(eventHandler, packageCacheOptions, outputBase, workingDirectory,
defaultsPackageContents, commandId);
handleDiffs(eventHandler);
}
/**
* The value types whose builders have direct access to the package locator, rather than accessing
* it via an explicit Skyframe dependency. They need to be invalidated if the package locator
* changes.
*/
private static final Set<SkyFunctionName> PACKAGE_LOCATOR_DEPENDENT_VALUES =
ImmutableSet.of(
SkyFunctions.AST_FILE_LOOKUP,
SkyFunctions.FILE_STATE,
SkyFunctions.FILE,
SkyFunctions.DIRECTORY_LISTING_STATE,
SkyFunctions.TARGET_PATTERN,
SkyFunctions.PREPARE_DEPS_OF_PATTERN,
SkyFunctions.WORKSPACE_FILE,
SkyFunctions.EXTERNAL_PACKAGE);
@Override
protected void onNewPackageLocator(PathPackageLocator oldLocator, PathPackageLocator pkgLocator) {
invalidate(SkyFunctionName.functionIsIn(PACKAGE_LOCATOR_DEPENDENT_VALUES));
}
@Override
protected void invalidate(Predicate<SkyKey> pred) {
recordingDiffer.invalidate(Iterables.filter(memoizingEvaluator.getValues().keySet(), pred));
}
private void invalidateDeletedPackages(Iterable<PackageIdentifier> deletedPackages) {
ArrayList<SkyKey> packagesToInvalidate = Lists.newArrayList();
for (PackageIdentifier deletedPackage : deletedPackages) {
packagesToInvalidate.add(PackageLookupValue.key(deletedPackage));
}
recordingDiffer.invalidate(packagesToInvalidate);
}
/**
* Sets the packages that should be treated as deleted and ignored.
*/
@Override
@VisibleForTesting // productionVisibility = Visibility.PRIVATE
public void setDeletedPackages(Iterable<PackageIdentifier> pkgs) {
// Invalidate the old deletedPackages as they may exist now.
invalidateDeletedPackages(deletedPackages.get());
deletedPackages.set(ImmutableSet.copyOf(pkgs));
// Invalidate the new deletedPackages as we need to pretend that they don't exist now.
invalidateDeletedPackages(deletedPackages.get());
}
/**
* Uses diff awareness on all the package paths to invalidate changed files.
*/
@VisibleForTesting
public void handleDiffs(EventHandler eventHandler) throws InterruptedException {
if (lastAnalysisDiscarded) {
// Values were cleared last build, but they couldn't be deleted because they were needed for
// the execution phase. We can delete them now.
dropConfiguredTargetsNow(eventHandler);
lastAnalysisDiscarded = false;
}
modifiedFiles = 0;
Map<Path, DiffAwarenessManager.ProcessableModifiedFileSet> modifiedFilesByPathEntry =
Maps.newHashMap();
Set<Pair<Path, DiffAwarenessManager.ProcessableModifiedFileSet>>
pathEntriesWithoutDiffInformation = Sets.newHashSet();
for (Path pathEntry : pkgLocator.get().getPathEntries()) {
DiffAwarenessManager.ProcessableModifiedFileSet modifiedFileSet =
diffAwarenessManager.getDiff(eventHandler, pathEntry);
if (modifiedFileSet.getModifiedFileSet().treatEverythingAsModified()) {
pathEntriesWithoutDiffInformation.add(Pair.of(pathEntry, modifiedFileSet));
} else {
modifiedFilesByPathEntry.put(pathEntry, modifiedFileSet);
}
}
handleDiffsWithCompleteDiffInformation(modifiedFilesByPathEntry);
handleDiffsWithMissingDiffInformation(eventHandler, pathEntriesWithoutDiffInformation);
}
/**
* Invalidates files under path entries whose corresponding {@link DiffAwareness} gave an exact
* diff. Removes entries from the given map as they are processed. All of the files need to be
* invalidated, so the map should be empty upon completion of this function.
*/
private void handleDiffsWithCompleteDiffInformation(
Map<Path, DiffAwarenessManager.ProcessableModifiedFileSet> modifiedFilesByPathEntry)
throws InterruptedException {
for (Path pathEntry : ImmutableSet.copyOf(modifiedFilesByPathEntry.keySet())) {
DiffAwarenessManager.ProcessableModifiedFileSet processableModifiedFileSet =
modifiedFilesByPathEntry.get(pathEntry);
ModifiedFileSet modifiedFileSet = processableModifiedFileSet.getModifiedFileSet();
Preconditions.checkState(!modifiedFileSet.treatEverythingAsModified(), pathEntry);
handleChangedFiles(ImmutableList.of(pathEntry),
getDiff(modifiedFileSet.modifiedSourceFiles(), pathEntry));
processableModifiedFileSet.markProcessed();
}
}
/**
* Finds and invalidates changed files under path entries whose corresponding
* {@link DiffAwareness} said all files may have been modified.
*/
private void handleDiffsWithMissingDiffInformation(EventHandler eventHandler,
Set<Pair<Path, DiffAwarenessManager.ProcessableModifiedFileSet>>
pathEntriesWithoutDiffInformation) throws InterruptedException {
if (pathEntriesWithoutDiffInformation.isEmpty()
&& Iterables.isEmpty(customDirtinessCheckers)
&& !externalFilesHelper.isExternalFileSeen()) {
// Avoid a full graph scan if we have good diff information for all path entries, there are
// no custom checkers that need to look at the whole graph, and no external (not under any
// path) files need to be checked.
return;
}
// Before running the FilesystemValueChecker, ensure that all values marked for invalidation
// have actually been invalidated (recall that invalidation happens at the beginning of the
// next evaluate() call), because checking those is a waste of time.
buildDriver.evaluate(ImmutableList.<SkyKey>of(), false,
DEFAULT_THREAD_COUNT, eventHandler);
FilesystemValueChecker fsvc = new FilesystemValueChecker(tsgm, null);
// We need to manually check for changes to known files. This entails finding all dirty file
// system values under package roots for which we don't have diff information. If at least
// one path entry doesn't have diff information, then we're going to have to iterate over
// the skyframe values at least once no matter what.
Set<Path> diffPackageRootsUnderWhichToCheck = new HashSet<>();
for (Pair<Path, DiffAwarenessManager.ProcessableModifiedFileSet> pair :
pathEntriesWithoutDiffInformation) {
diffPackageRootsUnderWhichToCheck.add(pair.getFirst());
}
Differencer.Diff diff =
fsvc.getDirtyKeys(
memoizingEvaluator.getValues(),
new UnionDirtinessChecker(
Iterables.concat(
customDirtinessCheckers,
ImmutableList.<SkyValueDirtinessChecker>of(
new ExternalDirtinessChecker(pkgLocator.get()),
new MissingDiffDirtinessChecker(diffPackageRootsUnderWhichToCheck)))));
handleChangedFiles(diffPackageRootsUnderWhichToCheck, diff);
for (Pair<Path, DiffAwarenessManager.ProcessableModifiedFileSet> pair :
pathEntriesWithoutDiffInformation) {
pair.getSecond().markProcessed();
}
}
private void handleChangedFiles(
Collection<Path> diffPackageRootsUnderWhichToCheck, Differencer.Diff diff) {
Collection<SkyKey> changedKeysWithoutNewValues = diff.changedKeysWithoutNewValues();
Map<SkyKey, SkyValue> changedKeysWithNewValues = diff.changedKeysWithNewValues();
logDiffInfo(diffPackageRootsUnderWhichToCheck, changedKeysWithoutNewValues,
changedKeysWithNewValues);
recordingDiffer.invalidate(changedKeysWithoutNewValues);
recordingDiffer.inject(changedKeysWithNewValues);
modifiedFiles += getNumberOfModifiedFiles(changedKeysWithoutNewValues);
modifiedFiles += getNumberOfModifiedFiles(changedKeysWithNewValues.keySet());
incrementalBuildMonitor.accrue(changedKeysWithoutNewValues);
incrementalBuildMonitor.accrue(changedKeysWithNewValues.keySet());
}
private static void logDiffInfo(Iterable<Path> pathEntries,
Collection<SkyKey> changedWithoutNewValue,
Map<SkyKey, ? extends SkyValue> changedWithNewValue) {
int numModified = changedWithNewValue.size() + changedWithoutNewValue.size();
StringBuilder result = new StringBuilder("DiffAwareness found ")
.append(numModified)
.append(" modified source files and directory listings for ")
.append(Joiner.on(", ").join(pathEntries));
if (numModified > 0) {
Iterable<SkyKey> allModifiedKeys = Iterables.concat(changedWithoutNewValue,
changedWithNewValue.keySet());
Iterable<SkyKey> trimmed = Iterables.limit(allModifiedKeys, 5);
result.append(": ")
.append(Joiner.on(", ").join(trimmed));
if (numModified > 5) {
result.append(", ...");
}
}
LOG.info(result.toString());
}
private static int getNumberOfModifiedFiles(Iterable<SkyKey> modifiedValues) {
// We are searching only for changed files, DirectoryListingValues don't depend on
// child values, that's why they are invalidated separately
return Iterables.size(Iterables.filter(modifiedValues,
SkyFunctionName.functionIs(SkyFunctions.FILE_STATE)));
}
@Override
public void decideKeepIncrementalState(boolean batch, BuildView.Options viewOptions) {
Preconditions.checkState(!active);
if (viewOptions == null) {
// Some blaze commands don't include the view options. Don't bother with them.
return;
}
if (batch && viewOptions.keepGoing && viewOptions.discardAnalysisCache) {
Preconditions.checkState(keepGraphEdges, "May only be called once if successful");
keepGraphEdges = false;
// Graph will be recreated on next sync.
}
}
@Override
public boolean hasIncrementalState() {
// TODO(bazel-team): Combine this method with clearSkyframeRelevantCaches() once legacy
// execution is removed [skyframe-execution].
return keepGraphEdges;
}
@Override
public void invalidateFilesUnderPathForTesting(EventHandler eventHandler,
ModifiedFileSet modifiedFileSet, Path pathEntry) throws InterruptedException {
if (lastAnalysisDiscarded) {
// Values were cleared last build, but they couldn't be deleted because they were needed for
// the execution phase. We can delete them now.
dropConfiguredTargetsNow(eventHandler);
lastAnalysisDiscarded = false;
}
Differencer.Diff diff;
if (modifiedFileSet.treatEverythingAsModified()) {
diff = new FilesystemValueChecker(tsgm, null).getDirtyKeys(memoizingEvaluator.getValues(),
new BasicFilesystemDirtinessChecker());
} else {
diff = getDiff(modifiedFileSet.modifiedSourceFiles(), pathEntry);
}
syscalls.set(newPerBuildSyscallCache(/*concurrencyLevel=*/42));
recordingDiffer.invalidate(diff.changedKeysWithoutNewValues());
recordingDiffer.inject(diff.changedKeysWithNewValues());
// Blaze invalidates transient errors on every build.
invalidateTransientErrors();
}
@Override
public void invalidateTransientErrors() {
checkActive();
recordingDiffer.invalidateTransientErrors();
}
@Override
protected void invalidateDirtyActions(Iterable<SkyKey> dirtyActionValues) {
recordingDiffer.invalidate(dirtyActionValues);
}
/**
* Save memory by removing references to configured targets and actions in Skyframe.
*
* <p>These values must be recreated on subsequent builds. We do not clear the top-level target
* values, since their configured targets are needed for the target completion middleman values.
*
* <p>The values are not deleted during this method call, because they are needed for the
* execution phase. Instead, their data is cleared. The next build will delete the values (and
* recreate them if necessary).
*/
private void discardAnalysisCache(Collection<ConfiguredTarget> topLevelTargets) {
try (AutoProfiler p = AutoProfiler.logged("discarding analysis cache", LOG)) {
lastAnalysisDiscarded = true;
for (Map.Entry<SkyKey, SkyValue> entry : memoizingEvaluator.getValues().entrySet()) {
if (!entry.getKey().functionName().equals(SkyFunctions.CONFIGURED_TARGET)) {
continue;
}
ConfiguredTargetValue ctValue = (ConfiguredTargetValue) entry.getValue();
// ctValue may be null if target was not successfully analyzed.
if (ctValue != null && !topLevelTargets.contains(ctValue.getConfiguredTarget())) {
ctValue.clear();
}
}
}
}
@Override
public void clearAnalysisCache(Collection<ConfiguredTarget> topLevelTargets) {
discardAnalysisCache(topLevelTargets);
}
@Override
public void dropConfiguredTargets() {
skyframeBuildView.clearInvalidatedConfiguredTargets();
skyframeBuildView.clearLegacyData();
memoizingEvaluator.delete(
// We delete any value that can hold an action -- all subclasses of ActionLookupValue -- as
// well as ActionExecutionValues, since they do not depend on ActionLookupValues.
SkyFunctionName.functionIsIn(ImmutableSet.of(
SkyFunctions.CONFIGURED_TARGET,
SkyFunctions.ACTION_LOOKUP,
SkyFunctions.BUILD_INFO,
SkyFunctions.TARGET_COMPLETION,
SkyFunctions.BUILD_INFO_COLLECTION,
SkyFunctions.ACTION_EXECUTION))
);
}
/**
* Deletes all ConfiguredTarget values from the Skyframe cache.
*
* <p>After the execution of this method all invalidated and marked for deletion values
* (and the values depending on them) will be deleted from the cache.
*
* <p>WARNING: Note that a call to this method leaves legacy data inconsistent with Skyframe.
* The next build should clear the legacy caches.
*/
private void dropConfiguredTargetsNow(final EventHandler eventHandler) {
dropConfiguredTargets();
// Run the invalidator to actually delete the values.
try {
progressReceiver.ignoreInvalidations = true;
Uninterruptibles.callUninterruptibly(new Callable<Void>() {
@Override
public Void call() throws InterruptedException {
buildDriver.evaluate(ImmutableList.<SkyKey>of(), false,
ResourceUsage.getAvailableProcessors(), eventHandler);
return null;
}
});
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
progressReceiver.ignoreInvalidations = false;
}
}
/**
* Returns true if the old set of Packages is a subset or superset of the new one.
*
* <p>Compares the names of packages instead of the Package objects themselves (Package doesn't
* yet override #equals). Since packages store their names as a String rather than a Label, it's
* easier to use strings here.
*/
@VisibleForTesting
static boolean isBuildSubsetOrSupersetOfPreviousBuild(Set<PackageIdentifier> oldPackages,
Set<PackageIdentifier> newPackages) {
if (newPackages.size() <= oldPackages.size()) {
return Sets.difference(newPackages, oldPackages).isEmpty();
} else if (oldPackages.size() < newPackages.size()) {
// No need to check for <= here, since the first branch does that already.
// If size(A) = size(B), then then A\B = 0 iff B\A = 0
return Sets.difference(oldPackages, newPackages).isEmpty();
} else {
return false;
}
}
@Override
public void updateLoadedPackageSet(Set<PackageIdentifier> loadedPackages) {
Preconditions.checkState(valueCacheEvictionLimit >= 0,
"should have called setMinLoadedPkgCountForCtValueEviction earlier");
// Make a copy to avoid nesting SetView objects. It also computes size(), which we need below.
Set<PackageIdentifier> union = ImmutableSet.copyOf(
Sets.union(allLoadedPackages, loadedPackages));
if (union.size() < valueCacheEvictionLimit
|| isBuildSubsetOrSupersetOfPreviousBuild(allLoadedPackages, loadedPackages)) {
allLoadedPackages = union;
} else {
dropConfiguredTargets();
allLoadedPackages = loadedPackages;
}
}
@Override
public void deleteOldNodes(long versionWindowForDirtyGc) {
// TODO(bazel-team): perhaps we should come up with a separate GC class dedicated to maintaining
// value garbage. If we ever do so, this logic should be moved there.
memoizingEvaluator.deleteDirty(versionWindowForDirtyGc);
}
@Override
public void dumpPackages(PrintStream out) {
Iterable<SkyKey> packageSkyKeys = Iterables.filter(memoizingEvaluator.getValues().keySet(),
SkyFunctions.isSkyFunction(SkyFunctions.PACKAGE));
out.println(Iterables.size(packageSkyKeys) + " packages");
for (SkyKey packageSkyKey : packageSkyKeys) {
Package pkg = ((PackageValue) memoizingEvaluator.getValues().get(packageSkyKey)).getPackage();
pkg.dump(out);
}
}
}