blob: 77f7254639b53f8fa8a5d249f4acfea940ef2426 [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.skyframe;
15
16import com.google.common.base.Function;
tomlua155b532017-11-08 20:12:47 +010017import com.google.common.base.Preconditions;
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +000018import com.google.common.base.Predicates;
Googlerece75722016-02-11 17:55:41 +000019import com.google.common.collect.ImmutableList;
Rumou Duana77f32c2016-04-13 21:59:21 +000020import com.google.common.collect.ImmutableSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.common.collect.Iterables;
Janak Ramakrishnan41fad442015-03-20 17:24:45 +000022import com.google.common.collect.Maps;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import com.google.common.collect.Sets;
24import com.google.devtools.build.lib.actions.Action;
25import com.google.devtools.build.lib.actions.ActionCacheChecker.Token;
26import com.google.devtools.build.lib.actions.ActionExecutionContext;
27import com.google.devtools.build.lib.actions.ActionExecutionException;
janakr93e3eea2017-03-30 22:09:37 +000028import com.google.devtools.build.lib.actions.ActionLookupData;
29import com.google.devtools.build.lib.actions.ActionLookupValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.devtools.build.lib.actions.AlreadyReportedActionExecutionException;
31import com.google.devtools.build.lib.actions.Artifact;
32import com.google.devtools.build.lib.actions.MissingInputFileException;
33import com.google.devtools.build.lib.actions.NotifyOnActionCacheHit;
Marian Lobur4e0f8562015-02-11 08:49:36 +000034import com.google.devtools.build.lib.actions.PackageRootResolver;
Klaus Aehligd1f4a162016-10-25 14:51:55 +000035import com.google.devtools.build.lib.causes.Cause;
36import com.google.devtools.build.lib.causes.LabelCause;
philwo3bcb9f62017-09-06 12:52:21 +020037import com.google.devtools.build.lib.clock.BlazeClock;
Ulf Adamsa28b5402017-02-24 09:28:44 +000038import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
Kristina Chodorow73fa2032015-08-28 17:57:46 +000039import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
41import com.google.devtools.build.lib.events.Event;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import com.google.devtools.build.lib.util.Pair;
43import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
Marian Lobur4e0f8562015-02-11 08:49:36 +000044import com.google.devtools.build.lib.vfs.PathFragment;
janakr7f15b682018-04-11 14:55:28 -070045import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010046import com.google.devtools.build.skyframe.SkyFunction;
47import com.google.devtools.build.skyframe.SkyFunctionException;
48import com.google.devtools.build.skyframe.SkyKey;
49import com.google.devtools.build.skyframe.SkyValue;
50import com.google.devtools.build.skyframe.ValueOrException2;
Han-Wen Nienhuys2c41b592015-02-18 16:59:12 +000051import java.io.IOException;
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +000052import java.util.ArrayList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053import java.util.Collection;
54import java.util.Collections;
55import java.util.HashMap;
56import java.util.HashSet;
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +000057import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010058import java.util.Map;
59import java.util.Set;
Janak Ramakrishnan41fad442015-03-20 17:24:45 +000060import java.util.concurrent.ConcurrentMap;
Ulf Adamsc73051c62016-03-23 09:18:13 +000061import java.util.concurrent.atomic.AtomicReference;
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +000062import javax.annotation.Nullable;
63
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064/**
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +000065 * A {@link SkyFunction} that creates {@link ActionExecutionValue}s. There are four points where
66 * this function can abort due to missing values in the graph:
67 * <ol>
68 * <li>For actions that discover inputs, if missing metadata needed to resolve an artifact from a
69 * string input in the action cache.</li>
70 * <li>If missing metadata for artifacts in inputs (including the artifacts above).</li>
71 * <li>For actions that discover inputs, if missing metadata for inputs discovered prior to
72 * execution.</li>
73 * <li>For actions that discover inputs, but do so during execution, if missing metadata for
74 * inputs discovered during execution.</li>
75 * </ol>
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010076 */
Janak Ramakrishnan41fad442015-03-20 17:24:45 +000077public class ActionExecutionFunction implements SkyFunction, CompletionReceiver {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010078 private final SkyframeActionExecutor skyframeActionExecutor;
Ulf Adamsc73051c62016-03-23 09:18:13 +000079 private final AtomicReference<TimestampGranularityMonitor> tsgm;
Janak Ramakrishnan41fad442015-03-20 17:24:45 +000080 private ConcurrentMap<Action, ContinuationState> stateMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010081
82 public ActionExecutionFunction(SkyframeActionExecutor skyframeActionExecutor,
Ulf Adamsc73051c62016-03-23 09:18:13 +000083 AtomicReference<TimestampGranularityMonitor> tsgm) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010084 this.skyframeActionExecutor = skyframeActionExecutor;
85 this.tsgm = tsgm;
Janak Ramakrishnan41fad442015-03-20 17:24:45 +000086 stateMap = Maps.newConcurrentMap();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087 }
88
89 @Override
90 public SkyValue compute(SkyKey skyKey, Environment env) throws ActionExecutionFunctionException,
91 InterruptedException {
janakr93e3eea2017-03-30 22:09:37 +000092 ActionLookupData actionLookupData = (ActionLookupData) skyKey.argument();
93 ActionLookupValue actionLookupValue =
janakrbaf52ae2018-02-14 09:03:18 -080094 (ActionLookupValue) env.getValue(actionLookupData.getActionLookupKey());
janakr93e3eea2017-03-30 22:09:37 +000095 int actionIndex = actionLookupData.getActionIndex();
96 Action action = actionLookupValue.getAction(actionIndex);
97 skyframeActionExecutor.noteActionEvaluationStarted(actionLookupData, action);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010098 // TODO(bazel-team): Non-volatile NotifyOnActionCacheHit actions perform worse in Skyframe than
99 // legacy when they are not at the top of the action graph. In legacy, they are stored
100 // separately, so notifying non-dirty actions is cheap. In Skyframe, they depend on the
101 // BUILD_ID, forcing invalidation of upward transitive closure on each build.
Laszlo Csomord4d99932015-07-10 08:36:11 +0000102 if ((action.isVolatile() && !(action instanceof SkyframeAwareAction))
103 || action instanceof NotifyOnActionCacheHit) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100104 // Volatile build actions may need to execute even if none of their known inputs have changed.
105 // Depending on the buildID ensure that these actions have a chance to execute.
106 PrecomputedValue.BUILD_ID.get(env);
107 }
Klaus Aehligcd708c32016-09-15 13:48:47 +0000108
109 // Look up the parts of the environment that influence the action.
110 Map<SkyKey, SkyValue> clientEnvLookup =
Benjamin Petersona7504682018-03-15 02:12:11 -0700111 env.getValues(
112 Iterables.transform(
113 action.getClientEnvironmentVariables(), ClientEnvironmentFunction::key));
Klaus Aehligcd708c32016-09-15 13:48:47 +0000114 if (env.valuesMissing()) {
115 return null;
116 }
117 Map<String, String> clientEnv = new HashMap<>();
jcatercecb3a82018-05-01 14:37:48 -0700118 for (Map.Entry<SkyKey, SkyValue> entry : clientEnvLookup.entrySet()) {
Klaus Aehligcd708c32016-09-15 13:48:47 +0000119 ClientEnvironmentValue envValue = (ClientEnvironmentValue) entry.getValue();
120 if (envValue.getValue() != null) {
121 clientEnv.put((String) entry.getKey().argument(), envValue.getValue());
122 }
123 }
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000124
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000125 // For restarts of this ActionExecutionFunction we use a ContinuationState variable, below, to
126 // avoid redoing work. However, if two actions are shared and the first one executes, when the
127 // second one goes to execute, we should detect that and short-circuit, even without taking
128 // ContinuationState into account.
129 boolean sharedActionAlreadyRan = skyframeActionExecutor.probeActionExecution(action);
130 ContinuationState state;
131 if (action.discoversInputs()) {
132 state = getState(action);
133 } else {
134 // Because this is a new state, all conditionals below about whether state has already done
135 // something will return false, and so we will execute all necessary steps.
136 state = new ContinuationState();
137 }
138 if (!state.hasCollectedInputs()) {
139 state.allInputs = collectInputs(action, env);
140 if (state.allInputs == null) {
141 // Missing deps.
142 return null;
143 }
144 } else if (state.allInputs.keysRequested != null) {
145 // Preserve the invariant that we ask for the same deps each build.
146 env.getValues(state.allInputs.keysRequested);
147 Preconditions.checkState(!env.valuesMissing(), "%s %s", action, state);
148 }
Rumou Duana77f32c2016-04-13 21:59:21 +0000149 Pair<Map<Artifact, FileArtifactValue>, Map<Artifact, Collection<Artifact>>> checkedInputs =
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000150 null;
151 try {
152 // Declare deps on known inputs to action. We do this unconditionally to maintain our
153 // invariant of asking for the same deps each build.
154 Map<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> inputDeps
155 = env.getValuesOrThrow(toKeys(state.allInputs.getAllInputs(),
Googlerece75722016-02-11 17:55:41 +0000156 action.discoversInputs() ? action.getMandatoryInputs() : null),
157 MissingInputFileException.class, ActionExecutionException.class);
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000158
159 if (!sharedActionAlreadyRan && !state.hasArtifactData()) {
160 // Do we actually need to find our metadata?
161 checkedInputs = checkInputs(env, action, inputDeps);
162 }
163 } catch (ActionExecutionException e) {
164 // Remove action from state map in case it's there (won't be unless it discovers inputs).
165 stateMap.remove(action);
166 throw new ActionExecutionFunctionException(e);
167 }
Laszlo Csomord4d99932015-07-10 08:36:11 +0000168
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100169 if (env.valuesMissing()) {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000170 // There was missing artifact metadata in the graph. Wait for it to be present.
Laszlo Csomord4d99932015-07-10 08:36:11 +0000171 // We must check this and return here before attempting to establish any Skyframe dependencies
172 // of the action; see establishSkyframeDependencies why.
173 return null;
174 }
175
176 try {
177 establishSkyframeDependencies(env, action);
178 } catch (ActionExecutionException e) {
179 throw new ActionExecutionFunctionException(e);
180 }
181 if (env.valuesMissing()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100182 return null;
183 }
184
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000185 if (checkedInputs != null) {
186 Preconditions.checkState(!state.hasArtifactData(), "%s %s", state, action);
187 state.inputArtifactData = checkedInputs.first;
Michael Thvedt434e68e2016-02-09 00:57:46 +0000188 state.expandedArtifacts = checkedInputs.second;
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000189 }
190
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191 ActionExecutionValue result;
192 try {
hlopko8b337842018-04-24 04:07:33 -0700193 result =
194 checkCacheAndExecuteIfNeeded(
195 action, state, env, clientEnv, actionLookupData, sharedActionAlreadyRan);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100196 } catch (ActionExecutionException e) {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000197 // Remove action from state map in case it's there (won't be unless it discovers inputs).
198 stateMap.remove(action);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100199 // In this case we do not report the error to the action reporter because we have already
200 // done it in SkyframeExecutor.reportErrorIfNotAbortingMode() method. That method
201 // prints the error in the top-level reporter and also dumps the recorded StdErr for the
202 // action. Label can be null in the case of, e.g., the SystemActionOwner (for build-info.txt).
203 throw new ActionExecutionFunctionException(new AlreadyReportedActionExecutionException(e));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100204 }
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000205
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100206 if (env.valuesMissing()) {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000207 Preconditions.checkState(stateMap.containsKey(action), action);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100208 return null;
209 }
210
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000211 // Remove action from state map in case it's there (won't be unless it discovers inputs).
212 stateMap.remove(action);
janakr93e3eea2017-03-30 22:09:37 +0000213 actionLookupValue.actionEvaluated(actionIndex, action);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100214 return result;
215 }
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000216
217 /**
218 * An action's inputs needed for execution. May not just be the result of Action#getInputs(). If
219 * the action cache's view of this action contains additional inputs, it will request metadata for
220 * them, so we consider those inputs as dependencies of this action as well. Returns null if some
221 * dependencies were missing and this ActionExecutionFunction needs to restart.
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000222 *
Michajlo Matijkiw331633c2015-06-09 22:09:03 +0000223 * @throws ActionExecutionFunctionException
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000224 */
225 @Nullable
Michajlo Matijkiw331633c2015-06-09 22:09:03 +0000226 private AllInputs collectInputs(Action action, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000227 throws ActionExecutionFunctionException, InterruptedException {
Michajlo Matijkiwdee781c2015-05-22 23:25:34 +0000228 Iterable<Artifact> allKnownInputs = Iterables.concat(
229 action.getInputs(), action.getRunfilesSupplier().getArtifacts());
Lukacs Berki5ea2b142017-02-28 10:46:53 +0000230 if (action.inputsDiscovered()) {
Michajlo Matijkiwdee781c2015-05-22 23:25:34 +0000231 return new AllInputs(allKnownInputs);
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000232 }
Michajlo Matijkiwdee781c2015-05-22 23:25:34 +0000233
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000234 Preconditions.checkState(action.discoversInputs(), action);
235 PackageRootResolverWithEnvironment resolver = new PackageRootResolverWithEnvironment(env);
ulfjack8989e192017-04-20 13:45:25 +0200236 Iterable<Artifact> actionCacheInputs =
237 skyframeActionExecutor.getActionCachedInputs(action, resolver);
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000238 if (actionCacheInputs == null) {
239 Preconditions.checkState(env.valuesMissing(), action);
240 return null;
241 }
Michajlo Matijkiwdee781c2015-05-22 23:25:34 +0000242 return new AllInputs(allKnownInputs, actionCacheInputs, resolver.keysRequested);
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000243 }
244
245 private static class AllInputs {
246 final Iterable<Artifact> defaultInputs;
247 @Nullable
248 final Iterable<Artifact> actionCacheInputs;
249 @Nullable
250 final List<SkyKey> keysRequested;
251
252 AllInputs(Iterable<Artifact> defaultInputs) {
253 this.defaultInputs = Preconditions.checkNotNull(defaultInputs);
254 this.actionCacheInputs = null;
255 this.keysRequested = null;
256 }
257
258 AllInputs(Iterable<Artifact> defaultInputs, Iterable<Artifact> actionCacheInputs,
259 List<SkyKey> keysRequested) {
260 this.defaultInputs = Preconditions.checkNotNull(defaultInputs);
261 this.actionCacheInputs = Preconditions.checkNotNull(actionCacheInputs);
262 this.keysRequested = keysRequested;
263 }
264
265 Iterable<Artifact> getAllInputs() {
266 return actionCacheInputs == null
267 ? defaultInputs
268 : Iterables.concat(defaultInputs, actionCacheInputs);
269 }
270 }
271
Marian Lobur4e0f8562015-02-11 08:49:36 +0000272 /**
273 * Skyframe implementation of {@link PackageRootResolver}. Should be used only from SkyFunctions,
274 * because it uses SkyFunction.Environment for evaluation of ContainingPackageLookupValue.
275 */
276 private static class PackageRootResolverWithEnvironment implements PackageRootResolver {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000277 final List<SkyKey> keysRequested = new ArrayList<>();
Marian Lobur4e0f8562015-02-11 08:49:36 +0000278 private final Environment env;
279
280 public PackageRootResolverWithEnvironment(Environment env) {
281 this.env = env;
282 }
283
284 @Override
janakr7f15b682018-04-11 14:55:28 -0700285 public Map<PathFragment, Root> findPackageRootsForFiles(Iterable<PathFragment> execPaths)
286 throws InterruptedException {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000287 Preconditions.checkState(keysRequested.isEmpty(),
288 "resolver should only be called once: %s %s", keysRequested, execPaths);
Marian Lobur4e0f8562015-02-11 08:49:36 +0000289 // Create SkyKeys list based on execPaths.
Michajlo Matijkiw331633c2015-06-09 22:09:03 +0000290 Map<PathFragment, SkyKey> depKeys = new HashMap<>();
Marian Lobur4e0f8562015-02-11 08:49:36 +0000291 for (PathFragment path : execPaths) {
Janak Ramakrishnan659f80e2015-11-12 23:16:17 +0000292 PathFragment parent = Preconditions.checkNotNull(
293 path.getParentDirectory(), "Must pass in files, not root directory");
294 Preconditions.checkArgument(!parent.isAbsolute(), path);
Ulf Adamsa28b5402017-02-24 09:28:44 +0000295 try {
296 SkyKey depKey =
297 ContainingPackageLookupValue.key(PackageIdentifier.discoverFromExecPath(path, true));
298 depKeys.put(path, depKey);
299 keysRequested.add(depKey);
300 } catch (LabelSyntaxException e) {
ulfjack8989e192017-04-20 13:45:25 +0200301 // This code is only used to do action cache checks. If one of the file names we got from
302 // the action cache is corrupted, or if the action cache is from a different Bazel
303 // binary, then the path may not be valid for this Bazel binary, and trigger this
304 // exception. In that case, it's acceptable for us to ignore the exception - we'll get an
305 // action cache miss and re-execute the action, which is what we should do.
306 continue;
Ulf Adamsa28b5402017-02-24 09:28:44 +0000307 }
Marian Lobur4e0f8562015-02-11 08:49:36 +0000308 }
Michajlo Matijkiw331633c2015-06-09 22:09:03 +0000309
ulfjack8989e192017-04-20 13:45:25 +0200310 Map<SkyKey, SkyValue> values = env.getValues(depKeys.values());
311 if (env.valuesMissing()) {
312 return null;
313 }
Michajlo Matijkiw331633c2015-06-09 22:09:03 +0000314
janakr7f15b682018-04-11 14:55:28 -0700315 Map<PathFragment, Root> result = new HashMap<>();
Marian Lobur4e0f8562015-02-11 08:49:36 +0000316 for (PathFragment path : execPaths) {
ulfjack8989e192017-04-20 13:45:25 +0200317 if (!depKeys.containsKey(path)) {
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +0000318 continue;
319 }
ulfjack8989e192017-04-20 13:45:25 +0200320 ContainingPackageLookupValue value =
321 (ContainingPackageLookupValue) values.get(depKeys.get(path));
Marian Lobur4e0f8562015-02-11 08:49:36 +0000322 if (value.hasContainingPackage()) {
323 // We have found corresponding root for current execPath.
tomlu1cdcdf92018-01-16 11:07:51 -0800324 result.put(
325 path,
janakr7f15b682018-04-11 14:55:28 -0700326 SkyframeExecutor.maybeTransformRootForRepository(
Ulf Adamsa28b5402017-02-24 09:28:44 +0000327 value.getContainingPackageRoot(),
328 value.getContainingPackageName().getRepository()));
Marian Lobur4e0f8562015-02-11 08:49:36 +0000329 } else {
330 // We haven't found corresponding root for current execPath.
331 result.put(path, null);
332 }
333 }
ulfjack8989e192017-04-20 13:45:25 +0200334 return result;
Marian Lobur4e0f8562015-02-11 08:49:36 +0000335 }
336 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100337
338 private ActionExecutionValue checkCacheAndExecuteIfNeeded(
janakr93e3eea2017-03-30 22:09:37 +0000339 Action action,
340 ContinuationState state,
341 Environment env,
342 Map<String, String> clientEnv,
hlopko8b337842018-04-24 04:07:33 -0700343 ActionLookupData actionLookupData,
344 boolean sharedActionAlreadyRan)
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000345 throws ActionExecutionException, InterruptedException {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000346 // If this is a shared action and the other action is the one that executed, we must use that
347 // other action's value, provided here, since it is populated with metadata for the outputs.
hlopko8b337842018-04-24 04:07:33 -0700348 if (sharedActionAlreadyRan) {
349 return skyframeActionExecutor.executeAction(
350 env.getListener(),
351 action,
352 /* metadataHandler= */ null,
353 /* actionStartTime= */ -1,
354 /* actionExecutionContext= */ null,
355 actionLookupData,
356 /* inputDiscoveryRan= */ false);
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000357 }
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000358 // This may be recreated if we discover inputs.
Janak Ramakrishnana5c1f962015-04-03 23:06:31 +0000359 ActionMetadataHandler metadataHandler = new ActionMetadataHandler(state.inputArtifactData,
Ulf Adamsc73051c62016-03-23 09:18:13 +0000360 action.getOutputs(), tsgm.get());
Lukacs Berki536d9cb2017-01-03 14:25:25 +0000361 long actionStartTime = BlazeClock.nanoTime();
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000362 // We only need to check the action cache if we haven't done it on a previous run.
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000363 if (!state.hasCheckedActionCache()) {
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000364 state.token =
365 skyframeActionExecutor.checkActionCache(
ulfjack225b57b2018-01-16 12:44:15 -0800366 env.getListener(),
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000367 action,
368 metadataHandler,
369 actionStartTime,
370 state.allInputs.actionCacheInputs,
371 clientEnv);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100372 }
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000373
374 if (state.token == null) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100375 // We got a hit from the action cache -- no need to execute.
376 return new ActionExecutionValue(
Rumou Duana77f32c2016-04-13 21:59:21 +0000377 metadataHandler.getOutputArtifactData(),
Googlerece75722016-02-11 17:55:41 +0000378 metadataHandler.getOutputTreeArtifactData(),
Janak Ramakrishnana5c1f962015-04-03 23:06:31 +0000379 metadataHandler.getAdditionalOutputData());
Han-Wen Nienhuys2c41b592015-02-18 16:59:12 +0000380 }
381
ulfjack1a328e32017-04-06 10:25:16 +0000382 // Delete the metadataHandler's cache of the action's outputs, since they are being deleted.
383 metadataHandler.discardOutputMetadata();
384
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000385 // This may be recreated if we discover inputs.
janakrd8a57532017-10-10 08:31:32 +0200386 PerActionFileCache perActionFileCache =
387 new PerActionFileCache(
388 state.inputArtifactData, /*missingArtifactsAllowed=*/ action.discoversInputs());
ulfjack1a328e32017-04-06 10:25:16 +0000389 if (action.discoversInputs()) {
390 if (state.discoveredInputs == null) {
391 try {
392 state.discoveredInputs = skyframeActionExecutor.discoverInputs(action,
393 perActionFileCache, metadataHandler, env);
394 Preconditions.checkState(state.discoveredInputs != null,
395 "discoverInputs() returned null on action %s", action);
396 } catch (MissingDepException e) {
397 Preconditions.checkState(env.valuesMissing(), action);
398 return null;
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000399 }
ulfjack1a328e32017-04-06 10:25:16 +0000400 }
401 addDiscoveredInputs(
402 state.inputArtifactData, state.expandedArtifacts, state.discoveredInputs, env);
403 if (env.valuesMissing()) {
404 return null;
405 }
janakrd8a57532017-10-10 08:31:32 +0200406 perActionFileCache =
407 new PerActionFileCache(state.inputArtifactData, /*missingArtifactsAllowed=*/ false);
ulfjack1a328e32017-04-06 10:25:16 +0000408
409 // Stage 1 finished, let's do stage 2. The stage 1 of input discovery will have added some
410 // files with addDiscoveredInputs() and then have waited for those files to be available
411 // by returning null if env.valuesMissing() returned true. So stage 2 can now access those
412 // inputs to discover even more inputs and then potentially also wait for those to be
413 // available.
414 if (state.discoveredInputsStage2 == null) {
415 state.discoveredInputsStage2 = action.discoverInputsStage2(env);
416 }
417 if (state.discoveredInputsStage2 != null) {
Googlereba6a442017-02-06 20:08:48 +0000418 addDiscoveredInputs(
ulfjack1a328e32017-04-06 10:25:16 +0000419 state.inputArtifactData, state.expandedArtifacts, state.discoveredInputsStage2, env);
Lukacs Berki41ab6db2017-01-31 10:48:31 +0000420 if (env.valuesMissing()) {
421 return null;
422 }
janakrd8a57532017-10-10 08:31:32 +0200423 perActionFileCache =
424 new PerActionFileCache(state.inputArtifactData, /*missingArtifactsAllowed=*/ false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100425 }
ulfjack1a328e32017-04-06 10:25:16 +0000426 metadataHandler =
427 new ActionMetadataHandler(state.inputArtifactData, action.getOutputs(), tsgm.get());
428 // Set the MetadataHandler to accept output information.
429 metadataHandler.discardOutputMetadata();
430 }
Lukacs Berki41ab6db2017-01-31 10:48:31 +0000431
ulfjack1a328e32017-04-06 10:25:16 +0000432 try (ActionExecutionContext actionExecutionContext =
433 skyframeActionExecutor.getContext(
434 perActionFileCache,
435 metadataHandler,
436 Collections.unmodifiableMap(state.expandedArtifacts))) {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000437 if (!state.hasExecutedAction()) {
janakr93e3eea2017-03-30 22:09:37 +0000438 state.value =
439 skyframeActionExecutor.executeAction(
hlopko8b337842018-04-24 04:07:33 -0700440 env.getListener(),
441 action,
442 metadataHandler,
443 actionStartTime,
444 actionExecutionContext,
445 actionLookupData,
446 /* inputDiscoveryRan= */ true);
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000447 }
ulfjack1a328e32017-04-06 10:25:16 +0000448 } catch (IOException e) {
449 throw new ActionExecutionException(
450 "Failed to close action output", e, action, /*catastrophe=*/ false);
Janak Ramakrishnan7a1db6e2015-06-30 19:56:11 +0000451 }
ulfjack1a328e32017-04-06 10:25:16 +0000452
Janak Ramakrishnane905ec32015-06-30 20:16:32 +0000453 if (action.discoversInputs()) {
Janak Ramakrishnane5060fd2016-08-19 17:26:24 +0000454 Iterable<Artifact> newInputs =
455 filterKnownInputs(action.getInputs(), state.inputArtifactData.keySet());
Michajlo Matijkiw9d4af3b2016-07-28 20:01:49 +0000456 Map<SkyKey, SkyValue> metadataFoundDuringActionExecution =
Janak Ramakrishnane5060fd2016-08-19 17:26:24 +0000457 env.getValues(toKeys(newInputs, action.getMandatoryInputs()));
Lukacs Berki41ab6db2017-01-31 10:48:31 +0000458 state.discoveredInputs = newInputs;
459 if (env.valuesMissing()) {
460 return null;
461 }
462 if (!Iterables.isEmpty(newInputs)) {
463 // We are in the interesting case of an action that discovered its inputs during
464 // execution, and found some new ones, but the new ones were already present in the graph.
465 // We must therefore cache the metadata for those new ones.
466 Map<Artifact, FileArtifactValue> inputArtifactData = new HashMap<>();
467 inputArtifactData.putAll(state.inputArtifactData);
468 for (Map.Entry<SkyKey, SkyValue> entry : metadataFoundDuringActionExecution.entrySet()) {
469 inputArtifactData.put(
470 ArtifactSkyKey.artifact(entry.getKey()), (FileArtifactValue) entry.getValue());
Janak Ramakrishnan271c10b2015-10-05 15:14:48 +0000471 }
Lukacs Berki41ab6db2017-01-31 10:48:31 +0000472 state.inputArtifactData = inputArtifactData;
ulfjack1a328e32017-04-06 10:25:16 +0000473 // TODO(ulfjack): This causes information loss about omitted and injected outputs. Also see
474 // the documentation on MetadataHandler.artifactOmitted. This works by accident because
475 // markOmitted is only called for remote execution, and this code only gets executed for
476 // local execution.
Lukacs Berki41ab6db2017-01-31 10:48:31 +0000477 metadataHandler =
478 new ActionMetadataHandler(state.inputArtifactData, action.getOutputs(), tsgm.get());
Janak Ramakrishnan7a1db6e2015-06-30 19:56:11 +0000479 }
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000480 }
Janak Ramakrishnandc896732015-12-08 18:44:45 +0000481 Preconditions.checkState(!env.valuesMissing(), action);
janakr93e3eea2017-03-30 22:09:37 +0000482 skyframeActionExecutor.afterExecution(
483 action, metadataHandler, state.token, clientEnv, actionLookupData);
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000484 return state.value;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100485 }
486
Janak Ramakrishnane5060fd2016-08-19 17:26:24 +0000487 private static final Function<Artifact, SkyKey> TO_NONMANDATORY_SKYKEY =
488 new Function<Artifact, SkyKey>() {
489 @Nullable
490 @Override
491 public SkyKey apply(@Nullable Artifact artifact) {
cushone1d2e392017-09-29 13:54:04 -0400492 return ArtifactSkyKey.key(artifact, /*isMandatory=*/ false);
Janak Ramakrishnane5060fd2016-08-19 17:26:24 +0000493 }
494 };
495
496 private static Iterable<SkyKey> newlyDiscoveredInputsToSkyKeys(
497 Iterable<Artifact> discoveredInputs, Set<Artifact> knownInputs) {
498 return Iterables.transform(
499 filterKnownInputs(discoveredInputs, knownInputs), TO_NONMANDATORY_SKYKEY);
500 }
501
Janak Ramakrishnana03258d2016-07-27 23:16:58 +0000502 private static void addDiscoveredInputs(
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000503 Map<Artifact, FileArtifactValue> inputData,
Googlereba6a442017-02-06 20:08:48 +0000504 Map<Artifact, Collection<Artifact>> expandedArtifacts,
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000505 Iterable<Artifact> discoveredInputs,
506 Environment env)
507 throws InterruptedException {
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000508 // We do not do a getValuesOrThrow() call for the following reasons:
509 // 1. No exceptions can be thrown for non-mandatory inputs;
510 // 2. Any derived inputs must be in the transitive closure of this action's inputs. Therefore,
511 // if there was an error building one of them, then that exception would have percolated up to
512 // this action already, through one of its declared inputs, and we would not have reached input
513 // discovery.
514 // Therefore there is no need to catch and rethrow exceptions as there is with #checkInputs.
Janak Ramakrishnane5060fd2016-08-19 17:26:24 +0000515 Map<SkyKey, SkyValue> nonMandatoryDiscovered =
516 env.getValues(newlyDiscoveredInputsToSkyKeys(discoveredInputs, inputData.keySet()));
Janak Ramakrishnana03258d2016-07-27 23:16:58 +0000517 if (!env.valuesMissing()) {
jcatercecb3a82018-05-01 14:37:48 -0700518 for (Map.Entry<SkyKey, SkyValue> entry : nonMandatoryDiscovered.entrySet()) {
Googlereba6a442017-02-06 20:08:48 +0000519 Artifact input = ArtifactSkyKey.artifact(entry.getKey());
520 if (entry.getValue() instanceof TreeArtifactValue) {
521 TreeArtifactValue treeValue = (TreeArtifactValue) entry.getValue();
522 expandedArtifacts.put(input, ImmutableSet.<Artifact>copyOf(treeValue.getChildren()));
523 inputData.putAll(treeValue.getChildValues());
524
525 inputData.put(input, treeValue.getSelfData());
526 } else {
527 inputData.put(input, (FileArtifactValue) entry.getValue());
528 }
Michajlo Matijkiw9d4af3b2016-07-28 20:01:49 +0000529 }
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000530 }
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000531 }
532
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000533 private static void establishSkyframeDependencies(Environment env, Action action)
534 throws ActionExecutionException, InterruptedException {
Laszlo Csomord4d99932015-07-10 08:36:11 +0000535 // Before we may safely establish Skyframe dependencies, we must build all action inputs by
536 // requesting their ArtifactValues.
537 // This is very important to do, because the establishSkyframeDependencies method may request
538 // FileValues for input files of this action (directly requesting them, or requesting some other
539 // SkyValue whose builder requests FileValues), which may not yet exist if their generating
540 // actions have not yet run.
541 // See SkyframeAwareActionTest.testRaceConditionBetweenInputAcquisitionAndSkyframeDeps
542 Preconditions.checkState(!env.valuesMissing(), action);
543
544 if (action instanceof SkyframeAwareAction) {
545 // Skyframe-aware actions should be executed unconditionally, i.e. bypass action cache
546 // checking. See documentation of SkyframeAwareAction.
547 Preconditions.checkState(action.executeUnconditionally(), action);
548
549 try {
550 ((SkyframeAwareAction) action).establishSkyframeDependencies(env);
551 } catch (SkyframeAwareAction.ExceptionBase e) {
552 throw new ActionExecutionException(e, action, false);
553 }
554 }
555 }
556
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100557 private static Iterable<SkyKey> toKeys(Iterable<Artifact> inputs,
558 Iterable<Artifact> mandatoryInputs) {
559 if (mandatoryInputs == null) {
560 // This is a non inputs-discovering action, so no need to distinguish mandatory from regular
561 // inputs.
Janak Ramakrishnanad77f972016-07-29 20:58:42 +0000562 return Iterables.transform(
563 inputs,
564 new Function<Artifact, SkyKey>() {
565 @Override
566 public SkyKey apply(Artifact artifact) {
567 return ArtifactSkyKey.key(artifact, true);
568 }
569 });
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100570 } else {
571 Collection<SkyKey> discoveredArtifacts = new HashSet<>();
572 Set<Artifact> mandatory = Sets.newHashSet(mandatoryInputs);
573 for (Artifact artifact : inputs) {
Janak Ramakrishnanad77f972016-07-29 20:58:42 +0000574 discoveredArtifacts.add(ArtifactSkyKey.key(artifact, mandatory.contains(artifact)));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100575 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100576 return discoveredArtifacts;
577 }
578 }
579
580 /**
581 * Declare dependency on all known inputs of action. Throws exception if any are known to be
582 * missing. Some inputs may not yet be in the graph, in which case the builder should abort.
583 */
Rumou Duana77f32c2016-04-13 21:59:21 +0000584 private Pair<Map<Artifact, FileArtifactValue>, Map<Artifact, Collection<Artifact>>>
Michael Thvedt434e68e2016-02-09 00:57:46 +0000585 checkInputs(Environment env, Action action,
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000586 Map<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> inputDeps)
587 throws ActionExecutionException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100588 int missingCount = 0;
589 int actionFailures = 0;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100590 // Only populate input data if we have the input values, otherwise they'll just go unused.
591 // We still want to loop through the inputs to collect missing deps errors. During the
592 // evaluator "error bubbling", we may get one last chance at reporting errors even though
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000593 // some deps are still missing.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100594 boolean populateInputData = !env.valuesMissing();
Klaus Aehligd1f4a162016-10-25 14:51:55 +0000595 NestedSetBuilder<Cause> rootCauses = NestedSetBuilder.stableOrder();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100596 Map<Artifact, FileArtifactValue> inputArtifactData =
597 new HashMap<>(populateInputData ? inputDeps.size() : 0);
Rumou Duana77f32c2016-04-13 21:59:21 +0000598 Map<Artifact, Collection<Artifact>> expandedArtifacts =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100599 new HashMap<>(populateInputData ? 128 : 0);
600
601 ActionExecutionException firstActionExecutionException = null;
602 for (Map.Entry<SkyKey, ValueOrException2<MissingInputFileException,
603 ActionExecutionException>> depsEntry : inputDeps.entrySet()) {
Janak Ramakrishnanad77f972016-07-29 20:58:42 +0000604 Artifact input = ArtifactSkyKey.artifact(depsEntry.getKey());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100605 try {
Janak Ramakrishnanad77f972016-07-29 20:58:42 +0000606 SkyValue value = depsEntry.getValue().get();
Googlerece75722016-02-11 17:55:41 +0000607 if (populateInputData) {
608 if (value instanceof AggregatingArtifactValue) {
609 AggregatingArtifactValue aggregatingValue = (AggregatingArtifactValue) value;
610 for (Pair<Artifact, FileArtifactValue> entry : aggregatingValue.getInputs()) {
611 inputArtifactData.put(entry.first, entry.second);
612 }
613 // We have to cache the "digest" of the aggregating value itself,
614 // because the action cache checker may want it.
615 inputArtifactData.put(input, aggregatingValue.getSelfData());
Rumou Duana77f32c2016-04-13 21:59:21 +0000616 ImmutableList.Builder<Artifact> expansionBuilder = ImmutableList.builder();
Googlerece75722016-02-11 17:55:41 +0000617 for (Pair<Artifact, FileArtifactValue> pair : aggregatingValue.getInputs()) {
618 expansionBuilder.add(pair.first);
619 }
620 expandedArtifacts.put(input, expansionBuilder.build());
621 } else if (value instanceof TreeArtifactValue) {
Rumou Duanbaeed332016-10-18 15:30:25 +0000622 TreeArtifactValue treeValue = (TreeArtifactValue) value;
Janak Ramakrishnan529fe5c2016-10-19 10:06:38 +0000623 expandedArtifacts.put(input, ImmutableSet.<Artifact>copyOf(treeValue.getChildren()));
Rumou Duanbaeed332016-10-18 15:30:25 +0000624 inputArtifactData.putAll(treeValue.getChildValues());
Rumou Duana77f32c2016-04-13 21:59:21 +0000625
Googlerece75722016-02-11 17:55:41 +0000626 // Again, we cache the "digest" of the value for cache checking.
Rumou Duanbaeed332016-10-18 15:30:25 +0000627 inputArtifactData.put(input, treeValue.getSelfData());
Janak Ramakrishnanad77f972016-07-29 20:58:42 +0000628 } else {
629 Preconditions.checkState(value instanceof FileArtifactValue, depsEntry);
Googlerece75722016-02-11 17:55:41 +0000630 inputArtifactData.put(input, (FileArtifactValue) value);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100631 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100632 }
633 } catch (MissingInputFileException e) {
634 missingCount++;
635 if (input.getOwner() != null) {
Klaus Aehligd1f4a162016-10-25 14:51:55 +0000636 rootCauses.add(new LabelCause(input.getOwner()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100637 }
638 } catch (ActionExecutionException e) {
639 actionFailures++;
janakr81228062017-11-06 23:13:28 +0100640 // Prefer a catastrophic exception as the one we propagate.
641 if (firstActionExecutionException == null
642 || !firstActionExecutionException.isCatastrophe() && e.isCatastrophe()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100643 firstActionExecutionException = e;
644 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100645 rootCauses.addTransitive(e.getRootCauses());
646 }
647 }
648 // We need to rethrow first exception because it can contain useful error message
649 if (firstActionExecutionException != null) {
650 if (missingCount == 0 && actionFailures == 1) {
651 // In the case a single action failed, just propagate the exception upward. This avoids
652 // having to copy the root causes to the upwards transitive closure.
653 throw firstActionExecutionException;
654 }
janakr81228062017-11-06 23:13:28 +0100655 throw new ActionExecutionException(
656 firstActionExecutionException.getMessage(),
657 firstActionExecutionException.getCause(),
658 action,
659 rootCauses.build(),
660 firstActionExecutionException.isCatastrophe(),
Googlera7158022016-01-22 21:04:55 +0000661 firstActionExecutionException.getExitCode());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100662 }
663
664 if (missingCount > 0) {
Klaus Aehligd1f4a162016-10-25 14:51:55 +0000665 for (Cause missingInput : rootCauses.build()) {
666 env.getListener()
667 .handle(
668 Event.error(
669 action.getOwner().getLocation(),
670 String.format(
671 "%s: missing input file '%s'",
672 action.getOwner().getLabel(), missingInput.getLabel())));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100673 }
674 throw new ActionExecutionException(missingCount + " input file(s) do not exist", action,
675 rootCauses.build(), /*catastrophe=*/false);
676 }
Googlereba6a442017-02-06 20:08:48 +0000677 return Pair.of(inputArtifactData, expandedArtifacts);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100678 }
679
Janak Ramakrishnane5060fd2016-08-19 17:26:24 +0000680 private static Iterable<Artifact> filterKnownInputs(
681 Iterable<Artifact> newInputs, Set<Artifact> knownInputs) {
682 return Iterables.filter(newInputs, Predicates.not(Predicates.in(knownInputs)));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100683 }
684
685 /**
686 * All info/warning messages associated with actions should be always displayed.
687 */
688 @Override
689 public String extractTag(SkyKey skyKey) {
690 return null;
691 }
692
693 /**
Janak Ramakrishnan29c5ab42015-05-14 19:38:12 +0000694 * Exception to be thrown if an action is missing Skyframe dependencies that it finds are missing
695 * during execution/input discovery.
696 */
697 public static class MissingDepException extends RuntimeException {}
698
699 /**
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000700 * Should be called once execution is over, and the intra-build cache of in-progress computations
701 * should be discarded. If the cache is non-empty (due to an interrupted/failed build), failure to
702 * call complete() can both cause a memory leak and incorrect results on the subsequent build.
703 */
704 @Override
705 public void complete() {
706 // Discard all remaining state (there should be none after a successful execution).
707 stateMap = Maps.newConcurrentMap();
708 }
709
710 private ContinuationState getState(Action action) {
711 ContinuationState state = stateMap.get(action);
712 if (state == null) {
713 state = new ContinuationState();
714 Preconditions.checkState(stateMap.put(action, state) == null, action);
715 }
716 return state;
717 }
718
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000719 /**
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000720 * State to save work across restarts of ActionExecutionFunction due to missing values in the
721 * graph for actions that discover inputs. There are three places where we save work, all for
722 * actions that discover inputs:
723 * <ol>
724 * <li>If not all known input metadata (coming from Action#getInputs) is available yet, then the
725 * calculated set of inputs (including the inputs resolved from the action cache) is saved.</li>
726 * <li>If not all discovered inputs' metadata is available yet, then the known input metadata
727 * together with the set of discovered inputs is saved, as well as the Token used to identify
728 * this action to the action cache.</li>
729 * <li>If, after execution, new inputs are discovered whose metadata is not yet available, then
730 * the same data as in the previous case is saved, along with the actual result of execution.
731 * </li>
732 * </ol>
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000733 */
734 private static class ContinuationState {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000735 AllInputs allInputs;
Janak Ramakrishnana03258d2016-07-27 23:16:58 +0000736 /** Mutable map containing metadata for known artifacts. */
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000737 Map<Artifact, FileArtifactValue> inputArtifactData = null;
Rumou Duana77f32c2016-04-13 21:59:21 +0000738 Map<Artifact, Collection<Artifact>> expandedArtifacts = null;
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000739 Token token = null;
Michajlo Matijkiw9d4af3b2016-07-28 20:01:49 +0000740 Iterable<Artifact> discoveredInputs = null;
Googler33d63512016-12-02 00:27:22 +0000741 Iterable<Artifact> discoveredInputsStage2 = null;
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000742 ActionExecutionValue value = null;
743
744 boolean hasCollectedInputs() {
745 return allInputs != null;
746 }
747
748 boolean hasArtifactData() {
749 boolean result = inputArtifactData != null;
Michael Thvedt434e68e2016-02-09 00:57:46 +0000750 Preconditions.checkState(result == (expandedArtifacts != null), this);
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000751 return result;
752 }
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000753
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000754 boolean hasCheckedActionCache() {
755 // If token is null because there was an action cache hit, this method is never called again
756 // because we return immediately.
757 return token != null;
758 }
759
760 boolean hasExecutedAction() {
761 return value != null;
762 }
763
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000764 @Override
765 public String toString() {
Janak Ramakrishnan90f3d342015-03-27 19:45:18 +0000766 return token + ", " + value + ", " + allInputs + ", " + inputArtifactData + ", "
767 + discoveredInputs;
Janak Ramakrishnan41fad442015-03-20 17:24:45 +0000768 }
769 }
770
771 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100772 * Used to declare all the exception types that can be wrapped in the exception thrown by
773 * {@link ActionExecutionFunction#compute}.
774 */
775 private static final class ActionExecutionFunctionException extends SkyFunctionException {
776
777 private final ActionExecutionException actionException;
778
779 public ActionExecutionFunctionException(ActionExecutionException e) {
780 // We conservatively assume that the error is transient. We don't have enough information to
781 // distinguish non-transient errors (e.g. compilation error from a deterministic compiler)
782 // from transient ones (e.g. IO error).
783 // TODO(bazel-team): Have ActionExecutionExceptions declare their transience.
784 super(e, Transience.TRANSIENT);
785 this.actionException = e;
786 }
787
788 @Override
789 public boolean isCatastrophic() {
790 return actionException.isCatastrophe();
791 }
792 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100793}