Expose aspect actions from Skylark.

Like with providers, consumers get a merged view of all actions from the merged configured target (all other aspects + the base target).

I had to rejig the aspect value / configured aspect to be symmetric with rule configured targets.

I do not expect significant memory bloat from this. All lists / maps already existed, only extra fields have been added.

RELNOTES: Expose aspect actions provider to Skylark.
PiperOrigin-RevId: 201697923
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
index fe64dbd..7ecbba6 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.eventbus.EventBus;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
 import com.google.devtools.build.lib.actions.util.ActionsTestUtil.NullAction;
 import com.google.devtools.build.lib.analysis.config.HostTransition;
 import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
@@ -437,7 +438,7 @@
       @Override
       public ConfiguredAspect create(
           ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters)
-          throws InterruptedException {
+          throws InterruptedException, ActionConflictException {
         Object lateBoundPrereq = ruleContext.getPrerequisite(":late", TARGET);
         return new ConfiguredAspect.Builder(this, parameters, ruleContext)
             .addProvider(
@@ -507,7 +508,7 @@
       @Override
       public ConfiguredAspect create(
           ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters)
-          throws InterruptedException {
+          throws InterruptedException, ActionConflictException {
         ruleContext.registerAction(new NullAction(ruleContext.createOutputArtifact()));
         return new ConfiguredAspect.Builder(this, parameters, ruleContext).build();
       }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
index cebf5db..22220e5 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
@@ -228,7 +228,8 @@
     implements ConfiguredAspectFactory {
     @Override
     public ConfiguredAspect create(
-        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters) {
+        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters)
+        throws ActionConflictException {
       String information = parameters.isEmpty()
           ? ""
           : " data " + Iterables.getFirst(parameters.getAttribute("baz"), null);
@@ -274,7 +275,8 @@
 
     @Override
     public ConfiguredAspect create(
-        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters) {
+        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters)
+        throws ActionConflictException {
       return new ConfiguredAspect.Builder(this, parameters, ruleContext)
           .addProvider(new FooProvider())
           .build();
@@ -293,7 +295,8 @@
 
     @Override
     public ConfiguredAspect create(
-        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters) {
+        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters)
+        throws ActionConflictException {
       return new ConfiguredAspect.Builder(this, parameters, ruleContext)
           .addProvider(new BarProvider())
           .build();
@@ -428,7 +431,8 @@
 
     @Override
     public ConfiguredAspect create(
-        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters) {
+        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters)
+        throws ActionConflictException {
       StringBuilder information = new StringBuilder("aspect " + ruleContext.getLabel());
       if (!parameters.isEmpty()) {
         information.append(" data " + Iterables.getFirst(parameters.getAttribute("baz"), null));
@@ -475,7 +479,8 @@
 
     @Override
     public ConfiguredAspect create(
-        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters) {
+        ConfiguredTargetAndData ctadBase, RuleContext ruleContext, AspectParameters parameters)
+        throws ActionConflictException {
       ruleContext.ruleWarning("Aspect warning on " + ctadBase.getTarget().getLabel());
       return new ConfiguredAspect.Builder(this, parameters, ruleContext).build();
     }
@@ -531,7 +536,7 @@
     @Override
     public ConfiguredAspect create(
         ConfiguredTargetAndData ctadBase, RuleContext context, AspectParameters parameters)
-        throws InterruptedException {
+        throws InterruptedException, ActionConflictException {
       return new ConfiguredAspect.Builder(this, parameters, context).build();
     }
   }
@@ -777,7 +782,7 @@
     @Override
     public ConfiguredAspect create(
         ConfiguredTargetAndData ctadBase, RuleContext context, AspectParameters parameters)
-        throws InterruptedException {
+        throws InterruptedException, ActionConflictException {
       return ConfiguredAspect.builder(this, parameters, context)
           .addProvider(Provider.class, new Provider(ctadBase.getConfiguredTarget().getLabel()))
           .build();
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java
index 57170f8..b81b493 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java
@@ -17,10 +17,12 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 import static com.google.devtools.build.lib.analysis.OutputGroupInfo.INTERNAL_SUFFIX;
+import static java.util.stream.Collectors.toList;
 import static org.junit.Assert.fail;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Action;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.AnalysisResult;
 import com.google.devtools.build.lib.analysis.ConfiguredAspect;
@@ -2589,6 +2591,58 @@
     assertThat(objcProtoProvider).isNotNull();
   }
 
+  @Test
+  public void testAspectActionProvider() throws Exception {
+    scratch.file(
+        "test/aspect.bzl",
+        "a1p = provider()",
+        "def _a1_impl(target,ctx):",
+        "  ctx.actions.run_shell(",
+        "    outputs = [ctx.actions.declare_file('a1')],",
+        "    command = 'touch $@'",
+        "  )",
+        "  return struct(a1p=a1p())",
+        "a1 = aspect(_a1_impl, attr_aspects = ['dep'], provides = ['a1p'])",
+        "a2p = provider()",
+        "def _a2_impl(target, ctx):",
+        "  value = []",
+        "  if hasattr(ctx.rule.attr, 'dep') and hasattr(ctx.rule.attr.dep, 'a2p'):",
+        "     value += ctx.rule.attr.dep.a2p.value",
+        "  value += target.actions",
+        "  return struct(a2p = a2p(value = value))",
+        "a2 = aspect(_a2_impl, attr_aspects = ['dep'], required_aspect_providers = ['a1p'])",
+        "def _r0_impl(ctx):",
+        "  ctx.actions.run_shell(",
+        "    outputs = [ctx.actions.declare_file('r0')],",
+        "    command = 'touch $@'",
+        "  )",
+        "def _r1_impl(ctx):",
+        "  pass",
+        "def _r2_impl(ctx):",
+        "  return struct(result = ctx.attr.dep.a2p.value)",
+        "r0 = rule(_r0_impl)",
+        "r1 = rule(_r1_impl, attrs = { 'dep' : attr.label(aspects = [a1])})",
+        "r2 = rule(_r2_impl, attrs = { 'dep' : attr.label(aspects = [a2])})");
+    scratch.file(
+        "test/BUILD",
+        "load(':aspect.bzl', 'r0', 'r1', 'r2')",
+        "r0(name = 'r0')",
+        "r1(name = 'r1', dep = ':r0')",
+        "r2(name = 'r2', dep = ':r1')");
+    AnalysisResult analysisResult = update("//test:r2");
+    ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
+    SkylarkList<?> result = (SkylarkList<?>) target.get("result");
+
+    // We should see both the action from the 'r0' rule, and the action from the 'a1' aspect
+    assertThat(result).hasSize(2);
+    assertThat(
+            result
+                .stream()
+                .map(a -> ((Action) a).getPrimaryOutput().getExecPath().getBaseName())
+                .collect(toList()))
+        .containsExactly("r0", "a1");
+  }
+
   /** SkylarkAspectTest with "keep going" flag */
   @RunWith(JUnit4.class)
   public static final class WithKeepGoing extends SkylarkDefinedAspectsTest {