aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/remote
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/remote')
-rw-r--r--src/main/java/com/google/devtools/build/lib/remote/BUILD1
-rw-r--r--src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java56
-rw-r--r--src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java16
-rw-r--r--src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnStrategy.java268
4 files changed, 125 insertions, 216 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/remote/BUILD b/src/main/java/com/google/devtools/build/lib/remote/BUILD
index 6d42b4320f..4df5c718e8 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/remote/BUILD
@@ -22,6 +22,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib:util",
"//src/main/java/com/google/devtools/build/lib:vfs",
"//src/main/java/com/google/devtools/build/lib/actions",
+ "//src/main/java/com/google/devtools/build/lib/exec/apple",
"//src/main/java/com/google/devtools/build/lib/exec/local",
"//src/main/java/com/google/devtools/build/lib/standalone",
"//src/main/java/com/google/devtools/common/options",
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java
index af15906e97..01d99aefd2 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java
@@ -11,7 +11,6 @@
// 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.remote;
import com.google.common.base.Preconditions;
@@ -19,20 +18,24 @@ import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.ActionInputFileCache;
import com.google.devtools.build.lib.actions.ResourceManager;
-import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions;
import com.google.devtools.build.lib.exec.ActionContextProvider;
import com.google.devtools.build.lib.exec.ActionInputPrefetcher;
import com.google.devtools.build.lib.exec.ExecutionOptions;
+import com.google.devtools.build.lib.exec.SpawnRunner;
+import com.google.devtools.build.lib.exec.apple.XCodeLocalEnvProvider;
+import com.google.devtools.build.lib.exec.local.LocalEnvProvider;
import com.google.devtools.build.lib.exec.local.LocalExecutionOptions;
+import com.google.devtools.build.lib.exec.local.LocalSpawnRunner;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import com.google.devtools.build.lib.standalone.StandaloneSpawnStrategy;
+import com.google.devtools.build.lib.util.OS;
/**
* Provide a remote execution context.
*/
final class RemoteActionContextProvider extends ActionContextProvider {
private final CommandEnvironment env;
+ private RemoteSpawnRunner spawnRunner;
private RemoteSpawnStrategy spawnStrategy;
RemoteActionContextProvider(CommandEnvironment env) {
@@ -43,17 +46,6 @@ final class RemoteActionContextProvider extends ActionContextProvider {
public void init(
ActionInputFileCache actionInputFileCache, ActionInputPrefetcher actionInputPrefetcher) {
ExecutionOptions executionOptions = env.getOptions().getOptions(ExecutionOptions.class);
- LocalExecutionOptions localExecutionOptions =
- env.getOptions().getOptions(LocalExecutionOptions.class);
- SpawnActionContext fallbackStrategy =
- new StandaloneSpawnStrategy(
- env.getExecRoot(),
- actionInputPrefetcher,
- localExecutionOptions,
- executionOptions.verboseFailures,
- env.getRuntime().getProductName(),
- ResourceManager.instance());
-
RemoteOptions remoteOptions = env.getOptions().getOptions(RemoteOptions.class);
AuthAndTLSOptions authAndTlsOptions = env.getOptions().getOptions(AuthAndTLSOptions.class);
ChannelOptions channelOptions = ChannelOptions.create(authAndTlsOptions);
@@ -84,14 +76,33 @@ final class RemoteActionContextProvider extends ActionContextProvider {
} else {
remoteExecutor = null;
}
+ spawnRunner = new RemoteSpawnRunner(
+ env.getExecRoot(),
+ remoteOptions,
+ createFallbackRunner(actionInputPrefetcher),
+ remoteCache,
+ remoteExecutor);
spawnStrategy =
new RemoteSpawnStrategy(
+ "remote",
+ spawnRunner,
+ executionOptions.verboseFailures);
+ }
+
+ private SpawnRunner createFallbackRunner(ActionInputPrefetcher actionInputPrefetcher) {
+ LocalExecutionOptions localExecutionOptions =
+ env.getOptions().getOptions(LocalExecutionOptions.class);
+ LocalEnvProvider localEnvProvider = OS.getCurrent() == OS.DARWIN
+ ? new XCodeLocalEnvProvider()
+ : LocalEnvProvider.UNMODIFIED;
+ return
+ new LocalSpawnRunner(
env.getExecRoot(),
- remoteOptions,
- remoteCache,
- remoteExecutor,
- executionOptions.verboseFailures,
- fallbackStrategy);
+ actionInputPrefetcher,
+ localExecutionOptions,
+ ResourceManager.instance(),
+ env.getRuntime().getProductName(),
+ localEnvProvider);
}
@Override
@@ -101,9 +112,10 @@ final class RemoteActionContextProvider extends ActionContextProvider {
@Override
public void executionPhaseEnding() {
- if (spawnStrategy != null) {
- spawnStrategy.close();
- spawnStrategy = null;
+ if (spawnRunner != null) {
+ spawnRunner.close();
}
+ spawnRunner = null;
+ spawnStrategy = null;
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
index 7b6971623c..7ba5e5edf7 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
@@ -42,6 +42,7 @@ import java.util.Collection;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeSet;
+import javax.annotation.Nullable;
/** A client for the remote execution service. */
@ThreadSafe
@@ -52,15 +53,15 @@ final class RemoteSpawnRunner implements SpawnRunner {
private final Platform platform;
private final SpawnRunner fallbackRunner;
- private final RemoteActionCache remoteCache;
- private final GrpcRemoteExecutor remoteExecutor;
+ @Nullable private final RemoteActionCache remoteCache;
+ @Nullable private final GrpcRemoteExecutor remoteExecutor;
RemoteSpawnRunner(
Path execRoot,
RemoteOptions options,
SpawnRunner fallbackRunner,
- RemoteActionCache remoteCache,
- GrpcRemoteExecutor remoteExecutor) {
+ @Nullable RemoteActionCache remoteCache,
+ @Nullable GrpcRemoteExecutor remoteExecutor) {
this.execRoot = execRoot;
this.options = options;
this.platform = options.parseRemotePlatformOverride();
@@ -210,4 +211,11 @@ final class RemoteSpawnRunner implements SpawnRunner {
}
return result;
}
+
+ /** Release resources associated with this spawn runner. */
+ public void close() {
+ if (remoteCache != null) {
+ remoteCache.close();
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnStrategy.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnStrategy.java
index 15545bdc44..9a1c415589 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnStrategy.java
@@ -11,42 +11,35 @@
// 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.remote;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.ActionInputFileCache;
import com.google.devtools.build.lib.actions.ActionStatusMessage;
+import com.google.devtools.build.lib.actions.EnvironmentalExecException;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.ExecutionStrategy;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.actions.Spawns;
-import com.google.devtools.build.lib.actions.UserExecException;
-import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.exec.SpawnExecException;
import com.google.devtools.build.lib.exec.SpawnInputExpander;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
-import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
+import com.google.devtools.build.lib.exec.SpawnResult;
+import com.google.devtools.build.lib.exec.SpawnResult.Status;
+import com.google.devtools.build.lib.exec.SpawnRunner;
+import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
+import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy;
import com.google.devtools.build.lib.rules.fileset.FilesetActionContext;
+import com.google.devtools.build.lib.standalone.StandaloneSpawnStrategy;
import com.google.devtools.build.lib.util.CommandFailureUtils;
-import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.remoteexecution.v1test.Action;
-import com.google.devtools.remoteexecution.v1test.ActionResult;
-import com.google.devtools.remoteexecution.v1test.Command;
-import com.google.devtools.remoteexecution.v1test.Digest;
-import com.google.devtools.remoteexecution.v1test.ExecuteRequest;
-import com.google.devtools.remoteexecution.v1test.ExecuteResponse;
-import com.google.devtools.remoteexecution.v1test.Platform;
-import com.google.protobuf.Duration;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
import java.util.SortedMap;
-import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
/**
* Strategy that uses a distributed cache for sharing action input and output files. Optionally this
@@ -57,99 +50,15 @@ import java.util.TreeSet;
contextType = SpawnActionContext.class
)
final class RemoteSpawnStrategy implements SpawnActionContext {
- private final Path execRoot;
- private final SpawnActionContext fallbackStrategy;
+ private final SpawnInputExpander spawnInputExpander = new SpawnInputExpander(/*strict=*/false);
+ private final String strategyName;
+ private final SpawnRunner spawnRunner;
private final boolean verboseFailures;
- private final RemoteOptions remoteOptions;
- // TODO(olaola): This will be set on a per-action basis instead.
- private final Platform platform;
- private final SpawnInputExpander spawnInputExpander = new SpawnInputExpander(/*strict=*/ false);
-
- private final RemoteActionCache remoteCache;
- private final GrpcRemoteExecutor workExecutor;
- RemoteSpawnStrategy(
- Path execRoot,
- RemoteOptions remoteOptions,
- RemoteActionCache remoteCache,
- GrpcRemoteExecutor remoteExecutor,
- boolean verboseFailures,
- SpawnActionContext fallbackStrategy) {
- this.execRoot = execRoot;
- this.fallbackStrategy = fallbackStrategy;
+ RemoteSpawnStrategy(String strategyName, SpawnRunner spawnRunner, boolean verboseFailures) {
+ this.strategyName = strategyName;
+ this.spawnRunner = spawnRunner;
this.verboseFailures = verboseFailures;
- this.remoteOptions = remoteOptions;
- this.platform = remoteOptions.parseRemotePlatformOverride();
- this.remoteCache = remoteCache;
- this.workExecutor = remoteExecutor;
- }
-
- /** Release resources associated with this spawn strategy. */
- public void close() {
- if (remoteCache != null) {
- remoteCache.close();
- }
- }
-
- private Action buildAction(
- Collection<? extends ActionInput> outputs,
- Digest command,
- Digest inputRoot,
- long timeoutSeconds) {
- Action.Builder action = Action.newBuilder();
- action.setCommandDigest(command);
- action.setInputRootDigest(inputRoot);
- // Somewhat ugly: we rely on the stable order of outputs here for remote action caching.
- for (ActionInput output : outputs) {
- // TODO: output directories should be handled here, when they are supported.
- action.addOutputFiles(output.getExecPathString());
- }
- if (platform != null) {
- action.setPlatform(platform);
- }
- if (timeoutSeconds > 0) {
- action.setTimeout(Duration.newBuilder().setSeconds(timeoutSeconds));
- }
- return action.build();
- }
-
- private Command buildCommand(List<String> arguments, ImmutableMap<String, String> environment) {
- Command.Builder command = Command.newBuilder();
- command.addAllArguments(arguments);
- // Sorting the environment pairs by variable name.
- TreeSet<String> variables = new TreeSet<>(environment.keySet());
- for (String var : variables) {
- command.addEnvironmentVariablesBuilder().setName(var).setValue(environment.get(var));
- }
- return command.build();
- }
-
- /**
- * Fallback: execute the spawn locally. If an ActionKey is provided, try to upload results to
- * remote action cache.
- */
- private void execLocally(
- Spawn spawn,
- ActionExecutionContext actionExecutionContext,
- RemoteActionCache remoteCache,
- ActionKey actionKey)
- throws ExecException, IOException, InterruptedException {
- fallbackStrategy.exec(spawn, actionExecutionContext);
- if (remoteOptions.remoteUploadLocalResults && remoteCache != null && actionKey != null) {
- ArrayList<Path> outputFiles = new ArrayList<>();
- for (ActionInput output : spawn.getOutputFiles()) {
- Path outputFile = execRoot.getRelative(output.getExecPathString());
- // Ignore non-existent files.
- // TODO(ulfjack): This is not ideal - in general, all spawn strategies should stat the
- // output files and return a list of existing files. We shouldn't re-stat the files here.
- if (!outputFile.exists()) {
- continue;
- }
- outputFiles.add(outputFile);
- }
- remoteCache.upload(
- actionKey, execRoot, outputFiles, actionExecutionContext.getFileOutErr());
- }
}
@Override
@@ -157,109 +66,88 @@ final class RemoteSpawnStrategy implements SpawnActionContext {
return "remote";
}
- /** Executes the given {@code spawn}. */
@Override
- public void exec(Spawn spawn, ActionExecutionContext actionExecutionContext)
+ public void exec(final Spawn spawn, final ActionExecutionContext actionExecutionContext)
throws ExecException, InterruptedException {
- String mnemonic = spawn.getMnemonic();
-
- if (!spawn.isRemotable() || remoteCache == null) {
- fallbackStrategy.exec(spawn, actionExecutionContext);
+ if (!spawn.isRemotable()) {
+ StandaloneSpawnStrategy standaloneStrategy =
+ Preconditions.checkNotNull(
+ actionExecutionContext.getContext(StandaloneSpawnStrategy.class));
+ standaloneStrategy.exec(spawn, actionExecutionContext);
return;
}
+
if (actionExecutionContext.reportsSubcommands()) {
actionExecutionContext.reportSubcommand(spawn);
}
- actionExecutionContext
- .getEventBus()
- .post(ActionStatusMessage.runningStrategy(spawn.getResourceOwner(), "remote"));
+ SpawnExecutionPolicy policy = new SpawnExecutionPolicy() {
+ @Override
+ public ActionInputFileCache getActionInputFileCache() {
+ return actionExecutionContext.getActionInputFileCache();
+ }
- ActionKey actionKey = null;
- try {
- // Temporary hack: the TreeNodeRepository should be created and maintained upstream!
- ActionInputFileCache inputFileCache = actionExecutionContext.getActionInputFileCache();
- TreeNodeRepository repository = new TreeNodeRepository(execRoot, inputFileCache);
- SortedMap<PathFragment, ActionInput> inputMap =
- spawnInputExpander.getInputMapping(
- spawn,
- actionExecutionContext.getArtifactExpander(),
- actionExecutionContext.getActionInputFileCache(),
- actionExecutionContext.getContext(FilesetActionContext.class));
- TreeNode inputRoot = repository.buildFromActionInputs(inputMap);
- repository.computeMerkleDigests(inputRoot);
- Command command = buildCommand(spawn.getArguments(), spawn.getEnvironment());
- Action action =
- buildAction(
- spawn.getOutputFiles(),
- Digests.computeDigest(command),
- repository.getMerkleDigest(inputRoot),
- Spawns.getTimeoutSeconds(spawn));
+ @Override
+ public void lockOutputFiles() throws InterruptedException {
+ // This is only needed for the dynamic spawn strategy, which we still need to actually
+ // implement.
+ }
- // Look up action cache, and reuse the action output if it is found.
- actionKey = Digests.computeActionKey(action);
- ActionResult result =
- this.remoteOptions.remoteAcceptCached
- ? remoteCache.getCachedActionResult(actionKey)
- : null;
- boolean acceptCachedResult = this.remoteOptions.remoteAcceptCached;
- if (result != null) {
- // We don't cache failed actions, so we know the outputs exist.
- // For now, download all outputs locally; in the future, we can reuse the digests to
- // just update the TreeNodeRepository and continue the build.
+ @Override
+ public long getTimeoutMillis() {
try {
- remoteCache.download(result, execRoot, actionExecutionContext.getFileOutErr());
- return;
- } catch (CacheNotFoundException e) {
- acceptCachedResult = false; // Retry the action remotely and invalidate the results.
+ return TimeUnit.SECONDS.toMillis(Spawns.getTimeoutSeconds(spawn));
+ } catch (ExecException e) {
+ // The exec info is set internally, so we can never fail to parse the timeout.
+ throw new RuntimeException(e);
}
}
- if (workExecutor == null) {
- execLocally(spawn, actionExecutionContext, remoteCache, actionKey);
- return;
+ @Override
+ public FileOutErr getFileOutErr() {
+ return actionExecutionContext.getFileOutErr();
}
- // Upload the command and all the inputs into the remote cache.
- remoteCache.ensureInputsPresent(repository, execRoot, inputRoot, command);
- // TODO(olaola): set BuildInfo and input total bytes as well.
- ExecuteRequest.Builder request =
- ExecuteRequest.newBuilder()
- .setInstanceName(remoteOptions.remoteInstanceName)
- .setAction(action)
- .setTotalInputFileCount(inputMap.size())
- .setSkipCacheLookup(!acceptCachedResult);
- ExecuteResponse reply = workExecutor.executeRemotely(request.build());
- result = reply.getResult();
- if (remoteOptions.remoteLocalFallback && result.getExitCode() != 0) {
- execLocally(spawn, actionExecutionContext, remoteCache, actionKey);
- return;
+ @Override
+ public SortedMap<PathFragment, ActionInput> getInputMapping() throws IOException {
+ return spawnInputExpander.getInputMapping(
+ spawn,
+ actionExecutionContext.getArtifactExpander(),
+ actionExecutionContext.getActionInputFileCache(),
+ actionExecutionContext.getContext(FilesetActionContext.class));
}
- remoteCache.download(result, execRoot, actionExecutionContext.getFileOutErr());
- if (result.getExitCode() != 0) {
- String cwd = actionExecutionContext.getExecRoot().getPathString();
- String message =
- CommandFailureUtils.describeCommandFailure(
- verboseFailures, spawn.getArguments(), spawn.getEnvironment(), cwd);
- throw new UserExecException(message + ": Exit " + result.getExitCode());
- }
- } catch (CacheNotFoundException | IOException e) {
- Exception reportedCause = e;
- // TODO(olaola): handle this exception by reuploading / reexecuting the action remotely.
- if (remoteOptions.remoteLocalFallback) {
- actionExecutionContext.getEventHandler().handle(
- Event.warn(mnemonic + " remote work failed (" + e + ")"));
- try {
- execLocally(spawn, actionExecutionContext, remoteCache, actionKey);
- return;
- } catch (IOException e2) {
- reportedCause = e2;
+
+ @Override
+ public void report(ProgressStatus state) {
+ EventBus eventBus = actionExecutionContext.getEventBus();
+ switch (state) {
+ case EXECUTING:
+ eventBus.post(
+ ActionStatusMessage.runningStrategy(spawn.getResourceOwner(), strategyName));
+ break;
+ case SCHEDULING:
+ eventBus.post(ActionStatusMessage.schedulingStrategy(spawn.getResourceOwner()));
+ break;
+ default:
+ break;
}
}
+ };
+
+ SpawnResult result;
+ try {
+ result = spawnRunner.exec(spawn, policy);
+ } catch (IOException e) {
+ throw new EnvironmentalExecException("Unexpected IO error.", e);
+ }
+
+ if ((result.status() != Status.SUCCESS) || (result.exitCode() != 0)) {
+ // TODO(ulfjack): Return SpawnResult from here and let the upper layers worry about error
+ // handling and reporting.
String cwd = actionExecutionContext.getExecRoot().getPathString();
String message =
CommandFailureUtils.describeCommandFailure(
verboseFailures, spawn.getArguments(), spawn.getEnvironment(), cwd);
- throw new UserExecException(message, reportedCause);
+ throw new SpawnExecException(message, result, /*catastrophe=*/ false);
}
}