Avoid expansion of NestedSets in MiddlemanFactory.

MiddlemanFactory wants to check if the middleman inputs set is empty
or singleton. A NestedSet can answer both queries efficiently if the
right APIs are used.

My ultimate goal here is to avoid the expansion of runfiles artifacts
nested sets until execution.

Change-Id: I29a269df757ef41b1410bbb492cf24c926df6114
PiperOrigin-RevId: 178600943
diff --git a/src/main/java/com/google/devtools/build/lib/actions/MiddlemanFactory.java b/src/main/java/com/google/devtools/build/lib/actions/MiddlemanFactory.java
index 5093344..fbbc100 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/MiddlemanFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/MiddlemanFactory.java
@@ -17,6 +17,8 @@
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.collect.CollectionUtils;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -96,6 +98,9 @@
   }
 
   private <T> boolean hasExactlyOneInput(Iterable<T> iterable) {
+    if (iterable instanceof NestedSet) {
+      return ((NestedSet) iterable).isSingleton();
+    }
     Iterator<T> it = iterable.iterator();
     if (!it.hasNext()) {
       return false;
@@ -150,7 +155,7 @@
   private Pair<Artifact, Action> createMiddleman(
       ActionOwner owner, String middlemanName, String purpose, Iterable<Artifact> inputs,
       Root middlemanDir, MiddlemanType middlemanType) {
-    if (inputs == null || Iterables.isEmpty(inputs)) {
+    if (inputs == null || CollectionUtils.isEmpty(inputs)) {
       return null;
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java b/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java
index acc30fa..84bb2e6 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java
@@ -25,6 +25,7 @@
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.RunUnder;
 import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.syntax.Type;
@@ -264,8 +265,8 @@
     return sourcesManifest;
   }
 
-  private Artifact createArtifactsMiddleman(ActionConstructionContext context,
-      Iterable<Artifact> allRunfilesArtifacts) {
+  private Artifact createArtifactsMiddleman(
+      ActionConstructionContext context, NestedSet<Artifact> allRunfilesArtifacts) {
     return context.getAnalysisEnvironment().getMiddlemanFactory().createRunfilesMiddleman(
         context.getActionOwner(), owningExecutable, allRunfilesArtifacts,
         context.getMiddlemanDirectory(),
diff --git a/src/main/java/com/google/devtools/build/lib/collect/CollectionUtils.java b/src/main/java/com/google/devtools/build/lib/collect/CollectionUtils.java
index 5c2f92e..369ef52 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/CollectionUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/CollectionUtils.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import java.util.ArrayList;
@@ -199,4 +200,14 @@
       Map<KEY_1, ? extends Map<KEY_2, VALUE>> map) {
     return new HashMap<>(Maps.transformValues(map, HashMap::new));
   }
+
+  /**
+   * A variant of {@link com.google.common.collect.Iterables.isEmpty} that avoids expanding nested
+   * sets.
+   */
+  public static <T> boolean isEmpty(Iterable<T> iterable) {
+    return (iterable instanceof NestedSet)
+        ? ((NestedSet) iterable).isEmpty()
+        : Iterables.isEmpty(iterable);
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
index 30d5805..062d24d 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
@@ -166,10 +166,8 @@
     return children == EMPTY_CHILDREN;
   }
 
-  /**
-   * Returns true if the set has exactly one element.
-   */
-  private boolean isSingleton() {
+  /** Returns true if the set has exactly one element. */
+  public boolean isSingleton() {
     return !(children instanceof Object[]);
   }