Expose actions fixed environment variables specified through feature configuration to Starlark

Some fixed environment variables are added to the actions environment [like CppCompile action](https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java#L877) based on features configuration [code](https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLine.java#L66). These variables can be available in the analysis phase.

This CL adds a flag `experimental_get_fixed_configured_action_env` to return these variables in `action.env`. This is needed to allow the use of aspects to output C++ actions information as a replacement for `print_action` command.

Addresses: https://github.com/bazelbuild/bazel/issues/10376
PiperOrigin-RevId: 464088598
Change-Id: Id1c4e2badf5078d4d51f733aee8762411fd3b9ba
diff --git a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
index 744ab55..2627544 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
@@ -36,6 +36,7 @@
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.packages.AspectDescriptor;
+import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
 import com.google.devtools.build.lib.server.FailureDetails.Execution.Code;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 import com.google.devtools.build.lib.starlarkbuildapi.ActionApi;
@@ -55,6 +56,7 @@
 import net.starlark.java.eval.EvalException;
 import net.starlark.java.eval.Printer;
 import net.starlark.java.eval.Sequence;
+import net.starlark.java.eval.StarlarkSemantics;
 
 /**
  * Abstract implementation of Action which implements basic functionality: the inputs, outputs, and
@@ -684,8 +686,16 @@
   }
 
   @Override
-  public Dict<String, String> getEnv() {
-    return Dict.immutableCopyOf(env.getFixedEnv());
+  public Dict<String, String> getEnv(StarlarkSemantics semantics) throws EvalException {
+    if (semantics.getBool(BuildLanguageOptions.EXPERIMENTAL_GET_FIXED_CONFIGURED_ACTION_ENV)) {
+      try {
+        return Dict.immutableCopyOf(getEffectiveEnvironment(/*clientEnv=*/ ImmutableMap.of()));
+      } catch (CommandLineExpansionException ex) {
+        throw new EvalException(ex);
+      }
+    } else {
+      return Dict.immutableCopyOf(env.getFixedEnv());
+    }
   }
 
   @Override
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 2f39770..925dfcc 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -89,6 +89,7 @@
         "//src/main/java/com/google/devtools/build/lib/concurrent",
         "//src/main/java/com/google/devtools/build/lib/events",
         "//src/main/java/com/google/devtools/build/lib/packages",
+        "//src/main/java/com/google/devtools/build/lib/packages/semantics",
         "//src/main/java/com/google/devtools/build/lib/profiler",
         "//src/main/java/com/google/devtools/build/lib/profiler:google-auto-profiler-utils",
         "//src/main/java/com/google/devtools/build/lib/shell",
diff --git a/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java b/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
index d7e4f85..becd2c4 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
@@ -585,6 +585,17 @@
               + "'cfg = \"exec\"' instead.")
   public boolean incompatibleDisableStarlarkHostTransitions;
 
+  @Option(
+      name = "experimental_get_fixed_configured_action_env",
+      defaultValue = "false",
+      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
+      help =
+          "If enabled, action.env will also return fixed environment variables"
+              + " specified through features configuration.")
+  public boolean experimentalGetFixedConfiguredEnvironment;
+
   /**
    * An interner to reduce the number of StarlarkSemantics instances. A single Blaze instance should
    * never accumulate a large number of these and being able to shortcut on object identity makes a
@@ -658,6 +669,9 @@
             .setBool(
                 INCOMPATIBLE_DISABLE_STARLARK_HOST_TRANSITIONS,
                 incompatibleDisableStarlarkHostTransitions)
+            .setBool(
+                EXPERIMENTAL_GET_FIXED_CONFIGURED_ACTION_ENV,
+                experimentalGetFixedConfiguredEnvironment)
             .build();
     return INTERNER.intern(semantics);
   }
@@ -730,6 +744,8 @@
       "-incompatible_top_level_aspects_require_providers";
   public static final String INCOMPATIBLE_DISABLE_STARLARK_HOST_TRANSITIONS =
       "-incompatible_disable_starlark_host_transitions";
+  public static final String EXPERIMENTAL_GET_FIXED_CONFIGURED_ACTION_ENV =
+      "-experimental_get_fixed_configured_action_env";
 
   // non-booleans
   public static final StarlarkSemantics.Key<String> EXPERIMENTAL_BUILTINS_BZL_PATH =
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/ActionApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/ActionApi.java
index ef05aa3..77c7ce6 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/ActionApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/ActionApi.java
@@ -24,6 +24,7 @@
 import net.starlark.java.eval.Dict;
 import net.starlark.java.eval.EvalException;
 import net.starlark.java.eval.Sequence;
+import net.starlark.java.eval.StarlarkSemantics;
 import net.starlark.java.eval.StarlarkValue;
 
 /** Interface for actions in Starlark. */
@@ -125,11 +126,12 @@
   @StarlarkMethod(
       name = "env",
       structField = true,
+      useStarlarkSemantics = true,
       doc =
           "The 'fixed' environment variables for this action. This includes only environment"
               + " settings which are explicitly set by the action definition, and thus omits"
               + " settings which are only pre-set in the execution environment.")
-  Dict<String, String> getEnv();
+  Dict<String, String> getEnv(StarlarkSemantics semantics) throws EvalException;
 
   @StarlarkMethod(
       name = "execution_info",
diff --git a/src/test/shell/bazel/cc_integration_test.sh b/src/test/shell/bazel/cc_integration_test.sh
index bb2f654..c50c12f 100755
--- a/src/test/shell/bazel/cc_integration_test.sh
+++ b/src/test/shell/bazel/cc_integration_test.sh
@@ -1457,4 +1457,53 @@
   FOO=1 bazel test //pkg:foo_test &> "$TEST_log" || fail "Should have inherited FOO env."
 }
 
+function test_getting_compile_action_env_with_cctoolchain_config_features() {
+  [ "$PLATFORM" != "darwin" ] || return 0
+
+  mkdir -p package
+
+  cat > "package/lib.bzl" <<EOF
+def _actions_test_impl(target, ctx):
+    compile_action = None
+
+    for action in target.actions:
+      if action.mnemonic in ["CppCompile", "ObjcCompile"]:
+        compile_action = action
+
+    print(compile_action.env)
+    return []
+
+actions_test_aspect = aspect(implementation = _actions_test_impl)
+EOF
+
+  cat > "package/x.cc" <<EOF
+#include <stdio.h>
+int main() {
+  printf("Hello\n");
+}
+EOF
+
+  cat > "package/BUILD" <<EOF
+cc_binary(
+  name = "x",
+  srcs = ["x.cc"],
+)
+EOF
+
+  # Without the flag, the env should not return extra fixed variables
+  bazel build "package:x" \
+      --aspects="//package:lib.bzl%actions_test_aspect" &>"$TEST_log" \
+      || fail "Build failed but should have succeeded"
+
+  expect_not_log "\"PWD\": \"/proc/self/cwd\""
+
+  # With the flag, the env should return extra fixed variables
+  bazel build "package:x" \
+      --aspects="//package:lib.bzl%actions_test_aspect" \
+      --experimental_get_fixed_configured_action_env &>"$TEST_log" \
+      || fail "Build failed but should have succeeded"
+
+  expect_log "\"PWD\": \"/proc/self/cwd\""
+}
+
 run_suite "cc_integration_test"