blob: 74f4ff432b0973bb8dd23f15881391202c9b5ffa [file] [log] [blame]
// Copyright 2024 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.truth.Truth.assertThat;
import static com.google.devtools.build.lib.skyframe.BzlLoadValue.keyForBuild;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.analysis.AnalysisResult;
import com.google.devtools.build.lib.analysis.ConfiguredAspect;
import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
import com.google.devtools.build.lib.analysis.util.MockRule;
import com.google.devtools.build.lib.analysis.util.TestAspects.DepsVisitingFileAspect;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.RuleClass.ToolchainResolutionMode;
import com.google.devtools.build.lib.packages.StarlarkInfo;
import com.google.devtools.build.lib.packages.StarlarkProvider;
import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import com.google.testing.junit.testparameterinjector.TestParameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Tests for Starlark aspects propagation to targets toolchain dependencies. */
@RunWith(TestParameterInjector.class)
public final class StarlarkAspectsToolchainPropagationTest extends AnalysisTestCase {
/**
* Sets up 3 toolchain rules:
*
* <p>test_toolchain: has no attribute dependency and no advertised providers
*
* <p>test_toolchain_with_provider: has an advertised provider but no attribute dependency
*
* <p>test_toolchain_with_dep: has an attribute dependency but no advertised providers
*
* <p>We also set up 3 toolchain types:
*
* <p>toolchain_type_1: resolved by `foo` of rule `test_toolchain`
*
* <p>toolchain_type_2: resolved by `foo_with_provider` of rule `test_toolchain_with_provider`
*
* <p>toolchain_type_3: resolved by `foo_with_dep` of rule `test_toolchain_with_dep`
*
* <p>Toolchain `foo_for_all` resolved both toolchain_type_2 and toolchain_type_3
*/
public void createToolchainsAndPlatforms() throws Exception {
scratch.overwriteFile(
"rule/test_toolchain.bzl",
"""
MyProvider = provider()
def _impl(ctx):
return [platform_common.ToolchainInfo(
tool = ctx.executable._tool,
files_to_run = ctx.attr._tool[DefaultInfo].files_to_run,
), MyProvider(value = str(ctx.label))]
test_toolchain = rule(
implementation = _impl,
attrs = {
"_tool": attr.label(
default = "//toolchain:a_tool",
executable = True,
cfg = "exec",
),
},
)
test_toolchain_with_provider = rule(
implementation = _impl,
attrs = {
"_tool": attr.label(
default = "//toolchain:a_tool",
executable = True,
cfg = "exec",
),
},
provides = [MyProvider]
)
test_toolchain_with_dep = rule(
implementation = _impl,
attrs = {
"_tool": attr.label(
default = "//toolchain:a_tool",
executable = True,
cfg = "exec",
),
"toolchain_dep": attr.label(),
},
)
""");
scratch.overwriteFile(
"rule/BUILD",
"""
exports_files(["test_toolchain/bzl"])
toolchain_type(name = "toolchain_type_1")
alias(name = "toolchain_type_1_alias", actual = ":toolchain_type_1")
toolchain_type(name = "toolchain_type_2")
toolchain_type(name = "toolchain_type_3")
""");
scratch.overwriteFile(
"toolchain/BUILD",
"""
load("//rule:test_toolchain.bzl", "test_toolchain",
"test_toolchain_with_provider", "test_toolchain_with_dep")
genrule(
name = "a_tool",
outs = ["atool"],
cmd = "",
executable = True,
)
test_toolchain(
name = "foo",
)
toolchain(
name = "foo_toolchain",
toolchain = ":foo",
toolchain_type = "//rule:toolchain_type_1",
)
test_toolchain_with_provider(
name = "foo_with_provider",
)
toolchain(
name = "foo_toolchain_with_provider",
toolchain = ":foo_with_provider",
toolchain_type = "//rule:toolchain_type_2",
)
sh_library(name = "toolchain_dep")
test_toolchain_with_dep(
name = "foo_with_dep",
toolchain_dep = ":toolchain_dep",
)
toolchain(
name = "foo_toolchain_with_dep",
toolchain = ":foo_with_dep",
toolchain_type = "//rule:toolchain_type_3",
)
test_toolchain(name = "foo_for_all")
toolchain(
name = "foo_type_2",
toolchain = ":foo_for_all",
toolchain_type = "//rule:toolchain_type_2",
)
toolchain(
name = "foo_type_3",
toolchain = ":foo_for_all",
toolchain_type = "//rule:toolchain_type_3",
)
""");
scratch.overwriteFile(
"platforms/BUILD",
"""
constraint_setting(name = "setting")
constraint_value(
name = "constraint_1",
constraint_setting = ":setting",
)
constraint_value(
name = "constraint_2",
constraint_setting = ":setting",
)
platform(
name = "platform_1",
constraint_values = [":constraint_1"],
)
platform(
name = "platform_2",
constraint_values = [":constraint_2"],
exec_properties = {
"watermelon.ripeness": "unripe",
"watermelon.color": "red",
},
)
""");
}
@Before
public void setup() throws Exception {
createToolchainsAndPlatforms();
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void aspectPropagatesToToolchain_singleDepAdded(String autoExecGroups) throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
)
no_toolchain_aspect = aspect(
implementation = _impl,
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var unused =
update(
ImmutableList.of(
"//test:defs.bzl%toolchain_aspect", "//test:defs.bzl%no_toolchain_aspect"),
"//test:t1");
var toolchainAspect =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var toolchainAspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(toolchainAspect))
.findFirst()
.orElse(null);
assertThat(toolchainAspectNode).isNotNull();
var noToolchainAspect =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%no_toolchain_aspect"));
var noToolchainAspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(noToolchainAspect))
.findFirst()
.orElse(null);
assertThat(noToolchainAspectNode).isNotNull();
var toolchainAspectDirectDeps =
ImmutableSet.copyOf(Iterables.filter(toolchainAspectNode.getDirectDeps(), SkyKey.class));
var noToolchainAspectDirectDeps =
ImmutableSet.copyOf(Iterables.filter(noToolchainAspectNode.getDirectDeps(), SkyKey.class));
// only one extra dependency is added for the toolchain propagating aspect
assertThat(toolchainAspectDirectDeps.size() - noToolchainAspectDirectDeps.size()).isEqualTo(1);
assertThat(toolchainAspectDirectDeps).containsAtLeastElementsIn(noToolchainAspectDirectDeps);
// the extra dependency is the aspect application on the target's resolved toolchain
var aspectOnToolchainDep =
Iterables.getOnlyElement(
Sets.difference(toolchainAspectDirectDeps, noToolchainAspectDirectDeps));
assertThat(aspectOnToolchainDep).isInstanceOf(AspectKey.class);
assertThat(((AspectKey) aspectOnToolchainDep).getAspectName())
.isEqualTo("//test:defs.bzl%toolchain_aspect");
assertThat(((AspectKey) aspectOnToolchainDep).getLabel().toString())
.isEqualTo("//toolchain:foo");
}
@Test
public void aspectPropagatesToExecGpToolchain_singleDepAdded() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
)
no_toolchain_aspect = aspect(
implementation = _impl,
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
exec_groups = {"gp": exec_group(toolchains = ['//rule:toolchain_type_1'])},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
var unused =
update(
ImmutableList.of(
"//test:defs.bzl%toolchain_aspect", "//test:defs.bzl%no_toolchain_aspect"),
"//test:t1");
var toolchainAspect =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var toolchainAspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(toolchainAspect))
.findFirst()
.orElse(null);
assertThat(toolchainAspectNode).isNotNull();
var noToolchainAspect =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%no_toolchain_aspect"));
var noToolchainAspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(noToolchainAspect))
.findFirst()
.orElse(null);
assertThat(noToolchainAspectNode).isNotNull();
var toolchainAspectDirectDeps =
ImmutableSet.copyOf(Iterables.filter(toolchainAspectNode.getDirectDeps(), SkyKey.class));
var noToolchainAspectDirectDeps =
ImmutableSet.copyOf(Iterables.filter(noToolchainAspectNode.getDirectDeps(), SkyKey.class));
// only one extra dependency is added for the toolchain propagating aspect
assertThat(toolchainAspectDirectDeps.size() - noToolchainAspectDirectDeps.size()).isEqualTo(1);
assertThat(toolchainAspectDirectDeps).containsAtLeastElementsIn(noToolchainAspectDirectDeps);
// the extra dependency is the aspect application on the target's resolved toolchain
var aspectOnToolchainDep =
Iterables.getOnlyElement(
Sets.difference(toolchainAspectDirectDeps, noToolchainAspectDirectDeps));
assertThat(aspectOnToolchainDep).isInstanceOf(AspectKey.class);
assertThat(((AspectKey) aspectOnToolchainDep).getAspectName())
.isEqualTo("//test:defs.bzl%toolchain_aspect");
assertThat(((AspectKey) aspectOnToolchainDep).getLabel().toString())
.isEqualTo("//toolchain:foo");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void aspectHasToolchains_dependencyEdgeCreated(String autoExecGroups) throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
toolchains = ['//rule:toolchain_type_2'],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain,//toolchain:foo_toolchain_with_provider",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
var toolchainAspect =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var toolchainAspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(toolchainAspect))
.findFirst()
.orElse(null);
assertThat(toolchainAspectNode).isNotNull();
// A dependency edge is created from the aspect to its own toolchain but not to the target's
// toolchain.
var aspectConfiguredTargetDeps =
Iterables.transform(
Iterables.filter(
toolchainAspectNode.getDirectDeps(), d -> d instanceof ConfiguredTargetKey),
d -> ((ConfiguredTargetKey) d).getLabel().toString());
assertThat(aspectConfiguredTargetDeps)
.containsExactly("//toolchain:foo_with_provider", "//test:t1");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void aspectPropagatesToToolchainUsingToolchainTypeAlias(String autoExecGroups)
throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
)
no_toolchain_aspect = aspect(
implementation = _impl,
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1_alias'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var unused =
update(
ImmutableList.of(
"//test:defs.bzl%toolchain_aspect", "//test:defs.bzl%no_toolchain_aspect"),
"//test:t1");
var toolchainAspect =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var toolchainAspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(toolchainAspect))
.findFirst()
.orElse(null);
assertThat(toolchainAspectNode).isNotNull();
var noToolchainAspect =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%no_toolchain_aspect"));
var noToolchainAspectNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(noToolchainAspect))
.findFirst()
.orElse(null);
assertThat(noToolchainAspectNode).isNotNull();
var toolchainAspectDirectDeps =
ImmutableSet.copyOf(Iterables.filter(toolchainAspectNode.getDirectDeps(), SkyKey.class));
var noToolchainAspectDirectDeps =
ImmutableSet.copyOf(Iterables.filter(noToolchainAspectNode.getDirectDeps(), SkyKey.class));
// only one extra dependency is added for the toolchain propagating aspect
assertThat(toolchainAspectDirectDeps.size() - noToolchainAspectDirectDeps.size()).isEqualTo(1);
assertThat(toolchainAspectDirectDeps).containsAtLeastElementsIn(noToolchainAspectDirectDeps);
// the extra dependency is the aspect application on the target's resolved toolchain
var aspectOnToolchainDep =
Iterables.getOnlyElement(
Sets.difference(toolchainAspectDirectDeps, noToolchainAspectDirectDeps));
assertThat(aspectOnToolchainDep).isInstanceOf(AspectKey.class);
assertThat(((AspectKey) aspectOnToolchainDep).getAspectName())
.isEqualTo("//test:defs.bzl%toolchain_aspect");
assertThat(((AspectKey) aspectOnToolchainDep).getLabel().toString())
.isEqualTo("//toolchain:foo");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void toolchainPropagationBasedOnAspectRequiredProviders(String autoExecGroups)
throws Exception {
scratch.file(
"test/defs.bzl",
"""
load("//rule:test_toolchain.bzl", "MyProvider")
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1', '//rule:toolchain_type_2'],
required_providers = [MyProvider],
)
def _rule_impl(ctx):
return [MyProvider()]
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
exec_groups = {"gp": exec_group(toolchains = ['//rule:toolchain_type_2'])},
provides = [MyProvider],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain,//toolchain:foo_toolchain_with_provider",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
var aspectOnTarget =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var aspectOnTargetNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnTarget))
.findFirst()
.orElse(null);
assertThat(aspectOnTargetNode).isNotNull();
// aspect propagated only to //toolchain:foo_with_provider
var aspectOnToolchain =
Iterables.getOnlyElement(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class));
assertThat(aspectOnToolchain.getLabel().toString()).isEqualTo("//toolchain:foo_with_provider");
assertThat(aspectOnToolchain.getAspectName()).isEqualTo("//test:defs.bzl%toolchain_aspect");
}
@Test
public void aspectPropagatesToToolchainDeps() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_3'],
attr_aspects = ['toolchain_dep'],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
exec_groups = {"gp": exec_group(toolchains = ['//rule:toolchain_type_3'])},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain_with_dep");
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
var aspectOnTarget =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var aspectOnTargetNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnTarget))
.findFirst()
.orElse(null);
assertThat(aspectOnTargetNode).isNotNull();
var aspectOnToolchain =
Iterables.getOnlyElement(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class));
var aspectOnToolchainNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnToolchain))
.findFirst()
.orElse(null);
assertThat(aspectOnToolchainNode).isNotNull();
assertThat(aspectOnToolchain.getLabel().toString()).isEqualTo("//toolchain:foo_with_dep");
var aspectOnToolchainDep =
Iterables.getOnlyElement(
Iterables.filter(aspectOnToolchainNode.getDirectDeps(), AspectKey.class));
assertThat(aspectOnToolchainDep.getLabel().toString()).isEqualTo("//toolchain:toolchain_dep");
assertThat(aspectOnToolchainDep.getAspectName()).isEqualTo("//test:defs.bzl%toolchain_aspect");
}
@Test
public void requiredAspectPropagatesToToolchain() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
required_aspect = aspect(implementation = _impl)
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
requires = [required_aspect],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
exec_groups = {"gp": exec_group(toolchains = ['//rule:toolchain_type_1'])},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
var aspectOnTarget =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var aspectOnTargetNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnTarget))
.findFirst()
.orElse(null);
assertThat(aspectOnTargetNode).isNotNull();
var aspectsDeps =
Iterables.transform(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class),
k -> k.getAspectName() + " on " + k.getLabel().toString());
assertThat(aspectsDeps).hasSize(3);
// toolchain_aspect requires required_aspect so required_aspect will be propagated before
// toolchain_aspect to //test:t1 and its toolchain
assertThat(aspectsDeps)
.containsExactly(
"//test:defs.bzl%required_aspect on //test:t1",
"//test:defs.bzl%toolchain_aspect on //toolchain:foo",
"//test:defs.bzl%required_aspect on //toolchain:foo");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void aspectOnAspectPropagateToToolchain(String autoExecGroups) throws Exception {
scratch.file(
"test/defs.bzl",
"""
Prov1 = provider()
Prov2 = provider()
def _impl(target, ctx):
return []
def _impl_1(target, ctx):
return [Prov1()]
def _impl_2(target, ctx):
return [Prov2()]
toolchain_aspect_1 = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
required_aspect_providers = [Prov1]
)
no_toolchain_aspect = aspect(
implementation = _impl_1,
provides = [Prov1],
required_aspect_providers = [Prov2]
)
toolchain_aspect_2 = aspect(
implementation = _impl_2,
toolchains_aspects = ['//rule:toolchain_type_1'],
provides = [Prov2],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var unused =
update(
ImmutableList.of(
"//test:defs.bzl%toolchain_aspect_2",
"//test:defs.bzl%no_toolchain_aspect", "//test:defs.bzl%toolchain_aspect_1"),
"//test:t1");
var aspectOnTarget =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect_1"));
assertThat(aspectOnTarget.getBaseKeys()).hasSize(1);
assertThat(aspectOnTarget.getBaseKeys().get(0).getAspectName())
.isEqualTo("//test:defs.bzl%no_toolchain_aspect");
var aspectOnTargetNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnTarget))
.findFirst()
.orElse(null);
assertThat(aspectOnTargetNode).isNotNull();
var aspectsOnToolchain =
Iterables.transform(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class),
k -> k.getAspectName() + " on " + k.getLabel().toString());
assertThat(aspectsOnToolchain).hasSize(4);
// Only `toolchain_aspect_1` and `toolchain_aspect_2` are propagated to the toolchain
assertThat(aspectsOnToolchain)
.containsExactly(
"//test:defs.bzl%toolchain_aspect_2 on //test:t1",
"//test:defs.bzl%no_toolchain_aspect on //test:t1",
"//test:defs.bzl%toolchain_aspect_1 on //toolchain:foo",
"//test:defs.bzl%toolchain_aspect_2 on //toolchain:foo");
var toolchainAspect1 =
Iterables.getOnlyElement(
Iterables.filter(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class),
k ->
k.getAspectName().equals("//test:defs.bzl%toolchain_aspect_1")
&& k.getLabel().toString().equals("//toolchain:foo")));
// Since `toolchain_aspect_1` only depends on `no_toolchain_aspect`, it will have no base keys
// when applied on the toolchain.
assertThat(toolchainAspect1.getBaseKeys()).isEmpty();
}
@Test
public void execGroupWithMultipleToolchainTypes_aspectsPropagateToRelevantTypes()
throws Exception {
scratch.file(
"test/defs.bzl",
"""
Prov1 = provider()
Prov2 = provider()
def _impl(target, ctx):
return []
def _impl_1(target, ctx):
return [Prov1()]
def _impl_2(target, ctx):
return [Prov2()]
toolchain_aspect_0 = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
required_aspect_providers = [[Prov1], [Prov2]]
)
toolchain_aspect_1 = aspect(
implementation = _impl_1,
toolchains_aspects = ['//rule:toolchain_type_3'],
provides = [Prov1],
required_aspect_providers = [Prov2]
)
toolchain_aspect_2 = aspect(
implementation = _impl_2,
toolchains_aspects = ['//rule:toolchain_type_1'],
provides = [Prov2],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
attrs = {
'dep': attr.label(),
},
exec_groups = {"gp": exec_group(
toolchains = ['//rule:toolchain_type_1', '//rule:toolchain_type_3'])},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain,//toolchain:foo_toolchain_with_dep");
var unused =
update(
ImmutableList.of(
"//test:defs.bzl%toolchain_aspect_2",
"//test:defs.bzl%toolchain_aspect_1", "//test:defs.bzl%toolchain_aspect_0"),
"//test:t1");
var aspectOnTarget =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect_0"));
var aspectOnTargetNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnTarget))
.findFirst()
.orElse(null);
assertThat(aspectOnTargetNode).isNotNull();
var aspectsOnToolchain =
Iterables.transform(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class),
k -> k.getAspectName() + " on " + k.getLabel().toString());
assertThat(aspectsOnToolchain).hasSize(5);
assertThat(aspectsOnToolchain)
.containsExactly(
"//test:defs.bzl%toolchain_aspect_1 on //test:t1",
"//test:defs.bzl%toolchain_aspect_2 on //test:t1",
// toolchain_aspect_0 and toolchain_aspect_2 propagate to //toolchain:foo of
// //rule:toolchain_type_1
"//test:defs.bzl%toolchain_aspect_0 on //toolchain:foo",
"//test:defs.bzl%toolchain_aspect_2 on //toolchain:foo",
// toolchain_aspect_1 propagates to //toolchain:foo_with_dep of //rule:toolchain_type_3
"//test:defs.bzl%toolchain_aspect_1 on //toolchain:foo_with_dep");
var toolchainAspect1 =
Iterables.getOnlyElement(
Iterables.filter(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class),
k ->
k.getAspectName().equals("//test:defs.bzl%toolchain_aspect_0")
&& k.getLabel().toString().equals("//toolchain:foo")));
// Since `toolchain_aspect_0` depends on `toolchain_aspect_2` when applied on //toolchain:foo,
assertThat(Iterables.getOnlyElement(toolchainAspect1.getBaseKeys()).getAspectName())
.isEqualTo("//test:defs.bzl%toolchain_aspect_2");
}
@Test
public void toolchainTypesResolvedToSameToolchain_aspectsPropagateToSameToolchain()
throws Exception {
scratch.file(
"test/defs.bzl",
"""
prov = provider()
def _impl(target, ctx):
return []
def _impl_1(target, ctx):
return [prov()]
toolchain_aspect_1 = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_2'],
required_aspect_providers = [prov]
)
toolchain_aspect_2 = aspect(
implementation = _impl_1,
toolchains_aspects = ['//rule:toolchain_type_3'],
provides = [prov],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
exec_groups = {"gp": exec_group(
toolchains = ['//rule:toolchain_type_2', '//rule:toolchain_type_3'])},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_type_2", "--extra_toolchains=//toolchain:foo_type_3");
var unused =
update(
ImmutableList.of(
"//test:defs.bzl%toolchain_aspect_2", "//test:defs.bzl%toolchain_aspect_1"),
"//test:t1");
var aspectOnTarget =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect_1"));
var aspectOnTargetNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnTarget))
.findFirst()
.orElse(null);
assertThat(aspectOnTargetNode).isNotNull();
var aspectsOnToolchain =
Iterables.transform(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class),
k -> k.getAspectName() + " on " + k.getLabel().toString());
assertThat(aspectsOnToolchain).hasSize(3);
assertThat(aspectsOnToolchain)
.containsExactly(
"//test:defs.bzl%toolchain_aspect_2 on //test:t1",
// both aspects propagated to //toolchain:foo_for_all because it resolves both the
// toolchain types
"//test:defs.bzl%toolchain_aspect_1 on //toolchain:foo_for_all",
"//test:defs.bzl%toolchain_aspect_2 on //toolchain:foo_for_all");
}
@Test
public void toolchainTypesResolvedToSameToolchainDiffExecPlatform_aspectPropagateTwice()
throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
exec_groups = {
"gp1": exec_group(
toolchains = ['//rule:toolchain_type_1'],
exec_compatible_with = ['//platforms:constraint_2']
),
"gp2": exec_group(toolchains = ['//rule:toolchain_type_1'])},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--extra_execution_platforms=//platforms:platform_1,//platforms:platform_2");
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
var aspectOnTarget =
Iterables.getOnlyElement(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect"));
var aspectOnTargetNode =
skyframeExecutor.getEvaluator().getInMemoryGraph().getAllNodeEntries().stream()
.filter(n -> n.getKey().equals(aspectOnTarget))
.findFirst()
.orElse(null);
assertThat(aspectOnTargetNode).isNotNull();
var aspectsOnToolchain =
Iterables.transform(
Iterables.filter(aspectOnTargetNode.getDirectDeps(), AspectKey.class),
k ->
k.getAspectName()
+ " on "
+ k.getLabel().toString()
+ ", exec_platform: "
+ k.getBaseConfiguredTargetKey().getExecutionPlatformLabel().toString());
assertThat(aspectsOnToolchain).hasSize(2);
// aspect propagated twice on the same toolchain target but with different execution platform
assertThat(aspectsOnToolchain)
.containsExactly(
"//test:defs.bzl%toolchain_aspect on //toolchain:foo, exec_platform:"
+ " //platforms:platform_2",
"//test:defs.bzl%toolchain_aspect on //toolchain:foo, exec_platform:"
+ " //platforms:platform_1");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void aspectPropagatesToToolchain_providersCollected(String autoExecGroups)
throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
def _impl(target, ctx):
target_res = "toolchain_aspect has param = " + ctx.attr.param
target_res += " on " + str(target.label)
if platform_common.ToolchainInfo in target:
target_res += " with tool in ToolchainInfo = "
target_res += str(target[platform_common.ToolchainInfo].tool)
result = [target_res]
if ctx.rule.toolchains and '//rule:toolchain_type_1' in ctx.rule.toolchains:
result.extend(ctx.rule.toolchains['//rule:toolchain_type_1'][AspectProvider].value)
return [AspectProvider(value = result)]
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
attrs = {
"param": attr.string(),
},
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var analysisResult =
update(
ImmutableList.of("//test:defs.bzl%toolchain_aspect"),
ImmutableMap.of("param", "xxx"),
"//test:t1");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key providerKey =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:defs.bzl")), "AspectProvider");
var value = ((StarlarkInfo) configuredAspect.get(providerKey)).getValue("value");
assertThat((Iterable<?>) value)
.containsExactly(
"toolchain_aspect has param = xxx on @@//test:t1",
"toolchain_aspect has param = xxx on @@//toolchain:foo with tool in ToolchainInfo ="
+ " <generated file toolchain/atool>");
}
@Test
public void aspectPropagatesToExecGpToolchain_providersCollected() throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
def _impl(target, ctx):
target_res = "toolchain_aspect has param = " + ctx.attr.param
target_res += " on " + str(target.label)
if platform_common.ToolchainInfo in target:
target_res += " with tool in ToolchainInfo = "
target_res += str(target[platform_common.ToolchainInfo].tool)
result = [target_res]
if ctx.rule.exec_groups and 'gp' in ctx.rule.exec_groups:
result.extend(
ctx.
rule.
exec_groups['gp'].
toolchains['//rule:toolchain_type_1'][AspectProvider].value)
return [AspectProvider(value = result)]
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
attrs = {
"param": attr.string(),
},
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
exec_groups = {"gp": exec_group(toolchains = ['//rule:toolchain_type_1'])},
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
var analysisResult =
update(
ImmutableList.of("//test:defs.bzl%toolchain_aspect"),
ImmutableMap.of("param", "xxx"),
"//test:t1");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key providerKey =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:defs.bzl")), "AspectProvider");
var value = ((StarlarkInfo) configuredAspect.get(providerKey)).getValue("value");
assertThat((Iterable<?>) value)
.containsExactly(
"toolchain_aspect has param = xxx on @@//test:t1",
"toolchain_aspect has param = xxx on @@//toolchain:foo with tool in"
+ " ToolchainInfo = <generated file toolchain/atool>");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void aspectPropagatesToToolchainUsingAlias_providersCollected(String autoExecGroups)
throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
def _impl(target, ctx):
target_res = "toolchain_aspect has param = " + ctx.attr.param
target_res += " on " + str(target.label)
if platform_common.ToolchainInfo in target:
target_res += " with tool in ToolchainInfo = "
target_res += str(target[platform_common.ToolchainInfo].tool)
result = [target_res]
if ctx.rule.toolchains and '//rule:toolchain_type_1_alias' in ctx.rule.toolchains:
result.extend(
ctx.rule.toolchains['//rule:toolchain_type_1_alias'][AspectProvider].value)
return [AspectProvider(value = result)]
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1_alias'],
attrs = {
"param": attr.string(),
},
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1_alias'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var analysisResult =
update(
ImmutableList.of("//test:defs.bzl%toolchain_aspect"),
ImmutableMap.of("param", "xxx"),
"//test:t1");
ConfiguredAspect configuredAspect =
Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
StarlarkProvider.Key providerKey =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:defs.bzl")), "AspectProvider");
var value = ((StarlarkInfo) configuredAspect.get(providerKey)).getValue("value");
assertThat((Iterable<?>) value)
.containsExactly(
"toolchain_aspect has param = xxx on @@//test:t1",
"toolchain_aspect has param = xxx on @@//toolchain:foo with tool in ToolchainInfo ="
+ " <generated file toolchain/atool>");
}
@Test
public void aspectPropagatesToToolchain_cannotSeeToolchainInfoOfDeps() throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
def _impl(target, ctx):
if ctx.rule.toolchains and '//rule:toolchain_type_1' in ctx.rule.toolchains:
print(ctx.rule.toolchains['//rule:toolchain_type_1'][platform_common.ToolchainInfo])
return [AspectProvider(value = [])]
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
reporter.removeHandler(failFastHandler);
try {
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
} catch (Exception unused) {
// expect to fail
}
assertContainsEvent(
"<ToolchainAspectsProviders for toolchain target: //toolchain:foo> doesn't contain declared"
+ " provider 'ToolchainInfo'");
}
@Test
public void aspectDoesNotPropagatesToToolchain_cannotSeeTargetToolchains() throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
def _impl(target, ctx):
print(ctx.rule.toolchains['//rule:toolchain_type_1'])
return [AspectProvider(value = [])]
toolchain_aspect = aspect(
implementation = _impl,
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
reporter.removeHandler(failFastHandler);
try {
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
} catch (Exception unused) {
// expect to fail
}
assertContainsEvent("Error: Toolchains are not valid in this context");
}
@Test
public void requiredAspectPropagatesToToolchain_providersCollected() throws Exception {
scratch.file(
"test/defs.bzl",
"""
ToolchainAspectProvider = provider()
RequiredAspectProvider = provider()
def _toolchain_aspect_impl(target, ctx):
target_res = "toolchain_aspect on " + str(target.label)
target_res += " can see required_aspect (" + target[RequiredAspectProvider].value + ")"
result = [target_res]
if ctx.rule.toolchains and '//rule:toolchain_type_1' in ctx.rule.toolchains:
result.extend(
ctx.rule.toolchains['//rule:toolchain_type_1'][ToolchainAspectProvider].value)
return [ToolchainAspectProvider(value = result)]
def _required_aspect_impl(target, ctx):
target_res = "required_aspect on " + str(target.label)
if platform_common.ToolchainInfo in target:
target_res += " with tool in ToolchainInfo = "
target_res += str(target[platform_common.ToolchainInfo].tool)
return [RequiredAspectProvider(value = target_res)]
required_aspect = aspect(implementation = _required_aspect_impl)
toolchain_aspect = aspect(
implementation = _toolchain_aspect_impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
requires = [required_aspect],
)
def _rule_impl(ctx):
pass
r1 = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
var analysisResult = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:t1");
ConfiguredAspect configuredAspect =
analysisResult
.getAspectsMap()
.get(getAspectKeys("//test:t1", "//test:defs.bzl%toolchain_aspect").get(0));
StarlarkProvider.Key providerKey =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:defs.bzl")), "ToolchainAspectProvider");
var value = ((StarlarkInfo) configuredAspect.get(providerKey)).getValue("value");
assertThat((Iterable<?>) value)
.containsExactly(
"toolchain_aspect on @@//test:t1 can see required_aspect (required_aspect on"
+ " @@//test:t1)",
"toolchain_aspect on @@//toolchain:foo can see required_aspect (required_aspect on"
+ " @@//toolchain:foo with tool in ToolchainInfo = <generated file"
+ " toolchain/atool>)");
}
@Test
public void aspectPropagatesToToolchainFromRule_providersCollected() throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
RuleProvider = provider()
def _impl(target, ctx):
result = ["toolchain_aspect on " + str(target.label)]
if ctx.rule.toolchains and '//rule:toolchain_type_3' in ctx.rule.toolchains:
result.extend(
ctx.rule.toolchains['//rule:toolchain_type_3'][AspectProvider].value)
if hasattr(ctx.rule.attr, 'toolchain_dep'):
result.extend(ctx.rule.attr.toolchain_dep[AspectProvider].value)
return [AspectProvider(value = result)]
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_3'],
attr_aspects = ['toolchain_dep'],
)
def _rule_1_impl(ctx):
return [RuleProvider(value = ctx.attr.rule_dep[AspectProvider].value)]
r1 = rule(
implementation = _rule_1_impl,
attrs = {
"rule_dep": attr.label(aspects = [toolchain_aspect]),
},
)
def _rule_2_impl(ctx):
pass
r2 = rule(
implementation = _rule_2_impl,
toolchains = ['//rule:toolchain_type_3'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1', 'r2')
r1(name = 't1', rule_dep = ':t2')
r2(name = 't2')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain_with_dep");
var analysisResult = update("//test:t1");
var configuredTarget = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
StarlarkProvider.Key providerKey =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:defs.bzl")), "RuleProvider");
var value = ((StarlarkInfo) configuredTarget.get(providerKey)).getValue("value");
assertThat((Iterable<?>) value)
.containsExactly(
"toolchain_aspect on @@//test:t2",
"toolchain_aspect on @@//toolchain:foo_with_dep",
"toolchain_aspect on @@//toolchain:toolchain_dep");
}
@Test
public void toolchainAspectOnOutputFile_notPropagatedToDeps() throws Exception {
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
def _impl(target, ctx):
return [AspectProvider(val="hi")]
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
attr_aspects = ['dep'],
)
def _rule_1_impl(ctx):
if ctx.outputs.out:
ctx.actions.write(ctx.outputs.out, 'hi')
return []
r1 = rule(
implementation = _rule_1_impl,
attrs = {
"out": attr.output(),
"dep": attr.label(),
},
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1', out = 'my_out.txt', dep = ':t2')
r1(name = 't2')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:my_out.txt");
// {@link AspectKey} is created for toolchain_aspect on the output file //test:my_out.txt but
// the aspect is not applied (no returned providers) because the aspect cannot be applied to
// output files. The aspect does not propagate to any of the generating rule dependencies.
var nodes =
skyframeExecutor.getEvaluator().getDoneValues().entrySet().stream()
.filter(
entry ->
entry.getKey() instanceof AspectKey
&& ((AspectKey) entry.getKey())
.getAspectClass()
.getName()
.equals("//test:defs.bzl%toolchain_aspect"))
.collect(toImmutableList());
assertThat(nodes).hasSize(1);
AspectKey aspectKey = (AspectKey) Iterables.getOnlyElement(nodes).getKey();
assertThat(aspectKey.getLabel().toString()).isEqualTo("//test:my_out.txt");
ConfiguredAspect aspectValue = (ConfiguredAspect) Iterables.getOnlyElement(nodes).getValue();
assertThat(aspectValue.getProviders().getProviderCount()).isEqualTo(0);
}
@Test
public void toolchainAspectApplyToGeneratingRule_propagateToDeps() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _impl(target, ctx):
return []
toolchain_aspect = aspect(
implementation = _impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
attr_aspects = ['dep'],
apply_to_generating_rules = True,
)
def _rule_1_impl(ctx):
if ctx.outputs.out:
ctx.actions.write(ctx.outputs.out, 'hi')
return []
r1 = rule(
implementation = _rule_1_impl,
attrs = {
"out": attr.output(),
"dep": attr.label(),
},
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1', out = 'my_out.txt', dep = ':t2')
r1(name = 't2')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:my_out.txt");
var visitedTargets =
skyframeExecutor.getEvaluator().getDoneValues().entrySet().stream()
.filter(
entry ->
entry.getKey() instanceof AspectKey
&& ((AspectKey) entry.getKey())
.getAspectClass()
.getName()
.equals("//test:defs.bzl%toolchain_aspect"))
.map(e -> ((AspectKey) e.getKey()).getLabel().toString())
.collect(toImmutableList());
// toolchain_aspect is applied to the generating rule of the output file and propagated to its
// attribute dependency and toolchain dependency.
assertThat(visitedTargets)
.containsExactly("//test:my_out.txt", "//test:t1", "//test:t2", "//toolchain:foo");
}
@Test
public void toolchainAspectApplyToFiles_notPropagatedToDeps() throws Exception {
DepsVisitingFileAspect aspect = new DepsVisitingFileAspect("dep", "//rule:toolchain_type_1");
setRulesAndAspectsAvailableInTests(ImmutableList.of(aspect), ImmutableList.of());
scratch.file(
"test/defs.bzl",
"""
def _rule_1_impl(ctx):
if ctx.outputs.out:
ctx.actions.write(ctx.outputs.out, 'hi')
return []
r1 = rule(
implementation = _rule_1_impl,
attrs = {
"out": attr.output(),
"dep": attr.label(),
},
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'r1')
r1(name = 't1', out = 'my_out.txt', dep = ':t2')
r1(name = 't2')
""");
useConfiguration("--extra_toolchains=//toolchain:foo_toolchain");
var unused = update(ImmutableList.of(aspect.getName()), "//test:my_out.txt");
// {@link DepsVisitingFileAspect} is only applied to //test:my_out.txt file therefore it does
// not propagate to the dependencies of its generating rule.
var nodes =
skyframeExecutor.getEvaluator().getDoneValues().entrySet().stream()
.filter(
entry ->
entry.getKey() instanceof AspectKey
&& ((AspectKey) entry.getKey())
.getAspectClass()
.getName()
.equals(aspect.getName()))
.collect(toImmutableList());
assertThat(nodes).hasSize(1);
AspectKey aspectKey = (AspectKey) Iterables.getOnlyElement(nodes).getKey();
assertThat(aspectKey.getLabel().toString()).isEqualTo("//test:my_out.txt");
ConfiguredAspect aspectValue = (ConfiguredAspect) Iterables.getOnlyElement(nodes).getValue();
StarlarkInfo provider =
(StarlarkInfo) aspectValue.get(DepsVisitingFileAspect.PROVIDER.getKey());
assertThat(provider.getValue("val")).isEqualTo("//test:my_out.txt");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void toolchainAspectOnTargetWithoutToolchain_success(String autoExecGroups)
throws Exception {
MockRule ruleWithoutToolchain =
() ->
MockRule.define(
"rule_without_toolchain",
(builder, env) ->
builder.toolchainResolutionMode(ToolchainResolutionMode.DISABLED));
setRulesAndAspectsAvailableInTests(ImmutableList.of(), ImmutableList.of(ruleWithoutToolchain));
scratch.file(
"test/defs.bzl",
"""
AspectProvider = provider()
def _aspect_impl(target, ctx):
return [AspectProvider(val = 'toolchain_aspect on %s' % str(target.label))]
toolchain_aspect = aspect(
implementation = _aspect_impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
)
def _rule_impl(ctx):
pass
rule_with_toolchain = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'rule_with_toolchain')
rule_with_toolchain(name = 'target_with_toolchain')
rule_without_toolchain(name = 'target_without_toolchain')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var unused = update(ImmutableList.of("//test:defs.bzl%toolchain_aspect"), "//test:all");
StarlarkProvider.Key providerKey =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:defs.bzl")), "AspectProvider");
var aspectOnVisitedTargets =
skyframeExecutor.getEvaluator().getDoneValues().entrySet().stream()
.filter(
entry ->
entry.getKey() instanceof AspectKey
&& ((AspectKey) entry.getKey())
.getAspectClass()
.getName()
.equals("//test:defs.bzl%toolchain_aspect"))
.map(e -> (ConfiguredAspect) e.getValue())
.map(a -> ((StarlarkInfo) a.get(providerKey)).getValue("val"))
.map(v -> (String) v)
.collect(toImmutableList());
// aspect successfully propagates to the 2 targets in //test package and to the toolchain of
// //test:target_with_toolchain
assertThat(aspectOnVisitedTargets)
.containsExactly(
"toolchain_aspect on @@//test:target_with_toolchain",
"toolchain_aspect on @@//test:target_without_toolchain",
"toolchain_aspect on @@//toolchain:foo");
}
@Test
@TestParameters({
"{autoExecGroups: True}",
"{autoExecGroups: False}",
})
public void requiredToolchainAspectOnTargetWithoutToolchain_success(String autoExecGroups)
throws Exception {
MockRule ruleWithoutToolchain =
() ->
MockRule.define(
"rule_without_toolchain",
(builder, env) ->
builder.toolchainResolutionMode(ToolchainResolutionMode.DISABLED));
setRulesAndAspectsAvailableInTests(ImmutableList.of(), ImmutableList.of(ruleWithoutToolchain));
scratch.file(
"test/defs.bzl",
"""
MainAspectProvider = provider()
RequiredAspectProvider = provider()
def _required_aspect_impl(target, ctx):
target_res = "required_aspect on " + str(target.label)
if platform_common.ToolchainInfo in target:
target_res += " with tool in ToolchainInfo = "
target_res += str(target[platform_common.ToolchainInfo].tool)
result = [target_res]
if ctx.rule.toolchains and '//rule:toolchain_type_1' in ctx.rule.toolchains:
result.extend(
ctx.rule.toolchains['//rule:toolchain_type_1'][RequiredAspectProvider].val)
return [RequiredAspectProvider(val = result)]
required_aspect = aspect(
implementation = _required_aspect_impl,
toolchains_aspects = ['//rule:toolchain_type_1'],
)
def _main_aspect_impl(target, ctx):
res = 'main_aspect on %s' % str(target.label)
return [MainAspectProvider(
main_aspect_val = res,
required_aspect_val = target[RequiredAspectProvider].val)]
main_aspect = aspect(
implementation = _main_aspect_impl,
requires = [required_aspect]
)
def _rule_impl(ctx):
pass
rule_with_toolchain = rule(
implementation = _rule_impl,
toolchains = ['//rule:toolchain_type_1'],
)
""");
scratch.file(
"test/BUILD",
"""
load('//test:defs.bzl', 'rule_with_toolchain')
rule_with_toolchain(name = 'target_with_toolchain')
rule_without_toolchain(name = 'target_without_toolchain')
""");
useConfiguration(
"--extra_toolchains=//toolchain:foo_toolchain",
"--incompatible_auto_exec_groups=" + autoExecGroups);
var analysisResult = update(ImmutableList.of("//test:defs.bzl%main_aspect"), "//test:all");
StarlarkProvider.Key providerKey =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:defs.bzl")), "MainAspectProvider");
// results on //test:target_with_toolchain
ConfiguredAspect aspectOnWithToolchainTarget =
getToplevelConfiguredAspect(
analysisResult, "//test:defs.bzl%main_aspect", "//test:target_with_toolchain");
var mainAspectValue =
((StarlarkInfo) aspectOnWithToolchainTarget.get(providerKey)).getValue("main_aspect_val");
assertThat((String) mainAspectValue).isEqualTo("main_aspect on @@//test:target_with_toolchain");
var requiredAspectValue =
((StarlarkInfo) aspectOnWithToolchainTarget.get(providerKey))
.getValue("required_aspect_val");
assertThat((Iterable<?>) requiredAspectValue)
.containsExactly(
"required_aspect on @@//test:target_with_toolchain",
"required_aspect on @@//toolchain:foo with tool in ToolchainInfo ="
+ " <generated file toolchain/atool>");
// test:target_without_toolchain
ConfiguredAspect aspectOnWithoutToolchainTarget =
getToplevelConfiguredAspect(
analysisResult, "//test:defs.bzl%main_aspect", "//test:target_without_toolchain");
mainAspectValue =
((StarlarkInfo) aspectOnWithoutToolchainTarget.get(providerKey))
.getValue("main_aspect_val");
assertThat((String) mainAspectValue)
.isEqualTo("main_aspect on @@//test:target_without_toolchain");
requiredAspectValue =
((StarlarkInfo) aspectOnWithoutToolchainTarget.get(providerKey))
.getValue("required_aspect_val");
assertThat((Iterable<?>) requiredAspectValue)
.containsExactly("required_aspect on @@//test:target_without_toolchain");
}
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 static ConfiguredAspect getToplevelConfiguredAspect(
AnalysisResult analysisResult, String aspectName, String targetLabel) {
return Iterables.getOnlyElement(
analysisResult.getAspectsMap().entrySet().stream()
.filter(
e ->
e.getKey().getAspectName().equals(aspectName)
&& e.getKey().getLabel().toString().equals(targetLabel))
.map(e -> e.getValue())
.collect(toImmutableList()));
}
}