| // Copyright 2018 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.truth.Truth.assertThat; | |
| import static com.google.devtools.build.lib.skyframe.BzlLoadValue.keyForBuild; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.Iterables; | |
| import com.google.devtools.build.lib.actions.AbstractAction; | |
| import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; | |
| import com.google.devtools.build.lib.actions.Artifact; | |
| 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.cmdline.Label; | |
| import com.google.devtools.build.lib.packages.StarlarkProvider; | |
| import com.google.devtools.build.lib.packages.StructImpl; | |
| import java.util.List; | |
| import java.util.stream.Collectors; | |
| import net.starlark.java.eval.Dict; | |
| import net.starlark.java.eval.Sequence; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| /** Tests for the Starlark-accessible actions provider on rule configured targets. */ | |
| @RunWith(JUnit4.class) | |
| public class StarlarkActionProviderTest extends AnalysisTestCase { | |
| @Test | |
| public void aspectGetsActionProviderForNativeRule() throws Exception { | |
| scratch.file( | |
| "test/aspect.bzl", | |
| """ | |
| foo = provider() | |
| def _impl(target, ctx): | |
| return [foo(actions = target.actions)] | |
| MyAspect = aspect(implementation = _impl) | |
| """); | |
| scratch.file( | |
| "test/BUILD", | |
| """ | |
| genrule( | |
| name = "xxx", | |
| outs = ["mygen.out"], | |
| cmd = 'echo "hello" > $@', | |
| ) | |
| """); | |
| AnalysisResult analysisResult = | |
| update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx"); | |
| ConfiguredAspect configuredAspect = | |
| Iterables.getOnlyElement(analysisResult.getAspectsMap().values()); | |
| StarlarkProvider.Key fooKey = | |
| new StarlarkProvider.Key(keyForBuild(Label.parseCanonical("//test:aspect.bzl")), "foo"); | |
| StructImpl fooProvider = (StructImpl) configuredAspect.get(fooKey); | |
| assertThat(fooProvider.getValue("actions")).isNotNull(); | |
| @SuppressWarnings("unchecked") | |
| Sequence<ActionAnalysisMetadata> actions = | |
| (Sequence<ActionAnalysisMetadata>) fooProvider.getValue("actions"); | |
| assertThat(actions).isNotEmpty(); | |
| ActionAnalysisMetadata action = actions.get(0); | |
| assertThat(action.getMnemonic()).isEqualTo("Genrule"); | |
| assertThat(action).isInstanceOf(AbstractAction.class); | |
| } | |
| @Test | |
| @SuppressWarnings("unchecked") | |
| public void aspectGetsActionProviderForStarlarkRule() throws Exception { | |
| scratch.file( | |
| "test/aspect.bzl", | |
| """ | |
| foo = provider() | |
| def _impl(target, ctx): | |
| mnemonics = [a.mnemonic for a in target.actions] | |
| envs = [a.env for a in target.actions] | |
| execution_info = [a.execution_info for a in target.actions] | |
| inputs = [a.inputs.to_list() for a in target.actions] | |
| outputs = [a.outputs.to_list() for a in target.actions] | |
| argv = [a.argv for a in target.actions] | |
| return [foo( | |
| actions = target.actions, | |
| mnemonics = mnemonics, | |
| envs = envs, | |
| execution_info = execution_info, | |
| inputs = inputs, | |
| outputs = outputs, | |
| argv = argv, | |
| )] | |
| MyAspect = aspect(implementation = _impl) | |
| """); | |
| scratch.file( | |
| "test/rule.bzl", | |
| """ | |
| def impl(ctx): | |
| output_file0 = ctx.actions.declare_file("myfile0") | |
| output_file1 = ctx.actions.declare_file("myfile1") | |
| executable = ctx.actions.declare_file("executable") | |
| ctx.actions.run( | |
| outputs = [output_file0], | |
| executable = executable, | |
| toolchain = None, | |
| mnemonic = "MyAction0", | |
| env = {"foo": "bar", "pet": "puppy"}, | |
| ) | |
| ctx.actions.run_shell( | |
| outputs = [executable, output_file1], | |
| command = "fakecmd", | |
| mnemonic = "MyAction1", | |
| env = {"pet": "bunny"}, | |
| ) | |
| return None | |
| my_rule = rule(impl) | |
| """); | |
| scratch.file( | |
| "test/BUILD", | |
| """ | |
| load("//test:rule.bzl", "my_rule") | |
| my_rule( | |
| name = "xxx", | |
| ) | |
| """); | |
| useConfiguration("--experimental_google_legacy_api"); | |
| AnalysisResult analysisResult = | |
| update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx"); | |
| ConfiguredAspect configuredAspect = | |
| Iterables.getOnlyElement(analysisResult.getAspectsMap().values()); | |
| StarlarkProvider.Key fooKey = | |
| new StarlarkProvider.Key(keyForBuild(Label.parseCanonical("//test:aspect.bzl")), "foo"); | |
| StructImpl fooProvider = (StructImpl) configuredAspect.get(fooKey); | |
| assertThat(fooProvider.getValue("actions")).isNotNull(); | |
| Sequence<ActionAnalysisMetadata> actions = | |
| (Sequence<ActionAnalysisMetadata>) fooProvider.getValue("actions"); | |
| assertThat(actions).hasSize(2); | |
| Sequence<String> mnemonics = (Sequence<String>) fooProvider.getValue("mnemonics"); | |
| assertThat(mnemonics).containsExactly("MyAction0", "MyAction1"); | |
| Sequence<Dict<String, String>> envs = | |
| (Sequence<Dict<String, String>>) fooProvider.getValue("envs"); | |
| assertThat(envs) | |
| .containsExactly( | |
| Dict.builder().put("foo", "bar").put("pet", "puppy").buildImmutable(), | |
| Dict.builder().put("pet", "bunny").buildImmutable()); | |
| Sequence<Dict<String, String>> executionInfo = | |
| (Sequence<Dict<String, String>>) fooProvider.getValue("execution_info"); | |
| assertThat(executionInfo).isNotNull(); | |
| Sequence<Sequence<Artifact>> inputs = | |
| (Sequence<Sequence<Artifact>>) fooProvider.getValue("inputs"); | |
| assertThat(flattenArtifactNames(inputs)).containsExactly("executable"); | |
| Sequence<Sequence<Artifact>> outputs = | |
| (Sequence<Sequence<Artifact>>) fooProvider.getValue("outputs"); | |
| assertThat(flattenArtifactNames(outputs)).containsExactly("myfile0", "executable", "myfile1"); | |
| Sequence<Sequence<String>> argv = (Sequence<Sequence<String>>) fooProvider.getValue("argv"); | |
| assertThat(argv.get(0)).hasSize(1); | |
| assertThat(argv.get(0).get(0)).endsWith("executable"); | |
| assertThat(argv.get(1)).contains("fakecmd"); | |
| } | |
| private static List<String> flattenArtifactNames(Sequence<Sequence<Artifact>> artifactLists) { | |
| return artifactLists.stream() | |
| .flatMap(artifacts -> artifacts.stream()) | |
| .map(artifact -> artifact.getFilename()) | |
| .collect(Collectors.toList()); | |
| } | |
| } |