use of com.google.devtools.build.lib.actions.ActionExecutionException in project bazel by bazelbuild.
the class CompletionFunction method compute.
@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws CompletionFunctionException, InterruptedException {
TValue value = completor.getValueFromSkyKey(skyKey, env);
TopLevelArtifactContext topLevelContext = completor.getTopLevelArtifactContext(skyKey);
if (env.valuesMissing()) {
return null;
}
Map<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> inputDeps = env.getValuesOrThrow(ArtifactSkyKey.mandatoryKeys(completor.getAllArtifactsToBuild(value, topLevelContext).getAllArtifacts()), MissingInputFileException.class, ActionExecutionException.class);
int missingCount = 0;
ActionExecutionException firstActionExecutionException = null;
MissingInputFileException missingInputException = null;
NestedSetBuilder<Cause> rootCausesBuilder = NestedSetBuilder.stableOrder();
for (Map.Entry<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> depsEntry : inputDeps.entrySet()) {
Artifact input = ArtifactSkyKey.artifact(depsEntry.getKey());
try {
depsEntry.getValue().get();
} catch (MissingInputFileException e) {
missingCount++;
final Label inputOwner = input.getOwner();
if (inputOwner != null) {
Cause cause = new LabelCause(inputOwner);
rootCausesBuilder.add(cause);
env.getListener().handle(completor.getRootCauseError(value, cause));
}
} catch (ActionExecutionException e) {
rootCausesBuilder.addTransitive(e.getRootCauses());
if (firstActionExecutionException == null) {
firstActionExecutionException = e;
}
}
}
if (missingCount > 0) {
missingInputException = completor.getMissingFilesException(value, missingCount);
}
NestedSet<Cause> rootCauses = rootCausesBuilder.build();
if (!rootCauses.isEmpty()) {
eventBusRef.get().post(completor.createFailed(value, rootCauses));
if (firstActionExecutionException != null) {
throw new CompletionFunctionException(firstActionExecutionException);
} else {
throw new CompletionFunctionException(missingInputException);
}
}
return env.valuesMissing() ? null : completor.createResult(value);
}
use of com.google.devtools.build.lib.actions.ActionExecutionException in project bazel by bazelbuild.
the class ActionExecutionFunction method compute.
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws ActionExecutionFunctionException, InterruptedException {
Preconditions.checkArgument(skyKey.argument() instanceof Action);
Action action = (Action) skyKey.argument();
// BUILD_ID, forcing invalidation of upward transitive closure on each build.
if ((action.isVolatile() && !(action instanceof SkyframeAwareAction)) || action instanceof NotifyOnActionCacheHit) {
// Volatile build actions may need to execute even if none of their known inputs have changed.
// Depending on the buildID ensure that these actions have a chance to execute.
PrecomputedValue.BUILD_ID.get(env);
}
// Look up the parts of the environment that influence the action.
Map<SkyKey, SkyValue> clientEnvLookup = env.getValues(Iterables.transform(action.getClientEnvironmentVariables(), VAR_TO_SKYKEY));
if (env.valuesMissing()) {
return null;
}
Map<String, String> clientEnv = new HashMap<>();
for (Entry<SkyKey, SkyValue> entry : clientEnvLookup.entrySet()) {
ClientEnvironmentValue envValue = (ClientEnvironmentValue) entry.getValue();
if (envValue.getValue() != null) {
clientEnv.put((String) entry.getKey().argument(), envValue.getValue());
}
}
// For restarts of this ActionExecutionFunction we use a ContinuationState variable, below, to
// avoid redoing work. However, if two actions are shared and the first one executes, when the
// second one goes to execute, we should detect that and short-circuit, even without taking
// ContinuationState into account.
boolean sharedActionAlreadyRan = skyframeActionExecutor.probeActionExecution(action);
ContinuationState state;
if (action.discoversInputs()) {
state = getState(action);
} else {
// Because this is a new state, all conditionals below about whether state has already done
// something will return false, and so we will execute all necessary steps.
state = new ContinuationState();
}
if (!state.hasCollectedInputs()) {
state.allInputs = collectInputs(action, env);
if (state.allInputs == null) {
// Missing deps.
return null;
}
} else if (state.allInputs.keysRequested != null) {
// Preserve the invariant that we ask for the same deps each build.
env.getValues(state.allInputs.keysRequested);
Preconditions.checkState(!env.valuesMissing(), "%s %s", action, state);
}
Pair<Map<Artifact, FileArtifactValue>, Map<Artifact, Collection<Artifact>>> checkedInputs = null;
try {
// Declare deps on known inputs to action. We do this unconditionally to maintain our
// invariant of asking for the same deps each build.
Map<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> inputDeps = env.getValuesOrThrow(toKeys(state.allInputs.getAllInputs(), action.discoversInputs() ? action.getMandatoryInputs() : null), MissingInputFileException.class, ActionExecutionException.class);
if (!sharedActionAlreadyRan && !state.hasArtifactData()) {
// Do we actually need to find our metadata?
checkedInputs = checkInputs(env, action, inputDeps);
}
} catch (ActionExecutionException e) {
// Remove action from state map in case it's there (won't be unless it discovers inputs).
stateMap.remove(action);
throw new ActionExecutionFunctionException(e);
}
if (env.valuesMissing()) {
// of the action; see establishSkyframeDependencies why.
return null;
}
try {
establishSkyframeDependencies(env, action);
} catch (ActionExecutionException e) {
throw new ActionExecutionFunctionException(e);
}
if (env.valuesMissing()) {
return null;
}
if (checkedInputs != null) {
Preconditions.checkState(!state.hasArtifactData(), "%s %s", state, action);
state.inputArtifactData = checkedInputs.first;
state.expandedArtifacts = checkedInputs.second;
}
ActionExecutionValue result;
try {
result = checkCacheAndExecuteIfNeeded(action, state, env, clientEnv);
} catch (ActionExecutionException e) {
// Remove action from state map in case it's there (won't be unless it discovers inputs).
stateMap.remove(action);
// action. Label can be null in the case of, e.g., the SystemActionOwner (for build-info.txt).
throw new ActionExecutionFunctionException(new AlreadyReportedActionExecutionException(e));
}
if (env.valuesMissing()) {
Preconditions.checkState(stateMap.containsKey(action), action);
return null;
}
// Remove action from state map in case it's there (won't be unless it discovers inputs).
stateMap.remove(action);
return result;
}
use of com.google.devtools.build.lib.actions.ActionExecutionException in project bazel by bazelbuild.
the class PopulateTreeArtifactActionTest method testInvalidManifestEntryPaths.
@Test
public void testInvalidManifestEntryPaths() throws Exception {
Action action = createPopulateTreeArtifactAction();
scratch.overwriteFile("archiveManifest.txt", "archive_members/1.class", "../invalid_relative_path/myfile.class");
ActionExecutionContext executionContext = actionExecutionContext(new ArrayList<Artifact>());
try {
action.execute(executionContext);
fail("Invalid manifest entry paths, expected exception");
} catch (ActionExecutionException e) {
// Expect ActionExecutionException
}
}
use of com.google.devtools.build.lib.actions.ActionExecutionException in project bazel by bazelbuild.
the class SkyframeAwareActionTest method testRaceConditionBetweenInputAcquisitionAndSkyframeDeps.
/**
* Regression test to avoid a potential race condition in {@link ActionExecutionFunction}.
*
* <p>The test ensures that when ActionExecutionFunction executes a Skyframe-aware action
* (implementor of {@link SkyframeAwareAction}), ActionExecutionFunction first requests the inputs
* of the action and ensures they are built before requesting any of its Skyframe dependencies.
*
* <p>This strict ordering is very important to avoid the race condition, which could arise if the
* compute method were too eager to request all dependencies: request input files but even if some
* are missing, request also the skyframe-dependencies. The race is described in this method's
* body.
*/
@Test
public void testRaceConditionBetweenInputAcquisitionAndSkyframeDeps() throws Exception {
// Sequence of events on threads A and B, showing SkyFunctions and requested SkyKeys, leading
// to an InconsistentFilesystemException:
//
// _______________[Thread A]_________________|_______________[Thread B]_________________
// ActionExecutionFunction(gen2_action: | idle
// genfiles/gen1 -> genfiles/foo/bar/gen2) |
// ARTIFACT:genfiles/gen1 |
// MOCK_VALUE:dummy_argument |
// env.valuesMissing():yes ==> return |
// |
// ArtifactFunction(genfiles/gen1) | MockFunction()
// CONFIGURED_TARGET://foo:gen1 | FILE:genfiles/foo
// ACTION_EXECUTION:gen1_action | env.valuesMissing():yes ==> return
// env.valuesMissing():yes ==> return |
// | FileFunction(genfiles/foo)
// ActionExecutionFunction(gen1_action) | FILE:genfiles
// ARTIFACT:genfiles/gen0 | env.valuesMissing():yes ==> return
// env.valuesMissing():yes ==> return |
// | FileFunction(genfiles)
// ArtifactFunction(genfiles/gen0) | FILE_STATE:genfiles
// CONFIGURED_TARGET://foo:gen0 | env.valuesMissing():yes ==> return
// ACTION_EXECUTION:gen0_action |
// env.valuesMissing():yes ==> return | FileStateFunction(genfiles)
// | stat genfiles
// ActionExecutionFunction(gen0_action) | return FileStateValue:non-existent
// create output directory: genfiles |
// working | FileFunction(genfiles/foo)
// | FILE:genfiles
// | FILE_STATE:genfiles/foo
// | env.valuesMissing():yes ==> return
// |
// | FileStateFunction(genfiles/foo)
// | stat genfiles/foo
// | return FileStateValue:non-existent
// |
// done, created genfiles/gen0 | FileFunction(genfiles/foo)
// return ActionExecutionValue(gen0_action) | FILE:genfiles
// | FILE_STATE:genfiles/foo
// ArtifactFunction(genfiles/gen0) | return FileValue(genfiles/foo:non-existent)
// CONFIGURED_TARGET://foo:gen0 |
// ACTION_EXECUTION:gen0_action | MockFunction()
// return ArtifactSkyKey(genfiles/gen0) | FILE:genfiles/foo
// | FILE:genfiles/foo/bar/gen1
// ActionExecutionFunction(gen1_action) | env.valuesMissing():yes ==> return
// ARTIFACT:genfiles/gen0 |
// create output directory: genfiles/foo/bar | FileFunction(genfiles/foo/bar/gen1)
// done, created genfiles/foo/bar/gen1 | FILE:genfiles/foo/bar
// return ActionExecutionValue(gen1_action) | env.valuesMissing():yes ==> return
// |
// idle | FileFunction(genfiles/foo/bar)
// | FILE:genfiles/foo
// | FILE_STATE:genfiles/foo/bar
// | env.valuesMissing():yes ==> return
// |
// | FileStateFunction(genfiles/foo/bar)
// | stat genfiles/foo/bar
// | return FileStateValue:directory
// |
// | FileFunction(genfiles/foo/bar)
// | FILE:genfiles/foo
// | FILE_STATE:genfiles/foo/bar
// | throw InconsistentFilesystemException:
// | genfiles/foo doesn't exist but
// | genfiles/foo/bar does!
Artifact genFile1 = createDerivedArtifact("foo/bar/gen1.txt");
Artifact genFile2 = createDerivedArtifact("gen2.txt");
registerAction(new SingleOutputAction(null, genFile1) {
@Override
public void execute(ActionExecutionContext actionExecutionContext) throws ActionExecutionException, InterruptedException {
writeOutput(null, "gen1");
}
});
registerAction(new SingleOutputSkyframeAwareAction(genFile1, genFile2) {
@Override
public void establishSkyframeDependencies(Environment env) throws ExceptionBase {
assertThat(env.valuesMissing()).isFalse();
}
@Override
public void execute(ActionExecutionContext actionExecutionContext) throws ActionExecutionException, InterruptedException {
writeOutput(readInput(), "gen2");
}
});
builder.buildArtifacts(reporter, ImmutableSet.of(genFile2), null, null, null, null, executor, null, false, null, null);
}
use of com.google.devtools.build.lib.actions.ActionExecutionException in project bazel by bazelbuild.
the class ParallelBuilderTest method assertNoNewJobsAreRunAfterFirstFailure.
// Regression test for bug #735765, "ParallelBuilder still issues new jobs
// after one has failed, without --keep-going." The incorrect behaviour is
// that, when the first job fails, while no new jobs are added to the queue
// of runnable jobs, the queue may have lots of work in it, and the
// ParallelBuilder always completes these jobs before it returns. The
// correct behaviour is to discard all the jobs in the queue after the first
// one fails.
public void assertNoNewJobsAreRunAfterFirstFailure(final boolean catastrophe, boolean keepGoing) throws Exception {
// Strategy: Limit parallelism to 3. Enqueue 10 runnable tasks that run
// for an appreciable period (say 100ms). Ensure that at most 3 of those
// tasks completed. This proves that all runnable tasks were dropped from
// the queue after the first batch (which included errors) was finished.
// It should be pretty robust even in the face of timing variations.
final AtomicInteger completedTasks = new AtomicInteger(0);
int numJobs = 50;
Artifact[] artifacts = new Artifact[numJobs];
for (int ii = 0; ii < numJobs; ++ii) {
Artifact out = createDerivedArtifact(ii + ".out");
List<Artifact> inputs = (catastrophe && ii > 10) ? ImmutableList.of(artifacts[0]) : Artifact.NO_ARTIFACTS;
final int iCopy = ii;
registerAction(new TestAction(new Callable<Void>() {
@Override
public Void call() throws Exception {
// 100ms
Thread.sleep(100);
completedTasks.getAndIncrement();
throw new IOException("task failed");
}
}, inputs, ImmutableList.of(out)) {
@Override
public void execute(ActionExecutionContext actionExecutionContext) throws ActionExecutionException {
if (catastrophe && iCopy == 0) {
try {
// 300ms
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
completedTasks.getAndIncrement();
throw new ActionExecutionException("This is a catastrophe", this, true);
}
super.execute(actionExecutionContext);
}
});
artifacts[ii] = out;
}
// Don't fail fast when we encounter the error
reporter.removeHandler(failFastHandler);
try {
buildArtifacts(createBuilder(3, keepGoing), artifacts);
fail();
} catch (BuildFailedException e) {
assertContainsEvent("task failed");
}
if (completedTasks.get() >= numJobs) {
fail("Expected early termination due to failed task, but all tasks ran to completion.");
}
}
Aggregations