/* * Copyright 2016-present Facebook, Inc. * * 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.facebook.buck.util; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; public interface ProcessExecutor { /** * Convenience method for {@link #launchAndExecute(ProcessExecutorParams, Set, Optional, Optional, * Optional)} with boolean values set to {@code false} and optional values set to absent. */ Result launchAndExecute(ProcessExecutorParams params) throws InterruptedException, IOException; Result launchAndExecute(ProcessExecutorParams params, ImmutableMap<String, String> context) throws InterruptedException, IOException; /** * Launches then executes a process with the specified {@code params}. * * <p>If {@code options} contains {@link Option#PRINT_STD_OUT}, then the stdout of the process * will be written directly to the stdout passed to the constructor of this executor. Otherwise, * the stdout of the process will be made available via {@link Result#getStdout()}. * * <p>If {@code options} contains {@link Option#PRINT_STD_ERR}, then the stderr of the process * will be written directly to the stderr passed to the constructor of this executor. Otherwise, * the stderr of the process will be made available via {@link Result#getStderr()}. */ Result launchAndExecute( ProcessExecutorParams params, Set<Option> options, Optional<String> stdin, Optional<Long> timeOutMs, Optional<Consumer<Process>> timeOutHandler) throws InterruptedException, IOException; Result launchAndExecute( ProcessExecutorParams params, ImmutableMap<String, String> context, Set<Option> options, Optional<String> stdin, Optional<Long> timeOutMs, Optional<Consumer<Process>> timeOutHandler) throws InterruptedException, IOException; /** Launches a {@link Process} given {@link ProcessExecutorParams}. */ LaunchedProcess launchProcess(ProcessExecutorParams params) throws IOException; LaunchedProcess launchProcess(ProcessExecutorParams params, ImmutableMap<String, String> context) throws IOException; /** Terminates a process previously returned by {@link #launchProcess(ProcessExecutorParams)}. */ void destroyLaunchedProcess(LaunchedProcess launchedProcess); /** * Blocks while waiting for a process previously returned by {@link * #launchProcess(ProcessExecutorParams)} to exit, then returns the exit code of the process. * * <p>After this method returns, the {@code launchedProcess} can no longer be passed to any * methods of this object. */ Result waitForLaunchedProcess(LaunchedProcess launchedProcess) throws InterruptedException; /** As {@link #waitForLaunchedProcess(LaunchedProcess)} but with a timeout in milliseconds. */ Result waitForLaunchedProcessWithTimeout( LaunchedProcess launchedProcess, long millis, Optional<Consumer<Process>> timeOutHandler) throws InterruptedException; /** * Options for {@link ProcessExecutor#launchAndExecute(ProcessExecutorParams, Set, Optional, * Optional, Optional)}. */ public enum Option { PRINT_STD_OUT, PRINT_STD_ERR, /** If set, will not highlight output to stdout or stderr when printing. */ EXPECTING_STD_OUT, EXPECTING_STD_ERR, /** * If set, do not write output to stdout or stderr. However, if the process exits with a * non-zero exit code, then the stdout and stderr from the process will be presented to the user * to aid in debugging. */ IS_SILENT, } /** Represents a running process returned by {@link #launchProcess(ProcessExecutorParams)}. */ public interface LaunchedProcess { boolean isAlive(); OutputStream getOutputStream(); InputStream getInputStream(); InputStream getErrorStream(); } /** * Wraps a {@link Process} and exposes only its I/O streams, so callers have to pass it back to * this class. */ @VisibleForTesting public static class LaunchedProcessImpl implements LaunchedProcess { public final Process process; public LaunchedProcessImpl(Process process) { this.process = process; } @Override public boolean isAlive() { return process.isAlive(); } @Override public OutputStream getOutputStream() { return process.getOutputStream(); } @Override public InputStream getInputStream() { return process.getInputStream(); } @Override public InputStream getErrorStream() { return process.getErrorStream(); } } /** * Values from the result of {@link ProcessExecutor#launchAndExecute(ProcessExecutorParams, Set, * Optional, Optional, Optional)}. */ public static class Result { private final int exitCode; private final boolean timedOut; private final Optional<String> stdout; private final Optional<String> stderr; public Result( int exitCode, boolean timedOut, Optional<String> stdout, Optional<String> stderr) { this.exitCode = exitCode; this.timedOut = timedOut; this.stdout = stdout; this.stderr = stderr; } public Result(int exitCode, String stdout, String stderr) { this(exitCode, /* timedOut */ false, Optional.of(stdout), Optional.of(stderr)); } public Result(int exitCode) { this(exitCode, /* timedOut */ false, Optional.empty(), Optional.empty()); } public int getExitCode() { return exitCode; } public boolean isTimedOut() { return timedOut; } public Optional<String> getStdout() { return stdout; } public Optional<String> getStderr() { return stderr; } public String getMessageForUnexpectedResult(String subject) { return getMessageForResult(subject + " finished with unexpected result"); } public String getMessageForResult(String message) { return String.format( "%s:\n" + "exit code: %s\n" + "stdout:\n" + "%s" + "\n" + "stderr:\n" + "%s" + "\n", message, getExitCode(), MoreStrings.truncatePretty(getStdout().orElse("")), MoreStrings.truncatePretty(getStderr().orElse(""))); } } /** Makes a clone of this process executor with the stdout and stderr streams overridden. */ ProcessExecutor cloneWithOutputStreams(PrintStream stdOutStream, PrintStream stdErrStream); }