blob: 7c26211a9908434b944a2815296401e14d08a63a [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.starlark;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Streams.stream;
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.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.AnalysisResult;
import com.google.devtools.build.lib.analysis.AspectValue;
import com.google.devtools.build.lib.analysis.ConfiguredAspect;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
import com.google.devtools.build.lib.analysis.util.TestAspects;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.TargetParsingException;
import com.google.devtools.build.lib.collect.nestedset.Depset;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.packages.AspectClass;
import com.google.devtools.build.lib.packages.AspectDefinition;
import com.google.devtools.build.lib.packages.StarlarkAspectClass;
import com.google.devtools.build.lib.packages.StarlarkInfo;
import com.google.devtools.build.lib.packages.StarlarkProvider;
import com.google.devtools.build.lib.packages.StructImpl;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
import com.google.devtools.build.lib.rules.java.JavaConfiguration;
import com.google.devtools.build.lib.server.FailureDetails.Analysis;
import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.starlark.java.eval.Sequence;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkInt;
import net.starlark.java.eval.StarlarkList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for Starlark aspects */
@RunWith(JUnit4.class)
public class StarlarkDefinedAspectsTest extends AnalysisTestCase {
protected boolean keepGoing() {
return false;
}
@Test
public void simpleAspect() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
print('This aspect does nothing')
return []
MyAspect = aspect(implementation=_impl)
""");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
assertThat(getAspectDescriptions(analysisResult))
.containsExactly("//test:aspect.bzl%MyAspect(//test:xxx)");
}
@Test
public void aspectWithSingleDeclaredProvider() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
foo = provider()
def _impl(target, ctx):
return foo()
MyAspect = aspect(implementation=_impl)
""");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
assertThat(getAspectDescriptions(analysisResult))
.containsExactly("//test:aspect.bzl%MyAspect(//test:xxx)");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key fooKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "foo");
assertThat(configuredAspect.get(fooKey).getProvider().getKey()).isEqualTo(fooKey);
}
@Test
public void aspectWithDeclaredProviders() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
foo = provider()
bar = provider()
def _impl(target, ctx):
return [foo(), bar()]
MyAspect = aspect(implementation=_impl)
""");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
assertThat(getAspectDescriptions(analysisResult))
.containsExactly("//test:aspect.bzl%MyAspect(//test:xxx)");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key fooKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "foo");
StarlarkProvider.Key barKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "bar");
assertThat(configuredAspect.get(fooKey).getProvider().getKey()).isEqualTo(fooKey);
assertThat(configuredAspect.get(barKey).getProvider().getKey()).isEqualTo(barKey);
}
@Test
public void aspectWithDeclaredProvidersInAStruct() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
foo = provider()
bar = provider()
def _impl(target, ctx):
return [foo(), bar()]
MyAspect = aspect(implementation=_impl)
""");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
assertThat(getAspectDescriptions(analysisResult))
.containsExactly("//test:aspect.bzl%MyAspect(//test:xxx)");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key fooKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "foo");
StarlarkProvider.Key barKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "bar");
assertThat(configuredAspect.get(fooKey).getProvider().getKey()).isEqualTo(fooKey);
assertThat(configuredAspect.get(barKey).getProvider().getKey()).isEqualTo(barKey);
}
private static Iterable<String> getAspectDescriptions(AnalysisResult analysisResult) {
return Iterables.transform(
analysisResult.getAspectsMap().keySet(),
aspectKey ->
String.format("%s(%s)", aspectKey.getAspectClass().getName(), aspectKey.getLabel()));
}
@Test
public void aspectCommandLineLabel() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
print('This aspect does nothing')
return []
MyAspect = aspect(implementation=_impl)
""");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("//test:aspect.bzl%MyAspect"), "//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
assertThat(getAspectDescriptions(analysisResult))
.containsExactly("//test:aspect.bzl%MyAspect(//test:xxx)");
}
@Test
public void aspectCommandLineRepoLabel() throws Exception {
scratch.overwriteFile(
"WORKSPACE",
scratch.readFile("WORKSPACE"),
"local_repository(name='local', path='local/repo')");
scratch.file("local/repo/WORKSPACE");
scratch.file(
"local/repo/aspect.bzl",
"""
def _impl(target, ctx):
print('This aspect does nothing')
return []
MyAspect = aspect(implementation=_impl)
""");
scratch.file("local/repo/BUILD");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("@local//:aspect.bzl%MyAspect"), "//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
assertThat(getAspectDescriptions(analysisResult))
.containsExactly("@@local//:aspect.bzl%MyAspect(//test:xxx)");
}
private static Iterable<String> getLabelsToBuild(AnalysisResult analysisResult) {
return Iterables.transform(
analysisResult.getTargetsToBuild(),
configuredTarget -> configuredTarget.getLabel().toString());
}
@Test
public void aspectAllowsFragmentsToBeSpecified() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
print('This aspect does nothing')
return []
MyAspect = aspect(implementation=_impl, fragments=['java'])
""");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
AspectKey key = Iterables.getOnlyElement(analysisResult.getAspectsMap().keySet());
AspectValue aspectValue = (AspectValue) skyframeExecutor.getEvaluator().getExistingValue(key);
AspectDefinition aspectDefinition = aspectValue.getAspect().getDefinition();
assertThat(
aspectDefinition
.getConfigurationFragmentPolicy()
.isLegalConfigurationFragment(JavaConfiguration.class))
.isTrue();
assertThat(
aspectDefinition
.getConfigurationFragmentPolicy()
.isLegalConfigurationFragment(CppConfiguration.class))
.isFalse();
}
@Test
public void aspectPropagating() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
Info = provider()
def _impl(target, ctx):
s = depset([target.label], transitive = [i[Info].target_labels for i in ctx.rule.attr.deps])
c = depset([ctx.rule.kind], transitive = [i[Info].rule_kinds for i in ctx.rule.attr.deps])
return Info(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(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
ConfiguredAspect configuredAspect = analysisResult.getAspectsMap().values().iterator().next();
assertThat(configuredAspect).isNotNull();
StarlarkInfo info = getStarlarkProvider(configuredAspect, "//test:aspect.bzl", "Info");
NestedSet<Label> names =
Depset.cast(info.getValue("target_labels"), Label.class, "target_labels");
assertThat(names.toList().stream().map(Label::toString))
.containsExactly("//test:xxx", "//test:yyy");
Object ruleKinds =
getStarlarkProvider(configuredAspect, "//test:aspect.bzl", "Info").getValue("rule_kinds");
assertThat(ruleKinds).isInstanceOf(Depset.class);
assertThat(((Depset) ruleKinds).toList()).containsExactly("java_library");
}
@Test
public void aspectsPropagatingForDefaultAndImplicit() throws Exception {
useConfiguration(
"--experimental_builtins_injection_override=+cc_library",
"--incompatible_enable_cc_toolchain_resolution");
scratch.file(
"test/aspect.bzl",
"""
Info = provider()
def _impl(target, ctx):
s = []
c = []
a = ctx.rule.attr
if getattr(a, '_defaultattr', None):
s += [a._defaultattr[Info].target_labels]
c += [a._defaultattr[Info].rule_kinds]
if getattr(a, '_cc_toolchain', None):
s += [a._cc_toolchain[Info].target_labels]
c += [a._cc_toolchain[Info].rule_kinds]
return Info(
target_labels = depset([target.label], transitive = s),
rule_kinds = depset([ctx.rule.kind], transitive = c))
def _rule_impl(ctx):
pass
my_rule = rule(implementation = _rule_impl,
attrs = { '_defaultattr' : attr.label(default = Label('//test:xxx')) },
)
MyAspect = aspect(
implementation=_impl,
attr_aspects=['_defaultattr', '_cc_toolchain'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
cc_library(
name = 'xxx',
)
my_rule(
name = 'yyy',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:yyy");
ConfiguredAspect configuredAspect = analysisResult.getAspectsMap().values().iterator().next();
assertThat(configuredAspect).isNotNull();
StarlarkProvider.Key infoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "Info");
Object nameSet = ((StarlarkInfo) configuredAspect.get(infoKey)).getValue("target_labels");
ImmutableList<String> names =
ImmutableList.copyOf(
Iterables.transform(
((Depset) nameSet).toList(),
o -> {
assertThat(o).isInstanceOf(Label.class);
return ((Label) o).getName();
}));
assertThat(names).containsAtLeast("xxx", "yyy");
}
@Test
public void aspectsDirOnMergedTargets() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
Info = provider()
def _impl(target, ctx):
return Info(aspect_provider = 'data')
p = provider()
MyAspect = aspect(implementation=_impl)
def _rule_impl(ctx):
if ctx.attr.dep:
return [p(dir = dir(ctx.attr.dep))]
return [p()]
my_rule = rule(implementation = _rule_impl,
attrs = { 'dep' : attr.label(aspects = [MyAspect]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx',)
my_rule(name = 'yyy', dep = ':xxx')
""");
AnalysisResult analysisResult = update("//test:yyy");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StructImpl names = getStarlarkProvider(target, "p");
assertThat((Iterable<?>) names.getValue("dir"))
.containsExactly(
"actions",
"data_runfiles",
"default_runfiles",
"files",
"files_to_run",
"label",
"output_groups");
}
@Test
public void aspectWithOutputGroupsDeclaredProvider() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" f = target[OutputGroupInfo]._hidden_top_level" + INTERNAL_SUFFIX,
" return [OutputGroupInfo(my_result = f)]",
"",
"MyAspect = aspect(",
" implementation=_impl,",
")");
scratch.file(
"test/BUILD",
"""
java_library(
name = 'xxx',
srcs = ['A.java'],
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
ConfiguredAspect configuredAspect = analysisResult.getAspectsMap().values().iterator().next();
OutputGroupInfo outputGroupInfo = OutputGroupInfo.get(configuredAspect);
assertThat(outputGroupInfo).isNotNull();
NestedSet<Artifact> names = outputGroupInfo.getOutputGroup("my_result");
assertThat(names.toList()).isNotEmpty();
// Configuration of the true Artifact may diverge slightly (e.g. be trimmed) causing owners to
// also diverge so just compare paths instead of the whole Artifact.
ImmutableList<Path> paths =
names.toList().stream().map(Artifact::getPath).collect(toImmutableList());
ImmutableList<Path> expectedPaths =
OutputGroupInfo.get(getConfiguredTarget("//test:xxx"))
.getOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL)
.toList()
.stream()
.map(Artifact::getPath)
.collect(toImmutableList());
assertThat(paths).containsExactlyElementsIn(expectedPaths);
}
@Test
public void aspectWithOutputGroupsAsListDeclaredProvider() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" g = target[OutputGroupInfo]._hidden_top_level" + INTERNAL_SUFFIX,
" return [OutputGroupInfo(my_result=g.to_list())]",
"",
"MyAspect = aspect(",
" implementation=_impl,",
")");
scratch.file(
"test/BUILD",
"""
java_library(
name = 'xxx',
srcs = ['A.java'],
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(
Iterables.transform(
analysisResult.getTargetsToBuild(),
configuredTarget -> configuredTarget.getLabel().toString()))
.containsExactly("//test:xxx");
ConfiguredAspect configuredAspect = analysisResult.getAspectsMap().values().iterator().next();
OutputGroupInfo outputGroupInfo = OutputGroupInfo.get(configuredAspect);
assertThat(outputGroupInfo).isNotNull();
NestedSet<Artifact> names = outputGroupInfo.getOutputGroup("my_result");
assertThat(names.toList()).isNotEmpty();
// Configuration of the true Artifact may diverge slightly (e.g. be trimmed) causing owners to
// also diverge so just compare paths instead of the whole Artifact.
ImmutableList<Path> paths =
names.toList().stream().map(Artifact::getPath).collect(toImmutableList());
ImmutableList<Path> expectedPaths =
OutputGroupInfo.get(getConfiguredTarget("//test:xxx"))
.getOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL)
.toList()
.stream()
.map(Artifact::getPath)
.collect(toImmutableList());
assertThat(paths).containsExactlyElementsIn(expectedPaths);
}
@Test
public void aspectsFromStarlarkRules() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
s = depset([target.label], transitive =
[i[AspectInfo].target_labels for i in ctx.rule.attr.deps])
return AspectInfo(target_labels = s)
TargetInfo = provider()
def _rule_impl(ctx):
s = depset(transitive = [i[AspectInfo].target_labels for i in ctx.attr.attr])
return TargetInfo(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.bzl', 'my_rule')
java_library(
name = 'yyy',
)
my_rule(
name = 'xxx',
attr = [':yyy'],
)
""");
AnalysisResult analysisResult = update("//test:xxx");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
NestedSet<Label> names =
Depset.cast(
getStarlarkProvider(target, "TargetInfo").getValue("rule_deps", Depset.class),
Label.class,
"rule_deps");
assertThat(names.toList().stream().map(Label::toString)).containsExactly("//test:yyy");
}
@Test
public void aspectsNonExported() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _aspect_impl(target, ctx):
return []
def _rule_impl(ctx):
pass
def mk_aspect():
return aspect(implementation=_aspect_impl)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'attr' : attr.label_list(aspects = [mk_aspect()]) }, # line 11
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
java_library(
name = 'yyy',
)
my_rule(
name = 'xxx',
attr = [':yyy'],
)
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult analysisResult = update("//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(analysisResult.hasError()).isTrue();
} catch (ViewCreationFailedException | TargetParsingException e) {
// expected
}
// attr.label_list() fails, stack=[<toplevel>@rules.bzl:11:38, label_list:<builtin>]
assertContainsEvent("File \"/workspace/test/aspect.bzl\", line 11, column 38, in <toplevel>");
assertContainsEvent(
"Error in label_list: Aspects should be top-level values in extension files that define"
+ " them.");
}
@Test
@SuppressWarnings("EmptyCatchBlock")
public void aspectReturnsNonExportedProvider() throws Exception {
scratch.file(
"test/inc.bzl",
"""
a = aspect(implementation = lambda target, ctx: [provider()()])
r = rule(
implementation = lambda ctx: [],
attrs = {'a': attr.label_list(aspects = [a])})
""");
scratch.file(
"test/BUILD",
"""
load('//test:inc.bzl', 'r')
java_library(name = 'j')
r(name = 'test', a = [':j'])
""");
reporter.removeHandler(failFastHandler);
try {
update("//test");
/* reached if --keep_going=true */
} catch (ViewCreationFailedException unused) {
/* reached if --keep_going=false */
}
assertContainsEvent(
"aspect function returned an instance of a provider "
+ "(defined at /workspace/test/inc.bzl:1:58) that is not a global");
}
@Test
public void providerNonExported() throws Exception {
scratch.file(
"test/rule.bzl",
"""
def mk_provider():
return provider()
def _rule_impl(ctx):
pass
my_rule = rule(
implementation=_rule_impl,
attrs = { 'attr' : attr.label_list(providers = [mk_provider()]) }, # line 7
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:rule.bzl', 'my_rule')
java_library(
name = 'yyy',
)
my_rule(
name = 'xxx',
attr = [':yyy'],
)
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult analysisResult = update("//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(analysisResult.hasError()).isTrue();
} catch (ViewCreationFailedException | TargetParsingException e) {
// expected
}
// attr.label_list() fails, stack=[<toplevel>@rules.bzl:7:38, label_list:<builtin>]
assertContainsEvent("File \"/workspace/test/rule.bzl\", line 7, column 38, in <toplevel>");
assertContainsEvent(
"Error in label_list: Providers should be top-level values in extension files that define"
+ " them.");
}
@Test
public void aspectOnLabelAttr() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
return AspectInfo(aspect_data='foo')
RuleInfo = provider()
def _rule_impl(ctx):
return RuleInfo(data=ctx.attr.attr[AspectInfo].aspect_data)
MyAspect = aspect(
implementation=_aspect_impl,
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'attr' :
attr.label(aspects = [MyAspect]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
java_library(
name = 'yyy',
)
my_rule(
name = 'xxx',
attr = ':yyy',
)
""");
AnalysisResult analysisResult = update("//test:xxx");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
String value = getStarlarkProvider(target, "RuleInfo").getValue("data", String.class);
assertThat(value).isEqualTo("foo");
}
@Test
public void labelKeyedStringDictAllowsAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
return AspectInfo(aspect_data=target.label.name)
RuleInfo = provider()
def _rule_impl(ctx):
return RuleInfo(
data=','.join(['{}:{}'.format(dep[AspectInfo].aspect_data, val)
for dep, val in ctx.attr.attr.items()]))
MyAspect = aspect(
implementation=_aspect_impl,
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'attr' :
attr.label_keyed_string_dict(aspects = [MyAspect]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
java_library(
name = 'yyy',
)
my_rule(
name = 'xxx',
attr = {':yyy': 'zzz'},
)
""");
AnalysisResult analysisResult = update("//test:xxx");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
String value = getStarlarkProvider(target, "RuleInfo").getValue("data", String.class);
assertThat(value).isEqualTo("yyy:zzz");
}
@Test
public void aspectsDoNotAttachToFiles() throws Exception {
FileSystemUtils.appendIsoLatin1(
scratch.resolve("WORKSPACE"), "bind(name = 'yyy', actual = '//test:zzz.jar')");
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
MyAspect = aspect(
implementation=_impl,
attr_aspects=['deps'],
)
""");
scratch.file("test/zzz.jar");
scratch.file(
"test/BUILD",
"""
exports_files(['zzz.jar'])
java_library(
name = 'xxx',
srcs = ['A.java'],
deps = ['//external:yyy'],
)
""");
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(result.hasError()).isFalse();
}
@Test
public void aspectsDoNotAttachToTopLevelFiles() throws Exception {
FileSystemUtils.appendIsoLatin1(
scratch.resolve("WORKSPACE"), "bind(name = 'yyy', actual = '//test:zzz.jar')");
scratch.file(
"test/aspect.bzl",
"""
p = provider()
def _impl(target, ctx):
return [p()]
MyAspect = aspect(
implementation=_impl,
attr_aspects=['deps'],
)
""");
scratch.file("test/zzz.jar");
scratch.file(
"test/BUILD",
"""
exports_files(['zzz.jar'])
java_library(
name = 'xxx',
srcs = ['A.java'],
deps = ['//external:yyy'],
)
""");
AnalysisResult result = update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:zzz.jar");
assertThat(result.hasError()).isFalse();
assertThat(
Iterables.getOnlyElement(result.getAspectsMap().values())
.getProviders()
.getProviderCount())
.isEqualTo(0);
}
@Test
public void aspectFailingExecution() 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.
}
// Stack doesn't include source lines because we haven't told EvalException
// how to read from scratch.
assertContainsEvent(
"ERROR /workspace/test/BUILD:1:13: in "
+ "//test:aspect.bzl%MyAspect aspect on java_library rule //test:xxx: \n"
+ "Traceback (most recent call last):\n"
+ "\tFile \"/workspace/test/aspect.bzl\", line 2, column 13, in _impl\n"
+ "Error: integer division by zero");
}
@Test
public void aspectFailingReturnsNotAStruct() 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 should return a struct, a list, or a provider "
+ "instance, but got int");
}
@Test
public void aspectFailingOrphanArtifacts() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
ctx.actions.declare_file('missing_in_action.txt')
return []
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:13: in "
+ "//test:aspect.bzl%MyAspect aspect on java_library rule //test:xxx: \n"
+ "The following files have no generating action:\n"
+ "test/missing_in_action.txt");
}
@Test
public void aspectSkippingOrphanArtifactsWithLocation() throws Exception {
scratch.file(
"simple/print.bzl",
"""
Info = provider()
def _print_expanded_location_impl(target, ctx):
return Info(result=ctx.expand_location(ctx.rule.attr.cmd, []))
print_expanded_location = aspect(
implementation = _print_expanded_location_impl,
)
""");
scratch.file(
"simple/BUILD",
"""
filegroup(
name = "files",
srcs = ["afile"],
)
genrule(
name = "concat_all_files",
srcs = [":files"],
outs = ["concatenated.txt"],
cmd = "$(location :files)"
)
""");
reporter.removeHandler(failFastHandler);
AnalysisResult analysisResult =
update(
ImmutableList.of("//simple:print.bzl%print_expanded_location"),
"//simple:concat_all_files");
assertThat(analysisResult.hasError()).isFalse();
ConfiguredAspect configuredAspect = analysisResult.getAspectsMap().values().iterator().next();
String result =
getStarlarkProvider(configuredAspect, "//simple:print.bzl", "Info")
.getValue("result", String.class);
assertThat(result).isEqualTo("simple/afile");
}
@Test
public void expandLocationFailsForTargetsWithSameLabel() throws Exception {
scratch.overwriteFile(
"tools/allowlists/function_transition_allowlist/BUILD",
"""
package_group(
name = 'function_transition_allowlist',
packages = [
'//a/...',
],
)
""");
scratch.file(
"a/defs.bzl",
"def _transition_impl(settings, attr):",
" return {",
" 'opt': {'//command_line_option:compilation_mode': 'opt'},",
" 'dbg': {'//command_line_option:compilation_mode': 'dbg'},",
" }",
"split_transition = transition(",
" implementation = _transition_impl,",
" inputs = [],",
" outputs = ['//command_line_option:compilation_mode'])",
"def _split_deps_rule_impl(ctx):",
" pass",
"split_deps_rule = rule(",
" implementation = _split_deps_rule_impl,",
" attrs = {",
" 'my_dep': attr.label(cfg = split_transition),",
" })",
"",
"Info = provider()",
"def _print_expanded_location_impl(target, ctx):",
" return Info(result=ctx.expand_location('$(location //a:lib)',"
+ " [ctx.rule.attr.my_dep[0], ctx.rule.attr.my_dep[1]]))",
"",
"print_expanded_location = aspect(",
" implementation = _print_expanded_location_impl,",
")");
scratch.file(
"a/BUILD",
"""
load('//a:defs.bzl', 'split_deps_rule')
cc_library(name = 'lib', srcs = ['lib.cc'])
split_deps_rule(
name = 'a',
my_dep = ':lib')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult analysisResult =
update(ImmutableList.of("//a:defs.bzl%print_expanded_location"), "//a");
assertThat(keepGoing()).isTrue();
assertThat(analysisResult.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent("Label \"//a:lib\" is found more than once in 'targets' list.");
}
@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 duplicateOutputGroups() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
f = ctx.actions.declare_file('f.txt')
ctx.actions.write(f, 'f')
return [OutputGroupInfo(duplicate = depset([f]))]
MyAspect = aspect(implementation=_impl)
def _rule_impl(ctx):
g = ctx.actions.declare_file('g.txt')
ctx.actions.write(g, 'g')
return [OutputGroupInfo(duplicate = depset([g]))]
my_rule = rule(_rule_impl)
def _noop(ctx):
pass
rbase = rule(_noop, attrs = { 'dep' : attr.label(aspects = [MyAspect]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'my_rule', 'rbase')
my_rule(name = 'xxx')
rbase(name = 'yyy', dep = ':xxx')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update("//test:yyy");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent("ERROR /workspace/test/BUILD:3:6: Output group duplicate provided twice");
}
@Test
public void outputGroupsFromOneAspect() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _a1_impl(target, ctx):
f = ctx.actions.declare_file(target.label.name + '_a1.txt')
ctx.actions.write(f, 'f')
return [OutputGroupInfo(a1_group = depset([f]))]
a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])
def _rule_impl(ctx):
if not ctx.attr.dep:
return []
og = {k:ctx.attr.dep.output_groups[k] for k in ctx.attr.dep[OutputGroupInfo]}
return [OutputGroupInfo(**og)]
my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'my_rule1')
my_rule1(name = 'base')
my_rule1(name = 'xxx', dep = ':base')
""");
AnalysisResult analysisResult = update("//test:xxx");
OutputGroupInfo outputGroupInfo =
OutputGroupInfo.get(Iterables.getOnlyElement(analysisResult.getTargetsToBuild()));
assertThat(getOutputGroupContents(outputGroupInfo, "a1_group"))
.containsExactly("test/base_a1.txt");
}
@Test
public void outputGroupsDeclaredProviderFromOneAspect() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _a1_impl(target, ctx):
f = ctx.actions.declare_file(target.label.name + '_a1.txt')
ctx.actions.write(f, 'f')
return [OutputGroupInfo(a1_group = depset([f]))]
a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])
def _rule_impl(ctx):
if not ctx.attr.dep:
return []
return [OutputGroupInfo(a1_group = ctx.attr.dep[OutputGroupInfo].a1_group)]
my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'my_rule1')
my_rule1(name = 'base')
my_rule1(name = 'xxx', dep = ':base')
""");
AnalysisResult analysisResult = update("//test:xxx");
OutputGroupInfo outputGroupInfo =
OutputGroupInfo.get(Iterables.getOnlyElement(analysisResult.getTargetsToBuild()));
assertThat(getOutputGroupContents(outputGroupInfo, "a1_group"))
.containsExactly("test/base_a1.txt");
}
@Test
public void outputGroupsFromTwoAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _a1_impl(target, ctx):
f = ctx.actions.declare_file(target.label.name + '_a1.txt')
ctx.actions.write(f, 'f')
return [OutputGroupInfo(a1_group = depset([f]))]
a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])
def _rule_impl(ctx):
if not ctx.attr.dep:
return []
og = {k:ctx.attr.dep.output_groups[k] for k in ctx.attr.dep.output_groups}
return [OutputGroupInfo(**og)]
my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })
def _a2_impl(target, ctx):
g = ctx.actions.declare_file(target.label.name + '_a2.txt')
ctx.actions.write(g, 'f')
return [OutputGroupInfo(a2_group = depset([g]))]
a2 = aspect(implementation=_a2_impl, attr_aspects = ['dep'])
my_rule2 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a2]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'my_rule1', 'my_rule2')
my_rule1(name = 'base')
my_rule1(name = 'xxx', dep = ':base')
my_rule2(name = 'yyy', dep = ':xxx')
""");
AnalysisResult analysisResult = update("//test:yyy");
OutputGroupInfo outputGroupInfo =
OutputGroupInfo.get(Iterables.getOnlyElement(analysisResult.getTargetsToBuild()));
assertThat(getOutputGroupContents(outputGroupInfo, "a1_group"))
.containsExactly("test/base_a1.txt");
assertThat(getOutputGroupContents(outputGroupInfo, "a2_group"))
.containsExactly("test/xxx_a2.txt");
}
@Test
public void outputGroupsDeclaredProvidersFromTwoAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _a1_impl(target, ctx):
f = ctx.actions.declare_file(target.label.name + '_a1.txt')
ctx.actions.write(f, 'f')
return [OutputGroupInfo(a1_group = depset([f]))]
a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])
def _rule_impl(ctx):
if not ctx.attr.dep:
return []
og = dict()
dep_og = ctx.attr.dep[OutputGroupInfo]
if hasattr(dep_og, 'a1_group'):
og['a1_group'] = dep_og.a1_group
if hasattr(dep_og, 'a2_group'):
og['a2_group'] = dep_og.a2_group
return [OutputGroupInfo(**og)]
my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })
def _a2_impl(target, ctx):
g = ctx.actions.declare_file(target.label.name + '_a2.txt')
ctx.actions.write(g, 'f')
return [OutputGroupInfo(a2_group = depset([g]))]
a2 = aspect(implementation=_a2_impl, attr_aspects = ['dep'])
my_rule2 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a2]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'my_rule1', 'my_rule2')
my_rule1(name = 'base')
my_rule1(name = 'xxx', dep = ':base')
my_rule2(name = 'yyy', dep = ':xxx')
""");
AnalysisResult analysisResult = update("//test:yyy");
OutputGroupInfo outputGroupInfo =
OutputGroupInfo.get(Iterables.getOnlyElement(analysisResult.getTargetsToBuild()));
assertThat(getOutputGroupContents(outputGroupInfo, "a1_group"))
.containsExactly("test/base_a1.txt");
assertThat(getOutputGroupContents(outputGroupInfo, "a2_group"))
.containsExactly("test/xxx_a2.txt");
}
@Test
public void duplicateOutputGroupsFromTwoAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _a1_impl(target, ctx):
f = ctx.actions.declare_file(target.label.name + '_a1.txt')
ctx.actions.write(f, 'f')
return [OutputGroupInfo(a1_group = depset([f]))]
a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])
def _rule_impl(ctx):
if not ctx.attr.dep:
return []
og = {k:ctx.attr.dep.output_groups[k] for k in ctx.attr.dep[OutputGroupInfo]}
return [OutputGroupInfo(**og)]
my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })
def _a2_impl(target, ctx):
g = ctx.actions.declare_file(target.label.name + '_a2.txt')
ctx.actions.write(g, 'f')
return [OutputGroupInfo(a1_group = depset([g]))]
a2 = aspect(implementation=_a2_impl, attr_aspects = ['dep'])
my_rule2 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a2]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'my_rule1', 'my_rule2')
my_rule1(name = 'base')
my_rule1(name = 'xxx', dep = ':base')
my_rule2(name = 'yyy', dep = ':xxx')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult analysisResult = update("//test:yyy");
assertThat(analysisResult.hasError()).isTrue();
assertThat(keepGoing()).isTrue();
} catch (ViewCreationFailedException e) {
// expected.
}
assertContainsEvent("ERROR /workspace/test/BUILD:3:9: Output group a1_group provided twice");
}
private static Iterable<String> getOutputGroupContents(
OutputGroupInfo outputGroupInfo, String groupName) {
return Iterables.transform(
outputGroupInfo.getOutputGroup(groupName).toList(), Artifact::getRootRelativePathString);
}
@Test
public void duplicateStarlarkProviders() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
Info = provider()
def _impl(target, ctx):
return Info(duplicate = 'x')
MyAspect = aspect(implementation=_impl)
def _rule_impl(ctx):
return Info(duplicate = 'y')
my_rule = rule(_rule_impl)
def _noop(ctx):
pass
rbase = rule(_noop, attrs = { 'dep' : attr.label(aspects = [MyAspect]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'my_rule', 'rbase')
my_rule(name = 'xxx')
rbase(name = 'yyy', dep = ':xxx')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update("//test:yyy");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
}
assertContainsEvent("ERROR /workspace/test/BUILD:3:6: Provider Info provided twice");
}
@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 is not exported from //test:aspect.bzl");
}
@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("cannot load '//test:aspect.bzl': no such 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");
}
/**
* Tests that a loading-level error (missing bzl file) is properly transformed by the configured
* target that requested the relevant package, and doesn't bubble up to a higher configured
* target/aspect that wasn't expecting a loading-level error. The complication is that the
* configured target that depends directly on the error tries to do configuration resolution after
* noticing the error, and configuration resolution is interruptible, so it is interrupted. It
* needs to then throw the error, rather than the interruption.
*
* <p>This test covers error propagation up to both the configured target that depends on the one
* in error, as well as the aspect on that configured target, since the error goes through both.
*/
@Test
public void aspectBaseConfiguredTargetTransitivelyDependingOnPackageInError() throws Exception {
setRulesAndAspectsAvailableInTests(ImmutableList.of(), ImmutableList.of());
scratch.overwriteFile(
"tools/allowlists/function_transition_allowlist/BUILD",
"package_group(name = 'function_transition_allowlist', packages = ['//aspect/...'])");
scratch.file(
"aspect/aspect.bzl",
"""
def _setting_impl(ctx):
return []
string_flag = rule(
implementation = _setting_impl,
build_setting = config.string(flag=True),
)
def _rule_impl(ctx):
pass
def _transition_impl(settings, attr):
return {'//aspect:formation': 'mesa'}
formation_transition = transition(
implementation = _transition_impl,
inputs = ['//aspect:formation'],
outputs = ['//aspect:formation'],
)
def _aspect_impl(target, ctx):
pass
myaspect = aspect(implementation = _aspect_impl)
cfgrule = rule(
implementation = _rule_impl,
attrs = {
'to': attr.label(),
'innocent': attr.label(cfg = formation_transition),
}
)
""");
scratch.file(
"aspect/BUILD",
"""
load('aspect.bzl', 'cfgrule', 'string_flag')
string_flag(name = 'formation', build_setting_default = 'canyon')
sh_library(name = 'innocent')
cfgrule(name = 'top', to = '//baz:baz', innocent = ':innocent')
""");
scratch.file("bar/BUILD", "sh_library(name = 'bar', deps = ['//baz:baz'])");
scratch.file(
"baz/BUILD",
"""
load('//baz/subdir:missing.bzl', 'sym')
sh_library(name = 'baz')
""");
scratch.file("baz/subdir/missing.bzl");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result =
update(ImmutableList.of("//aspect:aspect.bzl%myaspect"), "//aspect:top");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect to fail.
assertThat(keepGoing()).isFalse();
}
assertContainsEvent("Label '//baz/subdir:missing.bzl' is invalid");
}
@Test
public void aspectParametersUncovered() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['aaa']) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (Exception e) {
// expect to fail.
}
assertContainsEvent( // "ERROR /workspace/test/aspect.bzl:9:11: "
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type string.");
}
@Test
public void aspectParametersTypeMismatch() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectMismatch = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['aaa']) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectMismatch]),
'my_attr' : attr.int() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx', my_attr = 4)
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (Exception e) {
// expect to fail.
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectMismatch requires rule my_rule to specify attribute "
+ "'my_attr' with type string.");
}
@Test
public void aspectParametersDontSupportSelect() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectMismatch = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['aaa']) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectMismatch]),
'my_attr' : attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx', my_attr = select({'//conditions:default': 'foo'}))
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (Exception e) {
// expect to fail.
}
assertContainsEvent(
"//test:xxx: attribute 'my_attr' has a select() and aspect "
+ "//test:aspect.bzl%MyAspectMismatch also declares '//test:xxx'. Aspect attributes "
+ "don't currently support select().");
}
@Test
public void aspectParametersBadDefault() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectBadDefault = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['a'], default='b') },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectBadDefault]) }, # line 11
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (Exception e) {
// expect to fail.
}
// aspect fails, stack = [<toplevel>@:5:28, aspect@<builtin>]
assertContainsEvent("File \"/workspace/test/aspect.bzl\", line 5, column 28, in <toplevel>");
assertContainsEvent(
"Error in aspect: Aspect parameter attribute 'my_attr' has a bad default value: has to be"
+ " one of 'a' instead of 'b'");
}
@Test
public void aspectParametersBadValue() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectBadValue = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['a']) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectBadValue]),
'my_attr' : attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx', my_attr='b')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(result.hasError()).isTrue();
} catch (Exception e) {
// expect to fail.
}
assertContainsEvent(
"ERROR /workspace/test/BUILD:2:8: //test:xxx: invalid value in 'my_attr' "
+ "attribute: has to be one of 'a' instead of 'b'");
}
@Test
public void aspectParameters() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspect = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['aaa']) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspect]),
'my_attr' : attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx', my_attr = 'aaa')
""");
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(result.hasError()).isFalse();
}
@Test
public void aspectParametersConfigurationField() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspect = aspect(
implementation=_impl,
attrs = { '_my_attr' : attr.label(default=
configuration_field(fragment='cpp', name = 'cc_toolchain')) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspect]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx')
""");
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(result.hasError()).isFalse();
}
@Test
public void aspectParameterComputedDefault() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
def _defattr():
return Label('//foo/bar:baz')
MyAspect = aspect(
implementation=_impl,
attrs = { '_extra' : attr.label(default = _defattr) }
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspect]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx')
""");
reporter.removeHandler(failFastHandler);
if (keepGoing()) {
AnalysisResult result = update("//test:xxx");
assertThat(result.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update("//test:xxx"));
}
assertContainsEvent(
"Aspect attribute '_extra' (label) with computed default value is unsupported.");
}
@Test
public void aspectParametersOptional() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectOptParam = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['aaa'], default='aaa') },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectOptParam]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx')
""");
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(result.hasError()).isFalse();
}
@Test
public void aspectParametersOptionalOverride() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
if (ctx.attr.my_attr == 'a'):
fail('Rule is not overriding default, still has value ' + ctx.attr.my_attr)
return []
def _rule_impl(ctx):
return []
MyAspectOptOverride = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.string(values=['a', 'b'], default='a') },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectOptOverride]),
'my_attr' : attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'xxx', my_attr = 'b')
""");
AnalysisResult result = update(ImmutableList.of(), "//test:xxx");
assertThat(result.hasError()).isFalse();
}
@Test
public void testMultipleExecutablesInTarget() throws Exception {
scratch.file(
"foo/extension.bzl",
"""
def _aspect_impl(target, ctx):
return []
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, cfg = 'exec'),
'exe2' : attr.label(executable = True, allow_files = True, cfg = 'exec'),
},
)
""");
scratch.file("foo/tool.sh", "#!/bin/bash");
scratch.file(
"foo/BUILD",
"""
load(':extension.bzl', 'my_rule')
my_rule(name = 'main', exe1 = ':tool.sh', exe2 = ':tool.sh')
""");
AnalysisResult analysisResultOfRule = update(ImmutableList.of(), "//foo:main");
assertThat(analysisResultOfRule.hasError()).isFalse();
AnalysisResult analysisResultOfAspect =
update(ImmutableList.of("/foo/extension.bzl%my_aspect"), "//foo:main");
assertThat(analysisResultOfAspect.hasError()).isFalse();
}
@Test
public void aspectFragmentAccessSuccess() throws Exception {
analyzeConfiguredTargetForAspectFragment("ctx.fragments.java.strict_java_deps", "'java'", "");
assertNoEvents();
}
@Test
public void aspectFragmentAccessError() {
reporter.removeHandler(failFastHandler);
assertThrows(
ViewCreationFailedException.class,
() ->
analyzeConfiguredTargetForAspectFragment(
"ctx.fragments.java.strict_java_deps", "'cpp'", "'cpp'"));
assertContainsEvent(
"//test:aspect.bzl%MyAspect aspect on my_rule has to declare 'java' as a "
+ "required fragment in order to access it. Please update the 'fragments' argument of "
+ "the rule definition (for example: fragments = [\"java\"])");
}
private void analyzeConfiguredTargetForAspectFragment(
String fullFieldName, String fragments, String ruleFragments) throws Exception {
scratch.file(
"test/aspect.bzl",
"AspectInfo = provider()",
"def _aspect_impl(target, ctx):",
" return AspectInfo(result = str(" + fullFieldName + "))",
"",
"RuleInfo = provider()",
"def _rule_impl(ctx):",
" return RuleInfo(stuff = '...')",
"",
"MyAspect = aspect(",
" implementation=_aspect_impl,",
" attr_aspects=['deps'],",
" fragments=[" + fragments + "],",
")",
"my_rule = rule(",
" implementation=_rule_impl,",
" attrs = { 'attr' : ",
" attr.label_list(mandatory=True, allow_files=True, aspects = [MyAspect]) },",
" fragments=[" + ruleFragments + "],",
")");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', '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();
String errorMessage = "Analysis failed";
throw new ViewCreationFailedException(
errorMessage,
FailureDetail.newBuilder()
.setMessage(errorMessage)
.setAnalysis(Analysis.newBuilder().setCode(Code.ANALYSIS_UNKNOWN))
.build());
}
assertThat(getConfiguredTarget("//test:xxx")).isNotNull();
}
@Test
public void invalidateAspectOnBzlFileChange() throws Exception {
scratch.file("test/build_defs.bzl", aspectBzlFile("'deps'"));
scratch.file(
"test/BUILD",
"""
load(':build_defs.bzl', '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.of(), "//test:r2");
ConfiguredTarget configuredTarget = result.getTargetsToBuild().iterator().next();
Depset ruleInfoValue =
(Depset) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("rule_info");
assertThat(ruleInfoValue.getSet(String.class).toList())
.containsExactlyElementsIn(expectedLabels);
}
private static String[] aspectBzlFile(String attrAspects) {
return new String[] {
"AspectInfo = provider()",
"def _repro_aspect_impl(target, ctx):",
" s = depset([str(target.label)], transitive =",
" [d[AspectInfo].aspect_info for d in ctx.rule.attr.deps if AspectInfo in d])",
" return AspectInfo(aspect_info = s)",
"",
"_repro_aspect = aspect(",
" _repro_aspect_impl,",
" attr_aspects = [" + attrAspects + "],",
")",
"",
"RuleInfo = provider()",
"def repro_impl(ctx):",
" s = depset(transitive = ",
" [d[AspectInfo].aspect_info for d in ctx.attr.deps if AspectInfo in d])",
" return RuleInfo(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],",
" )",
" },",
")"
};
}
@Test
public void aspectOutputsToBinDirectory() throws Exception {
scratch.file(
"foo/extension.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
file = ctx.actions.declare_file('aspect-output-' + target.label.name)
ctx.actions.write(file, 'data')
return AspectInfo(aspect_file = file)
my_aspect = aspect(_aspect_impl)
def _rule_impl(ctx):
pass
rule_bin_out = rule(_rule_impl, output_to_genfiles=False)
rule_gen_out = rule(_rule_impl, output_to_genfiles=True)
RuleInfo = provider()
def _main_rule_impl(ctx):
s = depset([d[AspectInfo].aspect_file for d in ctx.attr.deps])
return RuleInfo(aspect_files = s)
main_rule = rule(_main_rule_impl,
attrs = { 'deps' : attr.label_list(aspects = [my_aspect]) },
)
""");
scratch.file(
"foo/BUILD",
"""
load(':extension.bzl', 'rule_bin_out', 'rule_gen_out', 'main_rule')
rule_bin_out(name = 'rbin')
rule_gen_out(name = 'rgen')
main_rule(name = 'main', deps = [':rbin', ':rgen'])
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//foo:main");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
NestedSet<Artifact> aspectFiles =
getStarlarkProvider(target, "RuleInfo")
.getValue("aspect_files", Depset.class)
.getSet(Artifact.class);
assertThat(Iterables.transform(aspectFiles.toList(), Artifact::getFilename))
.containsExactly("aspect-output-rbin", "aspect-output-rgen");
for (Artifact aspectFile : aspectFiles.toList()) {
String rootPath = aspectFile.getRoot().getExecPath().toString();
assertWithMessage("Artifact %s should not be in genfiles", aspectFile)
.that(rootPath)
.doesNotContain("genfiles");
assertWithMessage("Artifact %s should be in bin", aspectFile).that(rootPath).endsWith("bin");
}
}
@Test
public void toplevelAspectOnFile() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
print('This aspect does nothing')
return []
MyAspect = aspect(implementation=_impl)
""");
scratch.file("test/BUILD", "exports_files(['file.txt'])");
scratch.file("test/file.txt", "");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:file.txt");
assertThat(analysisResult.hasError()).isFalse();
assertThat(
Iterables.getOnlyElement(analysisResult.getAspectsMap().values())
.getProviders()
.getProviderCount())
.isEqualTo(0);
}
@Test
public void sharedAttributeDefinitionWithAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _aspect_impl(target,ctx):
return []
my_aspect = aspect(implementation = _aspect_impl)
_ATTR = { 'deps' : attr.label_list(aspects = [my_aspect]) }
def _dummy_impl(ctx):
pass
r1 = rule(_dummy_impl, attrs = _ATTR)
r2 = rule(_dummy_impl, attrs = _ATTR)
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r1', 'r2')
r1(name = 't1')
r2(name = 't2', deps = [':t1'])
""");
AnalysisResult analysisResult = update("//test:t2");
assertThat(analysisResult.hasError()).isFalse();
}
@Test
public void multipleAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _aspect_impl(target,ctx):
return []
my_aspect = aspect(implementation = _aspect_impl)
def _dummy_impl(ctx):
pass
r1 = rule(_dummy_impl,
attrs = { 'deps' : attr.label_list(aspects = [my_aspect, my_aspect]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r1')
r1(name = 't1')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult result = update("//test:r1");
assertThat(result.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update("//test:r1"));
}
assertContainsEvent("aspect //test:aspect.bzl%my_aspect added more than once");
}
@Test
public void topLevelAspectsAndExtraActions() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _aspect_impl(target,ctx):
f = ctx.actions.declare_file('dummy.txt')
ctx.actions.run_shell(outputs = [f], command='echo xxx > $(location f)',
mnemonic='AspectAction')
return []
my_aspect = aspect(implementation = _aspect_impl)
""");
scratch.file(
"test/BUILD",
"""
extra_action(
name = 'xa',
cmd = 'echo $(EXTRA_ACTION_FILE) > $(output file.xa)',
out_templates = ['file.xa'],
)
action_listener(
name = 'al',
mnemonics = [ 'AspectAction' ],
extra_actions = [ ':xa' ])
java_library(name = 'xxx')
""");
useConfiguration("--experimental_action_listener=//test:al");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%my_aspect"), "//test:xxx");
assertThat(Iterables.transform(analysisResult.getArtifactsToBuild(), Artifact::getFilename))
.contains("file.xa");
}
/** Regression test for b/137960630. */
@Test
public void topLevelAspectsAndExtraActionsWithConflict() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _aspect_impl(target, ctx):
f = ctx.actions.declare_file('dummy.txt')
ctx.actions.run_shell(outputs = [f], command='echo xxx > $(location f)',
mnemonic='AspectAction')
return []
my_aspect = aspect(implementation = _aspect_impl)
""");
scratch.file(
"test/BUILD",
"""
extra_action(
name = 'xa',
cmd = 'echo $(EXTRA_ACTION_FILE) > $(output file.xa)',
out_templates = ['file.xa'],
)
action_listener(
name = 'al',
mnemonics = ['AspectAction'],
extra_actions = [':xa'],
)
java_library(name = 'xxx')
java_library(name = 'yyy')
""");
useConfiguration("--experimental_action_listener=//test:al");
reporter.removeHandler(failFastHandler); // We expect an error.
if (keepGoing()) {
AnalysisResult result =
update(ImmutableList.of("test/aspect.bzl%my_aspect"), "//test:xxx", "//test:yyy");
assertThat(result.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() -> update(ImmutableList.of("test/aspect.bzl%my_aspect"), "//test:xxx", "//test:yyy"));
}
assertContainsEvent(
"file 'extra_actions/test/xa/test/file.xa' is generated by these conflicting actions");
}
@Test
public void aspectsPropagatingToAllAttributes() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
s = depset([target.label], transitive =
[i.target_labels for i in ctx.rule.attr.runtime_deps]
if hasattr(ctx.rule.attr, 'runtime_deps') else [])
return struct(target_labels = s)
MyAspect = aspect(
implementation=_impl,
attrs = { '_tool' : attr.label(default = Label('//test:tool')) },
attr_aspects=['*'],
)
""");
scratch.file(
"test/BUILD",
"""
java_library(
name = 'tool',
)
java_library(
name = 'bar',
runtime_deps = [':tool'],
)
java_library(
name = 'foo',
runtime_deps = [':bar'],
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:foo");
ConfiguredAspect configuredAspect = analysisResult.getAspectsMap().values().iterator().next();
assertThat(configuredAspect).isNotNull();
Object names = configuredAspect.get("target_labels");
assertThat(names).isInstanceOf(Depset.class);
assertThat(
Iterables.transform(
((Depset) names).toList(),
o -> {
assertThat(o).isInstanceOf(Label.class);
return ((Label) o).getName();
}))
.containsExactly("foo", "bar", "tool");
}
/** Simple straightforward linear aspects-on-aspects. */
@Test
public void aspectOnAspectLinear() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
a1p = provider()
def _a1_impl(target,ctx):
return a1p(text = 'random')
a1 = aspect(_a1_impl, attr_aspects = ['dep'], provides = [a1p])
a2p = provider()
def _a2_impl(target,ctx):
value = []
if ctx.rule.attr.dep and a2p in ctx.rule.attr.dep:
value += ctx.rule.attr.dep[a2p].value
if a1p in target:
value.append(str(target.label) + str(ctx.aspect_ids) + '=yes')
else:
value.append(str(target.label) + str(ctx.aspect_ids) + '=no')
return a2p(value = value)
a2 = aspect(_a2_impl, attr_aspects = ['dep'], required_aspect_providers = [a1p])
def _r1_impl(ctx):
pass
r2info = provider()
def _r2_impl(ctx):
return r2info(result = ctx.attr.dep[a2p].value)
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', 'r1', 'r2')
r1(name = 'r0')
r1(name = 'r1', dep = ':r0')
r2(name = 'r2', dep = ':r1')
""");
AnalysisResult analysisResult = update("//test:r2");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
Sequence<?> result = getStarlarkProvider(target, "r2info").getValue("result", Sequence.class);
// "yes" means that aspect a2 sees a1's providers.
assertThat(result)
.containsExactly(
"@//test:r0[\"//test:aspect.bzl%a1\", \"//test:aspect.bzl%a2\"]=yes",
"@//test:r1[\"//test:aspect.bzl%a2\"]=no");
}
/**
* Diamond case. rule r1 depends or r0 with aspect a1. rule r2 depends or r0 with aspect a2. rule
* rcollect depends on r1, r2 with aspect a3.
*
* <p>Aspect a3 should be applied twice to target r0: once in [a1, a3] sequence and once in [a2,
* a3] sequence.
*/
@Test
public void aspectOnAspectDiamond() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
a1p = provider()
a2p = provider()
a3p = provider()
def _a1_impl(target,ctx):
return [a1p(value = 'text from a1')]
a1 = aspect(_a1_impl, attr_aspects = ['deps'], provides = [a1p])
def _a2_impl(target,ctx):
return [a2p(value = 'text from a2')]
a2 = aspect(_a2_impl, attr_aspects = ['deps'], provides = [a2p])
def _a3_impl(target,ctx):
value = []
f = ctx.actions.declare_file('a3.out')
ctx.actions.write(f, 'text')
for dep in ctx.rule.attr.deps:
if a3p in dep:
value += dep[a3p].value
s = str(target.label) + str(ctx.aspect_ids) + '='
if a1p in target:
s += 'a1p'
if a2p in target:
s += 'a2p'
value.append(s)
return [a3p(value = value)]
a3 = aspect(_a3_impl, attr_aspects = ['deps'],
required_aspect_providers = [[a1p], [a2p]])
def _r1_impl(ctx):
pass
RCollectInfo = provider()
def _rcollect_impl(ctx):
value = []
for dep in ctx.attr.deps:
if a3p in dep:
value += dep[a3p].value
return RCollectInfo(result = value)
r1 = rule(_r1_impl, attrs = { 'deps' : attr.label_list(aspects = [a1])})
r2 = rule(_r1_impl, attrs = { 'deps' : attr.label_list(aspects = [a2])})
rcollect = rule(_rcollect_impl, attrs = { 'deps' : attr.label_list(aspects = [a3])})
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r1', 'r2', 'rcollect')
r1(name = 'r0')
r1(name = 'r1', deps = [':r0'])
r2(name = 'r2', deps = [':r0'])
rcollect(name = 'rcollect', deps = [':r1', ':r2'])
""");
AnalysisResult analysisResult = update("//test:rcollect");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
Sequence<?> result =
(Sequence<?>) getStarlarkProvider(target, "RCollectInfo").getValue("result");
assertThat(result)
.containsExactly(
"@//test:r0[\"//test:aspect.bzl%a1\", \"//test:aspect.bzl%a3\"]=a1p",
"@//test:r1[\"//test:aspect.bzl%a3\"]=",
"@//test:r0[\"//test:aspect.bzl%a2\", \"//test:aspect.bzl%a3\"]=a2p",
"@//test:r2[\"//test:aspect.bzl%a3\"]=");
}
/**
* Linear with duplicates. r2_1 depends on r0 with aspect a2. r1 depends on r2_1 with aspect a1.
* r2 depends on r1 with aspect a2.
*
* <p>a2 is not interested in a1. There should be just one instance of aspect a2 on r0, and is
* should *not* see a1.
*/
@Test
public void aspectOnAspectLinearDuplicates() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
a1p = provider()
def _a1_impl(target,ctx):
return a1p()
a1 = aspect(_a1_impl, attr_aspects = ['dep'], provides = [a1p])
a2p = provider()
def _a2_impl(target,ctx):
value = []
if ctx.rule.attr.dep and a2p in ctx.rule.attr.dep:
value += ctx.rule.attr.dep[a2p].value
if a1p in target:
value.append(str(target.label) + str(ctx.aspect_ids) + '=yes')
else:
value.append(str(target.label) + str(ctx.aspect_ids) + '=no')
return a2p(value = value)
a2 = aspect(_a2_impl, attr_aspects = ['dep'], required_aspect_providers = [])
def _r1_impl(ctx):
pass
r2info = provider()
def _r2_impl(ctx):
return r2info(result = ctx.attr.dep[a2p].value)
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', 'r1', 'r2')
r1(name = 'r0')
r2(name = 'r2_1', dep = ':r0')
r1(name = 'r1', dep = ':r2_1')
r2(name = 'r2', dep = ':r1')
""");
AnalysisResult analysisResult = update("//test:r2");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
Sequence<?> result = getStarlarkProvider(target, "r2info").getValue("result", Sequence.class);
// "yes" means that aspect a2 sees a1's providers.
assertThat(result)
.containsExactly(
"@//test:r0[\"//test:aspect.bzl%a2\"]=no",
"@//test:r1[\"//test:aspect.bzl%a2\"]=no", "@//test:r2_1[\"//test:aspect.bzl%a2\"]=no");
}
/** Linear aspects-on-aspects with alias rule. */
@Test
public void aspectOnAspectLinearAlias() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
a1p = provider()
def _a1_impl(target,ctx):
return [a1p(text = 'random')]
a1 = aspect(_a1_impl, attr_aspects = ['dep'], provides = [a1p])
a2p = provider()
def _a2_impl(target,ctx):
value = []
if ctx.rule.attr.dep and a2p in ctx.rule.attr.dep:
value += ctx.rule.attr.dep[a2p].value
if a1p in target:
value.append(str(target.label) + str(ctx.aspect_ids) + '=yes')
else:
value.append(str(target.label) + str(ctx.aspect_ids) + '=no')
return [a2p(value = value)]
a2 = aspect(_a2_impl, attr_aspects = ['dep'], required_aspect_providers = [a1p])
def _r1_impl(ctx):
pass
r2info = provider()
def _r2_impl(ctx):
return r2info(result = ctx.attr.dep[a2p].value)
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', 'r1', 'r2')
r1(name = 'r0')
alias(name = 'a0', actual = ':r0')
r1(name = 'r1', dep = ':a0')
r2(name = 'r2', dep = ':r1')
""");
AnalysisResult analysisResult = update("//test:r2");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
Sequence<?> result = getStarlarkProvider(target, "r2info").getValue("result", Sequence.class);
// "yes" means that aspect a2 sees a1's providers.
assertThat(result)
.containsExactly(
"@//test:r0[\"//test:aspect.bzl%a1\", \"//test:aspect.bzl%a2\"]=yes",
"@//test:r1[\"//test:aspect.bzl%a2\"]=no");
}
@Test
public void aspectDescriptions() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _a_impl(target,ctx):
s = str(target.label) + str(ctx.aspect_ids) + '='
value = []
if ctx.rule.attr.dep:
d = ctx.rule.attr.dep
this_id = ctx.aspect_ids[len(ctx.aspect_ids) - 1]
s += str(d.label) + str(d[AspectInfo].my_ids) + ',' + str(this_id in d[AspectInfo].my_ids)
value += ctx.rule.attr.dep[AspectInfo].ap
else:
s += 'None'
value.append(s)
return AspectInfo(ap = value, my_ids = ctx.aspect_ids)
a = aspect(_a_impl, attr_aspects = ['dep'])
RuleInfo = provider()
def _r_impl(ctx):
if not ctx.attr.dep:
return RuleInfo(result = [])
return RuleInfo(result = ctx.attr.dep[AspectInfo].ap)
r = rule(_r_impl, attrs = { 'dep' : attr.label(aspects = [a])})
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r')
r(name = 'r0')
r(name = 'r1', dep = ':r0')
r(name = 'r2', dep = ':r1')
""");
AnalysisResult analysisResult = update("//test:r2");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
Sequence<?> result = (Sequence<?>) getStarlarkProvider(target, "RuleInfo").getValue("result");
assertThat(result)
.containsExactly(
"@//test:r0[\"//test:aspect.bzl%a\"]=None",
"@//test:r1[\"//test:aspect.bzl%a\"]=@//test:r0[\"//test:aspect.bzl%a\"],True");
}
@Test
public void attributesWithAspectsReused() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
my_aspect = aspect(_impl)
a_dict = { 'foo' : attr.label_list(aspects = [my_aspect]) }
""");
scratch.file(
"test/r1.bzl",
"""
load(':aspect.bzl', 'my_aspect', 'a_dict')
def _rule_impl(ctx):
pass
r1 = rule(_rule_impl, attrs = a_dict)
""");
scratch.file(
"test/r2.bzl",
"""
load(':aspect.bzl', 'my_aspect', 'a_dict')
def _rule_impl(ctx):
pass
r2 = rule(_rule_impl, attrs = a_dict)
""");
scratch.file(
"test/BUILD",
"""
load(':r1.bzl', 'r1')
load(':r2.bzl', 'r2')
r1(name = 'x1')
r2(name = 'x2', foo = [':x1'])
""");
AnalysisResult analysisResult = update("//test:x2");
assertThat(analysisResult.hasError()).isFalse();
}
@Test
public void aspectAdvertisingProviders() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
my_aspect = aspect(_impl, provides = ['foo'])
a_dict = { 'foo' : attr.label_list(aspects = [my_aspect]) }
""");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult analysisResult =
update(ImmutableList.of("//test:aspect.bzl%my_aspect"), "//test:xxx");
assertThat(keepGoing()).isTrue();
assertThat(analysisResult.hasError()).isTrue();
} catch (ViewCreationFailedException e) {
// expect exception
}
assertContainsEvent(
"Aspect '//test:aspect.bzl%my_aspect', applied to '//test:xxx', "
+ "does not provide advertised provider 'foo'");
}
@Test
public void aspectOnAspectInconsistentVisibility() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
a1p = provider()
def _a1_impl(target,ctx):
return [a1p(text = 'random')]
a1 = aspect(_a1_impl, attr_aspects = ['dep'], provides = [a1p])
a2p = provider()
def _a2_impl(target,ctx):
return [a2p(value = 'random')]
a2 = aspect(_a2_impl, attr_aspects = ['dep'], required_aspect_providers = [a1p])
def _r1_impl(ctx):
pass
r2info = provider()
def _r2_impl(ctx):
return r2info(result = ctx.attr.dep[a2p].value)
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', 'r1', 'r2')
r1(name = 'r0')
r1(name = 'r1', dep = ':r0')
r2(name = 'r2', dep = ':r1')
r1(name = 'r1_1', dep = ':r2')
r2(name = 'r2_1', dep = ':r1_1')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult analysisResult = update("//test:r2_1");
assertThat(analysisResult.hasError()).isTrue();
assertThat(keepGoing()).isTrue();
} catch (ViewCreationFailedException e) {
// expected
}
assertContainsEvent(
"ERROR /workspace/test/BUILD:3:3: Aspect //test:aspect.bzl%a2 is"
+ " applied twice, both before and after aspect //test:aspect.bzl%a1 "
+ "(when propagating to //test:r1)");
}
@Test
public void aspectOnAspectInconsistentVisibilityIndirect() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
a1p = provider()
def _a1_impl(target,ctx):
return [a1p(text = 'random')]
a1 = aspect(_a1_impl, attr_aspects = ['dep'], provides = [a1p])
a2p = provider()
def _a2_impl(target,ctx):
return [a2p(value = 'random')]
a2 = aspect(_a2_impl, attr_aspects = ['dep'], required_aspect_providers = [a1p])
def _r1_impl(ctx):
pass
r2info = provider()
def _r2_impl(ctx):
return r2info(result = ctx.attr.dep[a2p].value)
r1 = rule(_r1_impl, attrs = { 'dep' : attr.label(aspects = [a1])})
r2 = rule(_r2_impl, attrs = { 'dep' : attr.label(aspects = [a2])})
def _r0_impl(ctx):
pass
r0 = rule(_r0_impl, attrs = { 'dep' : attr.label()})
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r0', 'r1', 'r2')
r0(name = 'r0')
r1(name = 'r1', dep = ':r0')
r2(name = 'r2', dep = ':r1')
r1(name = 'r1_1', dep = ':r2')
r2(name = 'r2_1', dep = ':r1_1')
r0(name = 'r0_2', dep = ':r2_1')
""");
reporter.removeHandler(failFastHandler);
try {
AnalysisResult analysisResult = update("//test:r0_2");
assertThat(analysisResult.hasError()).isTrue();
assertThat(keepGoing()).isTrue();
} catch (ViewCreationFailedException e) {
// expected
}
assertContainsEvent(
"ERROR /workspace/test/BUILD:3:3: Aspect //test:aspect.bzl%a2 is"
+ " applied twice, both before and after aspect //test:aspect.bzl%a1 "
+ "(when propagating to //test:r1)");
}
/**
* Aspect a3 sees aspect a2, aspect a2 sees aspect a1, but a3 does not see a1. All three aspects
* should still propagate together.
*/
@Test
public void aspectOnAspectOnAspect() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
p1 = provider()
def _a1_impl(target, ctx):
return [p1()]
a1 = aspect(_a1_impl, attr_aspects = ['dep'], provides = [p1])
p2 = provider()
def _a2_impl(target, ctx):
value = True if p1 in target else False
return [p2(has_p1 = value)]
a2 = aspect(_a2_impl, attr_aspects = ['dep'],
required_aspect_providers = [p1], provides = [p2])
p3 = provider()
def _a3_impl(target, ctx):
list = []
if ctx.rule.attr.dep:
list = ctx.rule.attr.dep[p3].value
my_value = str(target.label) +'=' + str(target[p2].has_p1 if p2 in target else False)
return [p3(value = list + [my_value])]
a3 = aspect(_a3_impl, attr_aspects = ['dep'],
required_aspect_providers = [p2])
def _r0_impl(ctx):
pass
r0 = rule(_r0_impl, attrs = { 'dep' : attr.label()})
def _r1_impl(ctx):
pass
def _r2_impl(ctx):
pass
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_1')
r0(name = 'r0_2', dep = ':r0_1')
r0(name = 'r0_3', dep = ':r0_2')
r1(name = 'r1_1', dep = ':r0_3')
r2(name = 'r2_1', dep = ':r1_1')
""");
AnalysisResult analysisResult = update(ImmutableList.of("//test:aspect.bzl%a3"), "//test:r2_1");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key p3 =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "p3");
StructImpl p3Provider = (StructImpl) configuredAspect.get(p3);
assertThat((Sequence<?>) p3Provider.getValue("value"))
.containsExactly(
"@//test:r0_1=True",
"@//test:r0_2=True",
"@//test:r0_3=True",
"@//test:r1_1=False",
"@//test:r2_1=False");
}
/**
* r0 is a dependency of r1 via two attributes, dep1 and dep2. r1 sends an aspect 'a' along dep1
* but not along dep2.
*
* <p>rcollect depends upon r1 and sends another aspect, 'collector', along its dep dependency.
* 'collector' wants to see aspect 'a' and propagates along dep1 and dep2. It should be applied
* both to r0 and to r0+a.
*/
@Test
public void multipleDepsDifferentAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
PAspect = provider()
PCollector = provider()
def _aspect_impl(target, ctx):
return [PAspect()]
a = aspect(_aspect_impl, attr_aspects = ['dep'], provides = [PAspect])
def _collector_impl(target, ctx):
suffix = '+PAspect' if PAspect in target else ''
result = [str(target.label)+suffix]
for a in ['dep', 'dep1', 'dep2']:
if hasattr(ctx.rule.attr, a):
result += getattr(ctx.rule.attr, a)[PCollector].result
return [PCollector(result=result)]
collector = aspect(_collector_impl, attr_aspects = ['*'],
required_aspect_providers = [PAspect])
def _rimpl(ctx):
pass
r0 = rule(_rimpl)
r1 = rule(_rimpl,
attrs = {
'dep1' : attr.label(),
'dep2' : attr.label(aspects = [a]),
},
)
def _rcollect_impl(ctx):
return [ctx.attr.dep[PCollector]]
rcollect = rule(_rcollect_impl,
attrs = {
'dep' : attr.label(aspects = [collector]),
})
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r0', 'r1', 'rcollect')
r0(name = 'r0')
r1(name = 'r1', dep1 = ':r0', dep2 = ':r0')
rcollect(name = 'rcollect', dep = ':r1')
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:rcollect");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkProvider.Key pCollector =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "PCollector");
StructImpl pCollectorProvider = (StructImpl) configuredTarget.get(pCollector);
assertThat((Sequence<?>) pCollectorProvider.getValue("result"))
.containsExactly("@//test:r1", "@//test:r0", "@//test:r0+PAspect");
}
@Test
public void aspectSeesOtherAspectAttributes() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
PAspect = provider(fields = [])
PCollector = provider(fields = ['aspect_attr'])
def _a_impl(target, ctx):
return [PAspect()]
a = aspect(_a_impl,
provides = [PAspect],
attrs = {'_a_attr' : attr.label(default = '//test:foo')})
def _rcollect(target, ctx):
if hasattr(ctx.rule.attr, '_a_attr'):
return [PCollector(aspect_attr = ctx.rule.attr._a_attr.label)]
if hasattr(ctx.rule.attr, 'dep'):
return [ctx.rule.attr.dep[PCollector]]
return [PCollector()]
acollect = aspect(_rcollect, attr_aspects = ['*'], required_aspect_providers = [PAspect])
def _rimpl(ctx):
pass
r0 = rule(_rimpl)
r = rule(_rimpl, attrs = { 'dep' : attr.label(aspects = [a]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r0', 'r')
r0(name = 'foo')
r0(name = 'bar')
r(name = 'baz', dep = ':bar')
""");
AnalysisResult analysisResult =
update(ImmutableList.of("//test:aspect.bzl%acollect"), "//test:baz");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key pCollector =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "PCollector");
StructImpl collector = (StructImpl) configuredAspect.get(pCollector);
assertThat(collector.getValue("aspect_attr")).isEqualTo(Label.parseCanonical("//test:foo"));
}
@Test
public void ruleAttributesWinOverAspects() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
PAspect = provider(fields = [])
PCollector = provider(fields = ['attr_value'])
def _a_impl(target, ctx):
return [PAspect()]
a = aspect(_a_impl,
provides = [PAspect],
attrs = {'_same_attr' : attr.int(default = 239)})
def _rcollect(target, ctx):
if hasattr(ctx.rule.attr, '_same_attr'):
return [PCollector(attr_value = ctx.rule.attr._same_attr)]
if hasattr(ctx.rule.attr, 'dep'):
return [ctx.rule.attr.dep[PCollector]]
return [PCollector()]
acollect = aspect(_rcollect, attr_aspects = ['*'], required_aspect_providers = [PAspect])
def _rimpl(ctx):
pass
r0 = rule(_rimpl)
r = rule(_rimpl,
attrs = {
'dep' : attr.label(aspects = [a]),
'_same_attr' : attr.int(default = 30)
})
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r0', 'r')
r0(name = 'foo')
r0(name = 'bar')
r(name = 'baz', dep = ':bar')
""");
AnalysisResult analysisResult =
update(ImmutableList.of("//test:aspect.bzl%acollect"), "//test:baz");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key pCollector =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "PCollector");
StructImpl collector = (StructImpl) configuredAspect.get(pCollector);
assertThat(collector.getValue("attr_value")).isEqualTo(StarlarkInt.of(30));
}
@Test
public void earlyAspectAttributesWin() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
PAspect1 = provider(fields = [])
PAspect2 = provider(fields = [])
PCollector = provider(fields = ['attr_value'])
def _a1_impl(target, ctx):
return [PAspect1()]
def _a2_impl(target, ctx):
return [PAspect2()]
a1 = aspect(_a1_impl,
provides = [PAspect1],
attrs = {'_same_attr' : attr.int(default = 30)})
a2 = aspect(_a2_impl,
provides = [PAspect2],
attrs = {'_same_attr' : attr.int(default = 239)})
def _rcollect(target, ctx):
if hasattr(ctx.rule.attr, 'dep'):
return [ctx.rule.attr.dep[PCollector]]
if hasattr(ctx.rule.attr, '_same_attr'):
return [PCollector(attr_value = ctx.rule.attr._same_attr)]
fail('???')
return [PCollector()]
acollect = aspect(_rcollect, attr_aspects = ['*'],
required_aspect_providers = [[PAspect1], [PAspect2]])
def _rimpl(ctx):
pass
r0 = rule(_rimpl)
r1 = rule(_rimpl,
attrs = {
'dep' : attr.label(aspects = [a1]),
})
r2 = rule(_rimpl,
attrs = {
'dep' : attr.label(aspects = [a2]),
})
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r0', 'r1', 'r2')
r0(name = 'bar')
r1(name = 'baz', dep = ':bar')
r2(name = 'quux', dep = ':baz')
""");
AnalysisResult analysisResult =
update(ImmutableList.of("//test:aspect.bzl%acollect"), "//test:quux");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key pCollector =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "PCollector");
StructImpl collector = (StructImpl) configuredAspect.get(pCollector);
assertThat(collector.getValue("attr_value")).isEqualTo(StarlarkInt.of(30));
}
@Test
public void aspectPropagatesOverOtherAspectAttributes() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
PAspect = provider(fields = [])
PCollector = provider(fields = ['visited'])
def _a_impl(target, ctx):
return [PAspect()]
a = aspect(_a_impl,
provides = [PAspect],
attrs = {'_a_attr' : attr.label(default = '//test:referenced_from_aspect_only')})
def _rcollect(target, ctx):
transitive = []
if hasattr(ctx.rule.attr, 'dep') and ctx.rule.attr.dep:
transitive += [ctx.rule.attr.dep[PCollector].visited]
if hasattr(ctx.rule.attr, '_a_attr') and ctx.rule.attr._a_attr:
transitive += [ctx.rule.attr._a_attr[PCollector].visited]
visited = depset([target.label], transitive = transitive, )
return [PCollector(visited = visited)]
acollect = aspect(_rcollect, attr_aspects = ['*'], required_aspect_providers = [PAspect])
def _rimpl(ctx):
pass
r0 = rule(_rimpl)
r = rule(_rimpl, attrs = { 'dep' : attr.label(aspects = [a]) })
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r0', 'r')
r0(name = 'referenced_from_aspect_only')
r0(name = 'bar')
r(name = 'baz', dep = ':bar')
""");
AnalysisResult analysisResult =
update(ImmutableList.of("//test:aspect.bzl%acollect"), "//test:baz");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key pCollector =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "PCollector");
StructImpl collector = (StructImpl) configuredAspect.get(pCollector);
assertThat(((Depset) collector.getValue("visited")).toList())
.containsExactly(
Label.parseCanonical("//test:referenced_from_aspect_only"),
Label.parseCanonical("//test:bar"),
Label.parseCanonical("//test:baz"));
}
@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 [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 a2p in ctx.rule.attr.dep:
value += ctx.rule.attr.dep[a2p].value
value += target.actions
return [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 $@'
)
RuleInfo = provider()
def _r1_impl(ctx):
pass
def _r2_impl(ctx):
return RuleInfo(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());
Sequence<?> result = (Sequence<?>) getStarlarkProvider(target, "RuleInfo").getValue("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");
}
@Test
public void testRuleAndAspectAttrConflict() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
MyInfo = provider()
def _impl(target, ctx):
return [MyInfo(hidden_attr_label = str(ctx.attr._hiddenattr.label))]
def _rule_impl(ctx):
return []
my_rule = rule(implementation = _rule_impl,
attrs = { '_hiddenattr' : attr.label(default = Label('//test:xxx')) },
)
MyAspect = aspect(
implementation=_impl,
attrs = { '_hiddenattr' : attr.label(default = Label('//test:zzz')) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
cc_library(
name = 'xxx',
)
my_rule(
name = 'yyy',
)
cc_library(
name = 'zzz',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:yyy");
ConfiguredAspect configuredAspect = analysisResult.getAspectsMap().values().iterator().next();
assertThat(configuredAspect).isNotNull();
StarlarkProvider.Key myInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:aspect.bzl"), "MyInfo");
StructImpl myInfo = (StructImpl) configuredAspect.get(myInfoKey);
assertThat(myInfo.getValue("hidden_attr_label")).isEqualTo("@//test:zzz");
}
/** Simple straightforward linear aspects-on-aspects. */
@Test
public void aspectOnAspectAttrConflict() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
MyInfo = provider()
a1p = provider()
def _a1_impl(target,ctx):
return a1p(text = 'random')
a1 = aspect(_a1_impl,
attrs = { '_hiddenattr' : attr.label(default = Label('//test:xxx')) },
attr_aspects = ['dep'], provides = [a1p])
a2p = provider()
def _a2_impl(target,ctx):
return [MyInfo(hidden_attr_label = str(ctx.attr._hiddenattr.label))]
a2 = aspect(_a2_impl,
attrs = { '_hiddenattr' : attr.label(default = Label('//test:zzz')) },
attr_aspects = ['dep'], required_aspect_providers = [a1p])
RuleInfo = provider()
def _r1_impl(ctx):
pass
def _r2_impl(ctx):
return RuleInfo(result = ctx.attr.dep[MyInfo].hidden_attr_label)
r1 = rule(_r1_impl, attrs = { 'dep' : attr.label(aspects = [a1])})
r2 = rule(_r2_impl, attrs = { 'dep' : attr.label(aspects = [a1, a2])})
""");
scratch.file(
"test/BUILD",
"""
load(':aspect.bzl', 'r1', 'r2')
r1(name = 'r0')
r1(name = 'r1', dep = ':r0')
r2(name = 'r2', dep = ':r1')
cc_library(
name = 'xxx',
)
cc_library(
name = 'zzz',
)
""");
AnalysisResult analysisResult = update("//test:r2");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
String result = getStarlarkProvider(target, "RuleInfo").getValue("result", String.class);
assertThat(result).isEqualTo("@//test:zzz");
}
@Test
public void testAllCcLibraryAttrsAreValidTypes() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
for entry in dir(ctx.rule.attr):
val = getattr(ctx.rule.attr, entry, None)
# Only legitimate Starlark values can be passed to dir(), so this effectively
# verifies val is an appropriate Starlark type.
_test_dir = dir(val)
return []
MyAspect = aspect(
implementation=_impl,
)
""");
scratch.file(
"test/BUILD",
"""
cc_library(
name = 'xxx',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx");
assertThat(analysisResult.getAspectsMap().values().iterator().next()).isNotNull();
}
@Test
public void testApplyToGeneratingRules() throws Exception {
// Create test rules:
// dep_rule: a rule which may depend on other dep_rule targets and may optionally create
// an output file.
// root_{with,no}_files: a rule which depends on dep_rule targets and attaches an aspect.
// The rule returns a RootInfo provider which contains two fields:
// 'from_aspect' : a list of all labels that the aspect propagated to
// 'non_aspect' : a list of all labels that information was obtained from without aspect
// root_with_files uses an aspect with apply_to_generating_rules=True, and root_no_files
// uses an aspect with apply_to_generating_rules=False.
scratch.file(
"test/lib.bzl",
"""
RootInfo = provider()
NonAspectInfo = provider()
FromAspectInfo = provider()
def _aspect_impl(target, ctx):
dep_labels = []
for dep in ctx.rule.attr.deps:
if FromAspectInfo in dep:
dep_labels += [dep[FromAspectInfo].labels]
return FromAspectInfo(labels = depset(direct = [ctx.label], transitive = dep_labels))
def _rule_impl(ctx):
non_aspect = []
from_aspect = []
for dep in ctx.attr.deps:
if NonAspectInfo in dep:
non_aspect += dep[NonAspectInfo].labels.to_list()
if FromAspectInfo in dep:
from_aspect += dep[FromAspectInfo].labels.to_list()
return RootInfo(from_aspect = from_aspect, non_aspect = non_aspect)
def _dep_rule_impl(ctx):
if ctx.outputs.output:
ctx.actions.run_shell(outputs = [ctx.outputs.output], command = 'dont run me')
dep_labels = []
for dep in ctx.attr.deps:
if NonAspectInfo in dep:
dep_labels += [dep[NonAspectInfo].labels]
return NonAspectInfo(labels = depset(direct = [ctx.label], transitive = dep_labels))
aspect_with_files = aspect(
implementation = _aspect_impl,
attr_aspects = ['deps'],
apply_to_generating_rules = True)
aspect_no_files = aspect(
implementation = _aspect_impl,
attr_aspects = ['deps'],
apply_to_generating_rules = False)
root_with_files = rule(implementation = _rule_impl,
attrs = {'deps' : attr.label_list(aspects = [aspect_with_files])})
root_no_files = rule(implementation = _rule_impl,
attrs = {'deps' : attr.label_list(aspects = [aspect_no_files])})
dep_rule = rule(implementation = _dep_rule_impl,
attrs = {'deps' : attr.label_list(allow_files = True), 'output' : attr.output()})
""");
// Create a target graph such that two graph roots each point to a common subgraph
// alpha -> beta_output -> charlie, where beta_output is a generated output file of target
// 'beta'.
scratch.file(
"test/BUILD",
"""
load('//test:lib.bzl', 'root_with_files', 'root_no_files', 'dep_rule')
root_with_files(name = 'test_with_files', deps = [':alpha'])
root_no_files(name = 'test_no_files', deps = [':alpha'])
dep_rule(name = 'alpha', deps = [':beta_output'])
dep_rule(name = 'beta', deps = [':charlie'], output = 'beta_output')
dep_rule(name = 'charlie')
""");
StarlarkProvider.Key rootInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "RootInfo");
AnalysisResult analysisResultWithFiles = update("//test:test_with_files");
ConfiguredTarget targetWithFiles =
Iterables.getOnlyElement(analysisResultWithFiles.getTargetsToBuild());
StructImpl rootInfoWithFiles = (StructImpl) targetWithFiles.get(rootInfoKey);
// With apply_to_generating_rules=True, the aspect should have traversed :beta_output and
// applied to both :beta and :charlie.
assertThat(rootInfoWithFiles.getValue("from_aspect", Sequence.class))
.containsExactly(
Label.parseCanonical("//test:charlie"),
Label.parseCanonical("//test:beta"),
Label.parseCanonical("//test:alpha"));
assertThat(rootInfoWithFiles.getValue("non_aspect", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
AnalysisResult analysisResultNoFiles = update("//test:test_no_files");
ConfiguredTarget targetNoFiles =
Iterables.getOnlyElement(analysisResultNoFiles.getTargetsToBuild());
StructImpl rootInfoNoFiles = (StructImpl) targetNoFiles.get(rootInfoKey);
// With apply_to_generating_rules=False, the aspect should have only accessed :alpha, as it
// must have stopped before :beta_output.
assertThat(rootInfoNoFiles.getValue("from_aspect", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
assertThat(rootInfoWithFiles.getValue("non_aspect", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
}
private void setupAspectOnAspectTargetGraph(
boolean applyRootToGeneratingRules, boolean applyDepToGeneratingRules) throws Exception {
// RootAspectInfo.both_labels returns a list of target labels which
// were evaluated as [root_aspect(dep_aspect(target))].
// RootAspectInfo.root_only_labels returns a list of target labels which
// were evaluated as [root_aspect(target)].
// DepAspectInfo.labels returns a list of target labels which were evaluated by dep_aspect.
scratch.file(
"test/lib.bzl",
"RootAspectInfo = provider()",
"DepAspectInfo = provider()",
"def _root_aspect_impl(target, ctx):",
" both_labels = []",
" root_only_labels = []",
" for dep in ctx.rule.attr.deps:",
" if RootAspectInfo in dep:",
" both_labels += dep[RootAspectInfo].both_labels",
" root_only_labels += dep[RootAspectInfo].root_only_labels",
" if DepAspectInfo in dep:",
" both_labels += [dep.label]",
" else:",
" root_only_labels += [dep.label]",
" return RootAspectInfo(both_labels = both_labels, root_only_labels = root_only_labels)",
"",
"def _dep_aspect_impl(target, ctx):",
" dep_labels = [ctx.label]",
" for dep in ctx.rule.attr.deps:",
" if DepAspectInfo in dep:",
" dep_labels += dep[DepAspectInfo].labels",
" return DepAspectInfo(labels = dep_labels)",
"",
"def _root_rule_impl(ctx):",
" return [ctx.attr.deps[0][RootAspectInfo], ctx.attr.deps[0][DepAspectInfo]]",
"",
"def _dep_with_aspect_rule_impl(ctx):",
" return [ctx.attr.deps[0][DepAspectInfo]]",
"",
"def _dep_rule_impl(ctx):",
" if ctx.outputs.output:",
" ctx.actions.run_shell(outputs = [ctx.outputs.output], command = 'dont run me')",
" return []",
"",
"root_aspect = aspect(",
" implementation = _root_aspect_impl,",
" attr_aspects = ['deps'],",
" required_aspect_providers = [DepAspectInfo],",
" apply_to_generating_rules = " + (applyRootToGeneratingRules ? "True" : "False") + ")",
"",
"dep_aspect = aspect(",
" implementation = _dep_aspect_impl,",
" attr_aspects = ['deps'],",
" provides = [DepAspectInfo],",
" apply_to_generating_rules = " + (applyDepToGeneratingRules ? "True" : "False") + ")",
"",
"root_rule = rule(implementation = _root_rule_impl,",
" attrs = {'deps' : attr.label_list(aspects = [root_aspect])})",
"",
"dep_with_aspect_rule = rule(implementation = _dep_with_aspect_rule_impl,",
" attrs = {'deps' : attr.label_list(aspects = [dep_aspect])})",
"",
"dep_rule = rule(implementation = _dep_rule_impl,",
" attrs = {'deps' : attr.label_list(allow_files = True), 'output' : attr.output()})");
// Target graph: test -> aspect_propagating_target -> alpha -> beta_output
// beta_output is an output of target `beta`
// beta -> charlie
scratch.file(
"test/BUILD",
"""
load('//test:lib.bzl', 'root_rule', 'dep_with_aspect_rule', 'dep_rule')
root_rule(name = 'test', deps = [':aspect_propagating_target'])
dep_with_aspect_rule(name = 'aspect_propagating_target', deps = [':alpha'])
dep_rule(name = 'alpha', deps = [':beta_output'])
dep_rule(name = 'beta', deps = [':charlie'], output = 'beta_output')
dep_rule(name = 'charlie')
""");
}
@Test
public void testAspectOnAspectApplyToGeneratingRules_bothPropagate() throws Exception {
setupAspectOnAspectTargetGraph(
/* applyRootToGeneratingRules= */ true, /* applyDepToGeneratingRules= */ true);
StarlarkProvider.Key rootInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "RootAspectInfo");
StarlarkProvider.Key depInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "DepAspectInfo");
AnalysisResult analysisResult = update("//test:test");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StructImpl rootInfo = (StructImpl) target.get(rootInfoKey);
StructImpl depInfo = (StructImpl) target.get(depInfoKey);
assertThat(rootInfo.getValue("both_labels", Sequence.class))
.containsExactly(
Label.parseCanonical("//test:alpha"),
Label.parseCanonical("//test:beta_output"),
Label.parseCanonical("//test:charlie"));
assertThat(rootInfo.getValue("root_only_labels", Sequence.class)).isEmpty();
assertThat(depInfo.getValue("labels", Sequence.class))
.containsExactly(
Label.parseCanonical("//test:alpha"),
Label.parseCanonical("//test:beta"),
Label.parseCanonical("//test:charlie"));
}
@Test
public void testAspectOnAspectApplyToGeneratingRules_neitherPropagate() throws Exception {
setupAspectOnAspectTargetGraph(
/* applyRootToGeneratingRules= */ false, /* applyDepToGeneratingRules= */ false);
StarlarkProvider.Key rootInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "RootAspectInfo");
StarlarkProvider.Key depInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "DepAspectInfo");
AnalysisResult analysisResult = update("//test:test");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StructImpl rootInfo = (StructImpl) target.get(rootInfoKey);
StructImpl depInfo = (StructImpl) target.get(depInfoKey);
assertThat(rootInfo.getValue("both_labels", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
assertThat(rootInfo.getValue("root_only_labels", Sequence.class)).isEmpty();
assertThat(depInfo.getValue("labels", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
}
@Test
public void testAspectOnAspectApplyToGeneratingRules_rootOnly() throws Exception {
setupAspectOnAspectTargetGraph(
/* applyRootToGeneratingRules= */ true, /* applyDepToGeneratingRules= */ false);
StarlarkProvider.Key rootInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "RootAspectInfo");
StarlarkProvider.Key depInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "DepAspectInfo");
AnalysisResult analysisResult = update("//test:test");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StructImpl rootInfo = (StructImpl) target.get(rootInfoKey);
StructImpl depInfo = (StructImpl) target.get(depInfoKey);
assertThat(rootInfo.getValue("both_labels", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
assertThat(rootInfo.getValue("root_only_labels", Sequence.class))
.containsExactly(
Label.parseCanonical("//test:beta_output"), Label.parseCanonical("//test:charlie"));
assertThat(depInfo.getValue("labels", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
}
@Test
public void testAspectOnAspectApplyToGeneratingRules_depOnly() throws Exception {
setupAspectOnAspectTargetGraph(
/* applyRootToGeneratingRules= */ false, /* applyDepToGeneratingRules= */ true);
StarlarkProvider.Key rootInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "RootAspectInfo");
StarlarkProvider.Key depInfoKey =
new StarlarkProvider.Key(Label.parseCanonical("//test:lib.bzl"), "DepAspectInfo");
AnalysisResult analysisResult = update("//test:test");
ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StructImpl rootInfo = (StructImpl) target.get(rootInfoKey);
StructImpl depInfo = (StructImpl) target.get(depInfoKey);
assertThat(rootInfo.getValue("both_labels", Sequence.class))
.containsExactly(Label.parseCanonical("//test:alpha"));
assertThat(rootInfo.getValue("root_only_labels", Sequence.class)).isEmpty();
assertThat(depInfo.getValue("labels", Sequence.class))
.containsExactly(
Label.parseCanonical("//test:alpha"),
Label.parseCanonical("//test:beta"),
Label.parseCanonical("//test:charlie"));
}
@Test
public void testAspectActionsDontInheritExecProperties() throws Exception {
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'my_target',
deps = [':my_dep'],
)
cc_binary(
name = 'my_dep',
srcs = ['dep.cc'],
exec_properties = {'mem': '16g'},
)
""");
scratch.file(
"test/defs.bzl",
"""
def _aspect_impl(target, ctx):
f = ctx.actions.declare_file('f.txt')
ctx.actions.write(f, 'f')
return []
my_aspect = aspect(
implementation = _aspect_impl,
attr_aspects = ['deps'],
)
def _rule_impl(ctx):
pass
my_rule = rule(
implementation = _rule_impl,
attrs = {
'deps': attr.label_list(aspects = [my_aspect])
},
)
""");
scratch.file("test/dep.cc", "int main() {return 0;}");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%my_aspect"), "//test:my_target");
assertThat(analysisResult).isNotNull();
ActionOwner owner =
Iterables.getOnlyElement(
Iterables.getOnlyElement(analysisResult.getAspectsMap().values()).getActions())
.getOwner();
assertThat(owner.getExecProperties()).isEmpty();
}
@Test
public void testAspectRequiredProviders_defaultNoRequiredProviders() throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
AspectInfo = provider()
def _my_aspect_impl(target, ctx):
targets_labels = ["//test:defs.bzl%my_aspect({})".format(target.label)]
for dep in ctx.rule.attr.deps:
if AspectInfo in dep:
targets_labels.extend(dep[AspectInfo].target_labels)
return [AspectInfo(target_labels = targets_labels)]
my_aspect = aspect(
implementation = _my_aspect_impl,
attr_aspects = ['deps'],
)
RuleInfo = provider()
def _rule_without_providers_impl(ctx):
s = []
for dep in ctx.attr.deps:
if AspectInfo in dep:
s.extend(dep[AspectInfo].target_labels)
return RuleInfo(rule_deps = s)
rule_without_providers = rule(
implementation = _rule_without_providers_impl,
attrs = {
'deps': attr.label_list(aspects = [my_aspect])
},
)
def _rule_with_providers_impl(ctx):
return [prov_a(), prov_b()]
rule_with_providers = rule(
implementation = _rule_with_providers_impl,
attrs = {
'deps': attr.label_list()
},
provides = [prov_a, prov_b]
)
rule_with_providers_not_advertised = rule(
implementation = _rule_with_providers_impl,
attrs = {
'deps': attr.label_list()
},
provides = []
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'rule_with_providers', 'rule_without_providers',
'rule_with_providers_not_advertised')
rule_without_providers(
name = 'main',
deps = [':target_without_providers', ':target_with_providers',
':target_with_providers_not_advertised'],
)
rule_without_providers(
name = 'target_without_providers',
)
rule_with_providers(
name = 'target_with_providers',
)
rule_with_providers(
name = 'target_with_providers_indeps',
)
rule_with_providers_not_advertised(
name = 'target_with_providers_not_advertised',
deps = [':target_with_providers_indeps'],
)
""");
AnalysisResult analysisResult = update("//test:main");
// my_aspect does not require any providers so it will be applied to all the dependencies of
// main target
List<String> expected = new ArrayList<>();
expected.add("//test:defs.bzl%my_aspect(@//test:target_without_providers)");
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_providers)");
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_providers_not_advertised)");
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_providers_indeps)");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:main");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
Object ruleDepsUnchecked = getStarlarkProvider(target, "RuleInfo").getValue("rule_deps");
assertThat(ruleDepsUnchecked).isInstanceOf(StarlarkList.class);
StarlarkList<?> ruleDeps = (StarlarkList) ruleDepsUnchecked;
assertThat(Starlark.toIterable(ruleDeps)).containsExactlyElementsIn(expected);
}
@Test
public void testAspectRequiredProviders_flatSetOfRequiredProviders() throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
AspectInfo = provider()
def _my_aspect_impl(target, ctx):
targets_labels = ["//test:defs.bzl%my_aspect({})".format(target.label)]
for dep in ctx.rule.attr.deps:
if AspectInfo in dep:
targets_labels.extend(dep[AspectInfo].target_labels)
return AspectInfo(target_labels = targets_labels)
my_aspect = aspect(
implementation = _my_aspect_impl,
attr_aspects = ['deps'],
required_providers = [prov_a, prov_b],
)
RuleInfo = provider()
def _rule_without_providers_impl(ctx):
s = []
for dep in ctx.attr.deps:
if AspectInfo in dep:
s.extend(dep[AspectInfo].target_labels)
return RuleInfo(rule_deps = s)
rule_without_providers = rule(
implementation = _rule_without_providers_impl,
attrs = {
'deps': attr.label_list(aspects=[my_aspect])
},
provides = []
)
def _rule_with_a_impl(ctx):
return [prov_a()]
rule_with_a = rule(
implementation = _rule_with_a_impl,
attrs = {
'deps': attr.label_list()
},
provides = [prov_a]
)
def _rule_with_ab_impl(ctx):
return [prov_a(), prov_b()]
rule_with_ab = rule(
implementation = _rule_with_ab_impl,
attrs = {
'deps': attr.label_list()
},
provides = [prov_a, prov_b]
)
rule_with_ab_not_advertised = rule(
implementation = _rule_with_ab_impl,
attrs = {
'deps': attr.label_list()
},
provides = []
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'rule_without_providers', 'rule_with_a', 'rule_with_ab',
'rule_with_ab_not_advertised')
rule_without_providers(
name = 'main',
deps = [':target_without_providers', ':target_with_a', ':target_with_ab',
':target_with_ab_not_advertised'],
)
rule_without_providers(
name = 'target_without_providers',
)
rule_with_a(
name = 'target_with_a',
deps = [':target_with_ab_indeps_not_reached']
)
rule_with_ab(
name = 'target_with_ab',
deps = [':target_with_ab_indeps_reached']
)
rule_with_ab(
name = 'target_with_ab_indeps_not_reached',
)
rule_with_ab(
name = 'target_with_ab_indeps_reached',
)
rule_with_ab_not_advertised(
name = 'target_with_ab_not_advertised',
)
""");
AnalysisResult analysisResult = update("//test:main");
// my_aspect will only be applied on target_with_ab and target_with_ab_indeps_reached since
// their rule (rule_with_ab) is the only rule that advertises the aspect required providers.
// However, my_aspect cannot be propagated to target_with_ab_indeps_not_reached because it was
// not applied to its parent (target_with_a)
List<String> expected = new ArrayList<>();
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_ab)");
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_ab_indeps_reached)");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:main");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
Object ruleDepsUnchecked = getStarlarkProvider(target, "RuleInfo").getValue("rule_deps");
assertThat(ruleDepsUnchecked).isInstanceOf(StarlarkList.class);
StarlarkList<?> ruleDeps = (StarlarkList) ruleDepsUnchecked;
assertThat(Starlark.toIterable(ruleDeps)).containsExactlyElementsIn(expected);
}
@Test
public void testAspectRequiredProviders_listOfRequiredProvidersLists() throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
prov_c = provider()
AspectInfo = provider()
def _my_aspect_impl(target, ctx):
targets_labels = ["//test:defs.bzl%my_aspect({})".format(target.label)]
for dep in ctx.rule.attr.deps:
if AspectInfo in dep:
targets_labels.extend(dep[AspectInfo].target_labels)
return [AspectInfo(target_labels = targets_labels)]
my_aspect = aspect(
implementation = _my_aspect_impl,
attr_aspects = ['deps'],
required_providers = [[prov_a, prov_b], [prov_c]],
)
RuleInfo = provider()
def _rule_without_providers_impl(ctx):
s = []
for dep in ctx.attr.deps:
if AspectInfo in dep:
s.extend(dep[AspectInfo].target_labels)
return RuleInfo(rule_deps = s)
rule_without_providers = rule(
implementation = _rule_without_providers_impl,
attrs = {
'deps': attr.label_list(aspects=[my_aspect])
},
provides = []
)
def _rule_with_a_impl(ctx):
return [prov_a()]
rule_with_a = rule(
implementation = _rule_with_a_impl,
attrs = {
'deps': attr.label_list()
},
provides = [prov_a]
)
def _rule_with_c_impl(ctx):
return [prov_c()]
rule_with_c = rule(
implementation = _rule_with_c_impl,
attrs = {
'deps': attr.label_list()
},
provides = [prov_c]
)
def _rule_with_ab_impl(ctx):
return [prov_a(), prov_b()]
rule_with_ab = rule(
implementation = _rule_with_ab_impl,
attrs = {
'deps': attr.label_list()
},
provides = [prov_a, prov_b]
)
rule_with_ab_not_advertised = rule(
implementation = _rule_with_ab_impl,
attrs = {
'deps': attr.label_list()
},
provides = []
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'rule_without_providers', 'rule_with_a', 'rule_with_c',
'rule_with_ab', 'rule_with_ab_not_advertised')
rule_without_providers(
name = 'main',
deps = [':target_without_providers', ':target_with_a', ':target_with_c',
':target_with_ab', 'target_with_ab_not_advertised'],
)
rule_without_providers(
name = 'target_without_providers',
)
rule_with_a(
name = 'target_with_a',
deps = [':target_with_c_indeps_not_reached'],
)
rule_with_c(
name = 'target_with_c',
)
rule_with_c(
name = 'target_with_c_indeps_reached',
)
rule_with_c(
name = 'target_with_c_indeps_not_reached',
)
rule_with_ab(
name = 'target_with_ab',
deps = [':target_with_c_indeps_reached'],
)
rule_with_ab_not_advertised(
name = 'target_with_ab_not_advertised',
)
""");
AnalysisResult analysisResult = update("//test:main");
// my_aspect will only be applied on target_with_ab, target_wtih_c and
// target_with_c_indeps_reached because their rules (rule_with_ab and rule_with_c) are the only
// rules advertising the aspect required providers
// However, my_aspect cannot be propagated to target_with_c_indeps_not_reached because it was
// not applied to its parent (target_with_a)
List<String> expected = new ArrayList<>();
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_ab)");
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_c)");
expected.add("//test:defs.bzl%my_aspect(@//test:target_with_c_indeps_reached)");
assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:main");
ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next();
Object ruleDepsUnchecked = getStarlarkProvider(target, "RuleInfo").getValue("rule_deps");
assertThat(ruleDepsUnchecked).isInstanceOf(StarlarkList.class);
StarlarkList<?> ruleDeps = (StarlarkList) ruleDepsUnchecked;
assertThat(Starlark.toIterable(ruleDeps)).containsExactlyElementsIn(expected);
}
@Test
public void testAspectRequiredByMultipleAspects_inheritsAttrAspects() throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
prov_c = provider()
def _aspect_c_impl(target, ctx):
res = ['aspect_c runs on target {}'.format(target.label)]
return [prov_c(val = res)]
aspect_c = aspect(
implementation = _aspect_c_impl,
)
def _aspect_b_impl(target, ctx):
res = []
res += target[prov_c].val
res += ['aspect_b runs on target {}'.format(target.label)]
if ctx.rule.attr.dep_b:
res += ctx.rule.attr.dep_b[prov_b].val
return [prov_b(val = res)]
aspect_b = aspect(
implementation = _aspect_b_impl,
attr_aspects = ['dep_b'],
requires = [aspect_c],
)
def _aspect_a_impl(target, ctx):
res = []
res += target[prov_c].val
res += ['aspect_a runs on target {}'.format(target.label)]
if ctx.rule.attr.dep_a:
res += ctx.rule.attr.dep_a[prov_a].val
return [prov_a(val = res)]
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep_a'],
requires = [aspect_c],
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = {
'dep_a': attr.label(),
'dep_b': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep_a = ':dep_target_a',
dep_b = ':dep_target_b',
)
my_rule(
name = 'dep_target_a',
)
my_rule(
name = 'dep_target_b',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
// aspect_a should run on main_target and dep_target_a and can retrieve aspect_c provider value
// on both of them
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkProvider.Key aResult =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "prov_a");
StructImpl aResultProvider = (StructImpl) aspectA.get(aResult);
assertThat((Sequence<?>) aResultProvider.getValue("val"))
.containsExactly(
"aspect_c runs on target @//test:dep_target_a",
"aspect_a runs on target @//test:dep_target_a",
"aspect_c runs on target @//test:main_target",
"aspect_a runs on target @//test:main_target");
// aspect_b should run on main_target and dep_target_b and can retrieve aspect_c provider value
// on both of them
ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
assertThat(aspectA).isNotNull();
StarlarkProvider.Key bResult =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "prov_b");
StructImpl bResultProvider = (StructImpl) aspectB.get(bResult);
assertThat((Sequence<?>) bResultProvider.getValue("val"))
.containsExactly(
"aspect_c runs on target @//test:dep_target_b",
"aspect_b runs on target @//test:dep_target_b",
"aspect_c runs on target @//test:main_target",
"aspect_b runs on target @//test:main_target");
}
@Test
public void testAspectRequiredByMultipleAspects_inheritsRequiredProviders() throws Exception {
scratch.file(
"test/defs.bzl",
"""
aspect_prov_a = provider()
aspect_prov_b = provider()
aspect_prov_c = provider()
rule_prov_a = provider()
rule_prov_b = provider()
rule_prov_c = provider()
def _aspect_c_impl(target, ctx):
res = ['aspect_c runs on target {}'.format(target.label)]
return [aspect_prov_c(val = res)]
aspect_c = aspect(
implementation = _aspect_c_impl,
required_providers = [rule_prov_c],
)
def _aspect_b_impl(target, ctx):
res = []
if aspect_prov_c in target:
res += target[aspect_prov_c].val
res += ['aspect_b runs on target {}'.format(target.label)]
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
if aspect_prov_b in dep:
res += dep[aspect_prov_b].val
return [aspect_prov_b(val = res)]
aspect_b = aspect(
implementation = _aspect_b_impl,
attr_aspects = ['deps'],
required_providers = [[rule_prov_b], [rule_prov_c]],
requires = [aspect_c],
)
def _aspect_a_impl(target, ctx):
res = []
if aspect_prov_c in target:
res += target[aspect_prov_c].val
res += ['aspect_a runs on target {}'.format(target.label)]
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
if aspect_prov_a in dep:
res += dep[aspect_prov_a].val
return [aspect_prov_a(val = res)]
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['deps'],
required_providers = [[rule_prov_a], [rule_prov_c]],
requires = [aspect_c],
)
def _my_rule_impl(ctx):
return [rule_prov_a(), rule_prov_b()]
my_rule = rule(
implementation = _my_rule_impl,
attrs = {
'deps': attr.label_list(),
},
provides = [rule_prov_a, rule_prov_b]
)
def _rule_with_prov_a_impl(ctx):
return [rule_prov_a()]
rule_with_prov_a = rule(
implementation = _rule_with_prov_a_impl,
attrs = {
'deps': attr.label_list(),
},
provides = [rule_prov_a]
)
def _rule_with_prov_b_impl(ctx):
return [rule_prov_b()]
rule_with_prov_b = rule(
implementation = _rule_with_prov_b_impl,
attrs = {
'deps': attr.label_list(),
},
provides = [rule_prov_b]
)
def _rule_with_prov_c_impl(ctx):
return [rule_prov_c()]
rule_with_prov_c = rule(
implementation = _rule_with_prov_c_impl,
attrs = {
'deps': attr.label_list(),
},
provides = [rule_prov_c]
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule', 'rule_with_prov_a',
'rule_with_prov_b', 'rule_with_prov_c')
my_rule(
name = 'main_target',
deps = [':dep_target_with_prov_a', ':dep_target_with_prov_b']
)
rule_with_prov_a(
name = 'dep_target_with_prov_a',
deps = [':dep_target_with_prov_c'],
)
rule_with_prov_b(
name = 'dep_target_with_prov_b',
deps = [':dep_target_with_prov_c'],
)
rule_with_prov_c(
name = 'dep_target_with_prov_c',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
// aspect_a runs on main_target, dep_target_with_prov_a and dep_target_with_prov_c and it can
// only retrieve aspect_c provider value on dep_target_with_prov_c
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkProvider.Key aResult =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "aspect_prov_a");
StructImpl aResultProvider = (StructImpl) aspectA.get(aResult);
assertThat((Sequence<?>) aResultProvider.getValue("val"))
.containsExactly(
"aspect_c runs on target @//test:dep_target_with_prov_c",
"aspect_a runs on target @//test:dep_target_with_prov_c",
"aspect_a runs on target @//test:dep_target_with_prov_a",
"aspect_a runs on target @//test:main_target");
// aspect_b runs on main_target, dep_target_with_prov_b and dep_target_with_prov_c and it can
// only retrieve aspect_c provider value on dep_target_with_prov_c
ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
assertThat(aspectA).isNotNull();
StarlarkProvider.Key bResult =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "aspect_prov_b");
StructImpl bResultProvider = (StructImpl) aspectB.get(bResult);
assertThat((Sequence<?>) bResultProvider.getValue("val"))
.containsExactly(
"aspect_c runs on target @//test:dep_target_with_prov_c",
"aspect_b runs on target @//test:dep_target_with_prov_c",
"aspect_b runs on target @//test:dep_target_with_prov_b",
"aspect_b runs on target @//test:main_target");
}
@Test
public void testAspectRequiredByMultipleAspects_withDifferentParametersValues() throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
prov_c = provider()
def _aspect_c_impl(target, ctx):
res = ['aspect_c runs on target {} and param = {}'.format(target.label, ctx.attr.p)]
return [prov_c(val = res)]
aspect_c = aspect(
implementation = _aspect_c_impl,
attrs = {
'p': attr.string(values=['rule_1_val', 'rule_2_val']),
},
)
def _aspect_b_impl(target, ctx):
res = []
res += target[prov_c].val
res += ['aspect_b runs on target {}'.format(target.label)]
if ctx.rule.attr.dep:
res += ctx.rule.attr.dep[prov_b].val
return [prov_b(val = res)]
aspect_b = aspect(
implementation = _aspect_b_impl,
attr_aspects = ['dep'],
requires = [aspect_c],
)
def _aspect_a_impl(target, ctx):
res = []
res += target[prov_c].val
res += ['aspect_a runs on target {}'.format(target.label)]
if ctx.rule.attr.dep:
res += ctx.rule.attr.dep[prov_a].val
return [prov_a(val = res)]
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
requires = [aspect_c],
)
def _rule_1_impl(ctx):
return ctx.attr.dep[prov_a]
rule_1 = rule(
implementation = _rule_1_impl,
attrs = {
'dep': attr.label(aspects = [aspect_a]),
'p': attr.string(values = ['rule_1_val', 'rule_2_val'])
},
)
def _rule_2_impl(ctx):
return ctx.attr.dep[prov_b]
rule_2 = rule(
implementation = _rule_2_impl,
attrs = {
'dep': attr.label(aspects = [aspect_b]),
'p': attr.string(values = ['rule_1_val', 'rule_2_val'])
},
)
def _rule_3_impl(ctx):
pass
rule_3 = rule(
implementation = _rule_3_impl,
attrs = {
'dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'rule_1', 'rule_2', 'rule_3')
rule_1(
name = 'target_1',
dep = ':dep_1',
p = 'rule_1_val'
)
rule_2(
name = 'target_2',
dep = ':dep_2',
p = 'rule_2_val'
)
rule_3(
name = 'dep_1',
dep = ':dep_3',
)
rule_3(
name = 'dep_2',
dep = ':dep_3',
)
rule_3(
name = 'dep_3',
)
""");
AnalysisResult analysisResult = update("//test:target_1", "//test:target_2");
Iterator<ConfiguredTarget> it = analysisResult.getTargetsToBuild().iterator();
// aspect_a runs on dep_1 and dep_3 and it can retrieve aspect_c provider value on them
// aspect_c here should get its parameter value from rule_2
ConfiguredTarget target1 = it.next();
StarlarkProvider.Key provAkey =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "prov_a");
StructImpl provA = (StructImpl) target1.get(provAkey);
assertThat((Sequence<?>) provA.getValue("val"))
.containsExactly(
"aspect_c runs on target @//test:dep_1 and param = rule_1_val",
"aspect_a runs on target @//test:dep_1",
"aspect_c runs on target @//test:dep_3 and param = rule_1_val",
"aspect_a runs on target @//test:dep_3");
// aspect_b runs on dep_2 and dep_3 and it can retrieve aspect_c provider value on them.
// aspect_c here should get its parameter value from rule_2
ConfiguredTarget target2 = it.next();
StarlarkProvider.Key provBkey =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "prov_b");
StructImpl provB = (StructImpl) target2.get(provBkey);
assertThat((Sequence<?>) provB.getValue("val"))
.containsExactly(
"aspect_c runs on target @//test:dep_2 and param = rule_2_val",
"aspect_b runs on target @//test:dep_2",
"aspect_c runs on target @//test:dep_3 and param = rule_2_val",
"aspect_b runs on target @//test:dep_3");
}
@Test
public void testAspectRequiresAspect_requireNativeAspect() throws Exception {
exposeNativeAspectToStarlark();
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
def _impl(target, ctx):
res = 'aspect_a on target {} '.format(target.label)
if hasattr(target, 'native_aspect_prov'):
res += 'can see native aspect provider'
else:
res += 'cannot see native aspect provider'
complete_res = [res]
if hasattr(ctx.rule.attr, 'dep'):
complete_res += ctx.rule.attr.dep[prov_a].val
return [prov_a(val = complete_res)]
aspect_a = aspect(implementation = _impl,
requires = [starlark_native_aspect],
attr_aspects = ['dep'],)
def _my_rule_impl(ctx):
pass
my_rule = rule(implementation = _my_rule_impl,
attrs = {'dep': attr.label()})
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_1',
)
my_rule(
name = 'dep_1',
dep = ':dep_2',
)
honest(
name = 'dep_2',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
// aspect_a runs on main_target, dep_1 and dep_2 but it can only see the required native aspect
// run on dep_2 because its rule satisfies its required provider.
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkProvider.Key aResult =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "prov_a");
StructImpl aResultProvider = (StructImpl) aspectA.get(aResult);
assertThat((Sequence<?>) aResultProvider.getValue("val"))
.containsExactly(
"aspect_a on target @//test:main_target cannot see native aspect provider",
"aspect_a on target @//test:dep_1 cannot see native aspect provider",
"aspect_a on target @//test:dep_2 can see native aspect provider");
}
@Test
public void testAspectRequiresAspect_aspectsParameters() throws Exception {
scratch.file(
"test/defs.bzl",
"""
RequiredAspectProv = provider()
BaseAspectProv = provider()
def _required_aspect_impl(target, ctx):
p1_val = 'In required_aspect, p1 = {} on target {}'.format(ctx.attr.p1, target.label)
p2_val = 'invalid value'
if not hasattr(ctx.attr, 'p2'):
p2_val = 'In required_aspect, p2 not found on target {}'.format(target.label)
return [RequiredAspectProv(p1_val = p1_val, p2_val = p2_val)]
required_aspect = aspect(
implementation = _required_aspect_impl,
attr_aspects = ['dep'],
attrs = {'p1' : attr.string(values = ['p1_v1', 'p1_v2'])}
)
def _base_aspect_impl(target, ctx):
p2_val = 'In base_aspect, p2 = {} on target {}'.format(ctx.attr.p2, target.label)
p1_val = 'invalid value'
if not hasattr(ctx.attr, 'p1'):
p1_val = 'In base_aspect, p1 not found on target {}'.format(target.label)
return [BaseAspectProv(p1_val = p1_val, p2_val = p2_val)]
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['dep'],
attrs = {'p2' : attr.string(values = ['p2_v1', 'p2_v2'])},
requires = [required_aspect],
)
def _main_rule_impl(ctx):
return [ctx.attr.dep[RequiredAspectProv], ctx.attr.dep[BaseAspectProv]]
def _dep_rule_impl(ctx):
pass
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects=[base_aspect]),
'p1' : attr.string(values = ['p1_v1', 'p1_v2']),
'p2' : attr.string(values = ['p2_v1', 'p2_v2'])
},
)
dep_rule = rule(
implementation = _dep_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'dep_rule')
main_rule(
name = 'main',
dep = ':dep_target',
p1 = 'p1_v1',
p2 = 'p2_v1'
)
dep_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult = update("//test:main");
// Both base_aspect and required_aspect can get their parameters values from the base rule
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkProvider.Key requiredAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "RequiredAspectProv");
StructImpl requiredAspectProvider = (StructImpl) configuredTarget.get(requiredAspectProv);
assertThat(requiredAspectProvider.getValue("p1_val"))
.isEqualTo("In required_aspect, p1 = p1_v1 on target @//test:dep_target");
assertThat(requiredAspectProvider.getValue("p2_val"))
.isEqualTo("In required_aspect, p2 not found on target @//test:dep_target");
StarlarkProvider.Key baseAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BaseAspectProv");
StructImpl baseAspectProvider = (StructImpl) configuredTarget.get(baseAspectProv);
assertThat(baseAspectProvider.getValue("p1_val"))
.isEqualTo("In base_aspect, p1 not found on target @//test:dep_target");
assertThat(baseAspectProvider.getValue("p2_val"))
.isEqualTo("In base_aspect, p2 = p2_v1 on target @//test:dep_target");
}
@Test
public void testAspectRequiresAspect_ruleAttributes() throws Exception {
scratch.file(
"test/defs.bzl",
"""
RequiredAspectProv = provider()
BaseAspectProv = provider()
def _required_aspect_impl(target, ctx):
p_val = 'In required_aspect, p = {} on target {}'.format(ctx.rule.attr.p, target.label)
return [RequiredAspectProv(p_val = p_val)]
required_aspect = aspect(
implementation = _required_aspect_impl,
)
def _base_aspect_impl(target, ctx):
p_val = 'In base_aspect, p = {} on target {}'.format(ctx.rule.attr.p, target.label)
return [BaseAspectProv(p_val = p_val)]
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['dep'],
requires = [required_aspect],
)
def _main_rule_impl(ctx):
return [ctx.attr.dep[RequiredAspectProv], ctx.attr.dep[BaseAspectProv]]
def _dep_rule_impl(ctx):
pass
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects=[base_aspect]),
},
)
dep_rule = rule(
implementation = _dep_rule_impl,
attrs = {
'p' : attr.string(values = ['p_v1', 'p_v2']),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'dep_rule')
main_rule(
name = 'main',
dep = ':dep_target',
)
dep_rule(
name = 'dep_target',
p = 'p_v2',
)
""");
AnalysisResult analysisResult = update("//test:main");
// Both base_aspect and required_aspect can see the attributes of the rule they run on
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkProvider.Key requiredAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "RequiredAspectProv");
StructImpl requiredAspectProvider = (StructImpl) configuredTarget.get(requiredAspectProv);
assertThat(requiredAspectProvider.getValue("p_val"))
.isEqualTo("In required_aspect, p = p_v2 on target @//test:dep_target");
StarlarkProvider.Key baseAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BaseAspectProv");
StructImpl baseAspectProvider = (StructImpl) configuredTarget.get(baseAspectProv);
assertThat(baseAspectProvider.getValue("p_val"))
.isEqualTo("In base_aspect, p = p_v2 on target @//test:dep_target");
}
@Test
public void testAspectRequiresAspect_inheritPropagationAttributes() throws Exception {
// base_aspect propagates over base_dep attribute and requires first_required_aspect which
// propagates over first_dep attribute and requires second_required aspect which propagates
// over second_dep attribute
scratch.file(
"test/defs.bzl",
"""
BaseAspectProv = provider()
FirstRequiredAspectProv = provider()
SecondRequiredAspectProv = provider()
def _second_required_aspect_impl(target, ctx):
result = []
if getattr(ctx.rule.attr, 'second_dep'):
result += getattr(ctx.rule.attr, 'second_dep')[SecondRequiredAspectProv].result
result += ['second_required_aspect run on target {}'.format(target.label)]
return [SecondRequiredAspectProv(result = result)]
second_required_aspect = aspect(
implementation = _second_required_aspect_impl,
attr_aspects = ['second_dep'],
)
def _first_required_aspect_impl(target, ctx):
result = []
result += target[SecondRequiredAspectProv].result
if getattr(ctx.rule.attr, 'first_dep'):
result += getattr(ctx.rule.attr, 'first_dep')[FirstRequiredAspectProv].result
result += ['first_required_aspect run on target {}'.format(target.label)]
return [FirstRequiredAspectProv(result = result)]
first_required_aspect = aspect(
implementation = _first_required_aspect_impl,
attr_aspects = ['first_dep'],
requires = [second_required_aspect],
)
def _base_aspect_impl(target, ctx):
result = []
result += target[FirstRequiredAspectProv].result
if getattr(ctx.rule.attr, 'base_dep'):
result += getattr(ctx.rule.attr, 'base_dep')[BaseAspectProv].result
result += ['base_aspect run on target {}'.format(target.label)]
return [BaseAspectProv(result = result)]
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['base_dep'],
requires = [first_required_aspect],
)
def _main_rule_impl(ctx):
return [ctx.attr.dep[BaseAspectProv]]
def _dep_rule_impl(ctx):
pass
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects=[base_aspect]),
},
)
dep_rule = rule(
implementation = _dep_rule_impl,
attrs = {
'base_dep': attr.label(),
'first_dep': attr.label(),
'second_dep': attr.label()
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'dep_rule')
main_rule(
name = 'main',
dep = ':dep_target',
)
dep_rule(
name = 'dep_target',
base_dep = ':base_dep_target',
first_dep = ':first_dep_target',
second_dep = ':second_dep_target',
)
dep_rule(
name = 'base_dep_target',
)
dep_rule(
name = 'first_dep_target',
)
dep_rule(
name = 'second_dep_target',
)
""");
AnalysisResult analysisResult = update("//test:main");
// base_aspect should propagate only along its attr_aspects: 'base_dep'
// first_required_aspect should propagate along 'base_dep' and 'first_dep'
// second_required_aspect should propagate along 'base_dep', 'first_dep' and `second_dep`
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkProvider.Key baseAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BaseAspectProv");
StructImpl baseAspectProvider = (StructImpl) configuredTarget.get(baseAspectProv);
assertThat((Sequence<?>) baseAspectProvider.getValue("result"))
.containsExactly(
"second_required_aspect run on target @//test:second_dep_target",
"second_required_aspect run on target @//test:dep_target",
"second_required_aspect run on target @//test:first_dep_target",
"first_required_aspect run on target @//test:first_dep_target",
"first_required_aspect run on target @//test:dep_target",
"second_required_aspect run on target @//test:base_dep_target",
"first_required_aspect run on target @//test:base_dep_target",
"base_aspect run on target @//test:base_dep_target",
"base_aspect run on target @//test:dep_target");
}
@Test
public void testAspectRequiresAspect_inheritRequiredProviders() throws Exception {
// aspect_a requires provider Prov_A and requires aspect_b which requires
// provider Prov_B and requires aspect_c which requires provider Prov_C
scratch.file(
"test/defs.bzl",
"Prov_A = provider()",
"Prov_B = provider()",
"Prov_C = provider()",
"",
"CollectorProv = provider()",
"",
"def _aspect_c_impl(target, ctx):",
" collector_result = ['aspect_c run on target {} and value of Prov_C ="
+ " {}'.format(target.label, target[Prov_C].val)]",
" return [CollectorProv(result = collector_result)]",
"aspect_c = aspect(",
" implementation = _aspect_c_impl,",
" required_providers = [Prov_C],",
" attr_aspects = ['dep'],",
")",
"",
"def _aspect_b_impl(target, ctx):",
" collector_result = []",
" collector_result += ctx.rule.attr.dep[CollectorProv].result",
" collector_result += ['aspect_b run on target {} and value of Prov_B ="
+ " {}'.format(target.label, target[Prov_B].val)]",
" return [ CollectorProv(result = collector_result)]",
"aspect_b = aspect(",
" implementation = _aspect_b_impl,",
" required_providers = [Prov_B],",
" requires = [aspect_c],",
" attr_aspects = ['dep'],",
")",
"",
"def _aspect_a_impl(target, ctx):",
" collector_result = []",
" collector_result += ctx.rule.attr.dep[CollectorProv].result",
" collector_result += ['aspect_a run on target {} and value of Prov_A ="
+ " {}'.format(target.label, target[Prov_A].val)]",
" return [CollectorProv(result = collector_result)]",
"aspect_a = aspect(",
" implementation = _aspect_a_impl,",
" attr_aspects = ['dep'],",
" required_providers = [Prov_A],",
" requires = [aspect_b],",
")",
"",
"def _main_rule_impl(ctx):",
" return [ctx.attr.dep[CollectorProv]]",
"main_rule = rule(",
" implementation = _main_rule_impl,",
" attrs = {",
" 'dep': attr.label(aspects = [aspect_a]),",
" },",
")",
"",
"def _rule_with_prov_a_impl(ctx):",
" return [Prov_A(val='val_a')]",
"rule_with_prov_a = rule(",
" implementation = _rule_with_prov_a_impl,",
" attrs = {",
" 'dep': attr.label(),",
" },",
" provides = [Prov_A]",
")",
"",
"def _rule_with_prov_b_impl(ctx):",
" return [Prov_B(val = 'val_b')]",
"rule_with_prov_b = rule(",
" implementation = _rule_with_prov_b_impl,",
" attrs = {",
" 'dep': attr.label(),",
" },",
" provides = [Prov_B]",
")",
"",
"def _rule_with_prov_c_impl(ctx):",
" return [Prov_C(val = 'val_c')]",
"rule_with_prov_c = rule(",
" implementation = _rule_with_prov_c_impl,",
" provides = [Prov_C]",
")");
scratch.file(
"test/BUILD",
"load('//test:defs.bzl', 'main_rule', 'rule_with_prov_a', 'rule_with_prov_b',"
+ " 'rule_with_prov_c')",
"main_rule(",
" name = 'main',",
" dep = ':target_with_prov_a',",
")",
"rule_with_prov_a(",
" name = 'target_with_prov_a',",
" dep = ':target_with_prov_b'",
")",
"rule_with_prov_b(",
" name = 'target_with_prov_b',",
" dep = ':target_with_prov_c'",
")",
"rule_with_prov_c(",
" name = 'target_with_prov_c'",
")");
AnalysisResult analysisResult = update("//test:main");
// aspect_a should only run on target_with_prov_a, aspect_b should only run on
// target_with_prov_b and aspect_c should only run on target_with_prov_c.
// aspect_c will reach target target_with_prov_c because it inherits the required_providers of
// aspect_b otherwise it would have stopped propagating after target_with_prov_b.
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkProvider.Key collectorProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "CollectorProv");
StructImpl collectorProvider = (StructImpl) configuredTarget.get(collectorProv);
assertThat((Sequence<?>) collectorProvider.getValue("result"))
.containsExactly(
"aspect_c run on target @//test:target_with_prov_c and value of Prov_C = val_c",
"aspect_b run on target @//test:target_with_prov_b and value of Prov_B = val_b",
"aspect_a run on target @//test:target_with_prov_a and value of Prov_A = val_a")
.inOrder();
}
@Test
public void testAspectRequiresAspect_inspectRequiredAspectActions() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _required_aspect_impl(target, ctx):
f = ctx.actions.declare_file('dummy.txt')
ctx.actions.run_shell(outputs = [f], command='echo xxx > $(location f)',
mnemonic='RequiredAspectAction')
return []
required_aspect = aspect(
implementation = _required_aspect_impl,
)
AspectInfo = provider()
def _base_aspect_impl(target, ctx):
required_aspect_action = None
for action in target.actions:
if action.mnemonic == 'RequiredAspectAction':
required_aspect_action = action
if required_aspect_action:
return AspectInfo(result = 'base_aspect can see required_aspect action')
else:
return AspectInfo(result = 'base_aspect cannot see required_aspect action')
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['dep'],
requires = [required_aspect]
)
RuleInfo = provider()
def _main_rule_impl(ctx):
return RuleInfo(result = ctx.attr.dep[AspectInfo].result)
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects = [base_aspect]),
},
)
def _dep_rule_impl(ctx):
pass
dep_rule = rule(
implementation = _dep_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'dep_rule')
main_rule(
name = 'main',
dep = ':dep_target',
)
dep_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult = update("//test:main");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
String result = (String) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("result");
assertThat(result).isEqualTo("base_aspect can see required_aspect action");
}
@Test
public void testAspectRequiresAspect_inspectRequiredAspectGeneratedFiles() throws Exception {
scratch.file(
"test/defs.bzl",
"def _required_aspect_impl(target, ctx):",
" file = ctx.actions.declare_file('required_aspect_file')",
" ctx.actions.write(file, 'data')",
" return [OutputGroupInfo(out = [file])]",
"required_aspect = aspect(",
" implementation = _required_aspect_impl,",
")",
"",
"AspectInfo = provider()",
"def _base_aspect_impl(target, ctx):",
" files = ['base_aspect can see file ' + f.path.split('/')[-1] for f in"
+ " target[OutputGroupInfo].out.to_list()]",
" return AspectInfo(my_files = files)",
"base_aspect = aspect(",
" implementation = _base_aspect_impl,",
" attr_aspects = ['dep'],",
" requires = [required_aspect]",
")",
"",
"RuleInfo = provider()",
"def _main_rule_impl(ctx):",
" return RuleInfo(my_files = ctx.attr.dep[AspectInfo].my_files)",
"main_rule = rule(",
" implementation = _main_rule_impl,",
" attrs = {",
" 'dep': attr.label(aspects = [base_aspect]),",
" },",
")",
"",
"def _dep_rule_impl(ctx):",
" pass",
"dep_rule = rule(",
" implementation = _dep_rule_impl,",
")");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'dep_rule')
main_rule(
name = 'main',
dep = ':dep_target',
)
dep_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult = update("//test:main");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> files =
(StarlarkList) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("my_files");
assertThat(Starlark.toIterable(files))
.containsExactly("base_aspect can see file required_aspect_file");
}
@Test
public void testAspectRequiresAspect_withRequiredAspectProvidersSatisfied() throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
prov_b_forwarded = provider()
AspectAInfo = provider()
AspectBInfo = provider()
RuleInfo = provider()
def _aspect_b_impl(target, ctx):
result = 'aspect_b on target {} '.format(target.label)
if prov_b in target:
result += 'found prov_b = {}'.format(target[prov_b].val)
return [AspectBInfo(aspect_b_result = result),
prov_b_forwarded(val = target[prov_b].val)]
else:
result += 'cannot find prov_b'
return [AspectBInfo(aspect_b_result = result)]
aspect_b = aspect(
implementation = _aspect_b_impl,
required_aspect_providers = [prov_b]
)
def _aspect_a_impl(target, ctx):
result = 'aspect_a on target {} '.format(target.label)
if prov_a in target:
result += 'found prov_a = {}'.format(target[prov_a].val)
else:
result += 'cannot find prov_a'
if prov_b_forwarded in target:
result += ' and found prov_b = {}'.format(target[prov_b_forwarded].val)
else:
result += ' but cannot find prov_b'
return AspectAInfo(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
required_aspect_providers = [prov_a],
attr_aspects = ['dep'],
requires = [aspect_b]
)
def _aspect_with_prov_a_impl(target, ctx):
return [prov_a(val = 'a1')]
aspect_with_prov_a = aspect(
implementation = _aspect_with_prov_a_impl,
provides = [prov_a],
attr_aspects = ['dep'],
)
def _aspect_with_prov_b_impl(target, ctx):
return [prov_b(val = 'b1')]
aspect_with_prov_b = aspect(
implementation = _aspect_with_prov_b_impl,
provides = [prov_b],
attr_aspects = ['dep'],
)
def _main_rule_impl(ctx):
return RuleInfo(aspect_a_result = ctx.attr.dep[AspectAInfo].aspect_a_result,
aspect_b_result = ctx.attr.dep[AspectBInfo].aspect_b_result)
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects = [aspect_with_prov_a, aspect_with_prov_b, aspect_a]),
},
)
def _dep_rule_impl(ctx):
pass
dep_rule = rule(
implementation = _dep_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'dep_rule')
main_rule(
name = 'main',
dep = ':dep_target',
)
dep_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult = update("//test:main");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkInfo targetInfo = getStarlarkProvider(configuredTarget, "RuleInfo");
String aspectAResult = targetInfo.getValue("aspect_a_result", String.class);
assertThat(aspectAResult)
.isEqualTo("aspect_a on target @//test:dep_target found prov_a = a1 and found prov_b = b1");
String aspectBResult = targetInfo.getValue("aspect_b_result", String.class);
assertThat(aspectBResult).isEqualTo("aspect_b on target @//test:dep_target found prov_b = b1");
}
@Test
public void testAspectRequiresAspect_withRequiredAspectProvidersNotFound() throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
AspectAInfo = provider()
AspectBInfo = provider()
def _aspect_b_impl(target, ctx):
result = 'aspect_b on target {} '.format(target.label)
if prov_b in target:
result += 'found prov_b = {}'.format(target[prov_b].val)
else:
result += 'cannot find prov_b'
return AspectBInfo(aspect_b_result = result)
aspect_b = aspect(
implementation = _aspect_b_impl,
required_aspect_providers = [prov_b]
)
def _aspect_a_impl(target, ctx):
result = 'aspect_a on target {} '.format(target.label)
if prov_a in target:
result += 'found prov_a = {}'.format(target[prov_a].val)
else:
result += 'cannot find prov_a'
return AspectAInfo(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
required_aspect_providers = [prov_a],
attr_aspects = ['dep'],
requires = [aspect_b]
)
def _aspect_with_prov_a_impl(target, ctx):
return [prov_a(val = 'a1')]
aspect_with_prov_a = aspect(
implementation = _aspect_with_prov_a_impl,
provides = [prov_a],
attr_aspects = ['dep'],
)
RuleInfo = provider()
def _main_rule_impl(ctx):
return RuleInfo(aspect_a_result = ctx.attr.dep[AspectAInfo].aspect_a_result,
aspect_b_result = ctx.attr.dep[AspectBInfo].aspect_b_result)
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects = [aspect_with_prov_a, aspect_a]),
},
)
def _dep_rule_impl(ctx):
pass
dep_rule = rule(
implementation = _dep_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'dep_rule')
main_rule(
name = 'main',
dep = ':dep_target',
)
dep_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult = update("//test:main");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkInfo ruleInfo = getStarlarkProvider(configuredTarget, "RuleInfo");
String aspectAResult = ruleInfo.getValue("aspect_a_result", String.class);
assertThat(aspectAResult).isEqualTo("aspect_a on target @//test:dep_target found prov_a = a1");
String aspectBResult = ruleInfo.getValue("aspect_b_result", String.class);
assertThat(aspectBResult).isEqualTo("aspect_b on target @//test:dep_target cannot find prov_b");
}
/**
* --aspects = a3, a2, a1: aspect a1 requires provider a1p, aspect a2 requires provider a2p and
* provides a1p and aspect a3 provides a2p. The three aspects will propagate together but aspect
* a1 will only see a1p and aspect a2 will only see a2p.
*/
@Test
public void testTopLevelAspectOnAspect_stackOfAspects() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a2p = provider()
a1_result = provider()
a2_result = provider()
a3_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
if a2p in target:
result += ' and sees a2p = {}'.format(target[a2p].value)
else:
result += ' and cannot see a2p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a1_result].value + [result]
else:
complete_result = [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['dep'],
required_aspect_providers = [a1p]
)
def _a2_impl(target, ctx):
result = 'aspect a2 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
if a2p in target:
result += ' and sees a2p = {}'.format(target[a2p].value)
else:
result += ' and cannot see a2p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a2_result].value + [result]
else:
complete_result = [result]
return [a2_result(value = complete_result), a1p(value = 'a1p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['dep'],
provides = [a1p],
required_aspect_providers = [a2p],
)
def _a3_impl(target, ctx):
result = 'aspect a3 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
if a2p in target:
result += ' and sees a2p = {}'.format(target[a2p].value)
else:
result += ' and cannot see a2p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a3_result].value + [result]
else:
complete_result = [result]
return [a3_result(value = complete_result), a2p(value = 'a2p_val')]
a3 = aspect(
implementation = _a3_impl,
attr_aspects = ['dep'],
provides = [a2p],
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
dep = ':dep_target',
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a3 = getConfiguredAspect(configuredAspects, "a3");
assertThat(a3).isNotNull();
StarlarkProvider.Key a3Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a3_result");
StructImpl a3ResultProvider = (StructImpl) a3.get(a3Result);
assertThat((Sequence<?>) a3ResultProvider.getValue("value"))
.containsExactly(
"aspect a3 on target @//test:dep_target cannot see a1p and cannot see a2p",
"aspect a3 on target @//test:main cannot see a1p and cannot see a2p");
ConfiguredAspect a2 = getConfiguredAspect(configuredAspects, "a2");
assertThat(a2).isNotNull();
StarlarkProvider.Key a2Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a2_result");
StructImpl a2ResultProvider = (StructImpl) a2.get(a2Result);
assertThat((Sequence<?>) a2ResultProvider.getValue("value"))
.containsExactly(
"aspect a2 on target @//test:dep_target cannot see a1p and sees a2p = a2p_val",
"aspect a2 on target @//test:main cannot see a1p and sees a2p = a2p_val");
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target sees a1p = a1p_val and cannot see a2p",
"aspect a1 on target @//test:main sees a1p = a1p_val and cannot see a2p");
}
/**
* --aspects = a3, a2, a1: aspect a1 requires provider a1p, aspect a2 and aspect a3 provides a1p.
* This should fail because provider a1p is provided twice.
*/
@Test
public void testTopLevelAspectOnAspect_requiredProviderProvidedTwiceFailed() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a1_result].value + [result]
else:
complete_result = [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['dep'],
required_aspect_providers = [a1p]
)
def _a2_impl(target, ctx):
return [a1p(value = 'a1p_a2_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['dep'],
provides = [a1p],
)
def _a3_impl(target, ctx):
return [a1p(value = 'a1p_a3_val')]
a3 = aspect(
implementation = _a3_impl,
attr_aspects = ['dep'],
provides = [a1p],
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
dep = ':dep_target',
)
simple_rule(
name = 'dep_target',
)
""");
reporter.removeHandler(failFastHandler);
// The call to `update` does not throw an exception when "--keep_going" is passed in the
// WithKeepGoing test suite. Otherwise, it throws ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult result =
update(
ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main");
assertThat(result.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main"));
}
assertContainsEvent("ERROR /workspace/test/BUILD:2:12: Provider a1p provided twice");
}
/**
* --aspects = a3, a1, a2: aspect a1 requires provider a1p, aspect a2 and aspect a3 provide a1p.
* a1 should see the value provided by a3 because a3 is listed before a1.
*/
@Test
public void testTopLevelAspectOnAspect_requiredProviderProvidedTwicePassed() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a1_result].value + [result]
else:
complete_result = [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['dep'],
required_aspect_providers = [a1p]
)
def _a2_impl(target, ctx):
return [a1p(value = 'a1p_a2_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['dep'],
provides = [a1p],
)
def _a3_impl(target, ctx):
return [a1p(value = 'a1p_a3_val')]
a3 = aspect(
implementation = _a3_impl,
attr_aspects = ['dep'],
provides = [a1p],
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
dep = ':dep_target',
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a1", "test/defs.bzl%a2"),
"//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target sees a1p = a1p_a3_val",
"aspect a1 on target @//test:main sees a1p = a1p_a3_val");
}
@Test
public void testTopLevelAspectOnAspect_requiredProviderNotProvided() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a2p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a1_result].value + [result]
else:
complete_result = [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['dep'],
required_aspect_providers = [a1p]
)
def _a2_impl(target, ctx):
return [a2p(value = 'a2p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['dep'],
provides = [a2p],
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
dep = ':dep_target',
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target cannot see a1p",
"aspect a1 on target @//test:main cannot see a1p");
}
/**
* --aspects = a1, a2: aspect a1 requires provider a1p, aspect a2 provides a1p but it was listed
* after a1 so aspect a1 cannot see a1p value.
*/
@Test
public void testTopLevelAspectOnAspect_requiredProviderProvidedAfterTheAspect() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a1_result].value + [result]
else:
complete_result = [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['dep'],
required_aspect_providers = [a1p]
)
def _a2_impl(target, ctx):
return [a1p(value = 'a1p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['dep'],
provides = [a1p],
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
dep = ':dep_target',
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2"), "//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target cannot see a1p",
"aspect a1 on target @//test:main cannot see a1p");
}
/**
* --aspects = a2, a1: aspect a1 requires provider a1p, aspect a2 provides a1p. But aspect a2
* propagates along different attr_aspects from a1 so a1 cannot get a1p on all dependency targets.
*/
@Test
public void testTopLevelAspectOnAspect_differentAttrAspects() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
complete_result = []
if ctx.rule.attr.dep:
complete_result += ctx.rule.attr.dep[a1_result].value
if ctx.rule.attr.extra_dep:
complete_result += ctx.rule.attr.extra_dep[a1_result].value
complete_result += [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['dep', 'extra_dep'],
required_aspect_providers = [a1p]
)
def _a2_impl(target, ctx):
return [a1p(value = 'a1p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['dep'],
provides = [a1p],
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'dep': attr.label(),
'extra_dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
dep = ':dep_target',
extra_dep = ':extra_dep_target',
)
simple_rule(
name = 'dep_target',
)
simple_rule(
name = 'extra_dep_target',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target sees a1p = a1p_val",
"aspect a1 on target @//test:extra_dep_target cannot see a1p",
"aspect a1 on target @//test:main sees a1p = a1p_val");
}
/**
* --aspects = a2, a1: aspect a1 requires provider a1p, aspect a2 provides a1p. But aspect a2
* propagates along different required_providers from a1 so a1 cannot get a1p on all dependency
* targets.
*/
@Test
public void testTopLevelAspectOnAspect_differentRequiredRuleProviders() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a1_result = provider()
rule_prov_a = provider()
rule_prov_b = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
complete_result = []
if hasattr(ctx.rule.attr, 'deps'):
for dep in ctx.rule.attr.deps:
complete_result += dep[a1_result].value
complete_result += [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['deps'],
required_aspect_providers = [a1p],
required_providers = [[rule_prov_a], [rule_prov_b]],
)
def _a2_impl(target, ctx):
return [a1p(value = 'a1p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['deps'],
provides = [a1p],
required_providers = [rule_prov_a],
)
def _main_rule_impl(ctx):
return [rule_prov_a(), rule_prov_b()]
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'deps': attr.label_list(),
},
)
def _rule_with_prov_a_impl(ctx):
return [rule_prov_a()]
rule_with_prov_a = rule(
implementation = _rule_with_prov_a_impl,
provides = [rule_prov_a]
)
def _rule_with_prov_b_impl(ctx):
return [rule_prov_b()]
rule_with_prov_b = rule(
implementation = _rule_with_prov_b_impl,
provides = [rule_prov_b]
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule', 'rule_with_prov_a', 'rule_with_prov_b')
main_rule(
name = 'main',
deps = [':target_with_prov_a', ':target_with_prov_b'],
)
rule_with_prov_a(
name = 'target_with_prov_a',
)
rule_with_prov_b(
name = 'target_with_prov_b',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:target_with_prov_a sees a1p = a1p_val",
"aspect a1 on target @//test:target_with_prov_b cannot see a1p",
"aspect a1 on target @//test:main sees a1p = a1p_val");
}
/**
* --aspects = a3, a2, a1: both aspects a1 and a2 require provider a3p, aspect a3 provides a3p. a1
* and a2 should be able to read a3p.
*/
@Test
public void testTopLevelAspectOnAspect_providerRequiredByMultipleAspects() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a3p = provider()
a1_result = provider()
a2_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a3p in target:
result += ' sees a3p = {}'.format(target[a3p].value)
else:
result += ' cannot see a3p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a1_result].value + [result]
else:
complete_result = [result]
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['dep'],
required_aspect_providers = [a3p]
)
def _a2_impl(target, ctx):
result = 'aspect a2 on target {}'.format(target.label)
if a3p in target:
result += ' sees a3p = {}'.format(target[a3p].value)
else:
result += ' cannot see a3p'
complete_result = []
if ctx.rule.attr.dep:
complete_result = ctx.rule.attr.dep[a2_result].value + [result]
else:
complete_result = [result]
return [a2_result(value = complete_result)]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['dep'],
required_aspect_providers = [a3p]
)
def _a3_impl(target, ctx):
return [a3p(value = 'a3p_val')]
a3 = aspect(
implementation = _a3_impl,
attr_aspects = ['dep'],
provides = [a3p],
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'dep': attr.label(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
dep = ':dep_target',
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a2 = getConfiguredAspect(configuredAspects, "a2");
assertThat(a2).isNotNull();
StarlarkProvider.Key a2Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a2_result");
StructImpl a2ResultProvider = (StructImpl) a2.get(a2Result);
assertThat((Sequence<?>) a2ResultProvider.getValue("value"))
.containsExactly(
"aspect a2 on target @//test:dep_target sees a3p = a3p_val",
"aspect a2 on target @//test:main sees a3p = a3p_val");
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target sees a3p = a3p_val",
"aspect a1 on target @//test:main sees a3p = a3p_val");
}
/**
* --aspects = a1, a2, a3: aspect a3 requires a1p and a2p, a1 provides a1p and a2 provides a2p.
*
* <p>top level target (main) has two dependencies t1 and t2. Aspects a1 and a3 can propagate to
* t1 and aspects a2 and a3 can propagate to t2. Both t1 and t2 have t0 as dependency, aspect a3
* will run twice on t0 once with aspects path (a1, a3) and the other with (a2, a3).
*/
@Test
public void testTopLevelAspectOnAspect_diamondCase() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a2p = provider()
a3_result = provider()
r1p = provider()
r2p = provider()
def _a1_impl(target, ctx):
return [a1p(value = 'a1p_val')]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['deps'],
required_providers = [r1p],
provides = [a1p]
)
def _a2_impl(target, ctx):
return [a2p(value = 'a2p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['deps'],
required_providers = [r2p],
provides = [a2p]
)
def _a3_impl(target, ctx):
result = 'aspect a3 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
if a2p in target:
result += ' and sees a2p = {}'.format(target[a2p].value)
else:
result += ' and cannot see a2p'
complete_result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
complete_result.extend(dep[a3_result].value)
complete_result.append(result)
return [a3_result(value = complete_result)]
a3 = aspect(
implementation = _a3_impl,
attr_aspects = ['deps'],
required_aspect_providers = [[a1p], [a2p]],
)
def _r0_impl(ctx):
return [r1p(), r2p()]
r0 = rule(
implementation = _r0_impl,
attrs = {
'deps': attr.label_list(),
},
provides = [r1p, r2p]
)
def _r1_impl(ctx):
return [r1p()]
r1 = rule(
implementation = _r1_impl,
attrs = {
'deps': attr.label_list(),
},
provides = [r1p]
)
def _r2_impl(ctx):
return [r2p()]
r2 = rule(
implementation = _r2_impl,
attrs = {
'deps': attr.label_list(),
},
provides = [r2p]
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r0', 'r1', 'r2')
r0(
name = 'main',
deps = [':t1', ':t2'],
)
r1(
name = 't1',
deps = [':t0'],
)
r2(
name = 't2',
deps = [':t0'],
)
r0(
name = 't0',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2", "test/defs.bzl%a3"),
"//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a3 = getConfiguredAspect(configuredAspects, "a3");
assertThat(a3).isNotNull();
StarlarkProvider.Key a3Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a3_result");
StructImpl a3ResultProvider = (StructImpl) a3.get(a3Result);
assertThat((Sequence<?>) a3ResultProvider.getValue("value"))
.containsExactly(
"aspect a3 on target @//test:t0 sees a1p = a1p_val and cannot see a2p",
"aspect a3 on target @//test:t0 cannot see a1p and sees a2p = a2p_val",
"aspect a3 on target @//test:t1 sees a1p = a1p_val and cannot see a2p",
"aspect a3 on target @//test:t2 cannot see a1p and sees a2p = a2p_val",
"aspect a3 on target @//test:main sees a1p = a1p_val and sees a2p = a2p_val");
}
@Test
public void testTopLevelAspectOnAspect_duplicateAspectsNotAllowed() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a2p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a2p in target:
result += ' sees a2p = {}'.format(target[a2p].value)
else:
result += ' cannot see a2p'
complete_result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
complete_result.extend(dep[a1_result].value)
complete_result.append(result)
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['deps'],
required_aspect_providers = [a2p]
)
def _a2_impl(target, ctx):
return [a2p(value = 'a2p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['deps'],
provides = [a2p]
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'deps': attr.label_list(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
deps = [':dep_target'],
)
simple_rule(
name = 'dep_target',
)
""");
reporter.removeHandler(failFastHandler);
// The call to `update` does not throw an exception when "--keep_going" is passed in the
// WithKeepGoing test suite. Otherwise, it throws ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult result =
update(
ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main");
assertThat(result.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main"));
}
assertContainsEvent("aspect //test:defs.bzl%a1 added more than once");
}
/**
* --aspects = a1 requires provider a2p provided by aspect a2. a1 is applied on top level target
* `main` whose rule propagates aspect a2 to its `deps`. So a1 on `main` cannot see a2p but it can
* see a2p on `main` deps.
*/
@Test
public void testTopLevelAspectOnAspect_requiredAspectProviderOnlyAvailableOnDep()
throws Exception {
scratch.file(
"test/defs.bzl",
"""
a2p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a2p in target:
result += ' sees a2p = {}'.format(target[a2p].value)
else:
result += ' cannot see a2p'
complete_result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
complete_result.extend(dep[a1_result].value)
complete_result.append(result)
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['deps'],
required_aspect_providers = [a2p]
)
def _a2_impl(target, ctx):
return [a2p(value = 'a2p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['deps'],
provides = [a2p]
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'deps': attr.label_list(aspects=[a2]),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
deps = [':dep_target'],
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult = update(ImmutableList.of("test/defs.bzl%a1"), "//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target sees a2p = a2p_val",
"aspect a1 on target @//test:main cannot see a2p");
}
@Test
public void testTopLevelAspectOnAspect_multipleTopLevelTargets() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a2p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a2p in target:
result += ' sees a2p = {}'.format(target[a2p].value)
else:
result += ' cannot see a2p'
complete_result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
complete_result.extend(dep[a1_result].value)
complete_result.append(result)
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['deps'],
required_aspect_providers = [a2p],
)
def _a2_impl(target, ctx):
return [a2p(value = 'a2p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['deps'],
provides = [a2p]
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'deps': attr.label_list(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 't1',
)
simple_rule(
name = 't2',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:t2", "//test:t1");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1Ont1 = getConfiguredAspect(configuredAspects, "a1", "t1");
assertThat(a1Ont1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1Ont1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly("aspect a1 on target @//test:t1 sees a2p = a2p_val");
ConfiguredAspect a1Ont2 = getConfiguredAspect(configuredAspects, "a1", "t2");
assertThat(a1Ont2).isNotNull();
a1ResultProvider = (StructImpl) a1Ont2.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly("aspect a1 on target @//test:t2 sees a2p = a2p_val");
}
@Test
public void testTopLevelAspectOnAspect_multipleRequiredProviders() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a2p = provider()
a3p = provider()
a1_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a2p in target:
result += ' sees a2p = {}'.format(target[a2p].value)
else:
result += ' cannot see a2p'
if a3p in target:
result += ' and sees a3p = {}'.format(target[a3p].value)
else:
result += ' and cannot see a3p'
complete_result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
complete_result.extend(dep[a1_result].value)
complete_result.append(result)
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['deps'],
required_aspect_providers = [[a2p], [a3p]],
)
def _a2_impl(target, ctx):
return [a2p(value = 'a2p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['deps'],
provides = [a2p]
)
def _a3_impl(target, ctx):
return [a3p(value = 'a3p_val')]
a3 = aspect(
implementation = _a3_impl,
attr_aspects = ['deps'],
provides = [a3p]
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'deps': attr.label_list(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
deps = [':dep_target'],
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target sees a2p = a2p_val and sees a3p = a3p_val",
"aspect a1 on target @//test:main sees a2p = a2p_val and sees a3p = a3p_val");
}
@Test
public void testTopLevelAspectOnAspect_multipleRequiredProviders2() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a2p = provider()
a3p = provider()
a1_result = provider()
a2_result = provider()
def _a1_impl(target, ctx):
result = 'aspect a1 on target {}'.format(target.label)
if a2p in target:
result += ' sees a2p = {}'.format(target[a2p].value)
else:
result += ' cannot see a2p'
if a3p in target:
result += ' and sees a3p = {}'.format(target[a3p].value)
else:
result += ' and cannot see a3p'
complete_result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
complete_result.extend(dep[a1_result].value)
complete_result.append(result)
return [a1_result(value = complete_result)]
a1 = aspect(
implementation = _a1_impl,
attr_aspects = ['deps'],
required_aspect_providers = [[a2p], [a3p]],
)
def _a2_impl(target, ctx):
result = 'aspect a2 on target {}'.format(target.label)
if a3p in target:
result += ' sees a3p = {}'.format(target[a3p].value)
else:
result += ' cannot see a3p'
complete_result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
complete_result.extend(dep[a2_result].value)
complete_result.append(result)
return [a2_result(value = complete_result), a2p(value = 'a2p_val')]
a2 = aspect(
implementation = _a2_impl,
attr_aspects = ['deps'],
provides = [a2p],
required_aspect_providers = [a3p]
)
def _a3_impl(target, ctx):
return [a3p(value = 'a3p_val')]
a3 = aspect(
implementation = _a3_impl,
attr_aspects = ['deps'],
provides = [a3p]
)
def _simple_rule_impl(ctx):
pass
simple_rule = rule(
implementation = _simple_rule_impl,
attrs = {
'deps': attr.label_list(),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'simple_rule')
simple_rule(
name = 'main',
deps = [':dep_target'],
)
simple_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
"//test:main");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
assertThat(a1).isNotNull();
StarlarkProvider.Key a1Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a1_result");
StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
.containsExactly(
"aspect a1 on target @//test:dep_target sees a2p = a2p_val and sees a3p = a3p_val",
"aspect a1 on target @//test:main sees a2p = a2p_val and sees a3p = a3p_val");
ConfiguredAspect a2 = getConfiguredAspect(configuredAspects, "a2");
assertThat(a2).isNotNull();
StarlarkProvider.Key a2Result =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "a2_result");
StructImpl a2ResultProvider = (StructImpl) a2.get(a2Result);
assertThat((Sequence<?>) a2ResultProvider.getValue("value"))
.containsExactly(
"aspect a2 on target @//test:dep_target sees a3p = a3p_val",
"aspect a2 on target @//test:main sees a3p = a3p_val");
}
/**
* aspects = a1, a2; aspect a1 provides a1p provider and aspect a2 requires a1p provider. These
* top-level aspects are applied on top-level target `main` whose rule also provides a1p.
*
* <p>By default, the dependency between a1 and a2 will be established, the build will fail since
* a2 will receive provider a1p twice (from a1 applied on `main` and from `main` target itself).
*/
@Test
public void testTopLevelAspects_duplicateRuleProviderError() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a1p = provider()
a2p = provider()
def _a1_impl(target, ctx):
return [a1p(value = 'aspect_a1p_val')]
a1 = aspect(
implementation = _a1_impl,
provides = [a1p],
)
def _a2_impl(target, ctx):
result = 'aspect a2 on target {}'.format(target.label)
if a1p in target:
result += ' sees a1p = {}'.format(target[a1p].value)
else:
result += ' cannot see a1p'
return [a2p(value = result)]
a2 = aspect(
implementation = _a2_impl,
provides = [a2p],
required_aspect_providers = [a1p]
)
def _my_rule_impl(ctx):
return [a1p(value = 'rule_a1p_val')]
my_rule = rule(
implementation = _my_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(name = 'main')
""");
reporter.removeHandler(failFastHandler);
// The call to `update` does not throw an exception when "--keep_going" is passed in the
// WithKeepGoing test suite. Otherwise, it throws ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult result =
update(ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2"), "//test:main");
assertThat(result.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() -> update(ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2"), "//test:main"));
}
assertContainsEvent("Provider a1p provided twice");
}
@Test
public void testTopLevelAspectRequiresAspect_stackOfRequiredAspects() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
aspect_c = aspect(implementation = _impl)
aspect_b = aspect(implementation = _impl, requires = [aspect_c])
aspect_a = aspect(implementation = _impl, requires = [aspect_b])
""");
scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
assertThat(configuredAspects).hasSize(3);
assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_c")).isNotNull();
}
@Test
public void testTopLevelAspectRequiresAspect_aspectRequiredByMultipleAspects() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
aspect_c = aspect(implementation = _impl)
aspect_b = aspect(implementation = _impl, requires = [aspect_c])
aspect_a = aspect(implementation = _impl, requires = [aspect_c])
""");
scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
assertThat(configuredAspects).hasSize(3);
assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_c")).isNotNull();
}
@Test
public void testTopLevelAspectRequiresAspect_aspectRequiredByMultipleAspects2() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
aspect_d = aspect(implementation = _impl)
aspect_c = aspect(implementation = _impl, requires = [aspect_d])
aspect_b = aspect(implementation = _impl, requires = [aspect_d])
aspect_a = aspect(implementation = _impl, requires = [aspect_b, aspect_c])
""");
scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
assertThat(configuredAspects).hasSize(4);
assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_c")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_d")).isNotNull();
}
@Test
public void testTopLevelAspectRequiresAspect_requireExistingAspect_passed() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
aspect_b = aspect(implementation = _impl)
aspect_a = aspect(implementation = _impl, requires = [aspect_b])
""");
scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%aspect_b", "test/defs.bzl%aspect_a"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
assertThat(configuredAspects).hasSize(2);
assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
}
@Test
public void testTopLevelAspectRequiresAspect_requireExistingAspect_failed() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
aspect_b = aspect(implementation = _impl)
aspect_a = aspect(implementation = _impl, requires = [aspect_b])
""");
scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
reporter.removeHandler(failFastHandler);
// The call to `update` does not throw an exception when "--keep_going" is passed in the
// WithKeepGoing test suite. Otherwise, it throws ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult result =
update(
ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
"//test:main_target");
assertThat(result.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
"//test:main_target"));
}
assertContainsEvent(
"aspect //test:defs.bzl%aspect_b was added before as a required"
+ " aspect of aspect //test:defs.bzl%aspect_a");
}
@Test
public void testTopLevelAspectRequiresAspect_ruleAttributes() throws Exception {
scratch.file(
"test/defs.bzl",
"""
RequiredAspectProv = provider()
BaseAspectProv = provider()
def _required_aspect_impl(target, ctx):
p_val = ['In required_aspect, p = {} on target {}'
.format(ctx.rule.attr.p, target.label)]
if ctx.rule.attr.dep and RequiredAspectProv in ctx.rule.attr.dep:
p_val += ctx.rule.attr.dep[RequiredAspectProv].p_val
return [RequiredAspectProv(p_val = p_val)]
required_aspect = aspect(
implementation = _required_aspect_impl,
)
def _base_aspect_impl(target, ctx):
p_val = []
p_val += target[RequiredAspectProv].p_val
p_val += ['In base_aspect, p = {} on target {}'.format(ctx.rule.attr.p, target.label)]
if ctx.rule.attr.dep:
p_val += ctx.rule.attr.dep[BaseAspectProv].p_val
return [BaseAspectProv(p_val = p_val)]
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['dep'],
requires = [required_aspect],
)
def _rule_impl(ctx):
pass
my_rule = rule(
implementation = _rule_impl,
attrs = {
'dep': attr.label(),
'p' : attr.string(values = ['main_val', 'dep_val']),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target',
p = 'main_val',
)
my_rule(
name = 'dep_target',
p = 'dep_val',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
// required_aspect can only run on main_target when propagated alone since its attr_aspects is
// empty.
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect requiredAspect = getConfiguredAspect(configuredAspects, "required_aspect");
assertThat(requiredAspect).isNotNull();
StarlarkProvider.Key requiredAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "RequiredAspectProv");
StructImpl requiredAspectProvider = (StructImpl) requiredAspect.get(requiredAspectProv);
assertThat((Sequence<?>) requiredAspectProvider.getValue("p_val"))
.containsExactly("In required_aspect, p = main_val on target @//test:main_target");
// base_aspect can run on main_target and dep_target and it can also see the providers created
// by running required_target on them.
ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
assertThat(baseAspect).isNotNull();
StarlarkProvider.Key baseAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BaseAspectProv");
StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
assertThat((Sequence<?>) baseAspectProvider.getValue("p_val"))
.containsExactly(
"In base_aspect, p = dep_val on target @//test:dep_target",
"In base_aspect, p = main_val on target @//test:main_target",
"In required_aspect, p = dep_val on target @//test:dep_target",
"In required_aspect, p = main_val on target @//test:main_target");
}
@Test
public void testTopLevelAspectRequiresAspect_inheritPropagationAttributes() throws Exception {
// base_aspect propagates over base_dep attribute and requires first_required_aspect which
// propagates over first_dep attribute and requires second_required_aspect which propagates over
// second_dep attribute
scratch.file(
"test/defs.bzl",
"""
BaseAspectProv = provider()
FirstRequiredAspectProv = provider()
SecondRequiredAspectProv = provider()
def _second_required_aspect_impl(target, ctx):
result = []
if getattr(ctx.rule.attr, 'second_dep'):
result += getattr(ctx.rule.attr, 'second_dep')[SecondRequiredAspectProv].result
result += ['second_required_aspect run on target {}'.format(target.label)]
return [SecondRequiredAspectProv(result = result)]
second_required_aspect = aspect(
implementation = _second_required_aspect_impl,
attr_aspects = ['second_dep'],
)
def _first_required_aspect_impl(target, ctx):
result = []
result += target[SecondRequiredAspectProv].result
if getattr(ctx.rule.attr, 'first_dep'):
result += getattr(ctx.rule.attr, 'first_dep')[FirstRequiredAspectProv].result
result += ['first_required_aspect run on target {}'.format(target.label)]
return [FirstRequiredAspectProv(result = result)]
first_required_aspect = aspect(
implementation = _first_required_aspect_impl,
attr_aspects = ['first_dep'],
requires = [second_required_aspect],
)
def _base_aspect_impl(target, ctx):
result = []
result += target[FirstRequiredAspectProv].result
if getattr(ctx.rule.attr, 'base_dep'):
result += getattr(ctx.rule.attr, 'base_dep')[BaseAspectProv].result
result += ['base_aspect run on target {}'.format(target.label)]
return [BaseAspectProv(result = result)]
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['base_dep'],
requires = [first_required_aspect],
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = {
'base_dep': attr.label(),
'first_dep': attr.label(),
'second_dep': attr.label()
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
base_dep = ':base_dep_target',
first_dep = ':first_dep_target',
second_dep = ':second_dep_target',
)
my_rule(
name = 'base_dep_target',
)
my_rule(
name = 'first_dep_target',
)
my_rule(
name = 'second_dep_target',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
// base_aspect should propagate only along its attr_aspects: 'base_dep'
// first_required_aspect should propagate along 'base_dep' and 'first_dep'
// second_required_aspect should propagate along 'base_dep', 'first_dep' and 'second_dep'
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
assertThat(baseAspect).isNotNull();
StarlarkProvider.Key baseAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BaseAspectProv");
StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
assertThat((Sequence<?>) baseAspectProvider.getValue("result"))
.containsExactly(
"second_required_aspect run on target @//test:second_dep_target",
"second_required_aspect run on target @//test:main_target",
"second_required_aspect run on target @//test:first_dep_target",
"second_required_aspect run on target @//test:base_dep_target",
"first_required_aspect run on target @//test:first_dep_target",
"first_required_aspect run on target @//test:main_target",
"first_required_aspect run on target @//test:base_dep_target",
"base_aspect run on target @//test:base_dep_target",
"base_aspect run on target @//test:main_target");
}
@Test
public void testTopLevelAspectRequiresAspect_inheritRequiredProviders() throws Exception {
// aspect_a requires provider Prov_A and requires aspect_b which requires
// provider Prov_B and requires aspect_c which requires provider Prov_C
scratch.file(
"test/defs.bzl",
"""
Prov_A = provider()
Prov_B = provider()
Prov_C = provider()
CollectorProv = provider()
def _aspect_c_impl(target, ctx):
collector_result = ['aspect_c run on target {} and value of Prov_C = {}'
.format(target.label, target[Prov_C].val)]
return [CollectorProv(result = collector_result)]
aspect_c = aspect(
implementation = _aspect_c_impl,
required_providers = [Prov_C],
attr_aspects = ['dep'],
)
def _aspect_b_impl(target, ctx):
collector_result = []
collector_result += ctx.rule.attr.dep[CollectorProv].result
collector_result += ['aspect_b run on target {} and value of Prov_B = {}'
.format(target.label, target[Prov_B].val)]
return [CollectorProv(result = collector_result)]
aspect_b = aspect(
implementation = _aspect_b_impl,
required_providers = [Prov_B],
requires = [aspect_c],
attr_aspects = ['dep'],
)
def _aspect_a_impl(target, ctx):
collector_result = []
collector_result += ctx.rule.attr.dep[CollectorProv].result
collector_result += ['aspect_a run on target {} and value of Prov_A = {}'
.format(target.label, target[Prov_A].val)]
return [CollectorProv(result = collector_result)]
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
required_providers = [Prov_A],
requires = [aspect_b],
)
def _my_rule_impl(ctx):
return [Prov_A(val='main_val_a')]
my_rule = rule(
implementation = _my_rule_impl,
attrs = {
'dep': attr.label(),
},
provides = [Prov_A]
)
def _rule_with_prov_a_impl(ctx):
return [Prov_A(val='val_a')]
rule_with_prov_a = rule(
implementation = _rule_with_prov_a_impl,
attrs = {
'dep': attr.label(),
},
provides = [Prov_A]
)
def _rule_with_prov_b_impl(ctx):
return [Prov_B(val = 'val_b')]
rule_with_prov_b = rule(
implementation = _rule_with_prov_b_impl,
attrs = {
'dep': attr.label(),
},
provides = [Prov_B]
)
def _rule_with_prov_c_impl(ctx):
return [Prov_C(val = 'val_c')]
rule_with_prov_c = rule(
implementation = _rule_with_prov_c_impl,
provides = [Prov_C]
)
""");
scratch.file(
"test/BUILD",
"load('//test:defs.bzl', 'my_rule', 'rule_with_prov_a', 'rule_with_prov_b',"
+ " 'rule_with_prov_c')",
"my_rule(",
" name = 'main_target',",
" dep = ':target_with_prov_a',",
")",
"rule_with_prov_a(",
" name = 'target_with_prov_a',",
" dep = ':target_with_prov_b'",
")",
"rule_with_prov_b(",
" name = 'target_with_prov_b',",
" dep = ':target_with_prov_c'",
")",
"rule_with_prov_c(",
" name = 'target_with_prov_c'",
")");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
// aspect_a should run on main_target and target_with_prov_a
// aspect_b can reach target_with_prov_b because it inherits the required_providers of aspect_a
// aspect_c can reach target_with_prov_c because it inherits the required_providers of aspect_a
// and aspect_b
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkProvider.Key collectorProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "CollectorProv");
StructImpl collectorProvider = (StructImpl) aspectA.get(collectorProv);
assertThat((Sequence<?>) collectorProvider.getValue("result"))
.containsExactly(
"aspect_c run on target @//test:target_with_prov_c and value of Prov_C = val_c",
"aspect_b run on target @//test:target_with_prov_b and value of Prov_B = val_b",
"aspect_a run on target @//test:target_with_prov_a and value of Prov_A = val_a",
"aspect_a run on target @//test:main_target and value of Prov_A = main_val_a")
.inOrder();
}
@Test
public void testTopLevelAspectRequiresAspect_inspectRequiredAspectActions() throws Exception {
scratch.file(
"test/defs.bzl",
"""
BaseAspectProvider = provider()
def _required_aspect_impl(target, ctx):
f = ctx.actions.declare_file('dummy.txt')
ctx.actions.run_shell(outputs = [f], command='echo xxx > $(location f)',
mnemonic='RequiredAspectAction')
return []
required_aspect = aspect(
implementation = _required_aspect_impl,
)
def _base_aspect_impl(target, ctx):
required_aspect_action = None
for action in target.actions:
if action.mnemonic == 'RequiredAspectAction':
required_aspect_action = action
if required_aspect_action:
return [BaseAspectProvider(result = 'base_aspect can see required_aspect action')]
else:
return [BaseAspectProvider(result = 'base_aspect cannot see required_aspect action')]
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['dep'],
requires = [required_aspect]
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
assertThat(baseAspect).isNotNull();
StarlarkProvider.Key baseAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BaseAspectProvider");
StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
assertThat(baseAspectProvider.getValue("result"))
.isEqualTo("base_aspect can see required_aspect action");
}
@Test
public void testTopLevelAspectRequiresAspect_inspectRequiredAspectGeneratedFiles()
throws Exception {
scratch.file(
"test/defs.bzl",
"""
BaseAspectProvider = provider()
def _required_aspect_impl(target, ctx):
file = ctx.actions.declare_file('required_aspect_file')
ctx.actions.write(file, 'data')
return [OutputGroupInfo(out = [file])]
required_aspect = aspect(
implementation = _required_aspect_impl,
)
def _base_aspect_impl(target, ctx):
files = ['base_aspect can see file ' + f.path.split('/')[-1]
for f in target[OutputGroupInfo].out.to_list()]
return [BaseAspectProvider(my_files = files)]
base_aspect = aspect(
implementation = _base_aspect_impl,
attr_aspects = ['dep'],
requires = [required_aspect]
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
assertThat(baseAspect).isNotNull();
StarlarkProvider.Key baseAspectProv =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BaseAspectProvider");
StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
assertThat((Sequence<?>) baseAspectProvider.getValue("my_files"))
.containsExactly("base_aspect can see file required_aspect_file");
}
@Test
public void testTopLevelAspectRequiresAspect_withRequiredAspectProvidersSatisfied()
throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
prov_b_forwarded = provider()
def _aspect_b_impl(target, ctx):
result = 'aspect_b on target {} '.format(target.label)
if prov_b in target:
result += 'found prov_b = {}'.format(target[prov_b].val)
return struct(aspect_b_result = result,
providers = [prov_b_forwarded(val = target[prov_b].val)])
else:
result += 'cannot find prov_b'
return struct(aspect_b_result = result)
aspect_b = aspect(
implementation = _aspect_b_impl,
required_aspect_providers = [prov_b]
)
def _aspect_a_impl(target, ctx):
result = 'aspect_a on target {} '.format(target.label)
if prov_a in target:
result += 'found prov_a = {}'.format(target[prov_a].val)
else:
result += 'cannot find prov_a'
if prov_b_forwarded in target:
result += ' and found prov_b = {}'.format(target[prov_b_forwarded].val)
else:
result += ' but cannot find prov_b'
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
required_aspect_providers = [prov_a],
attr_aspects = ['dep'],
requires = [aspect_b]
)
def _aspect_with_prov_a_impl(target, ctx):
return [prov_a(val = 'a1')]
aspect_with_prov_a = aspect(
implementation = _aspect_with_prov_a_impl,
provides = [prov_a],
attr_aspects = ['dep'],
)
def _aspect_with_prov_b_impl(target, ctx):
return [prov_b(val = 'b1')]
aspect_with_prov_b = aspect(
implementation = _aspect_with_prov_b_impl,
provides = [prov_b],
attr_aspects = ['dep'],
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of(
"test/defs.bzl%aspect_with_prov_a",
"test/defs.bzl%aspect_with_prov_b", "test/defs.bzl%aspect_a"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
String aspectAResult = (String) aspectA.get("aspect_a_result");
assertThat(aspectAResult)
.isEqualTo(
"aspect_a on target @//test:main_target found prov_a = a1 and found prov_b = b1");
ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
assertThat(aspectB).isNotNull();
String aspectBResult = (String) aspectB.get("aspect_b_result");
assertThat(aspectBResult).isEqualTo("aspect_b on target @//test:main_target found prov_b = b1");
}
@Test
public void testTopLevelAspectRequiresAspect_withRequiredAspectProvidersNotFound()
throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov_a = provider()
prov_b = provider()
def _aspect_b_impl(target, ctx):
result = 'aspect_b on target {} '.format(target.label)
if prov_b in target:
result += 'found prov_b = {}'.format(target[prov_b].val)
else:
result += 'cannot find prov_b'
return struct(aspect_b_result = result)
aspect_b = aspect(
implementation = _aspect_b_impl,
required_aspect_providers = [prov_b]
)
def _aspect_a_impl(target, ctx):
result = 'aspect_a on target {} '.format(target.label)
if prov_a in target:
result += 'found prov_a = {}'.format(target[prov_a].val)
else:
result += 'cannot find prov_a'
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
required_aspect_providers = [prov_a],
attr_aspects = ['dep'],
requires = [aspect_b]
)
def _aspect_with_prov_a_impl(target, ctx):
return [prov_a(val = 'a1')]
aspect_with_prov_a = aspect(
implementation = _aspect_with_prov_a_impl,
provides = [prov_a],
attr_aspects = ['dep'],
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("test/defs.bzl%aspect_with_prov_a", "test/defs.bzl%aspect_a"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
String aspectAResult = (String) aspectA.get("aspect_a_result");
assertThat(aspectAResult).isEqualTo("aspect_a on target @//test:main_target found prov_a = a1");
ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
assertThat(aspectB).isNotNull();
String aspectBResult = (String) aspectB.get("aspect_b_result");
assertThat(aspectBResult)
.isEqualTo("aspect_b on target @//test:main_target cannot find prov_b");
}
@Test
public void testDependentAspectWithNonExecutableTool_doesNotCrash() throws Exception {
scratch.file(
"test/BUILD",
"""
sh_binary(name='bin', srcs=['bin.sh'])
sh_library(name='lib')
""");
scratch.file(
"test/defs.bzl",
"AInfo = provider()",
"BInfo = provider()",
"def _aspect_a(target, ctx):",
" return [AInfo(value=str(ctx.attr._attr.label))]",
"aspect_a = aspect(",
" implementation = _aspect_a,",
" provides=[AInfo],",
" attrs = {'_attr':" + " attr.label(default=':lib')},",
")",
"def _aspect_b(target, ctx):",
" return [BInfo(value=str(ctx.executable._attr.path.split('/')[-1]))]",
"aspect_b = aspect(",
" implementation = _aspect_b,",
" required_aspect_providers = [AInfo],",
" attrs = {'_attr': attr.label(default=':bin', executable=True, cfg='exec')},",
")");
scratch.file("test/bin.sh").setExecutable(true);
AnalysisResult result =
update(ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"), "//test:bin");
ConfiguredAspect aspectB =
result.getAspectsMap().entrySet().stream()
.filter(a -> a.getKey().getAspectName().endsWith("aspect_b"))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
assertThat(aspectB).isNotNull();
StarlarkProvider.Key provB =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "BInfo");
assertThat(((StructImpl) aspectB.get(provB)).getValue("value")).isEqualTo("bin");
ConfiguredAspect aspectA =
result.getAspectsMap().entrySet().stream()
.filter(a -> a.getKey().getAspectName().endsWith("aspect_a"))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
assertThat(aspectA).isNotNull();
StarlarkProvider.Key provA =
new StarlarkProvider.Key(Label.parseCanonical("//test:defs.bzl"), "AInfo");
assertThat(((StructImpl) aspectA.get(provA)).getValue("value")).isEqualTo("@//test:lib");
}
@Test
public void testTopLevelAspectsWithParameters() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p1 = {} and a_p = {}'.
format(target.label, ctx.attr.p1, ctx.attr.a_p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2']),
'a_p' : attr.string(values = ['a_p_v1', 'a_p_v2'])},
)
def _aspect_b_impl(target, ctx):
result = ['aspect_b on target {}, p1 = {} and b_p = {}'.
format(target.label, ctx.attr.p1, ctx.attr.b_p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_b_result
return struct(aspect_b_result = result)
aspect_b = aspect(
implementation = _aspect_b_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2']),
'b_p' : attr.string(values = ['b_p_v1', 'b_p_v2'])},
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a", "//test:defs.bzl%aspect_b"),
ImmutableMap.of("p1", "p1_v1", "a_p", "a_p_v1", "b_p", "b_p_v1"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p1 = p1_v1 and a_p = a_p_v1",
"aspect_a on target @//test:dep_target_1, p1 = p1_v1 and a_p = a_p_v1",
"aspect_a on target @//test:dep_target_2, p1 = p1_v1 and a_p = a_p_v1");
ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
assertThat(aspectB).isNotNull();
StarlarkList<?> aspectBResult = (StarlarkList) aspectB.get("aspect_b_result");
assertThat(Starlark.toIterable(aspectBResult))
.containsExactly(
"aspect_b on target @//test:main_target, p1 = p1_v1 and b_p = b_p_v1",
"aspect_b on target @//test:dep_target_1, p1 = p1_v1 and b_p = b_p_v1",
"aspect_b on target @//test:dep_target_2, p1 = p1_v1 and b_p = b_p_v1");
}
@Test
public void testTopLevelAspectsWithParameters_differentAllowedValues() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p1 = {} and p2 = {}'.
format(target.label, ctx.attr.p1, ctx.attr.p2)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2']) },
)
def _aspect_b_impl(target, ctx):
result = ['aspect_b on target {}, p1 = {} and p2 = {}'.
format(target.label, ctx.attr.p1, ctx.attr.p2)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_b_result
return struct(aspect_b_result = result)
aspect_b = aspect(
implementation = _aspect_b_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v2', 'p1_v3']) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a", "//test:defs.bzl%aspect_b"),
ImmutableMap.of("p1", "p1_v1"),
"//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("//test:defs.bzl%aspect_a", "//test:defs.bzl%aspect_b"),
ImmutableMap.of("p1", "p1_v1"),
"//test:main_target"));
}
assertContainsEvent(
"//test:defs.bzl%aspect_b: invalid value in 'p1' attribute: has to be one of 'p1_v2' or"
+ " 'p1_v3' instead of 'p1_v1'");
}
@Test
public void testTopLevelAspectsWithParameters_useDefaultValue() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p1 = {} and p2 = {}'.
format(target.label, ctx.attr.p1, ctx.attr.p2)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2'], default = 'p1_v1'),
'p2' : attr.string(values = ['p2_v1', 'p2_v2'], default = 'p2_v1')},
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p1", "p1_v2"),
"//test:main_target");
Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p1 = p1_v2 and p2 = p2_v1",
"aspect_a on target @//test:dep_target_1, p1 = p1_v2 and p2 = p2_v1",
"aspect_a on target @//test:dep_target_2, p1 = p1_v2 and p2 = p2_v1");
}
@Test
public void testTopLevelAspectsWithParameters_passParametersToRequiredAspect() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_b_impl(target, ctx):
result = ['aspect_b on target {}, p1 = {} and p3 = {}'.
format(target.label, ctx.attr.p1, ctx.attr.p3)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_b_result
return struct(aspect_b_result = result)
aspect_b = aspect(
implementation = _aspect_b_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2']),
'p3' : attr.string(values = ['p3_v1', 'p3_v2', 'p3_v3'])},
)
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p1 = {} and p2 = {}'.
format(target.label, ctx.attr.p1, ctx.attr.p2)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2']),
'p2' : attr.string(values = ['p2_v1', 'p2_v2'])},
requires = [aspect_b],
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p1", "p1_v1", "p2", "p2_v2", "p3", "p3_v3"),
"//test:main_target");
ImmutableMap<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p1 = p1_v1 and p2 = p2_v2",
"aspect_a on target @//test:dep_target_1, p1 = p1_v1 and p2 = p2_v2",
"aspect_a on target @//test:dep_target_2, p1 = p1_v1 and p2 = p2_v2");
ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
assertThat(aspectB).isNotNull();
StarlarkList<?> aspectBResult = (StarlarkList) aspectB.get("aspect_b_result");
assertThat(Starlark.toIterable(aspectBResult))
.containsExactly(
"aspect_b on target @//test:main_target, p1 = p1_v1 and p3 = p3_v3",
"aspect_b on target @//test:dep_target_1, p1 = p1_v1 and p3 = p3_v3",
"aspect_b on target @//test:dep_target_2, p1 = p1_v1 and p3 = p3_v3");
}
@Test
public void testTopLevelAspectsWithParameters_invalidParameterValue() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.string(values = ['p_v1', 'p_v2']) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "p_v"),
"//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "p_v"),
"//test:main_target"));
}
assertContainsEvent(
"//test:defs.bzl%aspect_a: invalid value in 'p' attribute: has to be one of 'p_v1' or"
+ " 'p_v2' instead of 'p_v'");
}
@Test
public void testTopLevelAspectsWithParameters_missingMandatoryParameter() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p1 = {}'.
format(target.label, ctx.attr.p1)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(mandatory = True, default = 'p1_v1',
values = ['p1_v1', 'p1_v2']),
'p2' : attr.string(values = ['p2_v1', 'p2_v2'])},
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p2", "p2_v1"),
"//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p2", "p2_v1"),
"//test:main_target"));
}
assertContainsEvent("Missing mandatory attribute 'p1' for aspect '//test:defs.bzl%aspect_a'");
}
@Test
public void testTopLevelAspectsWithParameters_unusedParameter() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p1 = {} and a_p = {}'.
format(target.label, ctx.attr.p1, ctx.attr.a_p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2']),
'a_p' : attr.string(values = ['a_p_v1', 'a_p_v2'])},
)
def _aspect_b_impl(target, ctx):
result = ['aspect_b on target {}, p1 = {} and b_p = {}'.
format(target.label, ctx.attr.p1, ctx.attr.b_p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_b_result
return struct(aspect_b_result = result)
aspect_b = aspect(
implementation = _aspect_b_impl,
attr_aspects = ['dep'],
attrs = { 'p1' : attr.string(values = ['p1_v1', 'p1_v2']),
'b_p' : attr.string(values = ['b_p_v1', 'b_p_v2'])},
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a", "//test:defs.bzl%aspect_b"),
ImmutableMap.of("p2", "p2_v1", "b_p", "b_p_v1"),
"//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("//test:defs.bzl%aspect_a", "//test:defs.bzl%aspect_b"),
ImmutableMap.of("p2", "p2_v1", "b_p", "b_p_v1"),
"//test:main_target"));
}
assertContainsEvent(
"Parameters '[p2]' are not parameters of any of the top-level aspects but they are"
+ " specified in --aspects_parameters.");
}
@Test
public void testTopLevelAspectsWithParameters_invalidDefaultParameterValue() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.string(values = ['p_v1', 'p_v2']) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult analysisResult =
update(ImmutableList.of("//test:defs.bzl%aspect_a"), "//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() -> update(ImmutableList.of("//test:defs.bzl%aspect_a"), "//test:main_target"));
}
assertContainsEvent(
"//test:defs.bzl%aspect_a: invalid value in 'p' attribute: has to be one of 'p_v1' or"
+ " 'p_v2' instead of ''");
}
@Test
public void testTopLevelAspectsWithParameters_noNeedForAllowedValues() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.string(default='val') },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "p_v"),
"//test:main_target");
ImmutableMap<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p = p_v",
"aspect_a on target @//test:dep_target_1, p = p_v",
"aspect_a on target @//test:dep_target_2, p = p_v");
}
/**
* Aspect parameter has to require set of values only if the aspect is used in a rule attribute.
*/
@Test
public void testAttrAspectParameterMissingRequiredValues() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
pass
my_aspect = aspect(_impl,
attrs = { 'param' : attr.string(default = 'c') }
)
def _rule_impl(ctx):
pass
r1 = rule(_rule_impl, attrs={'dep': attr.label(aspects = [my_aspect])})
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 'main_target')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update("//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update("//test:main_target"));
}
assertContainsEvent(
"Aspect //test:defs.bzl%my_aspect: Aspect parameter attribute 'param' must use the 'values'"
+ " restriction.");
}
/**
* Aspect parameter has to require set of values only if the aspect is used in a rule attribute.
*/
@Test
public void testAttrRequiredAspectParameterMissingRequiredValues() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
pass
required_aspect = aspect(_impl,
attrs = { 'p1' : attr.string(default = 'b') }
)
my_aspect = aspect(_impl,
attrs = { 'p2' : attr.string(default = 'c', values = ['c']) },
requires = [required_aspect],
)
def _rule_impl(ctx):
pass
r1 = rule(_rule_impl, attrs={'dep': attr.label(aspects = [my_aspect])})
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 'main_target')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update("//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update("//test:main_target"));
}
assertContainsEvent(
"Aspect //test:defs.bzl%required_aspect: Aspect parameter attribute 'p1' must use the"
+ " 'values' restriction.");
}
@Test
public void integerAspectParameter_mandatoryAttrNotCoveredByRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.int(default = 1, values = [1, 2], mandatory = True) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type int.");
}
@Test
public void integerAspectParameter_mandatoryAttrWithWrongTypeInRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.int(default = 1, values = [1, 2], mandatory = True) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]),
'my_attr': attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type int.");
}
@Test
public void integerAspectParameter_attrWithoutDefaultNotCoveredByRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.int(values = [1, 2]) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type int.");
}
@Test
public void integerAspectParameter_attrWithoutDefaultWrongTypeInRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.int(values = [1, 2]) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]),
'my_attr': attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type int.");
}
@Test
public void integerAspectParameter_missingValuesRestriction() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.int() },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]),
'my_attr' : attr.int() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered: Aspect parameter attribute 'my_attr' must use"
+ " the 'values' restriction.");
}
@Test
public void integerAspectParameter_invalidDefault() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.int(default = 2, values = [0, 1]) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect parameter attribute 'my_attr' has a bad default value: has to be one of '0' or '1'"
+ " instead of '2'");
}
@Test
public void aspectIntegerParameter_withDefaultValue() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
result = ['my_aspect on target {}, my_attr = {}'.
format(target.label, ctx.attr.my_attr)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep[AspectInfo].my_aspect_result
return AspectInfo(my_aspect_result = result)
RuleInfo = provider()
def _rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(my_rule_result = ctx.attr.dep[AspectInfo].my_aspect_result)
pass
MyAspect = aspect(
implementation = _aspect_impl,
attrs = { 'my_attr' : attr.int(default = 1, values = [1, 2, 3]) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'dep' : attr.label(aspects=[MyAspect]) }
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'main_target',
dep = ':dep_target',
)
my_rule(name = 'dep_target')
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main_target");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> ruleResult =
(StarlarkList) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("my_rule_result");
assertThat(Starlark.toIterable(ruleResult))
.containsExactly("my_aspect on target @//test:dep_target, my_attr = 1");
}
@Test
public void aspectIntegerParameter_valueOverwrittenByRuleDefault() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
result = ['my_aspect on target {}, my_attr = {}'.
format(target.label, ctx.attr.my_attr)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.my_aspect_result
return [AspectInfo(my_aspect_result = result)]
RuleInfo = provider()
def _rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(my_rule_result = ctx.attr.dep[AspectInfo].my_aspect_result)
pass
MyAspect = aspect(
implementation = _aspect_impl,
attrs = { 'my_attr' : attr.int(default = 1, values = [1, 2, 3]) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'dep' : attr.label(aspects=[MyAspect]),
'my_attr': attr.int(default = 2) }
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'main_target',
dep = ':dep_target',
)
my_rule(name = 'dep_target')
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main_target");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> ruleResult =
(StarlarkList) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("my_rule_result");
assertThat(Starlark.toIterable(ruleResult))
.containsExactly("my_aspect on target @//test:dep_target, my_attr = 2");
}
@Test
public void aspectIntegerParameter_valueOverwrittenByTargetValue() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
result = ['my_aspect on target {}, my_attr = {}'.
format(target.label, ctx.attr.my_attr)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep[AspectInfo].my_aspect_result
return AspectInfo(my_aspect_result = result)
RuleInfo = provider()
def _rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(my_rule_result = ctx.attr.dep[AspectInfo].my_aspect_result)
pass
MyAspect = aspect(
implementation = _aspect_impl,
attrs = { 'my_attr' : attr.int(default = 1, values = [1, 2, 3]) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'dep' : attr.label(aspects=[MyAspect]),
'my_attr': attr.int(default = 2) }
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'main_target',
dep = ':dep_target',
my_attr = 3,
)
my_rule(name = 'dep_target')
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main_target");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> ruleResult =
(StarlarkList) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("my_rule_result");
assertThat(Starlark.toIterable(ruleResult))
.containsExactly("my_aspect on target @//test:dep_target, my_attr = 3");
}
@Test
public void testTopLevelAspectsWithParameters_invalidIntegerParameterValue() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.int(values = [1, 2]) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "3"),
"//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "3"),
"//test:main_target"));
}
assertContainsEvent(
"//test:defs.bzl%aspect_a: invalid value in 'p' attribute: has to be one of '1' or"
+ " '2' instead of '3'");
}
@Test
public void testTopLevelAspectsWithIntegerParameter() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.int(values = [1, 2, 3]) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "2"),
"//test:main_target");
ImmutableMap<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p = 2",
"aspect_a on target @//test:dep_target_1, p = 2",
"aspect_a on target @//test:dep_target_2, p = 2");
}
@Test
public void testTopLevelAspectsWithIntegerParameter_useDefaultValue() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.int(default = 1, values = [1, 2, 3]) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("//test:defs.bzl%aspect_a"), "//test:main_target");
ImmutableMap<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p = 1",
"aspect_a on target @//test:dep_target_1, p = 1",
"aspect_a on target @//test:dep_target_2, p = 1");
}
@Test
public void booleanAspectParameter_mandatoryAttrNotCoveredByRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.bool(default = True, mandatory = True) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type boolean.");
}
@Test
public void booleanAspectParameter_mandatoryAttrWithWrongTypeInRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.bool(default = True, mandatory = True) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]),
'my_attr': attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type boolean.");
}
@Test
public void booleanAspectParameter_attrWithoutDefaultNotCoveredByRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.bool() },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type boolean.");
}
@Test
public void booleanAspectParameter_attrWithoutDefaultWrongTypeInRule() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
def _impl(target, ctx):
return []
def _rule_impl(ctx):
return []
MyAspectUncovered = aspect(
implementation=_impl,
attrs = { 'my_attr' : attr.bool() },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'deps' : attr.label_list(aspects=[MyAspectUncovered]),
'my_attr': attr.string() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name ='main')
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update(ImmutableList.of(), "//test:main"));
}
assertContainsEvent(
"Aspect //test:aspect.bzl%MyAspectUncovered requires rule my_rule to specify attribute "
+ "'my_attr' with type boolean.");
}
@Test
public void aspectBooleanParameter_withDefaultValue() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
result = ['my_aspect on target {}, my_attr = {}'.
format(target.label, ctx.attr.my_attr)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.my_aspect_result
return AspectInfo(my_aspect_result = result)
RuleInfo = provider()
def _rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(my_rule_result = ctx.attr.dep[AspectInfo].my_aspect_result)
pass
MyAspect = aspect(
implementation = _aspect_impl,
attrs = { 'my_attr' : attr.bool(default = True) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'dep' : attr.label(aspects=[MyAspect]) }
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'main_target',
dep = ':dep_target',
)
my_rule(name = 'dep_target')
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main_target");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> ruleResult =
getStarlarkProvider(configuredTarget, "RuleInfo")
.getValue("my_rule_result", StarlarkList.class);
assertThat(Starlark.toIterable(ruleResult))
.containsExactly("my_aspect on target @//test:dep_target, my_attr = True");
}
@Test
public void aspectBooleanParameter_valueOverwrittenByRuleDefault() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
result = ['my_aspect on target {}, my_attr = {}'.
format(target.label, ctx.attr.my_attr)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep[AspectInfo].my_aspect_result
return AspectInfo(my_aspect_result = result)
RuleInfo = provider()
def _rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(my_rule_result = ctx.attr.dep[AspectInfo].my_aspect_result)
pass
MyAspect = aspect(
implementation = _aspect_impl,
attrs = { 'my_attr' : attr.bool(default = True) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'dep' : attr.label(aspects=[MyAspect]),
'my_attr': attr.bool(default = False) }
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'main_target',
dep = ':dep_target',
)
my_rule(name = 'dep_target')
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main_target");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> ruleResult =
(StarlarkList) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("my_rule_result");
assertThat(Starlark.toIterable(ruleResult))
.containsExactly("my_aspect on target @//test:dep_target, my_attr = False");
}
@Test
public void aspectBooleanParameter_valueOverwrittenByTargetValue() throws Exception {
scratch.file(
"test/aspect.bzl",
"""
AspectInfo = provider()
def _aspect_impl(target, ctx):
result = ['my_aspect on target {}, my_attr = {}'.
format(target.label, ctx.attr.my_attr)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep[AspectInfo].my_aspect_result
return AspectInfo(my_aspect_result = result)
RuleInfo = provider()
def _rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(my_rule_result = ctx.attr.dep[AspectInfo].my_aspect_result)
pass
MyAspect = aspect(
implementation = _aspect_impl,
attrs = { 'my_attr' : attr.bool(default = True) },
)
my_rule = rule(
implementation=_rule_impl,
attrs = { 'dep' : attr.label(aspects=[MyAspect]),
'my_attr': attr.bool(default = True) }
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:aspect.bzl', 'my_rule')
my_rule(name = 'main_target',
dep = ':dep_target',
my_attr = False,
)
my_rule(name = 'dep_target')
""");
AnalysisResult analysisResult = update(ImmutableList.of(), "//test:main_target");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> ruleResult =
(StarlarkList) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("my_rule_result");
assertThat(Starlark.toIterable(ruleResult))
.containsExactly("my_aspect on target @//test:dep_target, my_attr = False");
}
@Test
public void testTopLevelAspectsWithBooleanParameter() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.bool() },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "y"),
"//test:main_target");
ImmutableMap<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p = True",
"aspect_a on target @//test:dep_target_1, p = True",
"aspect_a on target @//test:dep_target_2, p = True");
}
@Test
public void testTopLevelAspectsWithBooleanParameter_useDefaultValue() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.bool(default = False) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
AnalysisResult analysisResult =
update(ImmutableList.of("//test:defs.bzl%aspect_a"), "//test:main_target");
ImmutableMap<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
assertThat(aspectA).isNotNull();
StarlarkList<?> aspectAResult = (StarlarkList) aspectA.get("aspect_a_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect_a on target @//test:main_target, p = False",
"aspect_a on target @//test:dep_target_1, p = False",
"aspect_a on target @//test:dep_target_2, p = False");
}
@Test
public void testTopLevelAspectsWithBooleanParameter_invalidValue() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.bool() },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label() },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a ViewCreationFailedException.
if (keepGoing()) {
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "x"),
"//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(
ViewCreationFailedException.class,
() ->
update(
ImmutableList.of("//test:defs.bzl%aspect_a"),
ImmutableMap.of("p", "x"),
"//test:main_target"));
}
assertContainsEvent(
"//test:defs.bzl%aspect_a: expected value of type 'bool' for attribute 'p' but got 'x'");
}
@Test
public void testRuleAspectWithMandatoryParameterNotProvided() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.string(default = 'p_v', values = ['p_v'], mandatory = True) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label(aspects = [aspect_a]) },
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update("//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update("//test:main_target"));
}
assertContainsEvent(
"Aspect //test:defs.bzl%aspect_a requires rule my_rule to specify attribute 'p' with type"
+ " string");
}
@Test
public void testRuleAspectWithMandatoryParameterProvidedWrongType() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _aspect_a_impl(target, ctx):
result = ['aspect_a on target {}, p = {}'.
format(target.label, ctx.attr.p)]
if ctx.rule.attr.dep:
result += ctx.rule.attr.dep.aspect_a_result
return struct(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.string(default = 'p_v', values = ['p_v'], mandatory = True) },
)
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = { 'dep' : attr.label(aspects = [aspect_a]),
'p': attr.int() }
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'my_rule')
my_rule(
name = 'main_target',
dep = ':dep_target_1',
)
my_rule(
name = 'dep_target_1',
dep = ':dep_target_2',
)
my_rule(
name = 'dep_target_2',
)
""");
reporter.removeHandler(failFastHandler);
// This call succeeds if "--keep_going" was passed, which it does in the WithKeepGoing test
// suite. Otherwise, it fails and throws a TargetParsingException.
if (keepGoing()) {
AnalysisResult analysisResult = update("//test:main_target");
assertThat(analysisResult.hasError()).isTrue();
} else {
assertThrows(TargetParsingException.class, () -> update("//test:main_target"));
}
assertContainsEvent(
"Aspect //test:defs.bzl%aspect_a requires rule my_rule to specify attribute 'p' with type"
+ " string");
}
@Test
public void testRuleAspectWithMandatoryParameter_useRuleDefault() throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectInfo = provider()
def _aspect_a_impl(target, ctx):
result = 'aspect_a on target {}, p = {}'.format(target.label, ctx.attr.p)
return AspectInfo(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.string(default = 'p_v1', values = ['p_v1', 'p_v2'],
mandatory = True) },
)
RuleInfo = provider()
def _main_rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(aspect_a_result = ctx.attr.dep[AspectInfo].aspect_a_result)
pass
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects = [aspect_a]),
'p' : attr.string(default = 'p_v2'),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule')
main_rule(
name = 'main',
dep = ':dep_target',
)
main_rule(
name = 'dep_target',
)
""");
AnalysisResult analysisResult = update("//test:main");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
String aspectAResult =
getStarlarkProvider(configuredTarget, "RuleInfo").getValue("aspect_a_result", String.class);
assertThat(aspectAResult).isEqualTo("aspect_a on target @//test:dep_target, p = p_v2");
}
@Test
public void testRuleAspectWithMandatoryParameterProvided() throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectInfo = provider()
def _aspect_a_impl(target, ctx):
result = 'aspect_a on target {}, p = {}'.format(target.label, ctx.attr.p)
return AspectInfo(aspect_a_result = result)
aspect_a = aspect(
implementation = _aspect_a_impl,
attr_aspects = ['dep'],
attrs = { 'p' : attr.string(default = 'p_v2', values = ['p_v1', 'p_v2'],
mandatory = True) },
)
RuleInfo = provider()
def _main_rule_impl(ctx):
if ctx.attr.dep:
return RuleInfo(aspect_a_result = ctx.attr.dep[AspectInfo].aspect_a_result)
pass
main_rule = rule(
implementation = _main_rule_impl,
attrs = {
'dep': attr.label(aspects = [aspect_a]),
'p' : attr.string(mandatory = True),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'main_rule')
main_rule(
name = 'main',
dep = ':dep_target',
p = 'p_v1',
)
main_rule(
name = 'dep_target',
p = 'p_v2',
)
""");
AnalysisResult analysisResult = update("//test:main");
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
String aspectAResult =
(String) getStarlarkProvider(configuredTarget, "RuleInfo").getValue("aspect_a_result");
assertThat(aspectAResult).isEqualTo("aspect_a on target @//test:dep_target, p = p_v1");
}
@Test
public void testAspectLabelIsRepoMapped() throws Exception {
scratch.appendFile("WORKSPACE", "workspace(name = 'my_repo')");
scratch.file(
"test/aspect.bzl",
"""
load(':rule.bzl', 'MyInfo')
def _impl(target, ctx):
if MyInfo not in target:
fail('Provider identity mismatch')
return []
MyAspect = aspect(implementation=_impl)
""");
scratch.file(
"test/rule.bzl",
"""
MyInfo = provider()
def _impl(ctx):
return [MyInfo()]
my_rule = rule(implementation=_impl)
""");
scratch.file(
"test/BUILD",
"""
load(':rule.bzl', 'my_rule')
my_rule(name = 'target')
""");
AnalysisResult result =
update(ImmutableList.of("@my_repo//test:aspect.bzl%MyAspect"), "//test:target");
assertThat(result.hasError()).isFalse();
}
@Test
public void testAspectKeyCreatedOnlyOnceForSameBaseKeysInDiffOrder() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a_provider = provider()
b_provider = provider()
c_provider = provider()
RuleInfo = provider()
def _a_impl(target, ctx):
result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
result.extend(dep[a_provider].value)
result.append('aspect a on target {} aspect_ids {}'.format(target.label,
ctx.aspect_ids))
return [a_provider(value = result)]
a = aspect(
implementation = _a_impl,
attr_aspects = ['deps'],
required_aspect_providers = [[b_provider], [c_provider]],
)
def _b_impl(target, ctx):
return [b_provider(value = ['aspect b on target {}'.format(target.label)])]
b = aspect(
implementation = _b_impl,
attr_aspects = ['deps'],
provides = [b_provider],
)
def _c_impl(target, ctx):
return [c_provider(value = ['aspect c on target {}'.format(target.label)])]
c = aspect(
implementation = _c_impl,
attr_aspects = ['deps'],
provides = [c_provider]
)
def _r1_impl(ctx):
result = []
if ctx.attr.deps:
for dep in ctx.attr.deps:
result.extend(dep[a_provider].value)
return RuleInfo(aspect_a_collected_result = result)
r1 = rule(
implementation = _r1_impl,
attrs = {
'deps': attr.label_list(aspects = [c, b, a]),
},
)
def _r2_impl(ctx):
pass
r2 = rule(
implementation = _r2_impl,
attrs = {
'deps': attr.label_list(aspects = [b]),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1', 'r2')
r1(
name = 't1',
# base_keys of aspect a on t3 are [c, b]
deps = [':t2', ':t3'],
)
r2(
name = 't2',
# aspects reaching t3 will be [b, c, b, a], after deduplicating aspects path, it will be
# [b, c, a] and as a result the base_keys of aspect a will be [b, c]
deps = [':t3'],
)
r2(
name = 't3',
)
""");
update("//test:t1");
// Aspect a should have a single AspectKey for its application on t3 and the baseKeys in it will
// be sorted as [b, c]
ImmutableList<AspectKey> keysForAspectAOnT3 = getAspectKeys("//test:t3", "//test:defs.bzl%a");
assertThat(keysForAspectAOnT3).hasSize(1);
ImmutableList<AspectKey> baseKeys = keysForAspectAOnT3.get(0).getBaseKeys();
assertThat(baseKeys.stream().map(k -> k.getAspectClass().getName()))
.containsExactly("//test:defs.bzl%b", "//test:defs.bzl%c")
.inOrder();
}
@Test
public void testAspectRunsTwiceWithDiffBaseAspectsDeps() throws Exception {
scratch.file(
"test/defs.bzl",
"""
a_provider = provider()
b_provider = provider()
c_provider = provider()
def _a_impl(target, ctx):
result = []
if ctx.rule.attr.deps:
for dep in ctx.rule.attr.deps:
result.extend(dep[a_provider].value)
if b_provider in target:
result.append('aspect a on {} sees b_provider = {}'.format(target.label,
target[b_provider].value))
return [a_provider(value = result)]
a = aspect(
implementation = _a_impl,
attr_aspects = ['deps'],
required_aspect_providers = [[b_provider], [c_provider]],
)
def _b_impl(target, ctx):
result = 'aspect b cannot see c_provider'
if c_provider in target:
result = 'aspect b can see c_provider'
return [b_provider(value = result)]
b = aspect(
implementation = _b_impl,
attr_aspects = ['deps'],
provides = [b_provider],
required_aspect_providers = [[c_provider]]
)
def _c_impl(target, ctx):
return [c_provider(value = ['aspect c on target {}'.format(target.label)])]
c = aspect(
implementation = _c_impl,
attr_aspects = ['deps'],
provides = [c_provider]
)
RuleInfo = provider()
def _r1_impl(ctx):
result = []
if ctx.attr.deps:
for dep in ctx.attr.deps:
result.extend(dep[a_provider].value)
return RuleInfo(aspect_a_collected_result = result)
r1 = rule(
implementation = _r1_impl,
attrs = {
'deps': attr.label_list(aspects = [a]),
},
)
def _r2_impl(ctx):
pass
r2 = rule(
implementation = _r2_impl,
attrs = {
'deps': attr.label_list(aspects = [c, b]),
},
)
def _r3_impl(ctx):
pass
r3 = rule(
implementation = _r3_impl,
attrs = {
'deps': attr.label_list(aspects = [b, c]),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1', 'r2', 'r3')
r1(
name = 't1',
# t1 propagate aspect (a) to targets (t2 and t3)
deps = [':t2', ':t3'],
)
r2(
name = 't2',
# t2 propagates aspects (c, b) to target t4 and aspect a is propagated from the prev
# level aspects path on t4 is [c, b, a], this means a can see b and b can see c
deps = [':t4'],
)
r3(
name = 't3',
# t3 propagates aspects (b, c) to target t4 and aspect a is propagated from the prev level
# aspects path on t4 is [b, c, a], this means a can see b but b cannot see c
deps = [':t4'],
)
r1(
name = 't4',
)
""");
AnalysisResult analysisResult = update("//test:t1");
// Aspect a should have 2 AspectKeys for its application on t4, one where in the basekeys b can
// see c and the other is where b cannot see c
ImmutableList<AspectKey> keysForAspectAOnT4 = getAspectKeys("//test:t4", "//test:defs.bzl%a");
assertThat(keysForAspectAOnT4).hasSize(2);
ConfiguredTarget configuredTarget =
Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkList<?> aspectAResult =
(StarlarkList)
getStarlarkProvider(configuredTarget, "RuleInfo").getValue("aspect_a_collected_result");
assertThat(Starlark.toIterable(aspectAResult))
.containsExactly(
"aspect a on @//test:t4 sees b_provider = aspect b can see c_provider",
"aspect a on @//test:t4 sees b_provider = aspect b cannot see c_provider");
}
@Test
public void testAspectWithSameExplicitAttributeNameAsUnderlyingTarget() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _a_impl(target, ctx):
value = 'x from aspect = {}, x from target = {}'.format(ctx.attr.x, ctx.rule.attr.x)
return struct(aspect_result = value)
a = aspect(
implementation = _a_impl,
attrs = {
'x': attr.string(default = 'xyz')
},
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
attrs = {
'x': attr.int(default = 4)
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
AnalysisResult analysisResult = update(ImmutableList.of("//test:defs.bzl%a"), "//test:t1");
ImmutableMap<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "a");
assertThat(aspectA).isNotNull();
String aspectAResult = (String) aspectA.get("aspect_result");
assertThat(aspectAResult).isEqualTo("x from aspect = xyz, x from target = 4");
}
@Test
public void testAspectNotDependOnTargetDeps() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _a_impl(target, ctx):
return []
a = aspect(
implementation = _a_impl,
attr_aspects = ['dep'],
attrs = {
'_tool': attr.label(default = '//test:tool'),
},
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
attrs = {
'dep': attr.label(),
'another_dep': attr.label(),
'_tool': attr.label(default = '//test:tool'),
},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1', dep = ':t2', another_dep = 't4')
r1(name = 't2', dep = ':t3')
r1(name = 't3')
r1(name = 't4')
sh_library(name = 'tool')
""");
AnalysisResult analysisResult = update(ImmutableList.of("//test:defs.bzl%a"), "//test:t1");
AspectKey key = Iterables.getOnlyElement(analysisResult.getAspectsMap().keySet());
var aspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(key))
.findFirst()
.orElse(null);
assertThat(aspectNode).isNotNull();
ImmutableList<String> configuredTargetsDeps =
stream(Iterables.filter(aspectNode.getDirectDeps(), ConfiguredTargetKey.class))
.map(k -> k.getLabel().toString())
.collect(toImmutableList());
// aspect depends only on its target and its implicit dependencies not the dependencies of its
// target
assertThat(configuredTargetsDeps).containsAtLeast("//test:tool", "//test:t1");
assertThat(configuredTargetsDeps).doesNotContain("//test:t2");
assertThat(configuredTargetsDeps).doesNotContain("//test:t3");
assertThat(configuredTargetsDeps).doesNotContain("//test:t4");
ImmutableList<String> aspectsDeps =
stream(Iterables.filter(aspectNode.getDirectDeps(), AspectKey.class))
.map(k -> k.getLabel().toString())
.collect(toImmutableList());
// aspect depends on the result of its application on the target deps if it propagates to them
assertThat(aspectsDeps).containsExactly("//test:t2");
}
private ImmutableList<AspectKey> getAspectKeys(String targetLabel, String aspectLabel) {
return skyframeExecutor.getEvaluator().getDoneValues().entrySet().stream()
.filter(
entry ->
entry.getKey() instanceof AspectKey
&& ((AspectKey) entry.getKey()).getAspectClass().getName().equals(aspectLabel)
&& ((AspectKey) entry.getKey()).getLabel().toString().equals(targetLabel))
.map(e -> (AspectKey) e.getKey())
.collect(toImmutableList());
}
private ConfiguredAspect getConfiguredAspect(
Map<AspectKey, ConfiguredAspect> aspectsMap, String aspectName) {
for (Map.Entry<AspectKey, ConfiguredAspect> entry : aspectsMap.entrySet()) {
AspectClass aspectClass = entry.getKey().getAspectClass();
if (aspectClass instanceof StarlarkAspectClass) {
String aspectExportedName = ((StarlarkAspectClass) aspectClass).getExportedName();
if (aspectExportedName.equals(aspectName)) {
return entry.getValue();
}
}
}
return null;
}
private ConfiguredAspect getConfiguredAspect(
Map<AspectKey, ConfiguredAspect> aspectsMap, String aspectName, String targetName) {
for (Map.Entry<AspectKey, ConfiguredAspect> entry : aspectsMap.entrySet()) {
AspectClass aspectClass = entry.getKey().getAspectClass();
if (aspectClass instanceof StarlarkAspectClass) {
String aspectExportedName = ((StarlarkAspectClass) aspectClass).getExportedName();
String target = entry.getKey().getLabel().getName();
if (aspectExportedName.equals(aspectName) && target.equals(targetName)) {
return entry.getValue();
}
}
}
return null;
}
private void exposeNativeAspectToStarlark() throws Exception {
ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
TestRuleClassProvider.addStandardRules(builder);
builder.addBzlToplevel(
"starlark_native_aspect", TestAspects.STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
builder.addBzlToplevel(
"parametrized_native_aspect",
TestAspects.PARAMETRIZED_STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
builder.addNativeAspectClass(TestAspects.STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
builder.addNativeAspectClass(TestAspects.PARAMETRIZED_STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
builder.addRuleDefinition(TestAspects.BASE_RULE);
builder.addRuleDefinition(TestAspects.HONEST_RULE);
useRuleClassProvider(builder.build());
}
/** StarlarkAspectTest with "keep going" flag */
@RunWith(JUnit4.class)
public static final class WithKeepGoing extends StarlarkDefinedAspectsTest {
@Override
protected FlagBuilder defaultFlags() {
return super.defaultFlags().with(Flag.KEEP_GOING);
}
@Override
protected boolean keepGoing() {
return true;
}
}
}