blob: ed193c92c97b1801745c752b4695c6143519fccc [file] [log] [blame]
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.skylark;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.OutputGroupProvider;
import com.google.devtools.build.lib.analysis.SkylarkProviders;
import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.packages.AspectDefinition;
import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
import com.google.devtools.build.lib.rules.java.Jvm;
import com.google.devtools.build.lib.skyframe.AspectValue;
import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* Tests for Skylark aspects
*/
@RunWith(JUnit4.class)
public class SkylarkAspectsTest extends AnalysisTestCase {
protected boolean keepGoing() {
return false;
}
@Test
public void testAspect() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" print('This aspect does nothing')",
" return struct()",
"MyAspect = aspect(implementation=_impl)");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(
transform(
analysisResult.getTargetsToBuild(),
new Function<ConfiguredTarget, String>() {
@Nullable
@Override
public String apply(ConfiguredTarget configuredTarget) {
return configuredTarget.getLabel().toString();
}
}))
.containsExactly("//test:xxx");
assertThat(
transform(
analysisResult.getAspects(),
new Function<AspectValue, String>() {
@Nullable
@Override
public String apply(AspectValue aspectValue) {
return String.format(
"%s(%s)",
aspectValue.getConfiguredAspect().getName(),
aspectValue.getLabel().toString());
}
}))
.containsExactly("//test:aspect.bzl%MyAspect(//test:xxx)");
}
@Test
public void testAspectAllowsFragmentsToBeSpecified() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" print('This aspect does nothing')",
" return struct()",
"MyAspect = aspect(implementation=_impl, fragments=['jvm'], host_fragments=['cpp'])");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
AspectValue aspectValue = Iterables.getOnlyElement(analysisResult.getAspects());
AspectKey aspectKey = aspectValue.getKey();
AspectDefinition aspectDefinition = aspectKey.getAspect().getDefinition();
assertThat(
aspectDefinition.getConfigurationFragmentPolicy()
.isLegalConfigurationFragment(Jvm.class, ConfigurationTransition.NONE))
.isTrue();
assertThat(
aspectDefinition.getConfigurationFragmentPolicy()
.isLegalConfigurationFragment(Jvm.class, ConfigurationTransition.HOST))
.isFalse();
assertThat(
aspectDefinition.getConfigurationFragmentPolicy()
.isLegalConfigurationFragment(CppConfiguration.class, ConfigurationTransition.NONE))
.isFalse();
assertThat(
aspectDefinition.getConfigurationFragmentPolicy()
.isLegalConfigurationFragment(CppConfiguration.class, ConfigurationTransition.HOST))
.isTrue();
}
@Test
public void testAspectPropagating() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" s = set([target.label])",
" c = set([ctx.rule.kind])",
" for i in ctx.rule.attr.deps:",
" s += i.target_labels",
" c += i.rule_kinds",
" return struct(target_labels = s, rule_kinds = c)",
"",
"MyAspect = aspect(",
" implementation=_impl,",
" attr_aspects=['deps'],",
")");
scratch.file(
"test/BUILD",
"java_library(",
" name = 'yyy',",
")",
"java_library(",
" name = 'xxx',",
" srcs = ['A.java'],",
" deps = [':yyy'],",
")");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(
transform(
analysisResult.getTargetsToBuild(),
new Function<ConfiguredTarget, String>() {
@Nullable
@Override
public String apply(ConfiguredTarget configuredTarget) {
return configuredTarget.getLabel().toString();
}
}))
.containsExactly("//test:xxx");
AspectValue aspectValue = analysisResult.getAspects().iterator().next();
SkylarkProviders skylarkProviders =
aspectValue.getConfiguredAspect().getProvider(SkylarkProviders.class);
assertThat(skylarkProviders).isNotNull();
Object names = skylarkProviders.getValue("target_labels");
assertThat(names).isInstanceOf(SkylarkNestedSet.class);
assertThat(
transform(
(SkylarkNestedSet) names,
new Function<Object, String>() {
@Nullable
@Override
public String apply(Object o) {
assertThat(o).isInstanceOf(Label.class);
return o.toString();
}
}))
.containsExactly("//test:xxx", "//test:yyy");
Object ruleKinds = skylarkProviders.getValue("rule_kinds");
assertThat(ruleKinds).isInstanceOf(SkylarkNestedSet.class);
assertThat((SkylarkNestedSet) ruleKinds).containsExactly("java_library");
}
@Test
public void aspectsPropagatingForDefaultAndImplicit() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" s = set([target.label])",
" c = set([ctx.rule.kind])",
" a = ctx.rule.attr",
" if hasattr(a, '_stl') and a._stl:",
" s += a._stl.target_labels",
" c += a._stl.rule_kinds",
" if hasattr(a, '_stl_default') and a._stl_default:",
" s += a._stl_default.target_labels",
" c += a._stl_default.rule_kinds",
" return struct(target_labels = s, rule_kinds = c)",
"",
"def _rule_impl(ctx):",
" pass",
"",
"my_rule = rule(implementation = _rule_impl,",
" attrs = { '_stl' : attr.label(default = Label('//test:xxx')) },",
")",
"MyAspect = aspect(",
" implementation=_impl,",
" attr_aspects=['_stl', '_stl_default'],",
")");
scratch.file(
"test/BUILD",
"load('/test/aspect', 'my_rule')",
"cc_library(",
" name = 'xxx',",
")",
"my_rule(",
" name = 'yyy',",
")"
);
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:yyy");
AspectValue aspectValue = analysisResult.getAspects().iterator().next();
SkylarkProviders skylarkProviders =
aspectValue.getConfiguredAspect().getProvider(SkylarkProviders.class);
assertThat(skylarkProviders).isNotNull();
Object names = skylarkProviders.getValue("target_labels");
assertThat(names).isInstanceOf(SkylarkNestedSet.class);
assertThat(
transform(
(SkylarkNestedSet) names,
new Function<Object, String>() {
@Nullable
@Override
public String apply(Object o) {
assertThat(o).isInstanceOf(Label.class);
return ((Label) o).getName();
}
}))
.containsExactly("stl", "xxx", "yyy");
}
@Test
public void testAspectWithOutputGroups() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" f = target.output_group('_hidden_top_level')",
" return struct(output_groups = { 'my_result' : f })",
"",
"MyAspect = aspect(",
" implementation=_impl,",
" attr_aspects=['deps'],",
")");
scratch.file(
"test/BUILD",
"java_library(",
" name = 'xxx',",
" srcs = ['A.java'],",
")");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(
transform(
analysisResult.getTargetsToBuild(),
new Function<ConfiguredTarget, String>() {
@Nullable
@Override
public String apply(ConfiguredTarget configuredTarget) {
return configuredTarget.getLabel().toString();
}
}))
.containsExactly("//test:xxx");
AspectValue aspectValue = analysisResult.getAspects().iterator().next();
OutputGroupProvider outputGroupProvider =
aspectValue.getConfiguredAspect().getProvider(OutputGroupProvider.class);
assertThat(outputGroupProvider).isNotNull();
NestedSet<Artifact> names = outputGroupProvider.getOutputGroup("my_result");
assertThat(names).isNotEmpty();
NestedSet<Artifact> expectedSet = getConfiguredTarget("//test:xxx")
.getProvider(OutputGroupProvider.class)
.getOutputGroup(OutputGroupProvider.HIDDEN_TOP_LEVEL);
assertThat(names).containsExactlyElementsIn(expectedSet);
}
@Test
public void testAspectsFromSkylarkRules() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _aspect_impl(target, ctx):",
" s = set([target.label])",
" for i in ctx.rule.attr.deps:",
" s += i.target_labels",
" return struct(target_labels = s)",
"",
"def _rule_impl(ctx):",
" s = set([])",
" for i in ctx.attr.attr:",
" s += i.target_labels",
" return struct(rule_deps = s)",
"",
"MyAspect = aspect(",
" implementation=_aspect_impl,",
" attr_aspects=['deps'],",
")",
"my_rule = rule(",
" implementation=_rule_impl,",
" attrs = { 'attr' : ",
" attr.label_list(mandatory=True, allow_files=True, aspects = [MyAspect]) },",
")");
scratch.file(
"test/BUILD",
"load('/test/aspect', 'my_rule')",
"java_library(",
" name = 'yyy',",
")",
"my_rule(",
" name = 'xxx',",
" attr = [':yyy'],",
")");
AnalysisResult analysisResult = update("//test:xxx");
assertThat(
transform(
analysisResult.getTargetsToBuild(),
new Function<ConfiguredTarget, String>() {
@Nullable
@Override
public String apply(ConfiguredTarget configuredTarget) {
return configuredTarget.getLabel().toString();
}
}))
.containsExactly("//test:xxx");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
SkylarkProviders skylarkProviders = target.getProvider(SkylarkProviders.class);
assertThat(skylarkProviders).isNotNull();
Object names = skylarkProviders.getValue("rule_deps");
assertThat(names).isInstanceOf(SkylarkNestedSet.class);
assertThat(
transform(
(SkylarkNestedSet) names,
new Function<Object, String>() {
@Nullable
@Override
public String apply(Object o) {
assertThat(o).isInstanceOf(Label.class);
return o.toString();
}
}))
.containsExactly("//test:yyy");
}
@Test
public void testAspectFailingExecution() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" return 1/0",
"",
"MyAspect = aspect(implementation=_impl)");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent(
"ERROR /workspace/test/BUILD:1:1: in "
+ "//test:aspect.bzl%MyAspect aspect on java_library rule //test:xxx: \n"
+ "Traceback (most recent call last):\n"
+ "\tFile \"/workspace/test/BUILD\", line 1\n"
+ "\t\t//test:aspect.bzl%MyAspect(...)\n"
+ "\tFile \"/workspace/test/aspect.bzl\", line 2, in _impl\n"
+ "\t\t1 / 0\n"
+ "integer division by zero");
}
@Test
public void testAspectFailingReturnsNotAStruct() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" return 0",
"",
"MyAspect = aspect(implementation=_impl)");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent("Aspect implementation doesn't return a struct");
}
@Test
public void testAspectFailingReturnsUnsafeObject() throws Exception {
scratch.file(
"test/aspect.bzl",
"def foo():",
" return 0",
"def _impl(target, ctx):",
" return struct(x = foo)",
"",
"MyAspect = aspect(implementation=_impl)");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent(
"ERROR /workspace/test/BUILD:1:1: in //test:aspect.bzl%MyAspect aspect on java_library rule"
+ " //test:xxx: \n"
+ "\n"
+ "\n"
+ "/workspace/test/aspect.bzl:4:11: Value of provider 'x' is of an illegal type: function");
}
@Test
public void testAspectFailingOrphanArtifacts() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" ctx.new_file('missing_in_action.txt')",
" return struct()",
"",
"MyAspect = aspect(implementation=_impl)");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent(
"ERROR /workspace/test/BUILD:1:1: in "
+ "//test:aspect.bzl%MyAspect aspect on java_library rule //test:xxx: \n"
+ "\n"
+ "\n"
+ "The following files have no generating action:\n"
+ "test/missing_in_action.txt\n");
}
@Test
public void topLevelAspectIsNotAnAspect() throws Exception {
scratch.file("test/aspect.bzl", "MyAspect = 4");
scratch.file("test/BUILD", "java_library(name = 'xxx')");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent("MyAspect from //test:aspect.bzl is not an aspect");
}
@Test
public void topLevelAspectDoesNotExist() throws Exception {
scratch.file("test/aspect.bzl", "");
scratch.file("test/BUILD", "java_library(name = 'xxx')");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent("MyAspect from //test:aspect.bzl is not an aspect");
}
@Test
public void topLevelAspectDoesNotExist2() throws Exception {
scratch.file("test/BUILD", "java_library(name = 'xxx')");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent(
"Extension file not found. Unable to load file '//test:aspect.bzl': "
+ "file doesn't exist or isn't a file");
}
@Test
public void topLevelAspectDoesNotExistNoBuildFile() throws Exception {
scratch.file("test/BUILD", "java_library(name = 'xxx')");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of("foo/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent(
"Every .bzl file must have a corresponding package, but 'foo' does not have one. "
+ "Please create a BUILD file in the same or any parent directory. "
+ "Note that this BUILD file does not need to do anything except exist.");
}
@Test
public void multipleExecutablesInTarget() throws Exception {
scratch.file("foo/extension.bzl",
"def _aspect_impl(target, ctx):",
" return struct()",
"my_aspect = aspect(_aspect_impl)",
"def _main_rule_impl(ctx):",
" pass",
"my_rule = rule(_main_rule_impl,",
" attrs = { ",
" 'exe1' : attr.label(executable = True, allow_files = True),",
" 'exe2' : attr.label(executable = True, allow_files = True),",
" },",
")"
);
scratch.file("foo/tool.sh", "#!/bin/bash");
scratch.file("foo/BUILD",
"load('extension', 'my_rule')",
"my_rule(name = 'main', exe1 = ':tool.sh', exe2 = ':tool.sh')"
);
AnalysisResult analysisResultOfRule =
update(ImmutableList.<String>of(), "//foo:main");
assertThat(analysisResultOfRule.hasError()).isFalse();
AnalysisResult analysisResultOfAspect =
update(ImmutableList.<String>of("/foo/extension.bzl%my_aspect"), "//foo:main");
assertThat(analysisResultOfAspect.hasError()).isFalse();
}
@Test
public void testAspectFragmentAccessSuccess() throws Exception {
getConfiguredTargetForAspectFragment(
"ctx.fragments.cpp.compiler", "'cpp'", "", "", "");
assertNoEvents();
}
@Test
public void testAspectHostFragmentAccessSuccess() throws Exception {
getConfiguredTargetForAspectFragment(
"ctx.host_fragments.cpp.compiler", "", "'cpp'", "", "");
assertNoEvents();
}
@Test
public void testAspectFragmentAccessError() throws Exception {
reporter.removeHandler(failFastHandler);
try {
getConfiguredTargetForAspectFragment(
"ctx.fragments.cpp.compiler", "'java'", "'cpp'", "'cpp'", "");
fail("update() should have failed");
} catch (ViewCreationFailedException e) {
// expected
}
assertContainsEvent(
"//test:aspect.bzl%MyAspect aspect on my_rule has to declare 'cpp' as a "
+ "required fragment in target configuration in order to access it. Please update the "
+ "'fragments' argument of the rule definition "
+ "(for example: fragments = [\"cpp\"])");
}
@Test
public void testAspectHostFragmentAccessError() throws Exception {
reporter.removeHandler(failFastHandler);
try {
getConfiguredTargetForAspectFragment(
"ctx.host_fragments.cpp.compiler", "'cpp'", "'java'", "", "'cpp'");
fail("update() should have failed");
} catch (ViewCreationFailedException e) {
// expected
}
assertContainsEvent(
"//test:aspect.bzl%MyAspect aspect on my_rule has to declare 'cpp' as a "
+ "required fragment in host configuration in order to access it. Please update the "
+ "'host_fragments' argument of the rule definition "
+ "(for example: host_fragments = [\"cpp\"])");
}
@Test
public void testAspectFragmentFallback() throws Exception {
// TODO(mstaib): Remove this test when rule fragment fallback is no longer permitted.
getConfiguredTargetForAspectFragment(
"ctx.fragments.cpp.compiler", "", "", "'cpp'", "");
assertNoEvents();
}
@Test
public void testAspectHostFragmentFallback() throws Exception {
// TODO(mstaib): Remove this test when rule fragment fallback is no longer permitted.
getConfiguredTargetForAspectFragment(
"ctx.host_fragments.cpp.compiler", "", "", "", "'cpp'");
assertNoEvents();
}
private ConfiguredTarget getConfiguredTargetForAspectFragment(
String fullFieldName,
String fragments,
String hostFragments,
String ruleFragments,
String ruleHostFragments)
throws Exception {
scratch.file(
"test/aspect.bzl",
"def _aspect_impl(target, ctx):",
" return struct(result = str(" + fullFieldName + "))",
"",
"def _rule_impl(ctx):",
" return struct(stuff = '...')",
"",
"MyAspect = aspect(",
" implementation=_aspect_impl,",
" attr_aspects=['deps'],",
" fragments=[" + fragments + "],",
" host_fragments=[" + hostFragments + "],",
")",
"my_rule = rule(",
" implementation=_rule_impl,",
" attrs = { 'attr' : ",
" attr.label_list(mandatory=True, allow_files=True, aspects = [MyAspect]) },",
" fragments=[" + ruleFragments + "],",
" host_fragments=[" + ruleHostFragments + "],",
")");
scratch.file(
"test/BUILD",
"load('/test/aspect', 'my_rule')",
"exports_files(['zzz'])",
"my_rule(",
" name = 'yyy',",
" attr = ['zzz'],",
")",
"my_rule(",
" name = 'xxx',",
" attr = ['yyy'],",
")");
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
if (result.hasError()) {
assertThat(keepGoing()).isTrue();
throw new ViewCreationFailedException("Analysis failed");
}
return getConfiguredTarget("//test:xxx");
}
@Test
public void invalidateAspectOnBzlFileChange() throws Exception {
scratch.file("test/build_defs.bzl", aspectBzlFile("'deps'"));
scratch.file(
"test/BUILD",
"load('build_defs', 'repro', 'repro_no_aspect')",
"repro_no_aspect(name = 'r0')",
"repro_no_aspect(name = 'r1', deps = [':r0'])",
"repro(name = 'r2', deps = [':r1'])");
buildTargetAndCheckRuleInfo("//test:r0", "//test:r1");
// Make aspect propagation list empty.
scratch.overwriteFile("test/build_defs.bzl", aspectBzlFile(""));
// The aspect should not propagate to //test:r0 anymore.
buildTargetAndCheckRuleInfo("//test:r1");
}
private void buildTargetAndCheckRuleInfo(String... expectedLabels) throws Exception {
AnalysisResult result = update(ImmutableList.<String>of(), "//test:r2");
ConfiguredTarget configuredTarget = result.getTargetsToBuild().iterator().next();
SkylarkNestedSet ruleInfoValue =
(SkylarkNestedSet)
configuredTarget.getProvider(SkylarkProviders.class).getValue("rule_info");
assertThat(ruleInfoValue.getSet(String.class))
.containsExactlyElementsIn(Arrays.asList(expectedLabels));
}
private String[] aspectBzlFile(String attrAspects) {
return new String[] {
"def _repro_aspect_impl(target, ctx):",
" s = set([str(target.label)])",
" for d in ctx.rule.attr.deps:",
" if hasattr(d, 'aspect_info'):",
" s = s | d.aspect_info",
" return struct(aspect_info = s)",
"",
"_repro_aspect = aspect(",
" _repro_aspect_impl,",
" attr_aspects = [" + attrAspects + "],",
")",
"",
"def repro_impl(ctx):",
" s = set()",
" for d in ctx.attr.deps:",
" if hasattr(d, 'aspect_info'):",
" s = s | d.aspect_info",
" return struct(rule_info = s)",
"",
"def repro_no_aspect_impl(ctx):",
" pass",
"",
"repro_no_aspect = rule(implementation = repro_no_aspect_impl,",
" attrs = {",
" 'deps': attr.label_list(",
" allow_files = True,",
" )",
" },",
")",
"",
"repro = rule(implementation = repro_impl,",
" attrs = {",
" 'deps': attr.label_list(",
" allow_files = True,",
" aspects = [_repro_aspect],",
" )",
" },",
")"
};
}
@RunWith(JUnit4.class)
public static final class WithKeepGoing extends SkylarkAspectsTest {
@Override
protected FlagBuilder defaultFlags() {
return new FlagBuilder().with(Flag.KEEP_GOING);
}
@Override
protected boolean keepGoing() {
return true;
}
}
}