Output the execution platform with other information when verbose

failures are output.

Fixes #6362.

Change-Id: Ie2309bf47a0d2d83d68a0cde0b6ae3f81238624f

Closes #6424.

Change-Id: I1a0d51dc0ca92a2eb4f228d3ffcd7ddbf79c7af7
PiperOrigin-RevId: 218565224
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 79d2c7d..3904b0b 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -182,11 +182,28 @@
     ],
 )
 
+COMMAND_UTILS_SRCS = [
+    "util/CommandFailureUtils.java",
+    "util/CommandUtils.java",
+]
+
+java_library(
+    name = "command-utils",
+    srcs = COMMAND_UTILS_SRCS,
+    deps = [
+        ":util",
+        "//src/main/java/com/google/devtools/build/lib/analysis/platform",
+        "//src/main/java/com/google/devtools/build/lib/shell",
+        "//third_party:guava",
+        "//third_party:jsr305",
+    ],
+)
+
 java_library(
     name = "util",
     srcs = glob(
         ["util/*.java"],
-        exclude = [
+        exclude = COMMAND_UTILS_SRCS + [
             "util/BlazeClock.java",
             "util/Clock.java",
             "util/ExitCode.java",
@@ -580,6 +597,7 @@
     deps = [
         ":base-util",
         ":build-request-options",
+        ":command-utils",
         ":events",
         ":exitcode-external",
         ":io",
@@ -1343,6 +1361,7 @@
     deps = [
         ":build-base",
         ":build-request-options",
+        ":command-utils",
         ":events",
         ":exitcode-external",
         ":io",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index 005e535..430e3a3 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -30,6 +30,7 @@
     deps = [
         ":commandline_item",
         ":localhost_capacity",
+        "//src/main/java/com/google/devtools/build/lib:command-utils",
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
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 e186975..d57a418 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
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.exec;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.eventbus.EventBus;
 import com.google.devtools.build.lib.actions.ActionExecutionContext;
@@ -141,13 +142,14 @@
       String cwd = actionExecutionContext.getExecRoot().getPathString();
       String resultMessage = spawnResult.getFailureMessage();
       String message =
-          resultMessage != ""
+          !Strings.isNullOrEmpty(resultMessage)
               ? resultMessage
               : CommandFailureUtils.describeCommandFailure(
                   actionExecutionContext.getVerboseFailures(),
                   spawn.getArguments(),
                   spawn.getEnvironment(),
-                  cwd);
+                  cwd,
+                  spawn.getExecutionPlatform());
       throw new SpawnExecException(message, spawnResult, /*forciblyRunRemotely=*/false);
     }
     return ImmutableList.of(spawnResult);
diff --git a/src/main/java/com/google/devtools/build/lib/query2/BUILD b/src/main/java/com/google/devtools/build/lib/query2/BUILD
index 64836e9..f027b3e 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/query2/BUILD
@@ -23,6 +23,7 @@
         ":query-engine",
         ":query-output",
         "//src/main/java/com/google/devtools/build/lib:build-base",
+        "//src/main/java/com/google/devtools/build/lib:command-utils",
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
         "//src/main/java/com/google/devtools/build/lib:util",
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
index 5610242..2d4eb24 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
@@ -123,19 +123,25 @@
         sandbox.getArguments().toArray(new String[0]),
         sandbox.getEnvironment(),
         sandbox.getSandboxExecRoot().getPathFile());
-      String failureMessage;
-      if (sandboxOptions.sandboxDebug) {
-        failureMessage =
-            CommandFailureUtils.describeCommandFailure(
-                true, sandbox.getArguments(), sandbox.getEnvironment(), execRoot.getPathString());
-      } else {
-        failureMessage =
-            CommandFailureUtils.describeCommandFailure(
-                verboseFailures,
-                originalSpawn.getArguments(),
-                originalSpawn.getEnvironment(),
-                execRoot.getPathString()) + SANDBOX_DEBUG_SUGGESTION;
-      }
+    String failureMessage;
+    if (sandboxOptions.sandboxDebug) {
+      failureMessage =
+          CommandFailureUtils.describeCommandFailure(
+              true,
+              sandbox.getArguments(),
+              sandbox.getEnvironment(),
+              execRoot.getPathString(),
+              null);
+    } else {
+      failureMessage =
+          CommandFailureUtils.describeCommandFailure(
+                  verboseFailures,
+                  originalSpawn.getArguments(),
+                  originalSpawn.getEnvironment(),
+                  execRoot.getPathString(),
+                  originalSpawn.getExecutionPlatform())
+              + SANDBOX_DEBUG_SUGGESTION;
+    }
 
     long startTime = System.currentTimeMillis();
     CommandResult commandResult;
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
index c8da1fd..5298b94 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
@@ -17,6 +17,7 @@
     ],
     deps = [
         "//src/main/java/com/google/devtools/build/lib:build-base",
+        "//src/main/java/com/google/devtools/build/lib:command-utils",
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
diff --git a/src/main/java/com/google/devtools/build/lib/util/CommandFailureUtils.java b/src/main/java/com/google/devtools/build/lib/util/CommandFailureUtils.java
index 32503f0..a5d98a5 100644
--- a/src/main/java/com/google/devtools/build/lib/util/CommandFailureUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/util/CommandFailureUtils.java
@@ -18,6 +18,7 @@
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Ordering;
+import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
 import java.io.File;
 import java.util.Collection;
 import java.util.Comparator;
@@ -225,40 +226,50 @@
   }
 
   /**
-   * Construct an error message that describes a failed command invocation.
-   * Currently this returns a message of the form "error executing command foo
-   * bar baz".
+   * Construct an error message that describes a failed command invocation. Currently this returns a
+   * message of the form "error executing command foo bar baz".
    */
   public static String describeCommandError(
       boolean verbose,
       Collection<String> commandLineElements,
       Map<String, String> env,
-      String cwd) {
+      String cwd,
+      @Nullable PlatformInfo executionPlatform) {
 
     CommandDescriptionForm form = verbose
         ? CommandDescriptionForm.COMPLETE
         : CommandDescriptionForm.ABBREVIATED;
-    return "error executing command " + (verbose ? "\n  " : "")
-        + describeCommand(form, /* prettyPrintArgs= */false, commandLineElements, env, cwd);
+
+    StringBuilder output = new StringBuilder();
+    output.append("error executing command ");
+    if (verbose) {
+      output.append("\n  ");
+    }
+    output.append(
+        describeCommand(form, /* prettyPrintArgs= */ false, commandLineElements, env, cwd));
+    if (verbose && executionPlatform != null) {
+      output.append("\n");
+      output.append("Execution platform: ").append(executionPlatform.label());
+    }
+    return output.toString();
   }
 
   /**
-   * Construct an error message that describes a failed command invocation.
-   * Currently this returns a message of the form "foo failed: error executing
-   * command /dir/foo bar baz".
+   * Construct an error message that describes a failed command invocation. Currently this returns a
+   * message of the form "foo failed: error executing command /dir/foo bar baz".
    */
   public static String describeCommandFailure(
       boolean verbose,
       Collection<String> commandLineElements,
       Map<String, String> env,
-      String cwd) {
+      String cwd,
+      @Nullable PlatformInfo executionPlatform) {
 
     String commandName = commandLineElements.iterator().next();
     // Extract the part of the command name after the last "/", if any.
     String shortCommandName = new File(commandName).getName();
     return shortCommandName
         + " failed: "
-        + describeCommandError(verbose, commandLineElements, env, cwd);
+        + describeCommandError(verbose, commandLineElements, env, cwd, executionPlatform);
   }
-
 }
diff --git a/src/main/java/com/google/devtools/build/lib/util/CommandUtils.java b/src/main/java/com/google/devtools/build/lib/util/CommandUtils.java
index a3a854c..cbb1b52 100644
--- a/src/main/java/com/google/devtools/build/lib/util/CommandUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/util/CommandUtils.java
@@ -18,7 +18,6 @@
 import com.google.devtools.build.lib.shell.Command;
 import com.google.devtools.build.lib.shell.CommandException;
 import com.google.devtools.build.lib.shell.CommandResult;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
@@ -48,8 +47,8 @@
    * bar baz".
    */
   public static String describeCommandError(boolean verbose, Command command) {
-    return CommandFailureUtils.describeCommandError(verbose, commandLine(command), env(command),
-                                                    cwd(command));
+    return CommandFailureUtils.describeCommandError(
+        verbose, commandLine(command), env(command), cwd(command), null);
   }
 
   /**
@@ -58,8 +57,8 @@
    * command /dir/foo bar baz".
    */
   public static String describeCommandFailure(boolean verbose, Command command) {
-    return CommandFailureUtils.describeCommandFailure(verbose, commandLine(command), env(command),
-                                                      cwd(command));
+    return CommandFailureUtils.describeCommandFailure(
+        verbose, commandLine(command), env(command), cwd(command), null);
   }
 
   /**
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 89afa94..5684ce4 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -318,9 +318,12 @@
         ":guava_junit_truth",
         ":test_runner",
         ":testutil",
+        "//src/main/java/com/google/devtools/build/lib:command-utils",
         "//src/main/java/com/google/devtools/build/lib:simple-log-handler",
         "//src/main/java/com/google/devtools/build/lib:single-line-formatter",
         "//src/main/java/com/google/devtools/build/lib:util",
+        "//src/main/java/com/google/devtools/build/lib/analysis/platform",
+        "//src/main/java/com/google/devtools/build/lib/cmdline",
         "//src/main/java/com/google/devtools/build/lib/shell",
         "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils",
         "//src/main/java/com/google/devtools/build/lib/vfs",
diff --git a/src/test/java/com/google/devtools/build/lib/util/CommandFailureUtilsTest.java b/src/test/java/com/google/devtools/build/lib/util/CommandFailureUtilsTest.java
index f13758e..1779848 100644
--- a/src/test/java/com/google/devtools/build/lib/util/CommandFailureUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/util/CommandFailureUtilsTest.java
@@ -15,6 +15,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
+import com.google.devtools.build.lib.cmdline.Label;
 import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -22,6 +24,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+/** Tests for {@link CommandFailureUtils}. */
 @RunWith(JUnit4.class)
 public class CommandFailureUtilsTest {
 
@@ -38,9 +41,14 @@
     env.put("FOO", "foo");
     env.put("PATH", "/usr/bin:/bin:/sbin");
     String cwd = "/my/working/directory";
-    String message = CommandFailureUtils.describeCommandError(false, Arrays.asList(args), env, cwd);
+    PlatformInfo executionPlatform =
+        PlatformInfo.builder().setLabel(Label.parseAbsoluteUnchecked("//platform:exec")).build();
+    String message =
+        CommandFailureUtils.describeCommandError(
+            false, Arrays.asList(args), env, cwd, executionPlatform);
     String verboseMessage =
-        CommandFailureUtils.describeCommandError(true, Arrays.asList(args), env, cwd);
+        CommandFailureUtils.describeCommandError(
+            true, Arrays.asList(args), env, cwd, executionPlatform);
     assertThat(message)
         .isEqualTo(
             "error executing command some_command arg1 "
@@ -60,7 +68,8 @@
                 + "arg11 arg12 arg13 arg14 arg15 arg16 arg17 arg18 "
                 + "arg19 arg20 arg21 arg22 arg23 arg24 arg25 arg26 "
                 + "arg27 arg28 arg29 arg30 arg31 arg32 arg33 arg34 "
-                + "arg35 arg36 arg37 arg38 arg39)");
+                + "arg35 arg36 arg37 arg38 arg39)\n"
+                + "Execution platform: //platform:exec");
   }
 
   @Test
@@ -73,10 +82,14 @@
     env.put("FOO", "foo");
     env.put("PATH", "/usr/bin:/bin:/sbin");
     String cwd = null;
+    PlatformInfo executionPlatform =
+        PlatformInfo.builder().setLabel(Label.parseAbsoluteUnchecked("//platform:exec")).build();
     String message =
-        CommandFailureUtils.describeCommandFailure(false, Arrays.asList(args), env, cwd);
+        CommandFailureUtils.describeCommandFailure(
+            false, Arrays.asList(args), env, cwd, executionPlatform);
     String verboseMessage =
-        CommandFailureUtils.describeCommandFailure(true, Arrays.asList(args), env, cwd);
+        CommandFailureUtils.describeCommandFailure(
+            true, Arrays.asList(args), env, cwd, executionPlatform);
     assertThat(message)
         .isEqualTo(
             "sh failed: error executing command "
@@ -87,7 +100,8 @@
                 + "  (exec env - \\\n"
                 + "    FOO=foo \\\n"
                 + "    PATH=/usr/bin:/bin:/sbin \\\n"
-                + "  /bin/sh -c 'echo Some errors 1>&2; echo Some output; exit 42')");
+                + "  /bin/sh -c 'echo Some errors 1>&2; echo Some output; exit 42')\n"
+                + "Execution platform: //platform:exec");
   }
 
   @Test