blob: f321be30b265edeaa5d49a1ea59990979cea5e3d [file] [log] [blame]
// Copyright 2015 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 static com.google.devtools.build.lib.skyframe.SkyFunctions.DIRECTORY_LISTING_STATE;
import static com.google.devtools.build.lib.vfs.FileStateKey.FILE_STATE;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.FileStateValue;
import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.FileType;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.lib.vfs.SyscallCache;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.Version;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Set;
import javax.annotation.Nullable;
/** Utilities for checking dirtiness of keys (mainly filesystem keys) in the graph. */
public class DirtinessCheckerUtils {
private DirtinessCheckerUtils() {}
/** Checks dirtiness of file keys in the graph. */
public static class FileDirtinessChecker extends SkyValueDirtinessChecker {
@Override
public boolean applies(SkyKey skyKey) {
return skyKey.functionName().equals(FILE_STATE);
}
@Override
@Nullable
public SkyValue createNewValue(
SkyKey key, SyscallCache syscallCache, @Nullable TimestampGranularityMonitor tsgm) {
try {
return FileStateValue.create((RootedPath) key.argument(), syscallCache, tsgm);
} catch (IOException e) {
// TODO(bazel-team): An IOException indicates a failure to get a file digest or a symlink
// target, not a missing file. Such a failure really shouldn't happen, so failing early
// may be better here.
return null;
}
}
}
/** Checks dirtiness of directory keys in the graph. */
public static class DirectoryDirtinessChecker extends SkyValueDirtinessChecker {
@Override
public boolean applies(SkyKey skyKey) {
return skyKey.functionName().equals(DIRECTORY_LISTING_STATE);
}
@Override
@Nullable
public SkyValue createNewValue(
SkyKey key, SyscallCache syscallCache, @Nullable TimestampGranularityMonitor tsgm) {
RootedPath rootedPath = (RootedPath) key.argument();
try {
return DirectoryListingStateValue.create(syscallCache.readdir(rootedPath.asPath()));
} catch (IOException e) {
return null;
}
}
}
static final class MissingDiffDirtinessChecker extends SkyValueDirtinessChecker {
private final Set<Root> missingDiffPackageRoots;
private final UnionDirtinessChecker checker = createBasicFilesystemDirtinessChecker();
MissingDiffDirtinessChecker(Set<Root> missingDiffPackageRoots) {
this.missingDiffPackageRoots = missingDiffPackageRoots;
}
@Override
public boolean applies(SkyKey key) {
return checker.applies(key)
&& missingDiffPackageRoots.contains(((RootedPath) key.argument()).getRoot());
}
@Nullable
@Override
public SkyValue createNewValue(
SkyKey key, SyscallCache syscallCache, @Nullable TimestampGranularityMonitor tsgm) {
return checker.createNewValue(key, syscallCache, tsgm);
}
}
/**
* Serves for tracking whether there are external and output files {@see ExternalFilesKnowledge}.
* Filtering of files, for which the new values should not be injected into evaluator, is done in
* SequencedSkyframeExecutor.handleChangedFiles().
*/
static final class ExternalDirtinessChecker extends SkyValueDirtinessChecker {
private final ExternalFilesHelper externalFilesHelper;
private final EnumSet<FileType> fileTypesToCheck;
private final UnionDirtinessChecker checker = createBasicFilesystemDirtinessChecker();
ExternalDirtinessChecker(ExternalFilesHelper externalFilesHelper,
EnumSet<FileType> fileTypesToCheck) {
this.externalFilesHelper = externalFilesHelper;
this.fileTypesToCheck = fileTypesToCheck;
}
@Override
public boolean applies(SkyKey key) {
if (!checker.applies(key)) {
return false;
}
FileType fileType = externalFilesHelper.getAndNoteFileType((RootedPath) key.argument());
return fileTypesToCheck.contains(fileType);
}
@Nullable
@Override
public SkyValue createNewValue(
SkyKey key, SyscallCache syscallCache, @Nullable TimestampGranularityMonitor tsgm) {
throw new UnsupportedOperationException();
}
@Override
public SkyValueDirtinessChecker.DirtyResult check(
SkyKey skyKey,
SkyValue oldValue,
@Nullable Version oldMtsv,
SyscallCache syscallCache,
@Nullable TimestampGranularityMonitor tsgm) {
FileType fileType = externalFilesHelper.getAndNoteFileType((RootedPath) skyKey.argument());
boolean cacheable = isCacheableType(fileType);
SkyValue newValue =
checker.createNewValue(skyKey, cacheable ? syscallCache : SyscallCache.NO_CACHE, tsgm);
if (Objects.equal(newValue, oldValue)) {
return SkyValueDirtinessChecker.DirtyResult.notDirty();
}
if (cacheable) {
return SkyValueDirtinessChecker.DirtyResult.dirtyWithNewValue(newValue);
}
// Files under output_base/external have a dependency on the WORKSPACE file, so we don't add
// a new SkyValue to the graph yet because it might change once the WORKSPACE file has been
// parsed. Similarly, output files might change during execution.
return SkyValueDirtinessChecker.DirtyResult.dirty();
}
private static boolean isCacheableType(FileType fileType) {
switch (fileType) {
case INTERNAL:
case EXTERNAL:
case BUNDLED:
return true;
case EXTERNAL_REPO:
case OUTPUT:
return false;
}
throw new AssertionError("Unknown type " + fileType);
}
}
static UnionDirtinessChecker createBasicFilesystemDirtinessChecker() {
return new UnionDirtinessChecker(
ImmutableList.of(new FileDirtinessChecker(), new DirectoryDirtinessChecker()));
}
/** {@link SkyValueDirtinessChecker} that encompasses a union of other dirtiness checkers. */
public static final class UnionDirtinessChecker extends SkyValueDirtinessChecker {
private final Iterable<SkyValueDirtinessChecker> dirtinessCheckers;
public UnionDirtinessChecker(Iterable<SkyValueDirtinessChecker> dirtinessCheckers) {
this.dirtinessCheckers = dirtinessCheckers;
}
@Nullable
private SkyValueDirtinessChecker getChecker(SkyKey key) {
for (SkyValueDirtinessChecker dirtinessChecker : dirtinessCheckers) {
if (dirtinessChecker.applies(key)) {
return dirtinessChecker;
}
}
return null;
}
@Override
public boolean applies(SkyKey key) {
return getChecker(key) != null;
}
@Override
@Nullable
public SkyValue createNewValue(
SkyKey key, SyscallCache syscallCache, @Nullable TimestampGranularityMonitor tsgm) {
return Preconditions.checkNotNull(getChecker(key), key)
.createNewValue(key, syscallCache, tsgm);
}
@Override
public DirtyResult check(
SkyKey key,
@Nullable SkyValue oldValue,
@Nullable Version oldMtsv,
SyscallCache syscallCache,
@Nullable TimestampGranularityMonitor tsgm) {
return Preconditions.checkNotNull(getChecker(key), key)
.check(key, oldValue, oldMtsv, syscallCache, tsgm);
}
@Override
@Nullable
public Version getMaxTransitiveSourceVersionForNewValue(SkyKey key, SkyValue value) {
return Preconditions.checkNotNull(getChecker(key), key)
.getMaxTransitiveSourceVersionForNewValue(key, value);
}
}
}