Add --compilation_mode support in transitions

Add support for converting //command_line_option:compilation_mode from
Java value to Starlark value so that it can be read in a transition.

Fixes --compilation_mode in #10690, but not the general case.

This patch does not contain any tests. What kind of test is expected and where should it be implemented?

Closes #11347.

PiperOrigin-RevId: 333249729
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index 53cc44d..d4461b2 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -1554,7 +1554,10 @@
 java_library(
     name = "config/compilation_mode",
     srcs = ["config/CompilationMode.java"],
-    deps = ["//src/main/java/com/google/devtools/common/options"],
+    deps = [
+        "//src/main/java/com/google/devtools/common/options",
+        "//src/main/java/net/starlark/java/eval",
+    ],
 )
 
 java_library(
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/CompilationMode.java b/src/main/java/com/google/devtools/build/lib/analysis/config/CompilationMode.java
index 1b83708..3058d76 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/CompilationMode.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/CompilationMode.java
@@ -14,12 +14,14 @@
 package com.google.devtools.build.lib.analysis.config;
 
 import com.google.devtools.common.options.EnumConverter;
+import net.starlark.java.eval.Printer;
+import net.starlark.java.eval.StarlarkValue;
 
-/**
- * This class represents the debug/optimization mode the binaries will be
- * built for.
- */
-public enum CompilationMode {
+/** This class represents the debug/optimization mode the binaries will be built for. */
+// TODO(bazel-team): Implementing StarlarkValue is a workaround until a well-defined Java-Starlark
+// conversion interface has been created. Avoid replicating this workaround.
+// See also https://github.com/bazelbuild/bazel/pull/11347#issuecomment-630260102
+public enum CompilationMode implements StarlarkValue {
 
   // Fast build mode (-g0).
   FASTBUILD("fastbuild"),
@@ -47,4 +49,9 @@
       super(CompilationMode.class, "compilation mode");
     }
   }
+
+  @Override
+  public void repr(Printer printer) {
+    printer.append(toString());
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java
index d59e41b..fce6873 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkAttrTransitionProviderTest.java
@@ -507,7 +507,7 @@
   @Test
   public void testOptionConversionCpu() throws Exception {
     writeOptionConversionTestFiles();
-    BazelMockAndroidSupport.setupNdk(mockToolsConfig);
+    BazelMockAndroidSupport.setupNdk(mockToolsConfig); // cc_binary needs this
 
     ConfiguredTarget target = getConfiguredTarget("//test/starlark:test");
 
@@ -518,6 +518,90 @@
     assertThat(getConfiguration(Iterables.getOnlyElement(dep)).getCpu()).isEqualTo("armeabi-v7a");
   }
 
+  private void writeReadAndPassthroughOptionsTestFiles() throws Exception {
+    setBuildLanguageOptions("--experimental_starlark_config_transitions=true");
+    writeAllowlistFile();
+
+    scratch.file(
+        "test/skylark/my_rule.bzl",
+        "load('//myinfo:myinfo.bzl', 'MyInfo')",
+        "settings_under_test = {",
+        "  '//command_line_option:cpu': 'armeabi-v7a',",
+        "  '//command_line_option:compilation_mode': 'dbg',",
+        "  '//command_line_option:crosstool_top': '//android/crosstool:everything',",
+        "  '//command_line_option:platform_suffix': 'my-platform-suffix',",
+        "}",
+        "def set_options_transition_func(settings, attr):",
+        "  return settings_under_test",
+        "def passthrough_transition_func(settings, attr):",
+        // All values in this test should be possible to copy within Starlark.
+        "  ret = dict(settings)",
+        // All values in this test should be possible to read within Starlark.
+        // This does not mean that it is possible to set a string value for all settings,
+        // e.g. //command_line_option:test_arg should be set to a list of strings.
+        "  for key, expected_value in settings_under_test.items():",
+        "    if str(ret[key]) != expected_value:",
+        "      fail('%s did not pass through, got %r expected %r' %",
+        "        (key, str(ret[key]), expected_value))",
+        "  ret['//command_line_option:test_arg'] = ['ok']",
+        "  return ret",
+        "my_set_options_transition = transition(",
+        "  implementation = set_options_transition_func,",
+        "  inputs = [],",
+        "  outputs = settings_under_test.keys())",
+        "my_passthrough_transition = transition(",
+        "  implementation = passthrough_transition_func,",
+        "  inputs = settings_under_test.keys(),",
+        "  outputs = ['//command_line_option:test_arg'] + settings_under_test.keys())",
+        "def impl(ctx): ",
+        "  return MyInfo(attr_dep = ctx.attr.dep)",
+        "my_set_options_rule = rule(",
+        "  implementation = impl,",
+        "  attrs = {",
+        "    'dep':  attr.label(cfg = my_set_options_transition),",
+        "    '_allowlist_function_transition': attr.label(",
+        "        default = '//tools/allowlists/function_transition_allowlist',",
+        "    ),",
+        "  })",
+        "my_passthrough_rule = rule(",
+        "  implementation = impl,",
+        "  attrs = {",
+        "    'dep':  attr.label(cfg = my_passthrough_transition),",
+        "    '_allowlist_function_transition': attr.label(",
+        "        default = '//tools/allowlists/function_transition_allowlist',",
+        "    ),",
+        "  })");
+
+    scratch.file(
+        "test/skylark/BUILD",
+        "load('//test/skylark:my_rule.bzl', 'my_passthrough_rule', 'my_set_options_rule')",
+        "my_set_options_rule(name = 'top', dep = ':test')",
+        "my_passthrough_rule(name = 'test', dep = ':main')",
+        "cc_binary(name = 'main', srcs = ['main.c'])");
+  }
+
+  @Test
+  public void testCompilationModeReadableInStarlarkTransitions() throws Exception {
+    writeReadAndPassthroughOptionsTestFiles();
+    BazelMockAndroidSupport.setupNdk(mockToolsConfig); // cc_binary needs this
+
+    ConfiguredTarget topTarget = getConfiguredTarget("//test/skylark:top");
+
+    @SuppressWarnings("unchecked")
+    List<ConfiguredTarget> topDep =
+        (List<ConfiguredTarget>) getMyInfoFromTarget(topTarget).getValue("attr_dep");
+    assertThat(topDep).hasSize(1);
+    ConfiguredTarget testTarget = Iterables.getOnlyElement(topDep);
+    @SuppressWarnings("unchecked")
+    List<ConfiguredTarget> testDep =
+        (List<ConfiguredTarget>) getMyInfoFromTarget(testTarget).getValue("attr_dep");
+    assertThat(testDep).hasSize(1);
+    ConfiguredTarget mainTarget = Iterables.getOnlyElement(testDep);
+    List<String> testArguments =
+        getConfiguration(mainTarget).getOptions().get(TestOptions.class).testArguments;
+    assertThat(testArguments).containsExactly("ok");
+  }
+
   @Test
   public void testUndeclaredOptionKey() throws Exception {
     setBuildLanguageOptions("--experimental_starlark_config_transitions=true");