Add an info item to show the currently inherited client environment

Add 'bazel info client-env' which outputs entries for the configuration
file that would freeze the current client environment. The main intended use case
is to use 'bazel info client-env >> .bazelrc' to keep the project reproducible
once a suitable value for the environment variables that used to be taken from the
client environment has been found.

--
Change-Id: Ib4d14dd824d223f335a4d4de04ee21c4a3ec4d83
Reviewed-on: https://bazel-review.googlesource.com/#/c/6112
MOS_MIGRATED_REVID=133699234
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 73c00d3..1208995 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
@@ -166,10 +166,10 @@
   }
 
   /**
-   * Return an ordered version of the client environment restricted to those variables
-   * whitelisted by the command-line options to be inheritable by actions.
+   * Return an ordered version of the client environment restricted to those variables whitelisted
+   * by the command-line options to be inheritable by actions.
    */
-  private Map<String, String> getCommandlineWhitelistedClientEnv() {
+  public Map<String, String> getWhitelistedClientEnv() {
     Map<String, String> visibleEnv = new TreeMap<>();
     for (String var : visibleClientEnv) {
       String value = clientEnv.get(var);
@@ -426,7 +426,7 @@
         getCommandId(),
         // TODO(bazel-team): this optimization disallows rule-specified additional dependencies
         // on the client environment!
-        getCommandlineWhitelistedClientEnv(),
+        getWhitelistedClientEnv(),
         timestampGranularityMonitor);
   }
 
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 0d7fb6a..b5e596f 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
@@ -193,29 +193,31 @@
 
   static Map<String, InfoItem> getHardwiredInfoItemMap(OptionsProvider commandOptions,
       String productName) {
-    List<InfoItem> hardwiredInfoItems = ImmutableList.<InfoItem>of(
-        new InfoItem.WorkspaceInfoItem(),
-        new InfoItem.InstallBaseInfoItem(),
-        new InfoItem.OutputBaseInfoItem(productName),
-        new InfoItem.ExecutionRootInfoItem(),
-        new InfoItem.OutputPathInfoItem(),
-        new InfoItem.BlazeBinInfoItem(productName),
-        new InfoItem.BlazeGenfilesInfoItem(productName),
-        new InfoItem.BlazeTestlogsInfoItem(productName),
-        new InfoItem.CommandLogInfoItem(),
-        new InfoItem.MessageLogInfoItem(),
-        new InfoItem.ReleaseInfoItem(productName),
-        new InfoItem.ServerPidInfoItem(productName),
-        new InfoItem.PackagePathInfoItem(commandOptions),
-        new InfoItem.UsedHeapSizeInfoItem(),
-        new InfoItem.UsedHeapSizeAfterGcInfoItem(),
-        new InfoItem.CommitedHeapSizeInfoItem(),
-        new InfoItem.MaxHeapSizeInfoItem(),
-        new InfoItem.GcTimeInfoItem(),
-        new InfoItem.GcCountInfoItem(),
-        new InfoItem.DefaultsPackageInfoItem(),
-        new InfoItem.BuildLanguageInfoItem(),
-        new InfoItem.DefaultPackagePathInfoItem(commandOptions));
+    List<InfoItem> hardwiredInfoItems =
+        ImmutableList.<InfoItem>of(
+            new InfoItem.WorkspaceInfoItem(),
+            new InfoItem.InstallBaseInfoItem(),
+            new InfoItem.OutputBaseInfoItem(productName),
+            new InfoItem.ExecutionRootInfoItem(),
+            new InfoItem.OutputPathInfoItem(),
+            new InfoItem.ClientEnv(),
+            new InfoItem.BlazeBinInfoItem(productName),
+            new InfoItem.BlazeGenfilesInfoItem(productName),
+            new InfoItem.BlazeTestlogsInfoItem(productName),
+            new InfoItem.CommandLogInfoItem(),
+            new InfoItem.MessageLogInfoItem(),
+            new InfoItem.ReleaseInfoItem(productName),
+            new InfoItem.ServerPidInfoItem(productName),
+            new InfoItem.PackagePathInfoItem(commandOptions),
+            new InfoItem.UsedHeapSizeInfoItem(),
+            new InfoItem.UsedHeapSizeAfterGcInfoItem(),
+            new InfoItem.CommitedHeapSizeInfoItem(),
+            new InfoItem.MaxHeapSizeInfoItem(),
+            new InfoItem.GcTimeInfoItem(),
+            new InfoItem.GcCountInfoItem(),
+            new InfoItem.DefaultsPackageInfoItem(),
+            new InfoItem.BuildLanguageInfoItem(),
+            new InfoItem.DefaultPackagePathInfoItem(commandOptions));
     ImmutableMap.Builder<String, InfoItem> result = new ImmutableMap.Builder<>();
     for (InfoItem item : hardwiredInfoItems) {
       result.put(item.getName(), item);
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java
index e8dc77f..e8836e5 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java
@@ -46,6 +46,7 @@
 import java.lang.management.MemoryMXBean;
 import java.lang.management.MemoryUsage;
 import java.util.Collection;
+import java.util.Map;
 
 /**
  * An item that is returned by <code>blaze info</code>.
@@ -482,6 +483,29 @@
     }
   }
 
+  /** Info item for the effective current client environment. */
+  public static final class ClientEnv extends InfoItem {
+    public ClientEnv() {
+      super(
+          "client-env",
+          "The specifications that need to be added to the project-specific rc file to freeze the"
+              + " current client environment",
+          true);
+    }
+
+    @Override
+    public byte[] get(Supplier<BuildConfiguration> configurationSupplier, CommandEnvironment env)
+        throws AbruptExitException {
+      String result = "";
+      for (Map.Entry<String, String> entry : env.getWhitelistedClientEnv().entrySet()) {
+        // TODO(bazel-team): as the syntax of our rc-files does not support to express new-lines in
+        // values, we produce syntax errors if the value of the entry contains a newline character.
+        result += "common --action_env=" + entry.getKey() + "=" + entry.getValue() + "\n";
+      }
+      return print(result);
+    }
+  }
+
   /**
    * Info item for the default package. It is deprecated, it still works, when
    * explicitly requested, but are not shown by default. It prints multi-line messages and thus