aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManager.java
diff options
context:
space:
mode:
authorGravatar Han-Wen Nienhuys <hanwen@google.com>2015-02-25 16:45:20 +0100
committerGravatar Han-Wen Nienhuys <hanwen@google.com>2015-02-25 16:45:20 +0100
commitd08b27fa9701fecfdb69e1b0d1ac2459efc2129b (patch)
tree5d50963026239ca5aebfb47ea5b8db7e814e57c8 /src/main/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManager.java
Update from Google.
-- MOE_MIGRATED_REVID=85702957
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManager.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManager.java188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManager.java b/src/main/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManager.java
new file mode 100644
index 0000000000..d1bb81e30c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManager.java
@@ -0,0 +1,188 @@
+// Copyright 2014 Google Inc. 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.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.skyframe.DiffAwareness.View;
+import com.google.devtools.build.lib.vfs.ModifiedFileSet;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/**
+ * Helper class to make it easier to correctly use the {@link DiffAwareness} interface in a
+ * sequential manner.
+ */
+public final class DiffAwarenessManager {
+
+ private final ImmutableSet<? extends DiffAwareness.Factory> diffAwarenessFactories;
+ private Map<Path, DiffAwarenessState> currentDiffAwarenessStates = Maps.newHashMap();
+ private final Reporter reporter;
+
+ public DiffAwarenessManager(Iterable<? extends DiffAwareness.Factory> diffAwarenessFactories,
+ Reporter reporter) {
+ this.diffAwarenessFactories = ImmutableSet.copyOf(diffAwarenessFactories);
+ this.reporter = reporter;
+ }
+
+ private static class DiffAwarenessState {
+ private final DiffAwareness diffAwareness;
+ /**
+ * The {@link View} that should be the baseline for the next {@link #getDiff} call, or
+ * {@code null} if the next {@link #getDiff} will be the first incremental one.
+ */
+ @Nullable
+ private View baselineView;
+
+ private DiffAwarenessState(DiffAwareness diffAwareness, @Nullable View baselineView) {
+ this.diffAwareness = diffAwareness;
+ this.baselineView = baselineView;
+ }
+ }
+
+ /** Reset internal {@link DiffAwareness} state. */
+ public void reset() {
+ for (DiffAwarenessState diffAwarenessState : currentDiffAwarenessStates.values()) {
+ diffAwarenessState.diffAwareness.close();
+ }
+ currentDiffAwarenessStates.clear();
+ }
+
+ /** A set of modified files that should be marked as processed. */
+ public interface ProcessableModifiedFileSet {
+ ModifiedFileSet getModifiedFileSet();
+
+ /**
+ * This should be called when the changes have been noted. Otherwise, the result from the next
+ * call to {@link #getDiff} will be from the baseline of the old, unprocessed, diff.
+ */
+ void markProcessed();
+ }
+
+ /**
+ * Gets the set of changed files since the last call with this path entry, or
+ * {@code ModifiedFileSet.EVERYTHING_MODIFIED} if this is the first such call.
+ */
+ public ProcessableModifiedFileSet getDiff(Path pathEntry) {
+ DiffAwarenessState diffAwarenessState = maybeGetDiffAwarenessState(pathEntry);
+ if (diffAwarenessState == null) {
+ return BrokenProcessableModifiedFileSet.INSTANCE;
+ }
+ DiffAwareness diffAwareness = diffAwarenessState.diffAwareness;
+ View newView;
+ try {
+ newView = diffAwareness.getCurrentView();
+ } catch (BrokenDiffAwarenessException e) {
+ handleBrokenDiffAwareness(pathEntry, e);
+ return BrokenProcessableModifiedFileSet.INSTANCE;
+ }
+
+ View baselineView = diffAwarenessState.baselineView;
+ if (baselineView == null) {
+ diffAwarenessState.baselineView = newView;
+ return BrokenProcessableModifiedFileSet.INSTANCE;
+ }
+
+ ModifiedFileSet diff;
+ try {
+ diff = diffAwareness.getDiff(baselineView, newView);
+ } catch (BrokenDiffAwarenessException e) {
+ handleBrokenDiffAwareness(pathEntry, e);
+ return BrokenProcessableModifiedFileSet.INSTANCE;
+ } catch (IncompatibleViewException e) {
+ throw new IllegalStateException(pathEntry + " " + baselineView + " " + newView, e);
+ }
+ ProcessableModifiedFileSet result = new ProcessableModifiedFileSetImpl(diff, pathEntry,
+ newView);
+ return result;
+ }
+
+ private void handleBrokenDiffAwareness(Path pathEntry, BrokenDiffAwarenessException e) {
+ currentDiffAwarenessStates.remove(pathEntry);
+ reporter.handle(Event.warn(e.getMessage() + "... temporarily falling back to manually "
+ + "checking files for changes"));
+ }
+
+ /**
+ * Returns the current diff awareness for the given path entry, or a fresh one if there is no
+ * current one, or otherwise {@code null} if no factory could make a fresh one.
+ */
+ @Nullable
+ private DiffAwarenessState maybeGetDiffAwarenessState(Path pathEntry) {
+ DiffAwarenessState diffAwarenessState = currentDiffAwarenessStates.get(pathEntry);
+ if (diffAwarenessState != null) {
+ return diffAwarenessState;
+ }
+ for (DiffAwareness.Factory factory : diffAwarenessFactories) {
+ DiffAwareness newDiffAwareness = factory.maybeCreate(pathEntry);
+ if (newDiffAwareness != null) {
+ diffAwarenessState = new DiffAwarenessState(newDiffAwareness, /*previousView=*/null);
+ currentDiffAwarenessStates.put(pathEntry, diffAwarenessState);
+ return diffAwarenessState;
+ }
+ }
+ return null;
+ }
+
+ private class ProcessableModifiedFileSetImpl implements ProcessableModifiedFileSet {
+
+ private final ModifiedFileSet modifiedFileSet;
+ private final Path pathEntry;
+ /**
+ * The {@link View} that should be the baseline on the next {@link #getDiff} call after
+ * {@link #markProcessed} is called.
+ */
+ private final View nextView;
+
+ private ProcessableModifiedFileSetImpl(ModifiedFileSet modifiedFileSet, Path pathEntry,
+ View nextView) {
+ this.modifiedFileSet = modifiedFileSet;
+ this.pathEntry = pathEntry;
+ this.nextView = nextView;
+ }
+
+ @Override
+ public ModifiedFileSet getModifiedFileSet() {
+ return modifiedFileSet;
+ }
+
+ @Override
+ public void markProcessed() {
+ DiffAwarenessState diffAwarenessState = currentDiffAwarenessStates.get(pathEntry);
+ if (diffAwarenessState != null) {
+ diffAwarenessState.baselineView = nextView;
+ }
+ }
+ }
+
+ private static class BrokenProcessableModifiedFileSet implements ProcessableModifiedFileSet {
+
+ private static final BrokenProcessableModifiedFileSet INSTANCE =
+ new BrokenProcessableModifiedFileSet();
+
+ @Override
+ public ModifiedFileSet getModifiedFileSet() {
+ return ModifiedFileSet.EVERYTHING_MODIFIED;
+ }
+
+ @Override
+ public void markProcessed() {
+ }
+ }
+}