aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD2
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/Bazel.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/SpawnLogModule.java78
-rw-r--r--src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java26
-rw-r--r--src/main/java/com/google/devtools/build/lib/exec/ExecutionOptions.java10
-rw-r--r--src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java254
-rw-r--r--src/main/java/com/google/devtools/build/lib/util/io/AsynchronousFileOutputStream.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/util/io/MessageOutputStream.java31
-rw-r--r--src/main/protobuf/BUILD1
-rw-r--r--src/main/protobuf/spawn.proto122
10 files changed, 527 insertions, 1 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 43c47187fb..070f31984b 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -560,6 +560,7 @@ java_library(
"//src/main/protobuf:analysis_java_proto",
"//src/main/protobuf:extra_actions_base_java_proto",
"//src/main/protobuf:invocation_policy_java_proto",
+ "//src/main/protobuf:spawn_java_proto",
"//src/main/protobuf:test_status_java_proto",
"//third_party:auto_value",
"//third_party:guava",
@@ -672,6 +673,7 @@ java_library(
],
deps = [
":bazel/BazelRepositoryModule",
+ ":exitcode-external",
"//src/main/java/com/google/devtools/build/lib:bazel",
"//src/main/java/com/google/devtools/build/lib:bazel-commands",
"//src/main/java/com/google/devtools/build/lib:bazel-repository",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
index 47c0e8553e..fde7dc75a6 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
@@ -42,6 +42,7 @@ public final class Bazel {
com.google.devtools.build.lib.bazel.BazelWorkspaceStatusModule.class,
com.google.devtools.build.lib.bazel.BazelDiffAwarenessModule.class,
com.google.devtools.build.lib.bazel.BazelRepositoryModule.class,
+ com.google.devtools.build.lib.bazel.SpawnLogModule.class,
com.google.devtools.build.lib.ssd.SsdModule.class,
com.google.devtools.build.lib.worker.WorkerModule.class,
com.google.devtools.build.lib.remote.RemoteModule.class,
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/SpawnLogModule.java b/src/main/java/com/google/devtools/build/lib/bazel/SpawnLogModule.java
new file mode 100644
index 0000000000..86ab87eea8
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/SpawnLogModule.java
@@ -0,0 +1,78 @@
+// 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.bazel;
+
+import com.google.devtools.build.lib.buildtool.BuildRequest;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.exec.ActionContextConsumer;
+import com.google.devtools.build.lib.exec.ExecutionOptions;
+import com.google.devtools.build.lib.exec.ExecutorBuilder;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
+import com.google.devtools.build.lib.exec.SpawnLogContext;
+import com.google.devtools.build.lib.runtime.BlazeModule;
+import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.util.AbruptExitException;
+import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.build.lib.util.io.AsynchronousFileOutputStream;
+import java.io.IOException;
+
+/**
+ * Module providing on-demand spawn logging.
+ */
+public final class SpawnLogModule extends BlazeModule {
+
+ private static final class SpawnLogContextConsumer implements ActionContextConsumer {
+ @Override
+ public void populate(SpawnActionContextMaps.Builder builder) {
+ builder.strategyByContextMap().put(SpawnLogContext.class, "");
+ }
+ }
+
+ private SpawnLogContext spawnLogContext;
+
+ @Override
+ public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
+ env.getEventBus().register(this);
+ ExecutionOptions executionOptions = env.getOptions().getOptions(ExecutionOptions.class);
+ if (executionOptions != null
+ && executionOptions.executionLogFile != null
+ && !executionOptions.executionLogFile.isEmpty()) {
+ try {
+ spawnLogContext = new SpawnLogContext(
+ env.getExecRoot(),
+ new AsynchronousFileOutputStream(executionOptions.executionLogFile));
+ } catch (IOException e) {
+ env.getReporter().handle(Event.error(e.getMessage()));
+ env.getBlazeModuleEnvironment().exit(new AbruptExitException(ExitCode.COMMAND_LINE_ERROR));
+ }
+ builder.addActionContext(spawnLogContext);
+ builder.addActionContextConsumer(new SpawnLogContextConsumer());
+ } else {
+ spawnLogContext = null;
+ }
+ }
+
+ @Override
+ public void afterCommand() {
+ if (spawnLogContext != null) {
+ try {
+ spawnLogContext.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ spawnLogContext = null;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
index fee7f52e6b..c516b7875a 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
@@ -31,6 +31,7 @@ import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.SpawnResult.Status;
import com.google.devtools.build.lib.actions.Spawns;
+import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.exec.SpawnCache.CacheHandle;
import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext;
@@ -86,6 +87,7 @@ public abstract class AbstractSpawnStrategy implements SandboxedSpawnActionConte
cache = SpawnCache.NO_CACHE;
}
SpawnResult spawnResult;
+ ExecException ex = null;
try {
try (CacheHandle cacheHandle = cache.lookup(spawn, context)) {
if (cacheHandle.hasResult()) {
@@ -101,6 +103,30 @@ public abstract class AbstractSpawnStrategy implements SandboxedSpawnActionConte
}
} catch (IOException e) {
throw new EnvironmentalExecException("Unexpected IO error.", e);
+ } catch (SpawnExecException e) {
+ ex = e;
+ spawnResult = e.getSpawnResult();
+ // Log the Spawn and re-throw.
+ }
+
+ SpawnLogContext spawnLogContext = actionExecutionContext.getContext(SpawnLogContext.class);
+ if (spawnLogContext != null) {
+ try {
+ spawnLogContext.logSpawn(
+ spawn,
+ actionExecutionContext.getActionInputFileCache(),
+ context.getInputMapping(),
+ context.getTimeout(),
+ spawnResult);
+ } catch (IOException e) {
+ actionExecutionContext
+ .getEventHandler()
+ .handle(
+ Event.warn("Exception " + e + " while logging properties of " + spawn.toString()));
+ }
+ }
+ if (ex != null) {
+ throw ex;
}
if (spawnResult.status() != Status.SUCCESS) {
diff --git a/src/main/java/com/google/devtools/build/lib/exec/ExecutionOptions.java b/src/main/java/com/google/devtools/build/lib/exec/ExecutionOptions.java
index 8c7e2e1248..b7f1b340bf 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/ExecutionOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/ExecutionOptions.java
@@ -275,6 +275,16 @@ public class ExecutionOptions extends OptionsBase {
)
public boolean enableCriticalPathProfiling;
+ @Option(
+ name = "experimental_execution_log_file",
+ defaultValue = "",
+ category = "verbosity",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "Log the executed spawns into this file as delimited Spawn protos."
+ )
+ public String executionLogFile;
+
/** Converter for the --flaky_test_attempts option. */
public static class TestAttemptsConverter extends PerLabelOptions.PerLabelOptionsConverter {
private static final int MIN_VALUE = 1;
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java
new file mode 100644
index 0000000000..c5952350bc
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java
@@ -0,0 +1,254 @@
+// 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.exec;
+
+import com.google.common.base.Preconditions;
+import com.google.common.hash.HashCode;
+import com.google.devtools.build.lib.actions.ActionContext;
+import com.google.devtools.build.lib.actions.ActionInput;
+import com.google.devtools.build.lib.actions.ExecutionStrategy;
+import com.google.devtools.build.lib.actions.MetadataProvider;
+import com.google.devtools.build.lib.actions.Spawn;
+import com.google.devtools.build.lib.actions.SpawnResult;
+import com.google.devtools.build.lib.actions.Spawns;
+import com.google.devtools.build.lib.actions.cache.Metadata;
+import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
+import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.exec.Protos.Digest;
+import com.google.devtools.build.lib.exec.Protos.File;
+import com.google.devtools.build.lib.exec.Protos.Platform;
+import com.google.devtools.build.lib.exec.Protos.SpawnExec;
+import com.google.devtools.build.lib.util.io.MessageOutputStream;
+import com.google.devtools.build.lib.vfs.Dirent;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Symlinks;
+import com.google.protobuf.TextFormat;
+import com.google.protobuf.TextFormat.ParseException;
+import java.io.ByteArrayOutputStream;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.annotation.Nullable;
+
+/**
+ * A logging utility for spawns.
+ */
+@ExecutionStrategy(
+ name = {"spawn-log"},
+ contextType = SpawnLogContext.class
+)
+public class SpawnLogContext implements ActionContext {
+
+ private static final Logger logger = Logger.getLogger(SpawnLogContext.class.getName());
+ private final Path execRoot;
+ private final MessageOutputStream executionLog;
+
+ public SpawnLogContext(Path execRoot, MessageOutputStream executionLog) {
+ this.execRoot = execRoot;
+ this.executionLog = executionLog;
+ }
+
+ /** Log the executed spawn to the output stream. */
+ public void logSpawn(
+ Spawn spawn,
+ MetadataProvider metadataProvider,
+ SortedMap<PathFragment, ActionInput> inputMap,
+ Duration timeout,
+ SpawnResult result)
+ throws IOException {
+ SortedMap<Path, ActionInput> existingOutputs = listExistingOutputs(spawn);
+ SpawnExec.Builder builder = SpawnExec.newBuilder();
+ builder.addAllCommandArgs(spawn.getArguments());
+
+ Map<String, String> env = spawn.getEnvironment();
+ // Sorting the environment pairs by variable name.
+ TreeSet<String> variables = new TreeSet<>(env.keySet());
+ for (String var : variables) {
+ builder.addEnvironmentVariablesBuilder().setName(var).setValue(env.get(var));
+ }
+
+ try {
+ for (Map.Entry<PathFragment, ActionInput> e : inputMap.entrySet()) {
+ ActionInput input = e.getValue();
+ Path inputPath = execRoot.getRelative(input.getExecPathString());
+ if (inputPath.isDirectory()) {
+ listDirectoryContents(inputPath, (file) -> builder.addInputs(file), metadataProvider);
+ } else {
+ Digest digest = computeDigest(input, null, metadataProvider);
+ builder.addInputsBuilder().setPath(input.getExecPathString()).setDigest(digest);
+ }
+ }
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error computing spawn inputs", e);
+ }
+ ArrayList<String> outputPaths = new ArrayList<>();
+ for (ActionInput output : spawn.getOutputFiles()) {
+ outputPaths.add(output.getExecPathString());
+ }
+ Collections.sort(outputPaths);
+ builder.addAllListedOutputs(outputPaths);
+ for (Map.Entry<Path, ActionInput> e : existingOutputs.entrySet()) {
+ Path path = e.getKey();
+ if (path.isDirectory()) {
+ listDirectoryContents(path, (file) -> builder.addActualOutputs(file), metadataProvider);
+ } else {
+ File.Builder outputBuilder = builder.addActualOutputsBuilder();
+ outputBuilder.setPath(path.relativeTo(execRoot).toString());
+ try {
+ outputBuilder.setDigest(computeDigest(e.getValue(), path, metadataProvider));
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Error computing spawn event output properties", ex);
+ }
+ }
+ }
+ builder.setRemotable(Spawns.mayBeExecutedRemotely(spawn));
+
+ PlatformInfo execPlatform = spawn.getExecutionPlatform();
+ if (execPlatform != null && execPlatform.remoteExecutionProperties() != null) {
+ builder.setPlatform(
+ buildPlatform(execPlatform.label(), execPlatform.remoteExecutionProperties()));
+ }
+ if (result.status() != SpawnResult.Status.SUCCESS) {
+ builder.setStatus(result.status().toString());
+ }
+ if (!timeout.isZero()) {
+ builder.setTimeoutMillis(timeout.toMillis());
+ }
+ builder.setCacheable(Spawns.mayBeCached(spawn));
+ builder.setExitCode(result.exitCode());
+ builder.setRemoteCacheHit(result.isCacheHit());
+ builder.setRunner(result.getRunnerName());
+ String progressMessage = spawn.getResourceOwner().getProgressMessage();
+ if (progressMessage != null) {
+ builder.setProgressMessage(progressMessage);
+ }
+ builder.setMnemonic(spawn.getMnemonic());
+ executionLog.write(builder.build());
+ }
+
+ public void close() throws IOException {
+ executionLog.close();
+ }
+
+ private static Platform buildPlatform(Label platformLabel, @Nullable String platformDescription) {
+ Platform.Builder platformBuilder = Platform.newBuilder();
+ try {
+ if (platformDescription != null) {
+ TextFormat.getParser().merge(platformDescription, platformBuilder);
+ }
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Failed to parse remote_execution_properties from platform %s", platformLabel),
+ e);
+ }
+ return platformBuilder.build();
+ }
+
+ private SortedMap<Path, ActionInput> listExistingOutputs(Spawn spawn) {
+ TreeMap<Path, ActionInput> result = new TreeMap<>();
+ for (ActionInput output : spawn.getOutputFiles()) {
+ Path outputPath = execRoot.getRelative(output.getExecPathString());
+ // TODO(olaola): once symlink API proposal is implemented, report symlinks here.
+ if (outputPath.exists()) {
+ result.put(outputPath, output);
+ }
+ }
+ return result;
+ }
+
+ private void listDirectoryContents(
+ Path path, Consumer<File> addFile, MetadataProvider metadataProvider) {
+ try {
+ // TODO(olaola): once symlink API proposal is implemented, report symlinks here.
+ List<Dirent> sortedDirent = new ArrayList<>(path.readdir(Symlinks.NOFOLLOW));
+ sortedDirent.sort(Comparator.comparing(Dirent::getName));
+ for (Dirent dirent : sortedDirent) {
+ String name = dirent.getName();
+ Path child = path.getRelative(name);
+ if (dirent.getType() == Dirent.Type.DIRECTORY) {
+ listDirectoryContents(child, addFile, metadataProvider);
+ } else {
+ addFile.accept(
+ File.newBuilder()
+ .setPath(child.relativeTo(execRoot).toString())
+ .setDigest(computeDigest(null, child, metadataProvider))
+ .build());
+ }
+ }
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error computing spawn event file properties", e);
+ }
+ }
+
+ /**
+ * Computes the digest of the given ActionInput or corresponding path. Will try to access the
+ * Metadata cache first, if it is available, and fall back to digesting the contents manually.
+ */
+ private Digest computeDigest(
+ @Nullable ActionInput input, @Nullable Path path, MetadataProvider metadataProvider)
+ throws IOException {
+ Preconditions.checkArgument(input != null || path != null);
+ FileSystem.HashFunction hashFunction = execRoot.getFileSystem().getDigestFunction();
+ Digest.Builder digest = Digest.newBuilder().setHashFunctionName(hashFunction.toString());
+ if (input != null) {
+ if (input instanceof VirtualActionInput) {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ ((VirtualActionInput) input).writeTo(buffer);
+ byte[] blob = buffer.toByteArray();
+ return digest
+ .setHash(hashFunction.getHash().hashBytes(blob).toString())
+ .setSizeBytes(blob.length)
+ .build();
+ }
+ // Try to access the cached metadata, otherwise fall back to local computation.
+ try {
+ Metadata metadata = metadataProvider.getMetadata(input);
+ if (metadata != null) {
+ byte[] hash = metadata.getDigest();
+ if (hash != null) {
+ return digest
+ .setHash(HashCode.fromBytes(hash).toString())
+ .setSizeBytes(metadata.getSize())
+ .build();
+ }
+ }
+ } catch (IOException | IllegalStateException e) {
+ // Pass through to local computation.
+ }
+ }
+ if (path == null) {
+ path = execRoot.getRelative(input.getExecPath());
+ }
+ // Compute digest manually.
+ return digest
+ .setHash(HashCode.fromBytes(path.getDigest()).toString())
+ .setSizeBytes(path.getFileSize())
+ .build();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/util/io/AsynchronousFileOutputStream.java b/src/main/java/com/google/devtools/build/lib/util/io/AsynchronousFileOutputStream.java
index 70284d08c9..531fe4ba21 100644
--- a/src/main/java/com/google/devtools/build/lib/util/io/AsynchronousFileOutputStream.java
+++ b/src/main/java/com/google/devtools/build/lib/util/io/AsynchronousFileOutputStream.java
@@ -45,7 +45,7 @@ import java.util.concurrent.atomic.AtomicReference;
* close future.
*/
@ThreadSafety.ThreadSafe
-public class AsynchronousFileOutputStream extends OutputStream {
+public class AsynchronousFileOutputStream extends OutputStream implements MessageOutputStream {
private final AsynchronousFileChannel ch;
private final WriteCompletionHandler completionHandler = new WriteCompletionHandler();
// The offset in the file to begin the next write at.
@@ -83,6 +83,7 @@ public class AsynchronousFileOutputStream extends OutputStream {
* multiple calls to write on the underlying stream, so we have to provide this method here
* instead of the caller using it directly.
*/
+ @Override
public void write(Message m) {
Preconditions.checkNotNull(m);
final int size = m.getSerializedSize();
diff --git a/src/main/java/com/google/devtools/build/lib/util/io/MessageOutputStream.java b/src/main/java/com/google/devtools/build/lib/util/io/MessageOutputStream.java
new file mode 100644
index 0000000000..56868cb45b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/util/io/MessageOutputStream.java
@@ -0,0 +1,31 @@
+// 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.util.io;
+
+import com.google.protobuf.Message;
+import java.io.IOException;
+
+/**
+ * A variation of OutputStream for protobuf messages.
+ */
+public interface MessageOutputStream {
+ /**
+ * Writes a delimited protocol buffer message in the same format as {@link
+ * MessageLite#writeDelimitedTo(java.io.OutputStream)}.
+ */
+ void write(Message m) throws IOException;
+
+ /** Closes the underlying stream, following writes will fail. */
+ void close() throws IOException;
+}
diff --git a/src/main/protobuf/BUILD b/src/main/protobuf/BUILD
index cb44e63d8c..684dcb2e73 100644
--- a/src/main/protobuf/BUILD
+++ b/src/main/protobuf/BUILD
@@ -27,6 +27,7 @@ FILES = [
"invocation_policy",
"java_compilation",
"plmerge",
+ "spawn",
"test_status",
"worker_protocol",
]
diff --git a/src/main/protobuf/spawn.proto b/src/main/protobuf/spawn.proto
new file mode 100644
index 0000000000..b6c6ec36d5
--- /dev/null
+++ b/src/main/protobuf/spawn.proto
@@ -0,0 +1,122 @@
+// 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.
+
+syntax = "proto3";
+
+package tools.protos;
+
+option java_package = "com.google.devtools.build.lib.exec";
+option java_outer_classname = "Protos";
+
+message Digest {
+ // Digest of a file's contents using the current FileSystem digest function.
+ string hash = 1;
+
+ // The size in bytes of the original content.
+ int64 size_bytes = 2;
+
+ // The digest function that was used to generate the hash.
+ // This is not an enum for compatibility reasons, and also because the
+ // purpose of these logs is to enable analysis by comparison of multiple
+ // builds. So, from the programmatic perspective, this is an opaque field.
+ string hash_function_name = 3;
+}
+
+message File {
+ // Path to the file relative to the execution root.
+ string path = 1;
+
+ // Digest of the file's contents.
+ Digest digest = 2;
+}
+
+// Contents of command environment.
+message EnvironmentVariable {
+ string name = 1;
+ string value = 2;
+}
+
+// Command execution platform. This message needs to be kept in sync
+// with [Platform][google.devtools.remoteexecution.v1test.Platform].
+message Platform {
+ message Property {
+ string name = 1;
+ string value = 2;
+ }
+ repeated Property properties = 1;
+}
+
+// Details of an executed spawn.
+// These will only be generated on demand, using the
+// --execution_log_file=<path> flag.
+// Each message contains an executed command, its full inputs and outputs.
+// The purpose of these is to enable comparisons of multiple builds to diagnose
+// output differences or more subtle problems such as remote caching misses.
+// Only the executed Spawns will be output -- local cache hits are ignored.
+message SpawnExec {
+ // The command that was run.
+ repeated string command_args = 1;
+
+ // The command environment.
+ repeated EnvironmentVariable environment_variables = 2;
+
+ // The command execution platform.
+ Platform platform = 3;
+
+ // The inputs at the time of the execution.
+ repeated File inputs = 4;
+
+ // All the listed outputs paths. The paths are relative to the execution root.
+ // Actual outputs are a subset of the listed outputs. These paths are sorted.
+ repeated string listed_outputs = 5;
+
+ // Was the Spawn allowed to be executed remotely.
+ bool remotable = 6;
+
+ // Was the Spawn result allowed to be cached.
+ bool cacheable = 7;
+
+ // The Spawn timeout.
+ int64 timeout_millis = 8;
+
+ // A user-friendly text message representing the spawn progress.
+ string progress_message = 9;
+
+ // An opaque string that identifies the type of the Spawn's action.
+ string mnemonic = 10;
+
+ // The outputs generated by the execution.
+ repeated File actual_outputs = 11;
+
+ // If the Spawn was actually executed, rather than a remote cache hit,
+ // this will be the name of the runner executing the spawn, e.g. remote or
+ // linux-sandbox. Note this is not the same as the "strategy" string -- even
+ // though the action strategy may be remote, a particular action may still
+ // fall back to local execution due to a variety of reasons. This field
+ // indicates what really happened for the particular Spawn+execution.
+ string runner = 12;
+
+ // Whether the Spawn was a remote cache hit, in which case it was not executed
+ // and the runner field will be empty.
+ bool remote_cache_hit = 13;
+
+ // A text status returned by the execution, in case there were any errors.
+ // Empty in case of successful execution.
+ string status = 14;
+
+ // This field contains the contents of SpawnResult.exitCode.
+ // Its semantics varies greatly depending on the status field.
+ // Dependable: if status is empty, exit_code is guaranteed to be zero.
+ int32 exit_code = 15;
+}