A prototype implementation of top-level aspects.

--
MOS_MIGRATED_REVID=101033236
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java
index 90b373f..5411f0f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java
@@ -22,6 +22,12 @@
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
 /**
  * {@code ConfiguredTarget}s implementing this interface can provide artifacts that <b>can</b> be
  * built when the target is mentioned on the command line (as opposed to being always built, like
@@ -93,7 +99,7 @@
 
   private final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
 
-  OutputGroupProvider(ImmutableMap<String, NestedSet<Artifact>> outputGroups) {
+  public OutputGroupProvider(ImmutableMap<String, NestedSet<Artifact>> outputGroups) {
     this.outputGroups = outputGroups;
   }
 
@@ -107,4 +113,32 @@
         ? outputGroups.get(outputGroupName)
         : NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER);
   }
+
+  /**
+   * Merges output groups from two output providers. The set of output groups must be disjoint.
+   *
+   * @param providers providers to merge {@code this} with.
+   */
+  @Nullable
+  public static OutputGroupProvider merge(List<OutputGroupProvider> providers) {
+    if (providers.size() == 0) {
+      return null;
+    }
+    if (providers.size() == 1) {
+      return providers.get(0);
+    }
+
+    ImmutableMap.Builder<String, NestedSet<Artifact>> resultBuilder = new ImmutableMap.Builder<>();
+    Set<String> seenGroups = new HashSet<>();
+    for (OutputGroupProvider provider : providers) {
+      for (String outputGroup : provider.outputGroups.keySet()) {
+        if (!seenGroups.add(outputGroup)) {
+          throw new IllegalStateException("Output group " + outputGroup + " provided twice");
+        }
+
+        resultBuilder.put(outputGroup, provider.getOutputGroup(outputGroup));
+      }
+    }
+    return new OutputGroupProvider(resultBuilder.build());
+  }
 }