blob: 16f926a2fc06a1cd77326df4860f91e449e5d1d6 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.actions;
15
Googler2f111922017-02-28 20:58:45 +000016import com.google.auto.value.AutoValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.common.base.Predicate;
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +000018import com.google.common.collect.ImmutableList;
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +000019import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.Iterables;
Rumou Duan33bab462016-04-25 17:55:12 +000021import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import com.google.devtools.build.lib.actions.cache.ActionCache;
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +000023import com.google.devtools.build.lib.actions.cache.ActionCache.Entry;
Shreya Bhattarai141b6c22016-08-22 22:00:24 +000024import com.google.devtools.build.lib.actions.cache.DigestUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.devtools.build.lib.actions.cache.Metadata;
26import com.google.devtools.build.lib.actions.cache.MetadataHandler;
27import com.google.devtools.build.lib.events.Event;
28import com.google.devtools.build.lib.events.EventHandler;
29import com.google.devtools.build.lib.events.EventKind;
Mark Schaller6df81792015-12-10 18:47:47 +000030import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032import java.io.IOException;
33import java.util.ArrayList;
34import java.util.HashMap;
35import java.util.List;
36import java.util.Map;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037import javax.annotation.Nullable;
38
39/**
40 * Checks whether an {@link Action} needs to be executed, or whether it has not changed since it was
41 * last stored in the action cache. Must be informed of the new Action data after execution as well.
42 *
43 * <p>The fingerprint, input files names, and metadata (either mtimes or MD5sums) of each action are
44 * cached in the action cache to avoid unnecessary rebuilds. Middleman artifacts are handled
45 * specially, avoiding the need to create actual files corresponding to the middleman artifacts.
46 * Instead of that, results of MiddlemanAction dependency checks are cached internally and then
47 * reused whenever an input middleman artifact is encountered.
48 *
49 * <p>While instances of this class hold references to action and metadata cache instances, they are
50 * otherwise lightweight, and should be constructed anew and discarded for each build request.
51 */
52public class ActionCacheChecker {
53 private final ActionCache actionCache;
54 private final Predicate<? super Action> executionFilter;
55 private final ArtifactResolver artifactResolver;
Googler2f111922017-02-28 20:58:45 +000056 private final CacheConfig cacheConfig;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010057
Googler2f111922017-02-28 20:58:45 +000058 /** Cache config parameters for ActionCacheChecker. */
59 @AutoValue
60 public abstract static class CacheConfig {
61 abstract boolean enabled();
62 // True iff --verbose_explanations flag is set.
63 abstract boolean verboseExplanations();
64
65 public static Builder builder() {
66 return new AutoValue_ActionCacheChecker_CacheConfig.Builder();
67 }
68
69 /** Builder for ActionCacheChecker.CacheConfig. */
70 @AutoValue.Builder
71 public abstract static class Builder {
72 public abstract Builder setVerboseExplanations(boolean value);
73
74 public abstract Builder setEnabled(boolean value);
75
76 public abstract CacheConfig build();
77 }
78 }
79
80 public ActionCacheChecker(
81 ActionCache actionCache,
82 ArtifactResolver artifactResolver,
83 Predicate<? super Action> executionFilter,
84 @Nullable CacheConfig cacheConfig) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010085 this.actionCache = actionCache;
86 this.executionFilter = executionFilter;
87 this.artifactResolver = artifactResolver;
Googler2f111922017-02-28 20:58:45 +000088 this.cacheConfig =
89 cacheConfig != null
90 ? cacheConfig
91 : CacheConfig.builder().setEnabled(true).setVerboseExplanations(false).build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092 }
93
94 public boolean isActionExecutionProhibited(Action action) {
95 return !executionFilter.apply(action);
96 }
97
98 /**
99 * Checks whether one of existing output paths is already used as a key.
100 * If yes, returns it - otherwise uses first output file as a key
101 */
102 private ActionCache.Entry getCacheEntry(Action action) {
Googler2f111922017-02-28 20:58:45 +0000103 if (!cacheConfig.enabled()) {
104 return null; // ignore existing cache when disabled.
105 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100106 for (Artifact output : action.getOutputs()) {
107 ActionCache.Entry entry = actionCache.get(output.getExecPathString());
108 if (entry != null) {
109 return entry;
110 }
111 }
112 return null;
113 }
114
Janak Ramakrishnane3f04b82015-03-27 16:45:34 +0000115 private void removeCacheEntry(Action action) {
116 for (Artifact output : action.getOutputs()) {
117 actionCache.remove(output.getExecPathString());
118 }
119 }
120
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100121 /**
122 * Validate metadata state for action input or output artifacts.
123 *
124 * @param entry cached action information.
125 * @param action action to be validated.
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000126 * @param actionInputs the inputs of the action. Normally just the result of action.getInputs(),
127 * but if this action doesn't yet know its inputs, we check the inputs from the cache.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100128 * @param metadataHandler provider of metadata for the artifacts this action interacts with.
129 * @param checkOutput true to validate output artifacts, Otherwise, just
130 * validate inputs.
131 *
132 * @return true if at least one artifact has changed, false - otherwise.
133 */
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000134 private boolean validateArtifacts(Entry entry, Action action,
135 Iterable<Artifact> actionInputs, MetadataHandler metadataHandler, boolean checkOutput) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100136 Iterable<Artifact> artifacts = checkOutput
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000137 ? Iterables.concat(action.getOutputs(), actionInputs)
138 : actionInputs;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139 Map<String, Metadata> mdMap = new HashMap<>();
140 for (Artifact artifact : artifacts) {
141 mdMap.put(artifact.getExecPathString(), metadataHandler.getMetadataMaybe(artifact));
142 }
Shreya Bhattarai141b6c22016-08-22 22:00:24 +0000143 return !DigestUtils.fromMetadata(mdMap).equals(entry.getFileDigest());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100144 }
145
146 private void reportCommand(EventHandler handler, Action action) {
147 if (handler != null) {
Googler2f111922017-02-28 20:58:45 +0000148 if (cacheConfig.verboseExplanations()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100149 String keyDescription = action.describeKey();
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000150 reportRebuild(handler, action, keyDescription == null
151 ? "action command has changed"
152 : "action command has changed.\nNew action: " + keyDescription);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100153 } else {
154 reportRebuild(handler, action,
155 "action command has changed (try --verbose_explanations for more info)");
156 }
157 }
158 }
159
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000160 private void reportClientEnv(EventHandler handler, Action action, Map<String, String> used) {
161 if (handler != null) {
Googler2f111922017-02-28 20:58:45 +0000162 if (cacheConfig.verboseExplanations()) {
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000163 StringBuilder message = new StringBuilder();
164 message.append("Effective client environment has changed. Now using\n");
165 for (Map.Entry<String, String> entry : used.entrySet()) {
166 message.append(" ").append(entry.getKey()).append("=").append(entry.getValue())
167 .append("\n");
168 }
169 reportRebuild(handler, action, message.toString());
170 } else {
171 reportRebuild(
172 handler,
173 action,
174 "Effective client environment has changed (try --verbose_explanations for more info)");
175 }
176 }
177 }
178
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100179 protected boolean unconditionalExecution(Action action) {
180 return !isActionExecutionProhibited(action) && action.executeUnconditionally();
181 }
182
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000183 private static Map<String, String> computeUsedClientEnv(
184 Action action, Map<String, String> clientEnv) {
185 Map<String, String> used = new HashMap<>();
186 for (String var : action.getClientEnvironmentVariables()) {
187 String value = clientEnv.get(var);
188 if (value != null) {
189 used.put(var, value);
190 }
191 }
192 return used;
193 }
194
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100195 /**
196 * Checks whether {@code action} needs to be executed and returns a non-null Token if so.
197 *
198 * <p>The method checks if any of the action's inputs or outputs have changed. Returns a non-null
199 * {@link Token} if the action needs to be executed, and null otherwise.
200 *
201 * <p>If this method returns non-null, indicating that the action will be executed, the
Janak Ramakrishnan73055be2015-04-13 18:32:49 +0000202 * metadataHandler's {@link MetadataHandler#discardOutputMetadata} method must be called, so that
203 * it does not serve stale metadata for the action's outputs after the action is executed.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100204 */
205 // Note: the handler should only be used for DEPCHECKER events; there's no
206 // guarantee it will be available for other events.
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000207 public Token getTokenIfNeedToExecute(
208 Action action,
209 Iterable<Artifact> resolvedCacheArtifacts,
210 Map<String, String> clientEnv,
211 EventHandler handler,
212 MetadataHandler metadataHandler) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100213 // TODO(bazel-team): (2010) For RunfilesAction/SymlinkAction and similar actions that
214 // produce only symlinks we should not check whether inputs are valid at all - all that matters
215 // that inputs and outputs are still exist (and new inputs have not appeared). All other checks
216 // are unnecessary. In other words, the only metadata we should check for them is file existence
217 // itself.
218
219 MiddlemanType middlemanType = action.getActionType();
220 if (middlemanType.isMiddleman()) {
221 // Some types of middlemen are not checked because they should not
222 // propagate invalidation of their inputs.
223 if (middlemanType != MiddlemanType.ERROR_PROPAGATING_MIDDLEMAN) {
224 checkMiddlemanAction(action, handler, metadataHandler);
225 }
226 return null;
227 }
Googler2f111922017-02-28 20:58:45 +0000228 if (!cacheConfig.enabled()) {
229 return new Token(getKeyString(action));
230 }
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000231 Iterable<Artifact> actionInputs = action.getInputs();
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000232 // Resolve action inputs from cache, if necessary.
Lukacs Berki5ea2b142017-02-28 10:46:53 +0000233 boolean inputsDiscovered = action.inputsDiscovered();
234 if (!inputsDiscovered && resolvedCacheArtifacts != null) {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000235 // The action doesn't know its inputs, but the caller has a good idea of what they are.
236 Preconditions.checkState(action.discoversInputs(),
237 "Actions that don't know their inputs must discover them: %s", action);
238 actionInputs = resolvedCacheArtifacts;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100239 }
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000240 ActionCache.Entry entry = getCacheEntry(action);
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000241 if (mustExecute(action, entry, handler, metadataHandler, actionInputs, clientEnv)) {
Janak Ramakrishnane3f04b82015-03-27 16:45:34 +0000242 if (entry != null) {
243 removeCacheEntry(action);
244 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100245 return new Token(getKeyString(action));
246 }
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000247
Lukacs Berki5ea2b142017-02-28 10:46:53 +0000248 if (!inputsDiscovered) {
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000249 action.updateInputs(actionInputs);
250 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100251 return null;
252 }
253
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000254 protected boolean mustExecute(
255 Action action,
256 @Nullable ActionCache.Entry entry,
257 EventHandler handler,
258 MetadataHandler metadataHandler,
259 Iterable<Artifact> actionInputs,
260 Map<String, String> clientEnv) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100261 // Unconditional execution can be applied only for actions that are allowed to be executed.
262 if (unconditionalExecution(action)) {
263 Preconditions.checkState(action.isVolatile());
264 reportUnconditionalExecution(handler, action);
265 return true; // must execute - unconditional execution is requested.
266 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100267 if (entry == null) {
268 reportNewAction(handler, action);
269 return true; // must execute -- no cache entry (e.g. first build)
270 }
271
272 if (entry.isCorrupted()) {
273 reportCorruptedCacheEntry(handler, action);
274 return true; // cache entry is corrupted - must execute
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000275 } else if (validateArtifacts(entry, action, actionInputs, metadataHandler, true)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100276 reportChanged(handler, action);
277 return true; // files have changed
Eric Fellheimer1a34a242016-01-21 19:17:19 +0000278 } else if (!entry.getActionKey().equals(action.getKey())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100279 reportCommand(handler, action);
280 return true; // must execute -- action key is different
281 }
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000282 Map<String, String> usedClientEnv = computeUsedClientEnv(action, clientEnv);
283 if (!entry.getUsedClientEnvDigest().equals(DigestUtils.fromEnv(usedClientEnv))) {
284 reportClientEnv(handler, action, usedClientEnv);
285 return true; // different values taken from the environment -- must execute
286 }
287
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100288
289 entry.getFileDigest();
290 return false; // cache hit
291 }
292
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000293 public void afterExecution(
294 Action action, Token token, MetadataHandler metadataHandler, Map<String, String> clientEnv)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100295 throws IOException {
Googler2f111922017-02-28 20:58:45 +0000296 if (!cacheConfig.enabled()) {
297 // Action cache is disabled, don't generate digests.
298 return;
299 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100300 Preconditions.checkArgument(token != null);
301 String key = token.cacheKey;
Janak Ramakrishnane3f04b82015-03-27 16:45:34 +0000302 if (actionCache.get(key) != null) {
303 // This cache entry has already been updated by a shared action. We don't need to do it again.
304 return;
305 }
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000306 Map<String, String> usedClientEnv = computeUsedClientEnv(action, clientEnv);
307 ActionCache.Entry entry =
Googler2f111922017-02-28 20:58:45 +0000308 new ActionCache.Entry(action.getKey(), usedClientEnv, action.discoversInputs());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100309 for (Artifact output : action.getOutputs()) {
310 // Remove old records from the cache if they used different key.
311 String execPath = output.getExecPathString();
312 if (!key.equals(execPath)) {
Philipp Wollermanne03758b2015-06-15 08:10:47 +0000313 actionCache.remove(execPath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100314 }
Michajlo Matijkiw13459b42015-03-12 19:43:20 +0000315 if (!metadataHandler.artifactOmitted(output)) {
316 // Output files *must* exist and be accessible after successful action execution.
317 Metadata metadata = metadataHandler.getMetadata(output);
318 Preconditions.checkState(metadata != null);
319 entry.addFile(output.getExecPath(), metadata);
320 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100321 }
322 for (Artifact input : action.getInputs()) {
323 entry.addFile(input.getExecPath(), metadataHandler.getMetadataMaybe(input));
324 }
325 entry.getFileDigest();
326 actionCache.put(key, entry);
327 }
328
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000329 @Nullable
Michajlo Matijkiw331633c2015-06-09 22:09:03 +0000330 public Iterable<Artifact> getCachedInputs(Action action, PackageRootResolver resolver)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000331 throws PackageRootResolutionException, InterruptedException {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000332 ActionCache.Entry entry = getCacheEntry(action);
333 if (entry == null || entry.isCorrupted()) {
334 return ImmutableList.of();
335 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100336
337 List<PathFragment> outputs = new ArrayList<>();
338 for (Artifact output : action.getOutputs()) {
339 outputs.add(output.getExecPath());
340 }
Lukacs Berki1f2caa52017-02-02 10:47:07 +0000341 List<PathFragment> inputExecPaths = new ArrayList<>();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100342 for (String path : entry.getPaths()) {
343 PathFragment execPath = new PathFragment(path);
344 // Code assumes that action has only 1-2 outputs and ArrayList.contains() will be
345 // most efficient.
346 if (!outputs.contains(execPath)) {
Lukacs Berki1f2caa52017-02-02 10:47:07 +0000347 inputExecPaths.add(execPath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100348 }
349 }
Lukacs Berki1f2caa52017-02-02 10:47:07 +0000350
351 // Note that this method may trigger a violation of the desirable invariant that getInputs()
352 // is a superset of getMandatoryInputs(). See bug about an "action not in canonical form"
353 // error message and the integration test test_crosstool_change_and_failure().
354 Map<PathFragment, Artifact> allowedDerivedInputsMap = new HashMap<>();
355 for (Artifact derivedInput : action.getAllowedDerivedInputs()) {
356 if (!derivedInput.isSourceArtifact()) {
357 allowedDerivedInputsMap.put(derivedInput.getExecPath(), derivedInput);
358 }
359 }
360
361 List<Artifact> inputArtifacts = new ArrayList<>();
362 List<PathFragment> unresolvedPaths = new ArrayList<>();
363 for (PathFragment execPath : inputExecPaths) {
364 Artifact artifact = allowedDerivedInputsMap.get(execPath);
365 if (artifact != null) {
366 inputArtifacts.add(artifact);
367 } else {
368 // Remember this execPath, we will try to resolve it as a source artifact.
369 unresolvedPaths.add(execPath);
370 }
371 }
372
373 Map<PathFragment, Artifact> resolvedArtifacts =
374 artifactResolver.resolveSourceArtifacts(unresolvedPaths, resolver);
375 if (resolvedArtifacts == null) {
376 // We are missing some dependencies. We need to rerun this update later.
377 return null;
378 }
379
380 for (PathFragment execPath : unresolvedPaths) {
381 Artifact artifact = resolvedArtifacts.get(execPath);
382 // If PathFragment cannot be resolved into the artifact, ignore it. This could happen if the
383 // rule has changed and the action no longer depends on, e.g., an additional source file in a
384 // separate package and that package is no longer referenced anywhere else. It is safe to
385 // ignore such paths because dependency checker would identify changes in inputs (ignored path
386 // was used before) and will force action execution.
387 if (artifact != null) {
388 inputArtifacts.add(artifact);
389 }
390 }
391 return inputArtifacts;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100392 }
393
394 /**
395 * Special handling for the MiddlemanAction. Since MiddlemanAction output
396 * artifacts are purely fictional and used only to stay within dependency
397 * graph model limitations (action has to depend on artifacts, not on other
398 * actions), we do not need to validate metadata for the outputs - only for
399 * inputs. We also do not need to validate MiddlemanAction key, since action
400 * cache entry key already incorporates that information for the middlemen
401 * and we will experience a cache miss when it is different. Whenever it
402 * encounters middleman artifacts as input artifacts for other actions, it
403 * consults with the aggregated middleman digest computed here.
404 */
405 protected void checkMiddlemanAction(Action action, EventHandler handler,
406 MetadataHandler metadataHandler) {
Googler2f111922017-02-28 20:58:45 +0000407 if (!cacheConfig.enabled()) {
408 // Action cache is disabled, don't generate digests.
409 return;
410 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100411 Artifact middleman = action.getPrimaryOutput();
412 String cacheKey = middleman.getExecPathString();
413 ActionCache.Entry entry = actionCache.get(cacheKey);
414 boolean changed = false;
415 if (entry != null) {
416 if (entry.isCorrupted()) {
417 reportCorruptedCacheEntry(handler, action);
418 changed = true;
Janak Ramakrishnana7c84b52015-03-18 21:59:16 +0000419 } else if (validateArtifacts(entry, action, action.getInputs(), metadataHandler, false)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100420 reportChanged(handler, action);
421 changed = true;
422 }
423 } else {
424 reportChangedDeps(handler, action);
425 changed = true;
426 }
427 if (changed) {
428 // Compute the aggregated middleman digest.
429 // Since we never validate action key for middlemen, we should not store
430 // it in the cache entry and just use empty string instead.
Googler2f111922017-02-28 20:58:45 +0000431 entry = new ActionCache.Entry("", ImmutableMap.<String, String>of(), false);
432 for (Artifact input : action.getInputs()) {
433 entry.addFile(input.getExecPath(), metadataHandler.getMetadataMaybe(input));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100434 }
435 }
436
437 metadataHandler.setDigestForVirtualArtifact(middleman, entry.getFileDigest());
438 if (changed) {
439 actionCache.put(cacheKey, entry);
440 }
441 }
442
443 /**
444 * Returns an action key. It is always set to the first output exec path string.
445 */
446 private static String getKeyString(Action action) {
447 Preconditions.checkState(!action.getOutputs().isEmpty());
448 return action.getOutputs().iterator().next().getExecPathString();
449 }
450
451
452 /**
453 * In most cases, this method should not be called directly - reportXXX() methods
454 * should be used instead. This is done to avoid cost associated with building
455 * the message.
456 */
457 private static void reportRebuild(@Nullable EventHandler handler, Action action, String message) {
458 // For MiddlemanAction, do not report rebuild.
459 if (handler != null && !action.getActionType().isMiddleman()) {
Ulf Adams760e7092016-04-21 08:09:51 +0000460 handler.handle(Event.of(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100461 EventKind.DEPCHECKER, null, "Executing " + action.prettyPrint() + ": " + message + "."));
462 }
463 }
464
465 // Called by IncrementalDependencyChecker.
466 protected static void reportUnconditionalExecution(
467 @Nullable EventHandler handler, Action action) {
468 reportRebuild(handler, action, "unconditional execution is requested");
469 }
470
471 private static void reportChanged(@Nullable EventHandler handler, Action action) {
472 reportRebuild(handler, action, "One of the files has changed");
473 }
474
475 private static void reportChangedDeps(@Nullable EventHandler handler, Action action) {
476 reportRebuild(handler, action, "the set of files on which this action depends has changed");
477 }
478
479 private static void reportNewAction(@Nullable EventHandler handler, Action action) {
480 reportRebuild(handler, action, "no entry in the cache (action is new)");
481 }
482
483 private static void reportCorruptedCacheEntry(@Nullable EventHandler handler, Action action) {
484 reportRebuild(handler, action, "cache entry is corrupted");
485 }
486
487 /** Wrapper for all context needed by the ActionCacheChecker to handle a single action. */
488 public static final class Token {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100489 private final String cacheKey;
490
491 private Token(String cacheKey) {
492 this.cacheKey = Preconditions.checkNotNull(cacheKey);
493 }
494 }
495}