Add py2-bin functionality, and experimental flag to gate it

IMPORTANT: This flag is not intended to be supported long term, or to be turned on by default in Bazel.

This adds --experimental_create_py2_bin_symlink. When enabled, a <prefix>-py2-bin symlink will be created that mirrors the usual <prefix>-bin symlink, except that it points to a corresponding PY2 output root. When <prefix>-bin is already PY2 (e.g. --python_version=PY2, or if --incompatible_py3_is_default is not turned on, or --use_top_level_targets_for_symlinks is enabled and the top-level target is PY2), the two symlinks have the same destination.

BUILD files:
- Add dependencies on Python rules, since -py2-bin is naturally Python-specific.

BuildRequestOptions:
- Add the flag, clarify unrelated doc.

ExecutionTool:
- Add helper to obtain config from options using the executor. Turns out we have a myriad of ways to get configurations, at different levels of abstraction over the configuration machinery and with different levels of skyframe entanglement. Since we're doing a transition to get the new config, we don't have a BuildConfigurationValue.Key handy and instead have to use a different API. Pass along this helper as a callback to the symlink utils class.

OutputDirectoryLinksUtils:
- Add ConfigGroup, a value class to hold a single config for which we want to generate symlinks, along with its derived configs (here, the py2 config). This encapsulates the (minimal) Python-specific logic.
- Add Py2BinSymlink.

ConvenienceSymlinkTest:
- Test that the flag controls availability of the symlink, that it may or may not point to the same place as -bin, and that even when -bin can't be created because you're building both PY2 and PY3 targets, you can still use -py2-bin. We implement this using a mock Python rule that uses the real Python transition (including its baggage of requiring a dummy default_python_version attr).

ConfigurationResolver:
- Fix typo that briefly confused me.

Note that this machinery can be used to enable other symlinks that point to other -bin-like configs in the future -- not that we necessarily want to do that.

RELNOTES: None
PiperOrigin-RevId: 282410753
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
index f481300..580d4c6 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
@@ -45,6 +45,8 @@
 import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
 import com.google.devtools.build.lib.analysis.actions.SymlinkTreeActionContext;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.buildtool.buildevent.ExecRootPreparedEvent;
 import com.google.devtools.build.lib.buildtool.buildevent.ExecutionPhaseCompleteEvent;
 import com.google.devtools.build.lib.buildtool.buildevent.ExecutionStartingEvent;
@@ -96,6 +98,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import javax.annotation.Nullable;
 
 /**
  * This class manages the execution phase. The entry point is {@link #executeBuild}.
@@ -444,6 +447,26 @@
   }
 
   /**
+   * Obtains the {@link BuildConfiguration} for a given {@link BuildOptions} for the purpose of
+   * symlink creation.
+   *
+   * <p>In the event of a {@link InvalidConfigurationException}, a warning is emitted and null is
+   * returned.
+   */
+  @Nullable
+  private static BuildConfiguration getConfiguration(
+      SkyframeExecutor executor, Reporter reporter, BuildOptions options) {
+    try {
+      return executor.getConfiguration(reporter, options, /*keepGoing=*/ false);
+    } catch (InvalidConfigurationException e) {
+      reporter.handle(
+          Event.warn(
+              "Couldn't get configuration for convenience symlink creation: " + e.getMessage()));
+      return null;
+    }
+  }
+
+  /**
    * Creates convenience symlinks based on the target configurations.
    *
    * <p>Exactly what target configurations we consider depends on the value of {@code
@@ -484,9 +507,11 @@
           env.getDirectories().getOutputPath(workspaceName),
           getReporter(),
           targetConfigurations,
+          options -> getConfiguration(executor, reporter, options),
           buildRequestOptions.getSymlinkPrefix(productName),
           productName,
-          !buildRequestOptions.incompatibleSkipGenfilesSymlink);
+          !buildRequestOptions.incompatibleSkipGenfilesSymlink,
+          buildRequestOptions.experimentalCreatePy2BinSymlink);
     }
   }