in src/main/java/build/buildfarm/worker/Executor.java [177:308]
private long executePolled(
Operation operation,
ResourceLimits limits,
Iterable<ExecutionPolicy> policies,
Duration timeout,
boolean isDefaultTimeout,
Stopwatch stopwatch)
throws InterruptedException {
/* execute command */
logger.log(Level.FINE, "Executor: Operation " + operation.getName() + " Executing command");
ActionResult.Builder resultBuilder = operationContext.executeResponse.getResultBuilder();
resultBuilder
.getExecutionMetadataBuilder()
.setExecutionStartTimestamp(Timestamps.fromMillis(System.currentTimeMillis()));
Command command = operationContext.command;
Path workingDirectory = operationContext.execDir;
if (!command.getWorkingDirectory().isEmpty()) {
workingDirectory = workingDirectory.resolve(command.getWorkingDirectory());
}
String operationName = operation.getName();
ImmutableList.Builder<String> arguments = ImmutableList.builder();
Code statusCode;
try (IOResource resource =
workerContext.limitExecution(
operationName, arguments, operationContext.command, workingDirectory)) {
for (ExecutionPolicy policy : policies) {
if (policy.getPolicyCase() == WRAPPER) {
arguments.addAll(transformWrapper(policy.getWrapper()));
}
}
if (System.getProperty("os.name").contains("Win")) {
// Make sure that the executable path is absolute, otherwise processbuilder fails on windows
Iterator<String> argumentItr = command.getArgumentsList().iterator();
if (argumentItr.hasNext()) {
String exe = argumentItr.next(); // Get first element, this is the executable
arguments.add(workingDirectory.resolve(exe).toAbsolutePath().normalize().toString());
argumentItr.forEachRemaining(arguments::add);
}
} else {
arguments.addAll(command.getArgumentsList());
}
statusCode =
executeCommand(
operationName,
workingDirectory,
arguments.build(),
command.getEnvironmentVariablesList(),
limits,
timeout,
isDefaultTimeout,
// executingMetadata.getStdoutStreamName(),
// executingMetadata.getStderrStreamName(),
resultBuilder);
// From Bazel Test Encyclopedia:
// If the main process of a test exits, but some of its children are still running,
// the test runner should consider the run complete and count it as a success or failure
// based on the exit code observed from the main process. The test runner may kill any stray
// processes. Tests should not leak processes in this fashion.
// Based on configuration, we will decide whether remaining resources should be an error.
if (workerContext.shouldErrorOperationOnRemainingResources()
&& resource.isReferenced()
&& statusCode == Code.OK) {
// there should no longer be any references to the resource. Any references will be
// killed upon close, but we must error the operation due to improper execution
// per the gRPC spec: 'The operation was attempted past the valid range.' Seems
// appropriate
statusCode = Code.OUT_OF_RANGE;
operationContext
.executeResponse
.getStatusBuilder()
.setMessage("command resources were referenced after execution completed");
}
} catch (IOException e) {
logger.log(Level.SEVERE, format("error executing operation %s", operationName), e);
operationContext.poller.pause();
putError();
return 0;
}
// switch poller to disable deadline
operationContext.poller.pause();
workerContext.resumePoller(
operationContext.poller,
"Executor(claim)",
operationContext.queueEntry,
ExecutionStage.Value.EXECUTING,
() -> {},
Deadline.after(10, DAYS));
resultBuilder
.getExecutionMetadataBuilder()
.setExecutionCompletedTimestamp(Timestamps.fromMillis(System.currentTimeMillis()));
long executeUSecs = stopwatch.elapsed(MICROSECONDS);
logger.log(
Level.FINE,
String.format(
"Executor::executeCommand(%s): Completed command: exit code %d",
operationName, resultBuilder.getExitCode()));
operationContext.executeResponse.getStatusBuilder().setCode(statusCode.getNumber());
OperationContext reportOperationContext =
operationContext.toBuilder().setOperation(operation).build();
boolean claimed = owner.output().claim(reportOperationContext);
operationContext.poller.pause();
if (claimed) {
try {
owner.output().put(reportOperationContext);
} catch (InterruptedException e) {
owner.output().release();
throw e;
}
} else {
logger.log(Level.FINE, "Executor: Operation " + operationName + " Failed to claim output");
boolean wasInterrupted = Thread.interrupted();
try {
putError();
} finally {
if (wasInterrupted) {
Thread.currentThread().interrupt();
}
}
}
return stopwatch.elapsed(MICROSECONDS) - executeUSecs;
}