diff options
Diffstat (limited to 'src/main')
29 files changed, 389 insertions, 185 deletions
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc index 195134c524..7035472d94 100644 --- a/src/main/cpp/blaze.cc +++ b/src/main/cpp/blaze.cc @@ -1780,10 +1780,9 @@ unsigned int GrpcBlazeServer::Communicate() { std::thread cancel_thread(&GrpcBlazeServer::CancelThread, this); bool command_id_set = false; bool pipe_broken = false; - int exit_code = -1; + command_server::RunResponse final_response; bool finished = false; bool finished_warning_emitted = false; - bool termination_expected = false; while (reader->Read(&response)) { if (finished && !finished_warning_emitted) { @@ -1799,8 +1798,7 @@ unsigned int GrpcBlazeServer::Communicate() { const char *broken_pipe_name = nullptr; if (response.finished()) { - exit_code = response.exit_code(); - termination_expected = response.termination_expected(); + final_response = response; finished = true; } @@ -1838,7 +1836,7 @@ unsigned int GrpcBlazeServer::Communicate() { // If the server has shut down, but does not terminate itself within a 1m // grace period, terminate it. - if (termination_expected && + if (final_response.termination_expected() && !AwaitServerProcessTermination(globals->server_pid, globals->options->output_base, kPostShutdownGracePeriodSeconds)) { @@ -1862,10 +1860,31 @@ unsigned int GrpcBlazeServer::Communicate() { "(log file: '%s')\n\n", globals->jvm_log_file.c_str()); return GetExitCodeForAbruptExit(*globals); + } else if (final_response.has_exec_request()) { + const command_server::ExecRequest& request = final_response.exec_request(); + if (request.argv_size() < 1) { + fprintf(stderr, + "\nServer requested exec() but did not pass a binary to execute\n\n"); + return blaze_exit_code::INTERNAL_ERROR; + } + + vector<string> argv; + argv.insert(argv.begin(), request.argv().begin(), request.argv().end()); + for (const auto& variable : request.environment_variable()) { + SetEnv(variable.name(), variable.value()); + } + + if (!blaze_util::ChangeDirectory(request.working_directory())) { + pdie(blaze_exit_code::INTERNAL_ERROR, "changing directory into %s failed", + request.working_directory().c_str()); + } + ExecuteProgram(request.argv(0), argv); } // We'll exit with exit code SIGPIPE on Unixes due to PropagateSignalOnExit() - return pipe_broken ? blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR : exit_code; + return pipe_broken + ? blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR + : final_response.exit_code(); } void GrpcBlazeServer::Disconnect() { diff --git a/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java b/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java index 7581de0eb0..202a29b45c 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/commands/FetchCommand.java @@ -28,6 +28,7 @@ import com.google.devtools.build.lib.query2.engine.QueryException; import com.google.devtools.build.lib.query2.engine.QueryExpression; import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; @@ -60,29 +61,29 @@ public final class FetchCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { BlazeRuntime runtime = env.getRuntime(); if (options.getResidue().isEmpty()) { env.getReporter().handle(Event.error(String.format( "missing fetch expression. Type '%s help fetch' for syntax and help", env.getRuntime().getProductName()))); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } try { env.setupPackageCache(options, runtime.getDefaultsPackageContent()); } catch (InterruptedException e) { env.getReporter().handle(Event.error("fetch interrupted")); - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } catch (AbruptExitException e) { env.getReporter().handle(Event.error(null, "Unknown error: " + e.getMessage())); - return e.getExitCode(); + return BlazeCommandResult.exitCode(e.getExitCode()); } PackageCacheOptions pkgOptions = options.getOptions(PackageCacheOptions.class); if (!pkgOptions.fetch) { env.getReporter().handle(Event.error(null, "You cannot run fetch with --fetch=false")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } // Querying for all of the dependencies of the targets has the side-effect of populating the @@ -110,7 +111,7 @@ public final class FetchCommand implements BlazeCommand { } catch (QueryException e) { env.getReporter().handle(Event.error( null, "Error while parsing '" + query + "': " + e.getMessage())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } env.getReporter() @@ -139,7 +140,7 @@ public final class FetchCommand implements BlazeCommand { .post( new NoBuildRequestFinishedEvent( ExitCode.COMMAND_LINE_ERROR, env.getRuntime().getClock().currentTimeMillis())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } catch (QueryException e) { // Keep consistent with reportBuildFileError() env.getReporter().handle(Event.error(e.getMessage())); @@ -147,7 +148,7 @@ public final class FetchCommand implements BlazeCommand { .post( new NoBuildRequestFinishedEvent( ExitCode.COMMAND_LINE_ERROR, env.getRuntime().getClock().currentTimeMillis())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } catch (IOException e) { // Should be impossible since our OutputFormatterCallback doesn't throw IOException. throw new IllegalStateException(e); @@ -162,6 +163,6 @@ public final class FetchCommand implements BlazeCommand { .post( new NoBuildRequestFinishedEvent( exitCode, env.getRuntime().getClock().currentTimeMillis())); - return exitCode; + return BlazeCommandResult.exitCode(exitCode); } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommand.java index b5759edf77..22c1e636f5 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommand.java @@ -13,7 +13,6 @@ // limitations under the License. package com.google.devtools.build.lib.runtime; -import com.google.devtools.build.lib.util.ExitCode; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.OptionsProvider; @@ -41,7 +40,7 @@ public interface BlazeCommand { * @throws BlazeCommandDispatcher.ShutdownBlazeServerException Indicates that the command wants to * shutdown the Blaze server. */ - ExitCode exec(CommandEnvironment env, OptionsProvider options) + BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) throws BlazeCommandDispatcher.ShutdownBlazeServerException; /** diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java index b664561d0d..e58d670e1b 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java @@ -80,19 +80,19 @@ public class BlazeCommandDispatcher { * the Blaze server process. */ public static class ShutdownBlazeServerException extends Exception { - private final int exitStatus; + private final ExitCode exitCode; - public ShutdownBlazeServerException(int exitStatus, Throwable cause) { + public ShutdownBlazeServerException(ExitCode exitCode, Throwable cause) { super(cause); - this.exitStatus = exitStatus; + this.exitCode = exitCode; } - public ShutdownBlazeServerException(int exitStatus) { - this.exitStatus = exitStatus; + public ShutdownBlazeServerException(ExitCode exitCode) { + this.exitCode = exitCode; } - public int getExitStatus() { - return exitStatus; + public ExitCode getExitCode() { + return exitCode; } } @@ -138,7 +138,7 @@ public class BlazeCommandDispatcher { * {@link ShutdownBlazeServerException} to indicate that a command wants to shutdown the Blaze * server. */ - int exec( + BlazeCommandResult exec( InvocationPolicy invocationPolicy, List<String> args, OutErr outErr, @@ -165,7 +165,7 @@ public class BlazeCommandDispatcher { if (command == null) { outErr.printErrLn(String.format( "Command '%s' not found. Try '%s help'.", commandName, runtime.getProductName())); - return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode(); + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } // Take the exclusive server lock. If we fail, we busy-wait until the lock becomes available. @@ -193,7 +193,7 @@ public class BlazeCommandDispatcher { case ERROR_OUT: outErr.printErrLn(String.format("Another command (" + currentClientDescription + ") is " + "running. Exiting immediately.")); - return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode(); + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); default: throw new IllegalStateException(); @@ -213,7 +213,7 @@ public class BlazeCommandDispatcher { try { if (shutdownReason != null) { outErr.printErrLn("Server shut down " + shutdownReason); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR.getNumericExitCode(); + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } return execExclusively( originalCommandLine, @@ -236,7 +236,7 @@ public class BlazeCommandDispatcher { } } - private int execExclusively( + private BlazeCommandResult execExclusively( OriginalUnstructuredCommandLineEvent unstructuredServerCommandLineEvent, InvocationPolicy invocationPolicy, List<String> args, @@ -325,7 +325,7 @@ public class BlazeCommandDispatcher { env.getEventBus().post(post); } // TODO(ulfjack): We're not calling BlazeModule.afterCommand here, even though we should. - return earlyExitCode.getNumericExitCode(); + return BlazeCommandResult.exitCode(earlyExitCode); } // Setup log filtering @@ -360,7 +360,7 @@ public class BlazeCommandDispatcher { // Do this before an actual crash so we don't have to worry about // allocating memory post-crash. String[] crashData = env.getCrashData(); - int numericExitCode = ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode(); + BlazeCommandResult result = BlazeCommandResult.exitCode(ExitCode.BLAZE_INTERNAL_ERROR); PrintStream savedOut = System.out; PrintStream savedErr = System.err; @@ -379,14 +379,14 @@ public class BlazeCommandDispatcher { } if (oomMoreEagerlyThreshold < 0 || oomMoreEagerlyThreshold > 100) { reporter.handle(Event.error("--oom_more_eagerly_threshold must be non-negative percent")); - return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode(); + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } if (oomMoreEagerlyThreshold != 100) { try { RetainedHeapLimiter.maybeInstallRetainedHeapLimiter(oomMoreEagerlyThreshold); } catch (OptionsParsingException e) { reporter.handle(Event.error(e.getMessage())); - return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode(); + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } } @@ -472,7 +472,7 @@ public class BlazeCommandDispatcher { invocationPolicy); } catch (AbruptExitException e) { reporter.handle(Event.error(e.getMessage())); - return e.getExitCode().getNumericExitCode(); + return BlazeCommandResult.exitCode(e.getExitCode()); } // Log the command line now that the modules have all had a change to register their listeners @@ -486,21 +486,29 @@ public class BlazeCommandDispatcher { env.getSkyframeExecutor().injectExtraPrecomputedValues(module.getPrecomputedValues()); } - ExitCode outcome = command.exec(env, options); - outcome = env.precompleteCommand(outcome); - numericExitCode = outcome.getNumericExitCode(); - return numericExitCode; + result = command.exec(env, options); + ExitCode moduleExitCode = env.precompleteCommand(result.getExitCode()); + // If Blaze did not suffer an infrastructure failure, check for errors in modules. + if (result.getExitCode() != null + && !result.getExitCode().isInfrastructureFailure() + && moduleExitCode != null) { + result = BlazeCommandResult.exitCode(moduleExitCode); + } + return result; } catch (ShutdownBlazeServerException e) { - numericExitCode = e.getExitStatus(); + // result is read in the finally block + result = BlazeCommandResult.exitCode(e.getExitCode()); throw e; } catch (Throwable e) { e.printStackTrace(); BugReport.printBug(outErr, e); BugReport.sendBugReport(e, args, crashData); - numericExitCode = BugReport.getExitCodeForThrowable(e); - throw new ShutdownBlazeServerException(numericExitCode, e); + throw new ShutdownBlazeServerException(BugReport.getExitCodeForThrowable(e), e); } finally { env.getEventBus().post(new AfterCommandEvent()); + int numericExitCode = result.getExitCode() == null + ? 0 + : result.getExitCode().getNumericExitCode(); runtime.afterCommand(env, numericExitCode); // Swallow IOException, as we are already in a finally clause Flushables.flushQuietly(outErr.getOutputStream()); @@ -533,7 +541,7 @@ public class BlazeCommandDispatcher { LockingMode.ERROR_OUT, clientDescription, runtime.getClock().currentTimeMillis(), - Optional.empty() /* startupOptionBundles */); + Optional.empty() /* startupOptionBundles */).getExitCode().getNumericExitCode(); } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandResult.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandResult.java new file mode 100644 index 0000000000..c1701c6ece --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandResult.java @@ -0,0 +1,53 @@ +// Copyright 2018 The Bazel Authors. 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.runtime; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.server.CommandProtos.ExecRequest; +import com.google.devtools.build.lib.util.ExitCode; +import javax.annotation.Nullable; + +/** + * The result of a Blaze command. It is usually an exit code, but can be an instruction to the + * client to execute a particular binary for "blaze run". + */ +@Immutable +public final class BlazeCommandResult { + private final ExitCode exitCode; + @Nullable + private final ExecRequest execDescription; + + private BlazeCommandResult(ExitCode exitCode, ExecRequest execDescription) { + this.exitCode = Preconditions.checkNotNull(exitCode); + this.execDescription = execDescription; + } + + public ExitCode getExitCode() { + return exitCode; + } + + @Nullable public ExecRequest getExecRequest() { + return execDescription; + } + + public static BlazeCommandResult exitCode(ExitCode exitCode) { + return new BlazeCommandResult(exitCode, null); + } + + public static BlazeCommandResult execute(ExecRequest execDescription) { + return new BlazeCommandResult(ExitCode.SUCCESS, Preconditions.checkNotNull(execDescription)); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java index 7789605f1d..7319206e80 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java @@ -54,6 +54,8 @@ import com.google.devtools.build.lib.query2.output.OutputFormatter; import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode; import com.google.devtools.build.lib.runtime.commands.InfoItem; import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy; +import com.google.devtools.build.lib.server.CommandProtos.EnvironmentVariable; +import com.google.devtools.build.lib.server.CommandProtos.ExecRequest; import com.google.devtools.build.lib.server.RPCServer; import com.google.devtools.build.lib.server.signal.InterruptSignalHandler; import com.google.devtools.build.lib.shell.JavaSubprocessFactory; @@ -86,9 +88,11 @@ import com.google.devtools.common.options.OptionsParsingException; import com.google.devtools.common.options.OptionsProvider; import com.google.devtools.common.options.TriState; import java.io.BufferedOutputStream; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -702,7 +706,7 @@ public final class BlazeRuntime { return new CommandLineOptions(startupArgs, otherArgs); } - private static void captureSigint() { + private static InterruptSignalHandler captureSigint() { final Thread mainThread = Thread.currentThread(); final AtomicInteger numInterrupts = new AtomicInteger(); @@ -718,7 +722,7 @@ public final class BlazeRuntime { } }; - new InterruptSignalHandler() { + return new InterruptSignalHandler() { @Override public void run() { logger.info("User interrupt"); @@ -743,7 +747,7 @@ public final class BlazeRuntime { * exit status of the program. */ private static int batchMain(Iterable<BlazeModule> modules, String[] args) { - captureSigint(); + InterruptSignalHandler signalHandler = captureSigint(); CommandLineOptions commandLineOptions = splitStartupOptions(modules, args); logger.info( "Running Blaze in batch mode with " @@ -773,10 +777,11 @@ public final class BlazeRuntime { } BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime); + boolean shutdownDone = false; try { logger.info(getRequestLogString(commandLineOptions.getOtherArgs())); - return dispatcher.exec( + BlazeCommandResult result = dispatcher.exec( policy, commandLineOptions.getOtherArgs(), OutErr.SYSTEM_OUT_ERR, @@ -784,14 +789,57 @@ public final class BlazeRuntime { "batch client", runtime.getClock().currentTimeMillis(), Optional.of(startupOptionsFromCommandLine.build())); + if (result.getExecRequest() == null) { + // Simple case: we are given an exit code + return result.getExitCode().getNumericExitCode(); + } + + // Not so simple case: we need to execute a binary on shutdown. exec() is not accessible from + // Java and is impossible on Windows in any case, so we just execute the binary after getting + // out of the way as completely as possible and forward its exit code. + // When this code is executed, no locks are held: the client lock is released by the client + // before it executes any command and the server lock is handled by BlazeCommandDispatcher, + // whose job is done by the time we get here. + runtime.shutdown(); + dispatcher.shutdown(); + shutdownDone = true; + signalHandler.uninstall(); + ExecRequest request = result.getExecRequest(); + String[] argv = new String[request.getArgvCount()]; + for (int i = 0; i < argv.length; i++) { + argv[i] = request.getArgv(i).toString(StandardCharsets.ISO_8859_1); + } + + String workingDirectory = request.getWorkingDirectory().toString(StandardCharsets.ISO_8859_1); + try { + ProcessBuilder process = new ProcessBuilder() + .command(argv) + .directory(new File(workingDirectory)) + .inheritIO(); + + for (int i = 0; i < request.getEnvironmentVariableCount(); i++) { + EnvironmentVariable variable = request.getEnvironmentVariable(i); + process.environment().put(variable.getName().toString(StandardCharsets.ISO_8859_1), + variable.getValue().toString(StandardCharsets.ISO_8859_1)); + } + + return process.start().waitFor(); + } catch (IOException e) { + // We are in batch mode, thus, stdout/stderr are the same as that of the client. + System.err.println("Cannot execute process for 'run' command: " + e.getMessage()); + logger.log(Level.SEVERE, "Exception while executing binary from 'run' command", e); + return ExitCode.LOCAL_ENVIRONMENTAL_ERROR.getNumericExitCode(); + } } catch (BlazeCommandDispatcher.ShutdownBlazeServerException e) { - return e.getExitStatus(); + return e.getExitCode().getNumericExitCode(); } catch (InterruptedException e) { // This is almost main(), so it's okay to just swallow it. We are exiting soon. return ExitCode.INTERRUPTED.getNumericExitCode(); } finally { - runtime.shutdown(); - dispatcher.shutdown(); + if (!shutdownDone) { + runtime.shutdown(); + dispatcher.shutdown(); + } } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java b/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java index 1d8c0d874a..af1d263e08 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BugReport.java @@ -87,7 +87,7 @@ public abstract class BugReport { * halts the runtime in that case. */ public static void handleCrash(Throwable throwable, String... args) { - int exitCode = getExitCodeForThrowable(throwable); + int exitCode = getExitCodeForThrowable(throwable).getNumericExitCode(); try { if (alreadyHandlingCrash.compareAndSet(false, true)) { try { @@ -131,10 +131,10 @@ public abstract class BugReport { } /** Get exit code corresponding to throwable. */ - public static int getExitCodeForThrowable(Throwable throwable) { + public static ExitCode getExitCodeForThrowable(Throwable throwable) { return (Throwables.getRootCause(throwable) instanceof OutOfMemoryError) - ? ExitCode.OOM_ERROR.getNumericExitCode() - : ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode(); + ? ExitCode.OOM_ERROR + : ExitCode.BLAZE_INTERNAL_ERROR; } private static void printThrowableTo(OutErr outErr, Throwable e) { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java index 34a2ebfcdd..f75f06a0b4 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java @@ -481,13 +481,7 @@ public final class CommandEnvironment { */ ExitCode precompleteCommand(ExitCode originalExit) { eventBus.post(new CommandPrecompleteEvent(originalExit)); - // If Blaze did not suffer an infrastructure failure, check for errors in modules. - ExitCode exitCode = originalExit; - ExitCode newExitCode = finalizeExitCode(); - if (!originalExit.isInfrastructureFailure() && newExitCode != null) { - exitCode = newExitCode; - } - return exitCode; + return finalizeExitCode(); } /** diff --git a/src/main/java/com/google/devtools/build/lib/runtime/CommandExecutor.java b/src/main/java/com/google/devtools/build/lib/runtime/CommandExecutor.java index f57c8ca04a..89c3a57911 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/CommandExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/CommandExecutor.java @@ -43,7 +43,7 @@ public class CommandExecutor implements ServerCommand { } @Override - public int exec( + public BlazeCommandResult exec( InvocationPolicy invocationPolicy, List<String> args, OutErr outErr, @@ -75,7 +75,7 @@ public class CommandExecutor implements ServerCommand { shutdown = true; runtime.shutdown(); dispatcher.shutdown(); - return e.getExitStatus(); + return BlazeCommandResult.exitCode(e.getExitCode()); } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java index a02c991839..4803aaf115 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java @@ -22,6 +22,7 @@ import com.google.devtools.build.lib.exec.local.LocalExecutionOptions; import com.google.devtools.build.lib.pkgcache.LoadingOptions; import com.google.devtools.build.lib.pkgcache.PackageCacheOptions; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; @@ -62,7 +63,7 @@ public final class BuildCommand implements BlazeCommand { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { BlazeRuntime runtime = env.getRuntime(); List<String> targets = ProjectFileSupport.getTargets(runtime.getProjectFileProvider(), options); @@ -71,6 +72,7 @@ public final class BuildCommand implements BlazeCommand { runtime.getStartupOptionsProvider(), targets, env.getReporter().getOutErr(), env.getCommandId(), env.getCommandStartTime()); - return new BuildTool(env).processRequest(request, null).getExitCondition(); + ExitCode exitCode = new BuildTool(env).processRequest(request, null).getExitCondition(); + return BlazeCommandResult.exitCode(exitCode); } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java index c2951f1d28..d17b281fbb 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java @@ -17,6 +17,7 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeCommandUtils; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; @@ -127,7 +128,7 @@ public final class CanonicalizeCommand implements BlazeCommand { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { BlazeRuntime runtime = env.getRuntime(); Options canonicalizeOptions = options.getOptions(Options.class); String commandName = canonicalizeOptions.forCommand; @@ -135,7 +136,7 @@ public final class CanonicalizeCommand implements BlazeCommand { if (command == null) { env.getReporter().handle(Event.error("Not a valid command: '" + commandName + "' (should be one of " + Joiner.on(", ").join(runtime.getCommandMap().keySet()) + ")")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } Collection<Class<? extends OptionsBase>> optionsClasses = ImmutableList.<Class<? extends OptionsBase>>builder() @@ -175,9 +176,9 @@ public final class CanonicalizeCommand implements BlazeCommand { } } catch (OptionsParsingException e) { env.getReporter().handle(Event.error(e.getMessage())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } @Override diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java index 721a0644fc..bf88f451a0 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java @@ -21,6 +21,7 @@ import com.google.devtools.build.lib.buildtool.OutputDirectoryLinksUtils; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.runtime.BlazeCommand; import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.ShutdownBlazeServerException; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.shell.CommandException; @@ -127,7 +128,7 @@ public final class CleanCommand implements BlazeCommand { private static final Logger logger = Logger.getLogger(CleanCommand.class.getName()); @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) throws ShutdownBlazeServerException { Options cleanOptions = options.getOptions(Options.class); boolean async = cleanOptions.async; @@ -165,16 +166,16 @@ public final class CleanCommand implements BlazeCommand { .getOptions(BuildRequestOptions.class) .getSymlinkPrefix(env.getRuntime().getProductName()); actuallyClean(env, env.getOutputBase(), cleanOptions.expunge, async, symlinkPrefix); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } catch (IOException e) { env.getReporter().handle(Event.error(e.getMessage())); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } catch (CommandException | ExecException e) { env.getReporter().handle(Event.error(e.getMessage())); - return ExitCode.RUN_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.RUN_FAILURE); } catch (InterruptedException e) { env.getReporter().handle(Event.error("clean interrupted")); - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } } @@ -265,7 +266,7 @@ public final class CleanCommand implements BlazeCommand { // shutdown on expunge cleans if (expunge) { - throw new ShutdownBlazeServerException(0); + throw new ShutdownBlazeServerException(ExitCode.SUCCESS); } System.gc(); } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java index b99002ad9c..1aa6c8dc4e 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java @@ -25,6 +25,7 @@ import com.google.devtools.build.lib.query2.engine.QueryException; import com.google.devtools.build.lib.query2.engine.QueryExpression; import com.google.devtools.build.lib.query2.engine.QueryParser; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; @@ -66,13 +67,13 @@ public final class CqueryCommand implements BlazeCommand { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { if (options.getResidue().isEmpty()) { env.getReporter() .handle( Event.error( "Missing query expression. Use the 'help cquery' command for syntax and help.")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } String query = Joiner.on(' ').join(options.getResidue()); HashMap<String, QueryFunction> functions = new HashMap<>(); @@ -85,7 +86,7 @@ public final class CqueryCommand implements BlazeCommand { } catch (QueryException e) { env.getReporter() .handle(Event.error("Error while parsing '" + query + "': " + e.getMessage())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } List<String> topLevelTargets = options.getOptions(CommonQueryOptions.class).universeScope; @@ -104,6 +105,7 @@ public final class CqueryCommand implements BlazeCommand { env.getReporter().getOutErr(), env.getCommandId(), env.getCommandStartTime()); - return new BuildTool(env).processRequest(request, null, expr).getExitCondition(); + ExitCode exitCode = new BuildTool(env).processRequest(request, null, expr).getExitCondition(); + return BlazeCommandResult.exitCode(exitCode); } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/DumpCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/DumpCommand.java index 96b3149463..6f92ae0a06 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/DumpCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/DumpCommand.java @@ -23,6 +23,7 @@ import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.profiler.memory.AllocationTracker; import com.google.devtools.build.lib.profiler.memory.AllocationTracker.RuleBytes; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeCommandUtils; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.BlazeWorkspace; @@ -166,7 +167,7 @@ public class DumpCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) {} @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { BlazeRuntime runtime = env.getRuntime(); DumpOptions dumpOptions = options.getOptions(DumpOptions.class); @@ -190,7 +191,7 @@ public class DumpCommand implements BlazeCommand { getClass(), optionList, categories, OptionsParser.HelpVerbosity.LONG, runtime.getProductName())); - return ExitCode.ANALYSIS_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.ANALYSIS_FAILURE); } PrintStream out = new PrintStream(env.getReporter().getOutErr().getOutputStream()); try { @@ -241,7 +242,7 @@ public class DumpCommand implements BlazeCommand { out.println(); } - return success ? ExitCode.SUCCESS : ExitCode.ANALYSIS_FAILURE; + return BlazeCommandResult.exitCode(success ? ExitCode.SUCCESS : ExitCode.ANALYSIS_FAILURE); } finally { out.flush(); diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java index 1e163e295e..6f47e3d1fb 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java @@ -29,6 +29,7 @@ import com.google.devtools.build.lib.analysis.NoBuildEvent; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeCommandUtils; import com.google.devtools.build.lib.runtime.BlazeModule; import com.google.devtools.build.lib.runtime.BlazeRuntime; @@ -176,7 +177,7 @@ public final class HelpCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) {} @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { env.getEventBus().post(new NoBuildEvent()); BlazeRuntime runtime = env.getRuntime(); @@ -185,11 +186,11 @@ public final class HelpCommand implements BlazeCommand { if (options.getResidue().isEmpty()) { emitBlazeVersionInfo(outErr, runtime.getProductName()); emitGenericHelp(outErr, runtime); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } if (options.getResidue().size() != 1) { env.getReporter().handle(Event.error("You must specify exactly one command")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } String helpSubject = options.getResidue().get(0); String productName = runtime.getProductName(); @@ -203,7 +204,7 @@ public final class HelpCommand implements BlazeCommand { runtime, getDeprecatedOptionCategoriesDescriptions(productName), helpOptions.useNewCategoryEnum); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); case "target-syntax": emitBlazeVersionInfo(outErr, runtime.getProductName()); emitTargetSyntaxHelp( @@ -212,19 +213,19 @@ public final class HelpCommand implements BlazeCommand { productName, helpOptions.useNewCategoryEnum); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); case "info-keys": emitInfoKeysHelp(env, outErr); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); case "completion": emitCompletionHelp(runtime, outErr); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); case "flags-as-proto": emitFlagsAsProtoHelp(runtime, outErr); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); case "everything-as-html": new HtmlEmitter(runtime, helpOptions.useNewCategoryEnum).emit(outErr); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); default: // fall out } @@ -236,11 +237,11 @@ public final class HelpCommand implements BlazeCommand { // There is a rule with a corresponding name outErr.printOut( BlazeRuleHelpPrinter.getRuleDoc(helpSubject, runtime.getProductName(), provider)); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } else { env.getReporter().handle(Event.error( null, "'" + helpSubject + "' is neither a command nor a build rule")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } } emitBlazeVersionInfo(outErr, productName); @@ -254,7 +255,7 @@ public final class HelpCommand implements BlazeCommand { productName, helpOptions.useNewCategoryEnum)); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } private void emitBlazeVersionInfo(OutErr outErr, String productName) { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java index ca32232cc0..e10e5735e1 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoCommand.java @@ -22,6 +22,7 @@ import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; @@ -101,7 +102,8 @@ public class InfoCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) { } @Override - public ExitCode exec(final CommandEnvironment env, final OptionsProvider optionsProvider) { + public BlazeCommandResult exec( + final CommandEnvironment env, final OptionsProvider optionsProvider) { final BlazeRuntime runtime = env.getRuntime(); env.getReporter().switchToAnsiAllowingHandler(); Options infoOptions = optionsProvider.getOptions(Options.class); @@ -152,7 +154,7 @@ public class InfoCommand implements BlazeCommand { List<String> residue = optionsProvider.getResidue(); if (residue.size() > 1) { env.getReporter().handle(Event.error("at most one key may be specified")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } String key = residue.size() == 1 ? residue.get(0) : null; @@ -163,14 +165,14 @@ public class InfoCommand implements BlazeCommand { value = items.get(key).get(configurationSupplier, env); } else { env.getReporter().handle(Event.error("unknown key: '" + key + "'")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } try { outErr.getOutputStream().write(value); outErr.getOutputStream().flush(); } catch (IOException e) { env.getReporter().handle(Event.error("Cannot write info block: " + e.getMessage())); - return ExitCode.ANALYSIS_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.ANALYSIS_FAILURE); } } else { // print them all configurationSupplier.get(); // We'll need this later anyway @@ -184,15 +186,15 @@ public class InfoCommand implements BlazeCommand { } } } catch (AbruptExitException e) { - return e.getExitCode(); + return BlazeCommandResult.exitCode(e.getExitCode()); } catch (ExitCausingRuntimeException e) { - return e.getExitCode(); + return BlazeCommandResult.exitCode(e.getExitCode()); } catch (IOException e) { - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } catch (InterruptedException e) { - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } static Map<String, InfoItem> getHardwiredInfoItemMap(OptionsProvider commandOptions, diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/LicenseCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/LicenseCommand.java index b735107e79..7ad512acb6 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/LicenseCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/LicenseCommand.java @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.runtime.commands; import com.google.common.io.Files; import com.google.devtools.build.lib.analysis.NoBuildEvent; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.util.ExitCode; @@ -52,7 +53,7 @@ public class LicenseCommand implements BlazeCommand { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { env.getEventBus().post(new NoBuildEvent()); OutErr outErr = env.getReporter().getOutErr(); @@ -81,7 +82,7 @@ public class LicenseCommand implements BlazeCommand { printJavaLicenseFiles(outErr, bundledJre); } - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } private static void printJavaLicenseFiles(OutErr outErr, Path bundledJdkOrJre) { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java index 930575f674..6b4b266076 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java @@ -44,6 +44,7 @@ import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.pkgcache.LoadingOptions; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; @@ -99,7 +100,7 @@ public final class PrintActionCommand implements BlazeCommand { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { LoadingOptions loadingOptions = options.getOptions(LoadingOptions.class); @@ -107,7 +108,7 @@ public final class PrintActionCommand implements BlazeCommand { PrintActionRunner runner = new PrintActionRunner(loadingOptions.compileOneDependency, options, env.getReporter().getOutErr(), options.getResidue(), Sets.newHashSet(printActionOptions.printActionMnemonics)); - return runner.printActionsForTargets(env); + return BlazeCommandResult.exitCode(runner.printActionsForTargets(env)); } /** diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java index 951e5c490b..f39f0054b3 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java @@ -30,6 +30,7 @@ import com.google.devtools.build.lib.profiler.statistics.MultiProfileStatistics; import com.google.devtools.build.lib.profiler.statistics.PhaseStatistics; import com.google.devtools.build.lib.profiler.statistics.PhaseSummaryStatistics; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.util.ExitCode; @@ -212,7 +213,7 @@ public final class ProfileCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) {} @Override - public ExitCode exec(final CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(final CommandEnvironment env, OptionsProvider options) { ProfileOptions opts = options.getOptions(ProfileOptions.class); @@ -325,7 +326,7 @@ public final class ProfileCommand implements BlazeCommand { } } } - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } /** diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java index d34ae7b4a5..75b20fea68 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java @@ -36,6 +36,7 @@ import com.google.devtools.build.lib.query2.output.OutputFormatter.StreamedForma import com.google.devtools.build.lib.query2.output.QueryOptions; import com.google.devtools.build.lib.query2.output.QueryOutputUtils; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; @@ -85,7 +86,7 @@ public final class QueryCommand implements BlazeCommand { * (only when --keep_going is in effect.) */ @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { BlazeRuntime runtime = env.getRuntime(); QueryOptions queryOptions = options.getOptions(QueryOptions.class); @@ -93,10 +94,10 @@ public final class QueryCommand implements BlazeCommand { env.setupPackageCache(options, runtime.getDefaultsPackageContent()); } catch (InterruptedException e) { env.getReporter().handle(Event.error("query interrupted")); - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } catch (AbruptExitException e) { env.getReporter().handle(Event.error(null, "Unknown error: " + e.getMessage())); - return e.getExitCode(); + return BlazeCommandResult.exitCode(e.getExitCode()); } String query; @@ -104,7 +105,7 @@ public final class QueryCommand implements BlazeCommand { if (!queryOptions.queryFile.isEmpty()) { env.getReporter() .handle(Event.error("Command-line query and --query_file cannot both be specified")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } query = Joiner.on(' ').join(options.getResidue()); } else if (!queryOptions.queryFile.isEmpty()) { @@ -115,13 +116,13 @@ public final class QueryCommand implements BlazeCommand { } catch (IOException e) { env.getReporter() .handle(Event.error("I/O error reading from " + residuePath.getPathString())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } } else { env.getReporter().handle(Event.error(String.format( "missing query expression. Type '%s help query' for syntax and help", runtime.getProductName()))); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } Iterable<OutputFormatter> formatters = runtime.getQueryOutputFormatters(); @@ -131,7 +132,7 @@ public final class QueryCommand implements BlazeCommand { env.getReporter().handle(Event.error( String.format("Invalid output format '%s'. Valid values are: %s", queryOptions.outputFormat, OutputFormatter.formatterNames(formatters)))); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } Set<Setting> settings = queryOptions.toSettings(); @@ -151,14 +152,14 @@ public final class QueryCommand implements BlazeCommand { } catch (QueryException e) { env.getReporter() .handle(Event.error(null, "Error while parsing '" + query + "': " + e.getMessage())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } try { formatter.verifyCompatible(queryEnv, expr); } catch (QueryException e) { env.getReporter().handle(Event.error(e.getMessage())); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } expr = queryEnv.transformParsedQuery(expr); @@ -194,21 +195,21 @@ public final class QueryCommand implements BlazeCommand { // TODO(bazel-team): this is a kludge to fix a bug observed in the wild. We should make // sure no null error messages ever get in. .handle(Event.error(e.getMessage() == null ? e.toString() : e.getMessage())); - return ExitCode.ANALYSIS_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.ANALYSIS_FAILURE); } catch (InterruptedException e) { catastrophe = false; IOException ioException = callback.getIoException(); if (ioException == null || ioException instanceof ClosedByInterruptException) { env.getReporter().handle(Event.error("query interrupted")); - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } else { env.getReporter().handle(Event.error("I/O error: " + e.getMessage())); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } } catch (IOException e) { catastrophe = false; env.getReporter().handle(Event.error("I/O error: " + e.getMessage())); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } finally { if (!catastrophe) { try { @@ -216,7 +217,7 @@ public final class QueryCommand implements BlazeCommand { } catch (IOException e) { env.getReporter().handle( Event.error("Failed to flush query results: " + e.getMessage())); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } } } @@ -236,17 +237,17 @@ public final class QueryCommand implements BlazeCommand { queryOptions.aspectDeps.createResolver(env.getPackageManager(), env.getReporter())); } catch (ClosedByInterruptException | InterruptedException e) { env.getReporter().handle(Event.error("query interrupted")); - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } catch (IOException e) { env.getReporter().handle(Event.error("I/O error: " + e.getMessage())); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } finally { try { out.flush(); } catch (IOException e) { env.getReporter().handle( Event.error("Failed to flush query results: " + e.getMessage())); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } } } @@ -258,7 +259,7 @@ public final class QueryCommand implements BlazeCommand { ExitCode exitCode = result.getSuccess() ? ExitCode.SUCCESS : ExitCode.PARTIAL_ANALYSIS_FAILURE; env.getEventBus() .post(new NoBuildRequestFinishedEvent(exitCode, runtime.getClock().currentTimeMillis())); - return exitCode; + return BlazeCommandResult.exitCode(exitCode); } /** diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java index 921fd7dda0..8535616a4b 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java @@ -46,9 +46,12 @@ import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.pkgcache.LoadingFailedException; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.runtime.ProcessWrapperUtil; +import com.google.devtools.build.lib.server.CommandProtos.EnvironmentVariable; +import com.google.devtools.build.lib.server.CommandProtos.ExecRequest; import com.google.devtools.build.lib.shell.AbnormalTerminationException; import com.google.devtools.build.lib.shell.BadExitStatusException; import com.google.devtools.build.lib.shell.CommandException; @@ -71,11 +74,13 @@ import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.OptionsProvider; +import com.google.protobuf.ByteString; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; /** * Builds and run a target with the given command line arguments. @@ -92,9 +97,21 @@ import java.util.List; completion = "label-bin", binaryStdErr = true) public class RunCommand implements BlazeCommand { - public static class RunOptions extends OptionsBase { @Option( + name = "direct_run", + category = "run", + defaultValue = "false", + documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS, + effectTags = {OptionEffectTag.EXECUTION}, + help = "If set, the 'run' command will execute the binary to be executed in the terminal " + + "where the command was called. Otherwise, it'll be executed as a child of the server " + + "process. If set, the binary will have access to direct terminal I/O and the command " + + "lock will not be held during its execution. This makes it possible to run other " + + "commands in parallel.") + public boolean direct; + + @Option( name = "script_path", category = "run", defaultValue = "null", @@ -139,7 +156,7 @@ public class RunCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { RunOptions runOptions = options.getOptions(RunOptions.class); // This list should look like: ["//executable:target", "arg1", "arg2"] List<String> targetAndArgs = options.getResidue(); @@ -147,7 +164,7 @@ public class RunCommand implements BlazeCommand { // The user must at the least specify an executable target. if (targetAndArgs.isEmpty()) { env.getReporter().handle(Event.error("Must specify a target to run")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } String targetString = targetAndArgs.get(0); List<String> runTargetArgs = targetAndArgs.subList(1, targetAndArgs.size()); @@ -172,7 +189,7 @@ public class RunCommand implements BlazeCommand { if (!result.getSuccess()) { env.getReporter().handle(Event.error("Build failed. Not running target")); - return result.getExitCondition(); + return BlazeCommandResult.exitCode(result.getExitCondition()); } // Make sure that we have exactly 1 built target (excluding --run_under), @@ -187,25 +204,25 @@ public class RunCommand implements BlazeCommand { int maxTargets = runUnder != null && runUnder.getLabel() != null ? 2 : 1; if (targetsBuilt.size() > maxTargets) { env.getReporter().handle(Event.error(SINGLE_TARGET_MESSAGE)); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } for (ConfiguredTarget target : targetsBuilt) { ExitCode targetValidation = fullyValidateTarget(env, target); if (!targetValidation.equals(ExitCode.SUCCESS)) { - return targetValidation; + return BlazeCommandResult.exitCode(targetValidation); } if (runUnder != null && target.getLabel().equals(runUnder.getLabel())) { if (runUnderTarget != null) { env.getReporter().handle(Event.error( null, "Can't identify the run_under target from multiple options?")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } runUnderTarget = target; } else if (targetToRun == null) { targetToRun = target; } else { env.getReporter().handle(Event.error(SINGLE_TARGET_MESSAGE)); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } } } @@ -215,7 +232,7 @@ public class RunCommand implements BlazeCommand { } if (targetToRun == null) { env.getReporter().handle(Event.error(NO_TARGET_MESSAGE)); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } Path executablePath = Preconditions.checkNotNull( @@ -231,13 +248,13 @@ public class RunCommand implements BlazeCommand { env.getReporter() .handle( Event.error("--nobuild_runfile_manifests is incompatible with the \"run\" command")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } try { workingDir = ensureRunfilesBuilt(env, targetToRun); } catch (CommandException e) { env.getReporter().handle(Event.error("Error creating runfiles: " + e.getMessage())); - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR; + return BlazeCommandResult.exitCode(ExitCode.LOCAL_ENVIRONMENTAL_ERROR); } List<String> args = Lists.newArrayList(); @@ -250,7 +267,7 @@ public class RunCommand implements BlazeCommand { Iterables.addAll(args, targetArgs.arguments()); } catch (CommandLineExpansionException e) { env.getReporter().handle(Event.error("Could not expand target command line: " + e)); - return ExitCode.ANALYSIS_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.ANALYSIS_FAILURE); } } args.addAll(runTargetArgs); @@ -271,7 +288,8 @@ public class RunCommand implements BlazeCommand { // process-wrapper does not work on Windows (nor is it necessary), so don't use it // on that platform. Also we skip it when writing the command-line to a file instead // of executing it directly. - if (OS.getCurrent() != OS.WINDOWS && runOptions.scriptPath == null) { + if (OS.getCurrent() != OS.WINDOWS && runOptions.scriptPath == null + && !runOptions.direct) { Preconditions.checkState(ProcessWrapperUtil.isSupported(env), "process-wraper not found in embedded tools"); cmdLine.add(ProcessWrapperUtil.getProcessWrapper(env).getPathString()); @@ -317,19 +335,38 @@ public class RunCommand implements BlazeCommand { CommandDescriptionForm.COMPLETE_UNISOLATED, cmdLine, null, workingDir.getPathString()); if (writeScript(env, runOptions.scriptPath, unisolatedCommand)) { - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } else { - return ExitCode.RUN_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.RUN_FAILURE); } } env.getReporter().handle(Event.info( null, "Running command line: " + ShellEscaper.escapeJoinAll(prettyCmdLine))); - com.google.devtools.build.lib.shell.Command command = new CommandBuilder() - .addArgs(cmdLine).setEnv(env.getClientEnv()).setWorkingDir(workingDir).build(); + if (runOptions.direct) { + ExecRequest.Builder execDescription = ExecRequest.newBuilder() + .setWorkingDirectory( + ByteString.copyFrom(workingDir.getPathString(), StandardCharsets.ISO_8859_1)); + + for (String arg : cmdLine) { + execDescription.addArgv(ByteString.copyFrom(arg, StandardCharsets.ISO_8859_1)); + } + + for (Map.Entry<String, String> variable : env.getClientEnv().entrySet()) { + execDescription.addEnvironmentVariable(EnvironmentVariable.newBuilder() + .setName(ByteString.copyFrom(variable.getKey(), StandardCharsets.ISO_8859_1)) + .setValue(ByteString.copyFrom(variable.getValue(), StandardCharsets.ISO_8859_1)) + .build()); + } + + return BlazeCommandResult.execute(execDescription.build()); + } try { + com.google.devtools.build.lib.shell.Command command = new CommandBuilder() + .addArgs(cmdLine).setEnv(env.getClientEnv()).setWorkingDir(workingDir).build(); + // Restore a raw EventHandler if it is registered. This allows for blaze run to produce the // actual output of the command being run even if --color=no is specified. env.getReporter().switchToAnsiAllowingHandler(); @@ -341,19 +378,19 @@ public class RunCommand implements BlazeCommand { .execute(outErr.getOutputStream(), outErr.getErrorStream()) .getTerminationStatus() .getExitCode(); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } catch (BadExitStatusException e) { String message = "Non-zero return code '" + e.getResult().getTerminationStatus().getExitCode() + "' from command: " + e.getMessage(); env.getReporter().handle(Event.error(message)); - return ExitCode.RUN_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.RUN_FAILURE); } catch (AbnormalTerminationException e) { // The process was likely terminated by a signal in this case. - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } catch (CommandException e) { env.getReporter().handle(Event.error("Error running program: " + e.getMessage())); - return ExitCode.RUN_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.RUN_FAILURE); } } @@ -480,7 +517,8 @@ public class RunCommand implements BlazeCommand { * Performs all available validation checks on an individual target. * * @param configuredTarget ConfiguredTarget to validate - * @return ExitCode.SUCCESS if all checks succeeded, otherwise a different error code. + * @return BlazeCommandResult.exitCode(ExitCode.SUCCESS) if all checks succeeded, otherwise a + * different error code. * @throws IllegalStateException if unable to find a target from the package manager. */ private ExitCode fullyValidateTarget(CommandEnvironment env, ConfiguredTarget configuredTarget) { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/ShutdownCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/ShutdownCommand.java index 09423c0b56..f242c9d7ce 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/ShutdownCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/ShutdownCommand.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.runtime.commands; import com.google.devtools.build.lib.runtime.BlazeCommand; import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.ShutdownBlazeServerException; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.util.ExitCode; @@ -56,7 +57,7 @@ public final class ShutdownCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) {} @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) throws ShutdownBlazeServerException { int limit = options.getOptions(Options.class).heapSizeLimit; @@ -69,9 +70,9 @@ public final class ShutdownCommand implements BlazeCommand { if (limit == 0 || Runtime.getRuntime().totalMemory() > limit * 1000L * 1000) { - throw new ShutdownBlazeServerException(0); + throw new ShutdownBlazeServerException(ExitCode.SUCCESS); } - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java index 1e3f61a97a..5887aa59da 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java @@ -29,6 +29,7 @@ import com.google.devtools.build.lib.exec.TestStrategy.TestOutputFormat; import com.google.devtools.build.lib.runtime.AggregatingTestListener; import com.google.devtools.build.lib.runtime.BlazeCommand; import com.google.devtools.build.lib.runtime.BlazeCommandEventHandler; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeRuntime; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; @@ -81,7 +82,7 @@ public class TestCommand implements BlazeCommand { } @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { TestOutputFormat testOutput = options.getOptions(ExecutionOptions.class).testOutput; if (testOutput == TestStrategy.TestOutputFormat.STREAMED) { env.getReporter().handle(Event.warn( @@ -105,7 +106,7 @@ public class TestCommand implements BlazeCommand { return doTest(env, options, testListener); } - private ExitCode doTest(CommandEnvironment env, + private BlazeCommandResult doTest(CommandEnvironment env, OptionsProvider options, AggregatingTestListener testListener) { BlazeRuntime runtime = env.getRuntime(); @@ -134,7 +135,7 @@ public class TestCommand implements BlazeCommand { ExitCode exitCode = buildResult.getSuccess() ? ExitCode.PARSING_FAILURE : buildResult.getExitCondition(); env.getEventBus().post(new TestingCompleteEvent(exitCode, buildResult.getStopTime())); - return exitCode; + return BlazeCommandResult.exitCode(exitCode); } // TODO(bazel-team): the check above shadows NO_TESTS_FOUND, but switching the conditions breaks // more tests @@ -146,7 +147,7 @@ public class TestCommand implements BlazeCommand { buildResult.getSuccess() ? ExitCode.NO_TESTS_FOUND : buildResult.getExitCondition(); env.getEventBus() .post(new NoTestsFound(exitCode, env.getRuntime().getClock().currentTimeMillis())); - return exitCode; + return BlazeCommandResult.exitCode(exitCode); } boolean buildSuccess = buildResult.getSuccess(); @@ -165,7 +166,7 @@ public class TestCommand implements BlazeCommand { ? (testSuccess ? ExitCode.SUCCESS : ExitCode.TESTS_FAILED) : buildResult.getExitCondition(); env.getEventBus().post(new TestingCompleteEvent(exitCode, buildResult.getStopTime())); - return exitCode; + return BlazeCommandResult.exitCode(exitCode); } /** diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/VersionCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/VersionCommand.java index eff8700248..729c3b7c25 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/VersionCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/VersionCommand.java @@ -17,6 +17,7 @@ import com.google.devtools.build.lib.analysis.BlazeVersionInfo; import com.google.devtools.build.lib.analysis.NoBuildEvent; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.util.ExitCode; @@ -38,14 +39,14 @@ public final class VersionCommand implements BlazeCommand { public void editOptions(OptionsParser optionsParser) {} @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { BlazeVersionInfo info = BlazeVersionInfo.instance(); if (info.getSummary() == null) { env.getReporter().handle(Event.error("Version information not available")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } env.getEventBus().post(new NoBuildEvent()); env.getReporter().getOutErr().printOutLn(info.getSummary()); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java index acdd49784d..d382daf5c4 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java @@ -28,6 +28,7 @@ import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.rules.android.WriteAdbArgsAction; import com.google.devtools.build.lib.rules.android.WriteAdbArgsAction.StartType; import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.Command; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.runtime.CommonCommandOptions; @@ -167,7 +168,7 @@ public class MobileInstallCommand implements BlazeCommand { private static final String NO_TARGET_MESSAGE = "No targets found to run"; @Override - public ExitCode exec(CommandEnvironment env, OptionsProvider options) { + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { Options mobileInstallOptions = options.getOptions(Options.class); WriteAdbArgsAction.Options adbOptions = options.getOptions(WriteAdbArgsAction.Options.class); @@ -193,7 +194,8 @@ public class MobileInstallCommand implements BlazeCommand { env.getReporter().getOutErr(), env.getCommandId(), env.getCommandStartTime()); - return new BuildTool(env).processRequest(request, null).getExitCondition(); + ExitCode exitCode = new BuildTool(env).processRequest(request, null).getExitCondition(); + return BlazeCommandResult.exitCode(exitCode); } // This list should look like: ["//executable:target", "arg1", "arg2"] @@ -202,7 +204,7 @@ public class MobileInstallCommand implements BlazeCommand { // The user must at least specify an executable target. if (targetAndArgs.isEmpty()) { env.getReporter().handle(Event.error("Must specify a target to run")); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } List<String> targets = ImmutableList.of(targetAndArgs.get(0)); @@ -222,17 +224,17 @@ public class MobileInstallCommand implements BlazeCommand { if (!result.getSuccess()) { env.getReporter().handle(Event.error("Build failed. Not running target")); - return result.getExitCondition(); + return BlazeCommandResult.exitCode(result.getExitCondition()); } Collection<ConfiguredTarget> targetsBuilt = result.getSuccessfulTargets(); if (targetsBuilt == null) { env.getReporter().handle(Event.error(NO_TARGET_MESSAGE)); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } if (targetsBuilt.size() != 1) { env.getReporter().handle(Event.error(SINGLE_TARGET_MESSAGE)); - return ExitCode.COMMAND_LINE_ERROR; + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } ConfiguredTarget targetToRun = Iterables.getOnlyElement(targetsBuilt); @@ -299,7 +301,7 @@ public class MobileInstallCommand implements BlazeCommand { .execute(outErr.getOutputStream(), outErr.getErrorStream()) .getTerminationStatus() .getExitCode(); - return ExitCode.SUCCESS; + return BlazeCommandResult.exitCode(ExitCode.SUCCESS); } catch (BadExitStatusException e) { String message = "Non-zero return code '" @@ -307,13 +309,13 @@ public class MobileInstallCommand implements BlazeCommand { + "' from command: " + e.getMessage(); env.getReporter().handle(Event.error(message)); - return ExitCode.RUN_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.RUN_FAILURE); } catch (AbnormalTerminationException e) { // The process was likely terminated by a signal in this case. - return ExitCode.INTERRUPTED; + return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); } catch (CommandException e) { env.getReporter().handle(Event.error("Error running program: " + e.getMessage())); - return ExitCode.RUN_FAILURE; + return BlazeCommandResult.exitCode(ExitCode.RUN_FAILURE); } } diff --git a/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java b/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java index c412131ead..aa575da2b5 100644 --- a/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java +++ b/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java @@ -26,6 +26,7 @@ import com.google.devtools.build.lib.clock.BlazeClock; import com.google.devtools.build.lib.clock.Clock; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.CommandExecutor; import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy; import com.google.devtools.build.lib.server.CommandProtos.CancelRequest; @@ -814,7 +815,7 @@ public class GrpcServerImpl implements RPCServer { } String commandId; - int exitCode; + BlazeCommandResult result; // TODO(b/63925394): This information needs to be passed to the GotOptionsEvent, which does not // currently have the explicit startup options. See Improved Command Line Reporting design doc @@ -847,7 +848,7 @@ public class GrpcServerImpl implements RPCServer { try { InvocationPolicy policy = InvocationPolicyParser.parsePolicy(request.getInvocationPolicy()); - exitCode = + result = commandExecutor.exec( policy, args.build(), @@ -858,10 +859,10 @@ public class GrpcServerImpl implements RPCServer { Optional.of(startupOptions.build())); } catch (OptionsParsingException e) { rpcOutErr.printErrLn(e.getMessage()); - exitCode = ExitCode.COMMAND_LINE_ERROR.getNumericExitCode(); + result = BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); } } catch (InterruptedException e) { - exitCode = ExitCode.INTERRUPTED.getNumericExitCode(); + result = BlazeCommandResult.exitCode(ExitCode.INTERRUPTED); commandId = ""; // The default value, the client will ignore it } @@ -883,17 +884,22 @@ public class GrpcServerImpl implements RPCServer { if (shutdown) { server.shutdown(); } - RunResponse response = - RunResponse.newBuilder() - .setCookie(responseCookie) - .setCommandId(commandId) - .setFinished(true) - .setExitCode(exitCode) - .setTerminationExpected(shutdown) - .build(); + + RunResponse.Builder response = RunResponse.newBuilder() + .setCookie(responseCookie) + .setCommandId(commandId) + .setFinished(true) + .setTerminationExpected(shutdown); + + if (result.getExecRequest() != null) { + response.setExitCode(0); + response.setExecRequest(result.getExecRequest()); + } else { + response.setExitCode(result.getExitCode().getNumericExitCode()); + } try { - observer.onNext(response); + observer.onNext(response.build()); observer.onCompleted(); } catch (StatusRuntimeException e) { // The client cancelled the call. Log an error and go on. diff --git a/src/main/java/com/google/devtools/build/lib/server/ServerCommand.java b/src/main/java/com/google/devtools/build/lib/server/ServerCommand.java index f0574cdbaa..aed9d73b98 100644 --- a/src/main/java/com/google/devtools/build/lib/server/ServerCommand.java +++ b/src/main/java/com/google/devtools/build/lib/server/ServerCommand.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.server; import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.io.OutErr; @@ -35,7 +36,7 @@ public interface ServerCommand { * --[no]flag or --flag=value form. If we don't have access to this information (--batch), * leave this parameter as Optional.empty(). */ - int exec( + BlazeCommandResult exec( InvocationPolicy policy, List<String> args, OutErr outErr, diff --git a/src/main/java/com/google/devtools/build/lib/shell/Command.java b/src/main/java/com/google/devtools/build/lib/shell/Command.java index 0e8c5a0247..1584275184 100644 --- a/src/main/java/com/google/devtools/build/lib/shell/Command.java +++ b/src/main/java/com/google/devtools/build/lib/shell/Command.java @@ -46,7 +46,7 @@ import javax.annotation.Nullable; * <p>The most basic use-case for this class is as follows: * <pre> * String[] args = { "/bin/du", "-s", directory }; - * CommandResult result = new Command(args).execute(); + * BlazeCommandResult result = new Command(args).execute(); * String output = new String(result.getStdout()); * </pre> * which writes the output of the {@code du(1)} command into {@code output}. More complex cases @@ -78,7 +78,7 @@ import javax.annotation.Nullable; * <p>To execute a shell command directly, use the following pattern: * <pre> * String[] args = { "/bin/sh", "-c", shellCommand }; - * CommandResult result = new Command(args).execute(); + * BlazeCommandResult result = new Command(args).execute(); * </pre> * {@code shellCommand} is a complete Bourne shell program, possibly containing all kinds of * unescaped metacharacters. For example, here's a shell command that enumerates the working diff --git a/src/main/protobuf/command_server.proto b/src/main/protobuf/command_server.proto index b55a68817d..bad35abdb6 100644 --- a/src/main/protobuf/command_server.proto +++ b/src/main/protobuf/command_server.proto @@ -68,6 +68,20 @@ message StartupOption { bytes source = 2; } +// Description of an environment variable +message EnvironmentVariable { + bytes name = 1; + bytes value = 2; +} + +// Description of a request by the server to the client to execute a binary +// after the command invocation finishes. +message ExecRequest { + bytes working_directory = 1; + repeated bytes argv = 2; + repeated EnvironmentVariable environment_variable = 3; +} + // Contains metadata and result data for a command execution. message RunResponse { // Request cookie from the output base of the server. This serves as a @@ -96,6 +110,10 @@ message RunResponse { // Whether the command has shut down the server; if set, the client should // wait until the server process dies before finishing. bool termination_expected = 7; + + // A command to exec() after the command invocation finishes. Should only be + // present if finished is set. + ExecRequest exec_request = 8; } // Passed to CommandServer.cancel to initiate graceful cancellation of an |