| // 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.actions.FileStateValue.FILE_STATE; |
| import static com.google.devtools.build.lib.skyframe.SkyFunctions.DIRECTORY_LISTING_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.skyframe.SkyKey; |
| import com.google.devtools.build.skyframe.SkyValue; |
| 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() {} |
| |
| static class FileDirtinessChecker extends SkyValueDirtinessChecker { |
| @Override |
| public boolean applies(SkyKey skyKey) { |
| return skyKey.functionName().equals(FILE_STATE); |
| } |
| |
| @Override |
| @Nullable |
| public SkyValue createNewValue(SkyKey key, @Nullable TimestampGranularityMonitor tsgm) { |
| RootedPath rootedPath = (RootedPath) key.argument(); |
| try { |
| return FileStateValue.create(rootedPath, 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; |
| } |
| } |
| } |
| |
| 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, @Nullable TimestampGranularityMonitor tsgm) { |
| RootedPath rootedPath = (RootedPath) key.argument(); |
| try { |
| return DirectoryListingStateValue.create(rootedPath); |
| } catch (IOException e) { |
| return null; |
| } |
| } |
| } |
| |
| static class BasicFilesystemDirtinessChecker extends SkyValueDirtinessChecker { |
| private final FileDirtinessChecker fdc = new FileDirtinessChecker(); |
| private final DirectoryDirtinessChecker ddc = new DirectoryDirtinessChecker(); |
| private final UnionDirtinessChecker checker = |
| new UnionDirtinessChecker(ImmutableList.of(fdc, ddc)); |
| |
| @Override |
| public boolean applies(SkyKey skyKey) { |
| return fdc.applies(skyKey) || ddc.applies(skyKey); |
| } |
| |
| @Override |
| @Nullable |
| public SkyValue createNewValue(SkyKey key, @Nullable TimestampGranularityMonitor tsgm) { |
| return checker.createNewValue(key, tsgm); |
| } |
| } |
| |
| static final class MissingDiffDirtinessChecker extends BasicFilesystemDirtinessChecker { |
| private final Set<Root> missingDiffPackageRoots; |
| |
| MissingDiffDirtinessChecker(final Set<Root> missingDiffPackageRoots) { |
| this.missingDiffPackageRoots = missingDiffPackageRoots; |
| } |
| |
| @Override |
| public boolean applies(SkyKey key) { |
| return super.applies(key) |
| && missingDiffPackageRoots.contains(((RootedPath) key.argument()).getRoot()); |
| } |
| } |
| |
| /** Checks files outside of the package roots for changes. */ |
| static final class ExternalDirtinessChecker extends BasicFilesystemDirtinessChecker { |
| private final ExternalFilesHelper externalFilesHelper; |
| private final EnumSet<FileType> fileTypesToCheck; |
| |
| ExternalDirtinessChecker(ExternalFilesHelper externalFilesHelper, |
| EnumSet<FileType> fileTypesToCheck) { |
| this.externalFilesHelper = externalFilesHelper; |
| this.fileTypesToCheck = fileTypesToCheck; |
| } |
| |
| @Override |
| public boolean applies(SkyKey key) { |
| if (!super.applies(key)) { |
| return false; |
| } |
| FileType fileType = externalFilesHelper.getAndNoteFileType((RootedPath) key.argument()); |
| return fileTypesToCheck.contains(fileType); |
| } |
| |
| @Nullable |
| @Override |
| public SkyValue createNewValue(SkyKey key, @Nullable TimestampGranularityMonitor tsgm) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public SkyValueDirtinessChecker.DirtyResult check( |
| SkyKey skyKey, SkyValue oldValue, @Nullable TimestampGranularityMonitor tsgm) { |
| SkyValue newValue = super.createNewValue(skyKey, tsgm); |
| if (Objects.equal(newValue, oldValue)) { |
| return SkyValueDirtinessChecker.DirtyResult.notDirty(oldValue); |
| } |
| FileType fileType = externalFilesHelper.getAndNoteFileType((RootedPath) skyKey.argument()); |
| if (fileType == FileType.EXTERNAL_REPO) { |
| // 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. |
| return SkyValueDirtinessChecker.DirtyResult.dirty(oldValue); |
| } |
| return SkyValueDirtinessChecker.DirtyResult.dirtyWithNewValue(oldValue, newValue); |
| } |
| } |
| |
| /** {@link SkyValueDirtinessChecker} that encompasses a union of other dirtiness checkers. */ |
| static final class UnionDirtinessChecker extends SkyValueDirtinessChecker { |
| private final Iterable<SkyValueDirtinessChecker> dirtinessCheckers; |
| |
| 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, @Nullable TimestampGranularityMonitor tsgm) { |
| return Preconditions.checkNotNull(getChecker(key), key).createNewValue(key, tsgm); |
| } |
| |
| @Override |
| public DirtyResult check(SkyKey key, @Nullable SkyValue oldValue, |
| @Nullable TimestampGranularityMonitor tsgm) { |
| return Preconditions.checkNotNull(getChecker(key), key).check(key, oldValue, tsgm); |
| } |
| } |
| } |