[aquery] Handle the case of aspect-on-aspect.
Problems:
Current aquery output:
- In the case of aspect-on-aspect: does include the full aspect path, but in reversed order.
- Lists the Target label as the "Owner" of an action that was generated from an Aspect applied on that Target. Technically the Owner of the action should be the Aspect.
Changes:
- AspectDescriptors now represent the aspect path (sorted by topological order of the dependency graph).
- Replace "Owner" by "Target" in aquery text output (there's no "Owner" field in proto output).
RELNOTES: [aquery] Handle the case of aspect-on-aspect.
PiperOrigin-RevId: 230528732
diff --git a/src/main/java/com/google/devtools/build/lib/query2/ActionGraphTextOutputFormatterCallback.java b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphTextOutputFormatterCallback.java
index 1bffbcc..9101208 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/ActionGraphTextOutputFormatterCallback.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphTextOutputFormatterCallback.java
@@ -129,14 +129,20 @@
BuildEvent configuration = actionOwner.getConfiguration();
BuildEventStreamProtos.Configuration configProto =
configuration.asStreamProto(/*context=*/ null).getConfiguration();
+
stringBuilder
- .append(" Owner: ")
- .append(actionOwner.getLabel().toString())
+ .append(" Target: ")
+ .append(actionOwner.getLabel())
.append('\n')
.append(" Configuration: ")
.append(configProto.getMnemonic())
.append('\n');
- ImmutableList<AspectDescriptor> aspectDescriptors = actionOwner.getAspectDescriptors();
+
+ // In the case of aspect-on-aspect, AspectDescriptors are listed in
+ // topological order of the dependency graph.
+ // e.g. [A -> B] would imply that aspect A is applied on top of aspect B.
+ ImmutableList<AspectDescriptor> aspectDescriptors =
+ actionOwner.getAspectDescriptors().reverse();
if (!aspectDescriptors.isEmpty()) {
stringBuilder
.append(" AspectDescriptors: [")
@@ -164,8 +170,7 @@
.append(')');
return aspectDescription.toString();
})
- .sorted()
- .collect(Collectors.joining(",\n")))
+ .collect(Collectors.joining("\n -> ")))
.append("]\n");
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java b/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java
index 3ee86a1..7ec2080 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java
@@ -200,13 +200,17 @@
BuildEvent event = actionOwner.getConfiguration();
actionBuilder.setConfigurationId(knownConfigurations.dataToId(event));
- // store aspect
- for (AspectDescriptor aspectDescriptor : actionOwner.getAspectDescriptors()) {
+ // Store aspects.
+ // Iterate through the aspect path and dump the aspect descriptors.
+ // In the case of aspect-on-aspect, AspectDescriptors are listed in topological order
+ // of the configured target graph.
+ // e.g. [A, B] would imply that aspect A is applied on top of aspect B.
+ for (AspectDescriptor aspectDescriptor : actionOwner.getAspectDescriptors().reverse()) {
actionBuilder.addAspectDescriptorIds(knownAspectDescriptors.dataToId(aspectDescriptor));
}
}
- // store inputs
+ // Store inputs
Iterable<Artifact> inputs = action.getInputs();
if (!(inputs instanceof NestedSet)) {
inputs = NestedSetBuilder.wrap(Order.STABLE_ORDER, inputs);
diff --git a/src/main/protobuf/analysis.proto b/src/main/protobuf/analysis.proto
index 794b1cf..16188c9 100644
--- a/src/main/protobuf/analysis.proto
+++ b/src/main/protobuf/analysis.proto
@@ -54,6 +54,9 @@
string target_id = 1;
// The aspects that were responsible for the creation of the action (if any).
+ // In the case of aspect-on-aspect, AspectDescriptors are listed in
+ // topological order of the dependency graph.
+ // e.g. [A, B] would imply that aspect A is applied on top of aspect B.
repeated string aspect_descriptor_ids = 2;
// Encodes all significant behavior that might affect the output. The key
diff --git a/src/test/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallbackTest.java b/src/test/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallbackTest.java
index f808013..1dc7c15 100644
--- a/src/test/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallbackTest.java
+++ b/src/test/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallbackTest.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.query2;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -23,9 +24,11 @@
import com.google.devtools.build.lib.analysis.AnalysisProtos.Action;
import com.google.devtools.build.lib.analysis.AnalysisProtos.ActionGraphContainer;
import com.google.devtools.build.lib.analysis.AnalysisProtos.Artifact;
+import com.google.devtools.build.lib.analysis.AnalysisProtos.AspectDescriptor;
import com.google.devtools.build.lib.analysis.AnalysisProtos.DepSetOfFiles;
import com.google.devtools.build.lib.analysis.AnalysisProtos.KeyValuePair;
import com.google.devtools.build.lib.analysis.AnalysisProtos.ParamFile;
+import com.google.devtools.build.lib.analysis.AnalysisProtos.Target;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.query2.ActionGraphProtoOutputFormatterCallback.OutputType;
@@ -504,6 +507,377 @@
assertThat(cppCompileActionTemplates).hasSize(expectedActionsCount);
}
+ @Test
+ public void testIncludeAspects_AspectOnAspect() throws Exception {
+ options.useAspects = true;
+ writeFile(
+ "test/rule.bzl",
+ "MyProvider = provider(",
+ " fields = {",
+ " 'dummy_field': 'dummy field'",
+ " }",
+ ")",
+ "def _my_jpl_aspect_imp(target, ctx):",
+ " if hasattr(ctx.rule.attr, 'srcs'):",
+ " out = ctx.actions.declare_file('out_jpl_{}'.format(target))",
+ " ctx.actions.run(",
+ " inputs = [f for src in ctx.rule.attr.srcs for f in src.files],",
+ " outputs = [out],",
+ " executable = 'dummy',",
+ " mnemonic = 'MyJplAspect'",
+ " )",
+ " return [MyProvider(dummy_field = 1)]",
+ "my_jpl_aspect = aspect(",
+ " attr_aspects = ['deps', 'exports'],",
+ " required_aspect_providers = [['proto_java']],",
+ " provides = [MyProvider],",
+ " implementation = _my_jpl_aspect_imp,",
+ ")",
+ "def _jpl_rule_impl(ctx):",
+ " return struct()",
+ "my_jpl_rule = rule(",
+ " attrs = {",
+ " 'deps': attr.label_list(aspects = [my_jpl_aspect]),",
+ " 'srcs': attr.label_list(allow_files = True),",
+ " },",
+ " implementation = _jpl_rule_impl",
+ ")",
+ "def _aspect_impl(target, ctx):",
+ " if hasattr(ctx.rule.attr, 'srcs'):",
+ " out = ctx.actions.declare_file('out{}'.format(target))",
+ " ctx.actions.run(",
+ " inputs = [f for src in ctx.rule.attr.srcs for f in src.files],",
+ " outputs = [out],",
+ " executable = 'dummy',",
+ " mnemonic = 'MyAspect'",
+ " )",
+ " return [struct()]",
+ "my_aspect = aspect(",
+ " attr_aspects = ['deps', 'exports'],",
+ " required_aspect_providers = [[MyProvider]],",
+ " attrs = {",
+ " 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])",
+ " },",
+ " implementation = _aspect_impl,",
+ ")",
+ "def _rule_impl(ctx):",
+ " return struct()",
+ "my_rule = rule(",
+ " attrs = {",
+ " 'deps': attr.label_list(aspects = [my_aspect]),",
+ " 'srcs': attr.label_list(allow_files = True),",
+ " 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])",
+ " },",
+ " implementation = _rule_impl",
+ ")");
+
+ writeFile(
+ "test/BUILD",
+ "load(':rule.bzl', 'my_rule', 'my_jpl_rule')",
+ "proto_library(",
+ " name = 'x',",
+ " srcs = [':x.proto']",
+ ")",
+ "my_jpl_rule(",
+ " name = 'my_java_proto',",
+ " deps = [':x'],",
+ ")",
+ "my_rule(",
+ " name = 'my_target',",
+ " deps = [':my_java_proto'],",
+ " srcs = ['foo.java'],",
+ " aspect_param = 'y'",
+ ")");
+
+ ActionGraphContainer actionGraphContainer =
+ getOutput("deps(//test:my_target)", AqueryActionFilter.emptyInstance());
+
+ Target protoLibraryTarget =
+ Iterables.getOnlyElement(
+ actionGraphContainer.getTargetsList().stream()
+ .filter(target -> target.getLabel().equals("//test:x"))
+ .collect(Collectors.toList()));
+ Action actionFromMyAspect =
+ Iterables.getOnlyElement(
+ actionGraphContainer.getActionsList().stream()
+ .filter(
+ action ->
+ action.getMnemonic().equals("MyAspect")
+ && action.getTargetId().equals(protoLibraryTarget.getId()))
+ .collect(Collectors.toList()));
+
+ // Verify the aspect path of the action.
+ assertThat(actionFromMyAspect.getAspectDescriptorIdsCount()).isEqualTo(2);
+ List<KeyValuePair> expectedMyAspectParams =
+ ImmutableList.of(KeyValuePair.newBuilder().setKey("aspect_param").setValue("y").build());
+ assertCorrectAspectDescriptor(
+ actionGraphContainer,
+ actionFromMyAspect.getAspectDescriptorIds(0),
+ "//test:rule.bzl%my_aspect",
+ expectedMyAspectParams);
+ assertCorrectAspectDescriptor(
+ actionGraphContainer,
+ actionFromMyAspect.getAspectDescriptorIds(1),
+ "//test:rule.bzl%my_jpl_aspect",
+ ImmutableList.of());
+ }
+
+ @Test
+ public void testIncludeAspects_SingleAspect() throws Exception {
+ options.useAspects = true;
+ writeFile(
+ "test/rule.bzl",
+ "MyProvider = provider(",
+ " fields = {",
+ " 'dummy_field': 'dummy field'",
+ " }",
+ ")",
+ "def _my_jpl_aspect_imp(target, ctx):",
+ " if hasattr(ctx.rule.attr, 'srcs'):",
+ " out = ctx.actions.declare_file('out_jpl_{}'.format(target))",
+ " ctx.actions.run(",
+ " inputs = [f for src in ctx.rule.attr.srcs for f in src.files],",
+ " outputs = [out],",
+ " executable = 'dummy',",
+ " mnemonic = 'MyJplAspect'",
+ " )",
+ " return [MyProvider(dummy_field = 1)]",
+ "my_jpl_aspect = aspect(",
+ " attr_aspects = ['deps', 'exports'],",
+ " required_aspect_providers = [['proto_java']],",
+ " attrs = {",
+ " 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])",
+ " },",
+ " implementation = _my_jpl_aspect_imp,",
+ ")",
+ "def _jpl_rule_impl(ctx):",
+ " return struct()",
+ "my_jpl_rule = rule(",
+ " attrs = {",
+ " 'deps': attr.label_list(aspects = [my_jpl_aspect]),",
+ " 'srcs': attr.label_list(allow_files = True),",
+ " 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])",
+ " },",
+ " implementation = _jpl_rule_impl",
+ ")");
+
+ writeFile(
+ "test/BUILD",
+ "load(':rule.bzl', 'my_jpl_rule')",
+ "proto_library(",
+ " name = 'x',",
+ " srcs = [':x.proto']",
+ ")",
+ "my_jpl_rule(",
+ " name = 'my_java_proto',",
+ " deps = [':x'],",
+ " aspect_param = 'y'",
+ ")");
+
+ ActionGraphContainer actionGraphContainer =
+ getOutput("deps(//test:my_java_proto)", AqueryActionFilter.emptyInstance());
+
+ Target protoLibraryTarget =
+ Iterables.getOnlyElement(
+ actionGraphContainer.getTargetsList().stream()
+ .filter(target -> target.getLabel().equals("//test:x"))
+ .collect(Collectors.toList()));
+ Action actionFromMyJplAspect =
+ Iterables.getOnlyElement(
+ actionGraphContainer.getActionsList().stream()
+ .filter(
+ action ->
+ action.getMnemonic().equals("MyJplAspect")
+ && action.getTargetId().equals(protoLibraryTarget.getId()))
+ .collect(Collectors.toList()));
+
+ // Verify the aspect of the action.
+ assertThat(actionFromMyJplAspect.getAspectDescriptorIdsCount()).isEqualTo(1);
+ List<KeyValuePair> expectedMyAspectParams =
+ ImmutableList.of(KeyValuePair.newBuilder().setKey("aspect_param").setValue("y").build());
+ assertCorrectAspectDescriptor(
+ actionGraphContainer,
+ Iterables.getOnlyElement(actionFromMyJplAspect.getAspectDescriptorIdsList()),
+ "//test:rule.bzl%my_jpl_aspect",
+ expectedMyAspectParams);
+ }
+
+ @Test
+ public void testIncludeAspects_twoAspectsOneTarget_separateAspectDescriptors() throws Exception {
+ options.useAspects = true;
+ writeFile(
+ "test/rule.bzl",
+ "JplProvider = provider(",
+ " fields = {",
+ " 'dummy_field': 'dummy field'",
+ " }",
+ ")",
+ "RandomProvider = provider(",
+ " fields = {",
+ " 'dummy_field': 'dummy field'",
+ " }",
+ ")",
+ "def _common_impl(target, ctx, outfilename, mnemonic, provider):",
+ " if hasattr(ctx.rule.attr, 'srcs'):",
+ " out = ctx.actions.declare_file(outfilename)",
+ " ctx.actions.run(",
+ " inputs = [f for src in ctx.rule.attr.srcs for f in src.files],",
+ " outputs = [out],",
+ " executable = 'dummy',",
+ " mnemonic = mnemonic",
+ " )",
+ " return [provider(dummy_field = 1)]",
+ "def _my_random_aspect_impl(target, ctx):",
+ " return _common_impl(target, ctx, 'rand_out', 'MyRandomAspect', RandomProvider)",
+ "my_random_aspect = aspect(",
+ " attr_aspects = ['deps', 'exports'],",
+ " required_aspect_providers = [['proto_java']],",
+ " provides = [RandomProvider],",
+ " implementation = _my_random_aspect_impl,",
+ ")",
+ "def _rule_impl(ctx):",
+ " return struct()",
+ "my_random_rule = rule(",
+ " attrs = {",
+ " 'deps': attr.label_list(aspects = [my_random_aspect]),",
+ " 'srcs': attr.label_list(allow_files = True),",
+ " },",
+ " implementation = _rule_impl",
+ ")",
+ "def _my_jpl_aspect_impl(target, ctx):",
+ " return _common_impl(target, ctx, 'jpl_out', 'MyJplAspect', JplProvider)",
+ "my_jpl_aspect = aspect(",
+ " attr_aspects = ['deps', 'exports'],",
+ " required_aspect_providers = [['proto_java']],",
+ " provides = [JplProvider],",
+ " implementation = _my_jpl_aspect_impl,",
+ ")",
+ "my_jpl_rule = rule(",
+ " attrs = {",
+ " 'deps': attr.label_list(aspects = [my_jpl_aspect]),",
+ " 'srcs': attr.label_list(allow_files = True),",
+ " },",
+ " implementation = _rule_impl",
+ ")");
+
+ writeFile(
+ "test/BUILD",
+ "load(':rule.bzl', 'my_jpl_rule', 'my_random_rule')",
+ "proto_library(",
+ " name = 'x',",
+ " srcs = [':x.proto']",
+ ")",
+ "my_jpl_rule(",
+ " name = 'target_1',",
+ " deps = [':x'],",
+ ")",
+ "my_random_rule(",
+ " name = 'target_2',",
+ " deps = [':x'],",
+ ")");
+
+ ActionGraphContainer actionGraphContainer =
+ getOutput("//test:all", AqueryActionFilter.emptyInstance());
+
+ Target protoLibraryTarget =
+ Iterables.getOnlyElement(
+ actionGraphContainer.getTargetsList().stream()
+ .filter(target -> target.getLabel().equals("//test:x"))
+ .collect(Collectors.toList()));
+ Action actionFromMyJplAspect =
+ Iterables.getOnlyElement(
+ actionGraphContainer.getActionsList().stream()
+ .filter(
+ action ->
+ action.getMnemonic().equals("MyJplAspect")
+ && action.getTargetId().equals(protoLibraryTarget.getId()))
+ .collect(Collectors.toList()));
+ Action actionFromMyRandomAspect =
+ Iterables.getOnlyElement(
+ actionGraphContainer.getActionsList().stream()
+ .filter(
+ action ->
+ action.getMnemonic().equals("MyRandomAspect")
+ && action.getTargetId().equals(protoLibraryTarget.getId()))
+ .collect(Collectors.toList()));
+
+ // Verify the aspect path of the action contains exactly 1 aspect.
+ assertThat(actionFromMyJplAspect.getAspectDescriptorIdsCount()).isEqualTo(1);
+ assertCorrectAspectDescriptor(
+ actionGraphContainer,
+ Iterables.getOnlyElement(actionFromMyJplAspect.getAspectDescriptorIdsList()),
+ "//test:rule.bzl%my_jpl_aspect",
+ ImmutableList.of());
+ assertThat(actionFromMyRandomAspect.getAspectDescriptorIdsCount()).isEqualTo(1);
+ assertCorrectAspectDescriptor(
+ actionGraphContainer,
+ Iterables.getOnlyElement(actionFromMyRandomAspect.getAspectDescriptorIdsList()),
+ "//test:rule.bzl%my_random_aspect",
+ ImmutableList.of());
+ }
+
+ @Test
+ public void testIncludeAspects_flagDisabled_NoAspect() throws Exception {
+ // The flag --include_aspects is set to false by default.
+ writeFile(
+ "test/rule.bzl",
+ "MyProvider = provider(",
+ " fields = {",
+ " 'dummy_field': 'dummy field'",
+ " }",
+ ")",
+ "def _my_jpl_aspect_imp(target, ctx):",
+ " if hasattr(ctx.rule.attr, 'srcs'):",
+ " out = ctx.actions.declare_file('out_jpl_{}'.format(target))",
+ " ctx.actions.run(",
+ " inputs = [f for src in ctx.rule.attr.srcs for f in src.files],",
+ " outputs = [out],",
+ " executable = 'dummy',",
+ " mnemonic = 'MyJplAspect'",
+ " )",
+ " return [MyProvider(dummy_field = 1)]",
+ "my_jpl_aspect = aspect(",
+ " attr_aspects = ['deps', 'exports'],",
+ " required_aspect_providers = [['proto_java']],",
+ " attrs = {",
+ " 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])",
+ " },",
+ " implementation = _my_jpl_aspect_imp,",
+ ")",
+ "def _jpl_rule_impl(ctx):",
+ " return struct()",
+ "my_jpl_rule = rule(",
+ " attrs = {",
+ " 'deps': attr.label_list(aspects = [my_jpl_aspect]),",
+ " 'srcs': attr.label_list(allow_files = True),",
+ " 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])",
+ " },",
+ " implementation = _jpl_rule_impl",
+ ")");
+
+ writeFile(
+ "test/BUILD",
+ "load(':rule.bzl', 'my_jpl_rule')",
+ "proto_library(",
+ " name = 'x',",
+ " srcs = [':x.proto']",
+ ")",
+ "my_jpl_rule(",
+ " name = 'my_java_proto',",
+ " deps = [':x'],",
+ " aspect_param = 'y'",
+ ")");
+
+ ActionGraphContainer actionGraphContainer =
+ getOutput("deps(//test:my_java_proto)", AqueryActionFilter.emptyInstance());
+
+ assertThat(
+ actionGraphContainer.getActionsList().stream()
+ .filter(action -> !action.getAspectDescriptorIdsList().isEmpty())
+ .collect(Collectors.toList()))
+ .isEmpty();
+ }
+
private AnalysisProtos.ActionGraphContainer getOutput(String queryExpression) throws Exception {
return getOutput(queryExpression, /* actionFilters= */ AqueryActionFilter.emptyInstance());
}
@@ -570,6 +944,22 @@
assertThat(action.getOutputIdsList()).contains(outputId);
}
+ private void assertCorrectAspectDescriptor(
+ ActionGraphContainer actionGraphContainer,
+ String aspectDescriptorId,
+ String expectedName,
+ List<KeyValuePair> expectedParameters) {
+ for (AspectDescriptor aspectDescriptor : actionGraphContainer.getAspectDescriptorsList()) {
+ if (!aspectDescriptorId.equals(aspectDescriptor.getId())) {
+ continue;
+ }
+ assertThat(aspectDescriptor.getName()).isEqualTo(expectedName);
+ assertThat(aspectDescriptor.getParametersList()).isEqualTo(expectedParameters);
+ return;
+ }
+ fail("Should have matched at least one AspectDescriptor.");
+ }
+
private AqueryActionFilter constructActionFilter(ImmutableMap<String, String> patternStrings) {
AqueryActionFilter.Builder builder = AqueryActionFilter.builder();
for (Entry<String, String> e : patternStrings.entrySet()) {
diff --git a/src/test/shell/integration/aquery_test.sh b/src/test/shell/integration/aquery_test.sh
index 86efff9..1346f0d 100755
--- a/src/test/shell/integration/aquery_test.sh
+++ b/src/test/shell/integration/aquery_test.sh
@@ -118,7 +118,7 @@
cat output >> "$TEST_log"
assert_contains "action 'Executing genrule //$pkg:bar'" output
assert_contains "Mnemonic: Genrule" output
- assert_contains "Owner: //$pkg:bar" output
+ assert_contains "Target: //$pkg:bar" output
assert_contains "Configuration: .*-fastbuild" output
# Only check that the inputs/outputs/command line/environment exist, but not
# their actual contents since that would be too much.
@@ -199,7 +199,7 @@
|| fail "Expected success"
cat output >> "$TEST_log"
assert_contains "Mnemonic: SkylarkAction" output
- assert_contains "Owner: //$pkg:goo" output
+ assert_contains "Target: //$pkg:goo" output
assert_contains "Environment: \[.*foo=bar" output
}
@@ -822,4 +822,129 @@
2> "$TEST_log" || fail "Expected success"
}
+function test_aquery_aspect_on_aspect() {
+ local pkg="${FUNCNAME[0]}"
+ mkdir -p "$pkg" || fail "mkdir -p $pkg"
+ cat > "$pkg/rule.bzl" <<'EOF'
+IntermediateProvider = provider(
+ fields = {
+ 'dummy_field': 'dummy field'
+ }
+)
+
+MyBaseRuleProvider = provider(
+ fields = {
+ 'dummy_field': 'dummy field'
+ }
+)
+
+def _base_rule_impl(ctx):
+ return [MyBaseRuleProvider(dummy_field = 1)]
+
+base_rule = rule(
+ attrs = {
+ 'srcs': attr.label_list(allow_files = True),
+ },
+ implementation = _base_rule_impl
+)
+
+def _intermediate_aspect_imp(target, ctx):
+ if hasattr(ctx.rule.attr, 'srcs'):
+ out = ctx.actions.declare_file('out_jpl_{}'.format(target))
+ ctx.actions.run(
+ inputs = [f for src in ctx.rule.attr.srcs for f in src.files],
+ outputs = [out],
+ executable = 'dummy',
+ mnemonic = 'MyIntermediateAspect'
+ )
+
+ return [IntermediateProvider(dummy_field = 1)]
+
+intermediate_aspect = aspect(
+ attr_aspects = ['deps', 'exports'],
+ required_aspect_providers = [[MyBaseRuleProvider]],
+ provides = [IntermediateProvider],
+ implementation = _intermediate_aspect_imp,
+)
+
+def _int_rule_impl(ctx):
+ return struct()
+
+intermediate_rule = rule(
+ attrs = {
+ 'deps': attr.label_list(aspects = [intermediate_aspect]),
+ 'srcs': attr.label_list(allow_files = True),
+ },
+ implementation = _int_rule_impl
+)
+
+def _aspect_impl(target, ctx):
+ if hasattr(ctx.rule.attr, 'srcs'):
+ out = ctx.actions.declare_file('out{}'.format(target))
+ ctx.actions.run(
+ inputs = [f for src in ctx.rule.attr.srcs for f in src.files],
+ outputs = [out],
+ executable = 'dummy',
+ mnemonic = 'MyAspect'
+ )
+
+ return [struct()]
+
+my_aspect = aspect(
+ attr_aspects = ['deps', 'exports'],
+ required_aspect_providers = [[IntermediateProvider], [MyBaseRuleProvider]],
+ attrs = {
+ 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])
+ },
+ implementation = _aspect_impl,
+)
+
+def _rule_impl(ctx):
+ return struct()
+
+my_rule = rule(
+ attrs = {
+ 'deps': attr.label_list(aspects = [my_aspect]),
+ 'srcs': attr.label_list(allow_files = True),
+ 'aspect_param': attr.string(default = 'x', values = ['x', 'y'])
+ },
+ implementation = _rule_impl
+)
+EOF
+
+ cat > "$pkg/BUILD" <<'EOF'
+load(':rule.bzl', 'my_rule', 'intermediate_rule', 'base_rule')
+
+base_rule(
+ name = 'x',
+ srcs = [':x.java'],
+)
+
+intermediate_rule(
+ name = 'int_target',
+ deps = [':x'],
+)
+
+my_rule(
+ name = 'my_target',
+ srcs = ['foo.java'],
+ aspect_param = 'y',
+ deps = [':int_target'],
+)
+EOF
+
+ QUERY="deps(//$pkg:my_target)"
+
+ bazel aquery --output=text --include_aspects ${QUERY} > output 2> "$TEST_log" \
+ || fail "Expected success"
+ cat output >> "$TEST_log"
+
+ assert_contains "Mnemonic: MyAspect" output
+ assert_contains "AspectDescriptors: \[.*:rule.bzl%my_aspect(aspect_param='y')" output
+ assert_contains "^.*->.*:rule.bzl%intermediate_aspect()\]$" output
+
+ bazel aquery --output=textproto --include_aspects --noinclude_commandline ${QUERY} > output \
+ 2> "$TEST_log" || fail "Expected success"
+}
+
run_suite "${PRODUCT_NAME} action graph query tests"