Remove the runfiles middleman from py_binary's data runfiles.

This requires polluting Runfiles and RuleConfiguredTargetBuilder with
some custom infrastructure to lift a py_binary runfiles middleman into
the FilesToRunProviders and output groups of its reverse
dependency. That can all go away when the
--experimental_build_transitive_python_runfiles transition is
complete.

This is another step towards https://bazel-review.googlesource.com/c/bazel/+/14010.

Change-Id: Ib750d72d4be42324c8edec485707480690b9fc9c
PiperOrigin-RevId: 173514090
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
index 65009a3..f75d290 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
@@ -201,6 +201,12 @@
   private final NestedSet<SymlinkEntry> rootSymlinks;
 
   /**
+   * A set of middlemen artifacts. {@link RuleConfiguredTargetBuilder} adds these to the {@link
+   * FilesToRunProvider} of binaries that include this runfiles tree in their runfiles.
+   */
+  private final NestedSet<Artifact> extraMiddlemen;
+
+  /**
    * Interface used for adding empty files to the runfiles at the last minute. Mainly to support
    * python-related rules adding __init__.py files.
    */
@@ -294,6 +300,7 @@
       NestedSet<SymlinkEntry> symlinks,
       NestedSet<SymlinkEntry> rootSymlinks,
       NestedSet<PruningManifest> pruningManifests,
+      NestedSet<Artifact> extraMiddlemen,
       EmptyFilesSupplier emptyFilesSupplier,
       ConflictPolicy conflictPolicy,
       boolean legacyExternalRunfiles) {
@@ -301,6 +308,7 @@
     this.unconditionalArtifacts = Preconditions.checkNotNull(artifacts);
     this.symlinks = Preconditions.checkNotNull(symlinks);
     this.rootSymlinks = Preconditions.checkNotNull(rootSymlinks);
+    this.extraMiddlemen = Preconditions.checkNotNull(extraMiddlemen);
     this.pruningManifests = Preconditions.checkNotNull(pruningManifests);
     this.emptyFilesSupplier = Preconditions.checkNotNull(emptyFilesSupplier);
     this.conflictPolicy = conflictPolicy;
@@ -331,6 +339,10 @@
     return Iterables.filter(unconditionalArtifacts, Artifact.MIDDLEMAN_FILTER);
   }
 
+  public NestedSet<Artifact> getExtraMiddlemen() {
+    return extraMiddlemen;
+  }
+
   /**
    * Returns the collection of runfiles as artifacts, including both unconditional artifacts and
    * pruning manifest candidates.
@@ -641,8 +653,11 @@
    * Returns if there are no runfiles.
    */
   public boolean isEmpty() {
-    return unconditionalArtifacts.isEmpty() && symlinks.isEmpty() && rootSymlinks.isEmpty()
-        && pruningManifests.isEmpty();
+    return unconditionalArtifacts.isEmpty()
+        && symlinks.isEmpty()
+        && rootSymlinks.isEmpty()
+        && pruningManifests.isEmpty()
+        && extraMiddlemen.isEmpty();
   }
 
   /**
@@ -753,6 +768,7 @@
         NestedSetBuilder.stableOrder();
     private NestedSetBuilder<PruningManifest> pruningManifestsBuilder =
         NestedSetBuilder.stableOrder();
+    private NestedSetBuilder<Artifact> extraMiddlemenBuilder = NestedSetBuilder.stableOrder();
     private EmptyFilesSupplier emptyFilesSupplier = DUMMY_EMPTY_FILES_SUPPLIER;
 
     /** Build the Runfiles object with this policy */
@@ -804,9 +820,16 @@
      * Builds a new Runfiles object.
      */
     public Runfiles build() {
-      return new Runfiles(suffix, artifactsBuilder.build(), symlinksBuilder.build(),
-          rootSymlinksBuilder.build(), pruningManifestsBuilder.build(),
-          emptyFilesSupplier, conflictPolicy, legacyExternalRunfiles);
+      return new Runfiles(
+          suffix,
+          artifactsBuilder.build(),
+          symlinksBuilder.build(),
+          rootSymlinksBuilder.build(),
+          pruningManifestsBuilder.build(),
+          extraMiddlemenBuilder.build(),
+          emptyFilesSupplier,
+          conflictPolicy,
+          legacyExternalRunfiles);
     }
 
     /**
@@ -1051,6 +1074,17 @@
     }
 
     /**
+     * Add extra middlemen artifacts that should be built by reverse dependency binaries. This
+     * method exists solely to support the unfortunate legacy behavior of some rules; new uses
+     * should not be added.
+     */
+    public Builder addLegacyExtraMiddleman(Artifact middleman) {
+      Preconditions.checkArgument(middleman.isMiddlemanArtifact(), middleman);
+      extraMiddlemenBuilder.add(middleman);
+      return this;
+    }
+
+    /**
      * Add the other {@link Runfiles} object transitively.
      */
     public Builder merge(Runfiles runfiles) {
@@ -1097,6 +1131,7 @@
       if (includePruningManifests) {
         pruningManifestsBuilder.addTransitive(runfiles.getPruningManifests());
       }
+      extraMiddlemenBuilder.addTransitive(runfiles.getExtraMiddlemen());
       if (emptyFilesSupplier == DUMMY_EMPTY_FILES_SUPPLIER) {
         emptyFilesSupplier = runfiles.getEmptyFilesProvider();
       } else {