| // 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.query2.cquery; |
| |
| import static com.google.common.collect.ImmutableSet.toImmutableSet; |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat; |
| import static com.google.devtools.build.lib.packages.Attribute.attr; |
| import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.eventbus.EventBus; |
| import com.google.devtools.build.lib.analysis.AnalysisProtosV2; |
| import com.google.devtools.build.lib.analysis.AnalysisProtosV2.Configuration; |
| import com.google.devtools.build.lib.analysis.AnalysisProtosV2.Fragment; |
| import com.google.devtools.build.lib.analysis.AnalysisProtosV2.FragmentOptions; |
| import com.google.devtools.build.lib.analysis.AnalysisProtosV2.Option; |
| import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; |
| import com.google.devtools.build.lib.analysis.config.ExecutionTransitionFactory; |
| import com.google.devtools.build.lib.analysis.util.MockRule; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.NullEventHandler; |
| import com.google.devtools.build.lib.events.Reporter; |
| import com.google.devtools.build.lib.packages.LabelPrinter; |
| import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment; |
| import com.google.devtools.build.lib.query2.common.CqueryNode; |
| import com.google.devtools.build.lib.query2.cquery.CqueryOptions.Transitions; |
| import com.google.devtools.build.lib.query2.cquery.ProtoOutputFormatterCallback.OutputType; |
| import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Setting; |
| import com.google.devtools.build.lib.query2.engine.QueryExpression; |
| import com.google.devtools.build.lib.query2.engine.QueryParser; |
| import com.google.devtools.build.lib.query2.proto.proto2api.Build; |
| import com.google.devtools.build.lib.query2.proto.proto2api.Build.ConfiguredRuleInput; |
| import com.google.devtools.build.lib.query2.query.aspectresolvers.AspectResolver.Mode; |
| import com.google.devtools.build.lib.util.FileTypeSet; |
| import com.google.protobuf.ExtensionRegistry; |
| import com.google.protobuf.Message; |
| import com.google.protobuf.Parser; |
| import com.google.protobuf.TextFormat; |
| import com.google.protobuf.util.JsonFormat; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Test for cquery's proto output format. |
| * |
| * <p>TODO(blaze-configurability): refactor all cquery output format tests to consolidate duplicate |
| * infrastructure. |
| */ |
| public class ProtoOutputFormatterCallbackTest extends ConfiguredTargetQueryTest { |
| |
| private CqueryOptions options; |
| private Reporter reporter; |
| private final List<Event> events = new ArrayList<>(); |
| |
| @Before |
| public final void setUpCqueryOptions() { |
| this.options = new CqueryOptions(); |
| // TODO(bazel-team): reduce the confusion about these two seemingly similar settings. |
| // options.aspectDeps impacts how proto and similar output formatters output aspect results. |
| // Setting.INCLUDE_ASPECTS impacts whether or not aspect dependencies are included when |
| // following target deps. See CommonQueryOptions for further flag details. |
| options.aspectDeps = Mode.OFF; |
| helper.setQuerySettings(Setting.INCLUDE_ASPECTS); |
| options.protoIncludeConfigurations = true; |
| options.protoIncludeRuleInputsAndOutputs = true; |
| this.reporter = new Reporter(new EventBus(), events::add); |
| } |
| |
| @Test |
| public void testSelectInAttribute() throws Exception { |
| MockRule depsRule = |
| () -> |
| MockRule.define( |
| "my_rule", |
| (builder, env) -> |
| builder |
| .add(attr("deps", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))); |
| ConfiguredRuleClassProvider ruleClassProvider = setRuleClassProviders(depsRule).build(); |
| helper.useRuleClassProvider(ruleClassProvider); |
| |
| writeFile( |
| "test/BUILD", |
| """ |
| my_rule( |
| name = "my_rule", |
| deps = select({ |
| ":garfield": [ |
| "lasagna.java", |
| "naps.java", |
| ], |
| "//conditions:default": ["mondays.java"], |
| }), |
| ) |
| |
| config_setting( |
| name = "garfield", |
| values = {"foo": "cat"}, |
| ) |
| """); |
| |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| AnalysisProtosV2.ConfiguredTarget myRuleProto = |
| Iterables.getOnlyElement( |
| getProtoOutput("//test:my_rule", AnalysisProtosV2.CqueryResult.parser()) |
| .getResultsList()); |
| List<Build.Attribute> attributes = myRuleProto.getTarget().getRule().getAttributeList(); |
| for (Build.Attribute attribute : attributes) { |
| if (!attribute.getName().equals("deps")) { |
| continue; |
| } |
| assertThat(attribute.getStringListValueCount()).isEqualTo(1); |
| assertThat(attribute.getStringListValue(0)).isEqualTo("//test:mondays.java"); |
| break; |
| } |
| |
| getHelper().useConfiguration("--foo=cat"); |
| myRuleProto = |
| Iterables.getOnlyElement( |
| getProtoOutput("//test:my_rule", AnalysisProtosV2.CqueryResult.parser()) |
| .getResultsList()); |
| attributes = myRuleProto.getTarget().getRule().getAttributeList(); |
| for (Build.Attribute attribute : attributes) { |
| if (!attribute.getName().equals("deps")) { |
| continue; |
| } |
| assertThat(attribute.getStringListValueCount()).isEqualTo(2); |
| assertThat(attribute.getStringListValue(0)).isEqualTo("//test:lasagna.java"); |
| assertThat(attribute.getStringListValue(1)).isEqualTo("//test:naps.java"); |
| break; |
| } |
| } |
| |
| @Test |
| @SuppressWarnings("deprecation") // only use for tests |
| public void testConfigurations() throws Exception { |
| options.transitions = Transitions.LITE; |
| |
| MockRule ruleWithPatch = |
| () -> |
| MockRule.define( |
| "my_rule", |
| (builder, env) -> |
| builder.add( |
| attr("deps", LABEL_LIST) |
| .allowedFileTypes(FileTypeSet.ANY_FILE) |
| .cfg(ExecutionTransitionFactory.createFactory()))); |
| MockRule parentRuleClass = |
| () -> |
| MockRule.define( |
| "parent_rule", |
| (builder, env) -> |
| builder |
| .add(attr("deps", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) |
| .add(attr("srcs", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))); |
| |
| ConfiguredRuleClassProvider ruleClassProvider = |
| setRuleClassProviders(ruleWithPatch, parentRuleClass, getSimpleRule()).build(); |
| helper.useRuleClassProvider(ruleClassProvider); |
| |
| writeFile( |
| "test/BUILD", |
| """ |
| parent_rule( |
| name = "parent_rule", |
| srcs = ["parent.source"], |
| deps = [":transition_rule"], |
| ) |
| |
| my_rule( |
| name = "transition_rule", |
| deps = [ |
| ":dep", |
| ":patched", |
| ], |
| ) |
| |
| simple_rule(name = "dep") |
| """); |
| |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| AnalysisProtosV2.CqueryResult cqueryResult = |
| getProtoOutput("deps(//test:parent_rule)", AnalysisProtosV2.CqueryResult.parser()); |
| List<Configuration> configurations = cqueryResult.getConfigurationsList(); |
| assertThat(configurations).hasSize(2); |
| |
| List<AnalysisProtosV2.ConfiguredTarget> resultsList = cqueryResult.getResultsList(); |
| |
| AnalysisProtosV2.ConfiguredTarget parentRuleProto = |
| getRuleProtoByName(resultsList, "//test:parent_rule"); |
| Set<CqueryNode> keyedTargets = eval("deps(//test:parent_rule)"); |
| |
| CqueryNode parentRule = getKeyedTargetByLabel(keyedTargets, "//test:parent_rule"); |
| assertThat(parentRuleProto.getConfiguration().getChecksum()) |
| .isEqualTo(parentRule.getConfigurationChecksum()); |
| |
| Configuration parentConfiguration = |
| getConfigurationForId(configurations, parentRuleProto.getConfigurationId()); |
| assertThat(parentConfiguration.getChecksum()).isEqualTo(parentRule.getConfigurationChecksum()); |
| assertThat(parentConfiguration) |
| .ignoringFieldDescriptors( |
| Configuration.getDescriptor().findFieldByName("checksum"), |
| Configuration.getDescriptor().findFieldByName("id"), |
| Configuration.getDescriptor().findFieldByName("fragments"), |
| Configuration.getDescriptor().findFieldByName("id"), |
| Configuration.getDescriptor().findFieldByName("fragment_options")) |
| .isEqualTo( |
| Configuration.newBuilder() |
| .setMnemonic("k8-fastbuild") |
| .setPlatformName("k8") |
| .setIsTool(false) |
| .build()); |
| |
| List<Fragment> fragmentsList = parentConfiguration.getFragmentsList(); |
| |
| assertThat(fragmentsList.stream().map(Fragment::getName)).isInOrder(); |
| assertThat(fragmentsList) |
| .contains( |
| Fragment.newBuilder() |
| .setName("com.google.devtools.build.lib.rules.cpp.CppConfiguration") |
| .addFragmentOptionNames( |
| "com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions") |
| .addFragmentOptionNames("com.google.devtools.build.lib.rules.cpp.CppOptions") |
| .build()); |
| |
| List<FragmentOptions> fragmentOptionsList = parentConfiguration.getFragmentOptionsList(); |
| assertThat(fragmentOptionsList.stream().map(FragmentOptions::getName)).isInOrder(); |
| |
| FragmentOptions appleFragmentOptions = |
| fragmentOptionsList.stream() |
| .filter( |
| fo -> |
| fo.getName() |
| .equals( |
| "com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions")) |
| .findFirst() |
| .get(); |
| assertThat(appleFragmentOptions.getName()) |
| .isEqualTo("com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions"); |
| assertThat(appleFragmentOptions.getOptionsList()) |
| .contains(Option.newBuilder().setName("apple_platform_type").setValue("macos").build()); |
| |
| assertThat(appleFragmentOptions.getOptionsList().stream().map(Option::getName)).isInOrder(); |
| |
| FragmentOptions cppFragmentOptions = |
| fragmentOptionsList.stream() |
| .filter(fo -> fo.getName().equals("com.google.devtools.build.lib.rules.cpp.CppOptions")) |
| .findFirst() |
| .get(); |
| assertThat(cppFragmentOptions.getName()) |
| .isEqualTo("com.google.devtools.build.lib.rules.cpp.CppOptions"); |
| assertThat(cppFragmentOptions.getOptionsList()) |
| .contains(Option.newBuilder().setName("dynamic_mode").setValue("DEFAULT").build()); |
| |
| assertThat(cppFragmentOptions.getOptionsList().stream().map(Option::getName)).isInOrder(); |
| |
| AnalysisProtosV2.ConfiguredTarget transitionRuleProto = |
| getRuleProtoByName(resultsList, "//test:transition_rule"); |
| CqueryNode transitionRule = getKeyedTargetByLabel(keyedTargets, "//test:transition_rule"); |
| assertThat(transitionRuleProto.getConfiguration().getChecksum()) |
| .isEqualTo(transitionRule.getConfigurationChecksum()); |
| |
| Configuration transitionConfiguration = |
| getConfigurationForId(configurations, transitionRuleProto.getConfigurationId()); |
| assertThat(transitionConfiguration.getChecksum()) |
| .isEqualTo(transitionRule.getConfigurationChecksum()); |
| |
| AnalysisProtosV2.ConfiguredTarget depRuleProto = getRuleProtoByName(resultsList, "//test:dep"); |
| Configuration depRuleConfiguration = |
| getConfigurationForId(configurations, depRuleProto.getConfigurationId()); |
| assertThat(depRuleConfiguration.getPlatformName()).isEqualTo("k8"); |
| assertThat(depRuleConfiguration.getMnemonic()).matches("k8-opt-exec-.*"); |
| assertThat(depRuleConfiguration.getIsTool()).isTrue(); |
| |
| CqueryNode depRule = getKeyedTargetByLabel(keyedTargets, "//test:dep"); |
| |
| assertThat(depRuleProto.getConfiguration().getChecksum()) |
| .isEqualTo(depRule.getConfigurationChecksum()); |
| |
| // Assert the proto checksums for targets in different configurations are not the same. |
| assertThat(depRuleConfiguration.getChecksum()) |
| .isNotEqualTo(transitionConfiguration.getChecksum()); |
| // Targets without a configuration have a configuration_id of 0. |
| AnalysisProtosV2.ConfiguredTarget fileTargetProto = |
| resultsList.stream() |
| .filter(result -> result.getTarget().getSourceFile().getName().equals("//test:patched")) |
| .findAny() |
| .orElseThrow(); |
| assertThat(fileTargetProto.getConfigurationId()).isEqualTo(0); |
| |
| assertThat(parentRuleProto.getTarget().getRule().getConfiguredRuleInputList()) |
| .containsExactly( |
| // Targets whose deps have no transitions should appear with identifical configuration |
| // information to their parent: |
| ConfiguredRuleInput.newBuilder() |
| .setLabel("//test:transition_rule") |
| .setConfigurationChecksum(parentRuleProto.getConfiguration().getChecksum()) |
| .setConfigurationId(parentRuleProto.getConfigurationId()) |
| .build(), |
| // Source file deps have no configurations: |
| ConfiguredRuleInput.newBuilder().setLabel("//test:parent.source").build()); |
| |
| // Targets with deps with transitions should show distinct configurations. |
| ConfiguredRuleInput patchedConfiguredRuleInput = |
| ConfiguredRuleInput.newBuilder().setLabel("//test:patched").build(); |
| ConfiguredRuleInput depConfiguredRuleInput = |
| ConfiguredRuleInput.newBuilder() |
| .setLabel("//test:dep") |
| .setConfigurationChecksum(depRuleProto.getConfiguration().getChecksum()) |
| .setConfigurationId(depRuleProto.getConfigurationId()) |
| .build(); |
| List<ConfiguredRuleInput> configuredRuleInputs = |
| transitionRuleProto.getTarget().getRule().getConfiguredRuleInputList(); |
| assertThat(configuredRuleInputs) |
| .containsExactly(patchedConfiguredRuleInput, depConfiguredRuleInput); |
| } |
| |
| @Test |
| public void configuredRuleInputsFromAspects() throws Exception { |
| options.transitions = Transitions.LITE; |
| writeFile( |
| "test/BUILD", |
| """ |
| load(":defs.bzl", "my_rule") |
| my_rule( |
| name = "parent", |
| deps = [":child"], |
| ) |
| my_rule(name = "child") |
| my_rule(name = "aspect_exec_config_dep") |
| my_rule(name = "aspect_same_config_dep") |
| """); |
| writeFile( |
| "test/defs.bzl", |
| """ |
| my_aspect = aspect( |
| implementation = lambda target, ctx: [], |
| attr_aspects = ["deps"], |
| attrs = { |
| "_aspect_exec_deps": attr.label_list( |
| cfg = "exec", |
| default = [":aspect_exec_config_dep"] |
| ), |
| "_aspect_deps": attr.label_list(default = [":aspect_same_config_dep"]), |
| } |
| ) |
| my_rule = rule( |
| implementation = lambda ctx: [], |
| attrs = { "deps": attr.label_list(aspects = [my_aspect]) } |
| ) |
| """); |
| |
| helper.setQuerySettings(Setting.INCLUDE_ASPECTS); |
| AnalysisProtosV2.CqueryResult cqueryResult = |
| getProtoOutput("deps(//test:parent)", AnalysisProtosV2.CqueryResult.parser()); |
| List<Configuration> configurations = cqueryResult.getConfigurationsList(); |
| assertThat(configurations).hasSize(3); // Target config, exec config, host platform config. |
| |
| List<AnalysisProtosV2.ConfiguredTarget> resultsList = cqueryResult.getResultsList(); |
| AnalysisProtosV2.ConfiguredTarget parentRuleProto = |
| getRuleProtoByName(resultsList, "//test:parent"); |
| AnalysisProtosV2.ConfiguredTarget directDepProto = |
| getRuleProtoByName(resultsList, "//test:child"); |
| AnalysisProtosV2.ConfiguredTarget aspectDepSameConfigProto = |
| getRuleProtoByName(resultsList, "//test:aspect_same_config_dep"); |
| AnalysisProtosV2.ConfiguredTarget aspectDepExecConfigProto = |
| getRuleProtoByName(resultsList, "//test:aspect_exec_config_dep"); |
| |
| assertThat(parentRuleProto.getTarget().getRule().getConfiguredRuleInputList()) |
| .containsAtLeast( |
| ConfiguredRuleInput.newBuilder() |
| .setLabel("//test:child") |
| .setConfigurationChecksum( |
| getConfigurationForId( |
| cqueryResult.getConfigurationsList(), |
| directDepProto.getConfigurationId()) |
| .getChecksum()) |
| .setConfigurationId(directDepProto.getConfigurationId()) |
| .build(), |
| ConfiguredRuleInput.newBuilder() |
| .setLabel("//test:aspect_same_config_dep") |
| .setConfigurationChecksum( |
| getConfigurationForId( |
| cqueryResult.getConfigurationsList(), |
| aspectDepSameConfigProto.getConfigurationId()) |
| .getChecksum()) |
| .setConfigurationId(aspectDepSameConfigProto.getConfigurationId()) |
| .build(), |
| ConfiguredRuleInput.newBuilder() |
| .setLabel("//test:aspect_exec_config_dep") |
| .setConfigurationChecksum( |
| getConfigurationForId( |
| cqueryResult.getConfigurationsList(), |
| aspectDepExecConfigProto.getConfigurationId()) |
| .getChecksum()) |
| .setConfigurationId(aspectDepExecConfigProto.getConfigurationId()) |
| .build()); |
| |
| assertThat(parentRuleProto.getConfigurationId()).isEqualTo(directDepProto.getConfigurationId()); |
| assertThat(parentRuleProto.getConfigurationId()) |
| .isEqualTo(aspectDepSameConfigProto.getConfigurationId()); |
| assertThat(parentRuleProto.getConfigurationId()) |
| .isNotEqualTo(aspectDepExecConfigProto.getConfigurationId()); |
| } |
| |
| /** Tests an alias's output. */ |
| @Test |
| public void aliasOutput() throws Exception { |
| writeFile( |
| "fake_licenses/BUILD", |
| """ |
| load("//test:defs.bzl", "my_rule") |
| my_rule(name = "license") |
| """); |
| writeFile( |
| "test/BUILD", |
| """ |
| load(":defs.bzl", "my_rule") |
| package( |
| default_applicable_licenses = ["//fake_licenses:license"], |
| ) |
| alias( |
| name = "my_alias", |
| actual = ":my_target", |
| ) |
| my_rule(name = "my_target") |
| """); |
| writeFile( |
| "test/defs.bzl", |
| """ |
| my_rule = rule( |
| implementation = lambda ctx: [], |
| attrs = {}, |
| ) |
| """); |
| |
| options.transitions = Transitions.LITE; |
| AnalysisProtosV2.CqueryResult cqueryResult = |
| getProtoOutput("deps(//test:my_alias)", AnalysisProtosV2.CqueryResult.parser()); |
| |
| AnalysisProtosV2.ConfiguredTarget aliasProto = |
| getRuleProtoByName(cqueryResult.getResultsList(), "//test:my_alias"); |
| AnalysisProtosV2.ConfiguredTarget actualProto = |
| getRuleProtoByName(cqueryResult.getResultsList(), "//test:my_target"); |
| |
| // Expect the alias's "name" field references the alias's label, not its actual. |
| assertThat(aliasProto.getTarget().getRule().getName()).isEqualTo("//test:my_alias"); |
| assertThat(aliasProto.getTarget().getRule().getRuleInputList()) |
| .containsExactly("//test:my_target"); |
| assertThat(aliasProto.getTarget().getRule().getConfiguredRuleInputList()) |
| .containsAtLeast( |
| ConfiguredRuleInput.newBuilder() |
| .setLabel("//test:my_target") |
| .setConfigurationChecksum( |
| getConfigurationForId( |
| cqueryResult.getConfigurationsList(), actualProto.getConfigurationId()) |
| .getChecksum()) |
| .setConfigurationId(actualProto.getConfigurationId()) |
| .build(), |
| ConfiguredRuleInput.newBuilder() |
| .setLabel("//fake_licenses:license") |
| // Don't use the aliases' configuration because top-level aliases include test |
| // configuration, which all non-test deps trim out. |
| .setConfigurationChecksum( |
| getConfigurationForId( |
| cqueryResult.getConfigurationsList(), actualProto.getConfigurationId()) |
| .getChecksum()) |
| .setConfigurationId(actualProto.getConfigurationId()) |
| .build()); |
| } |
| |
| /** Tests output where one of the deps is an alias. */ |
| @Test |
| public void outputOnAliasDep() throws Exception { |
| writeFile( |
| "test/BUILD", |
| """ |
| load(":defs.bzl", "my_rule") |
| my_rule( |
| name = "my_target", |
| deps = [":my_alias"], |
| ) |
| alias( |
| name = "my_alias", |
| actual = ":my_child", |
| ) |
| my_rule(name = "my_child") |
| """); |
| writeFile( |
| "test/defs.bzl", |
| """ |
| my_rule = rule( |
| implementation = lambda ctx: [], |
| attrs = { "deps": attr.label_list() }, |
| ) |
| """); |
| |
| options.transitions = Transitions.LITE; |
| AnalysisProtosV2.CqueryResult cqueryResult = |
| getProtoOutput("deps(//test:my_target)", AnalysisProtosV2.CqueryResult.parser()); |
| Build.Rule targetRule = |
| getRuleProtoByName(cqueryResult.getResultsList(), "//test:my_target").getTarget().getRule(); |
| |
| assertThat(targetRule.getRuleInputList()).contains("//test:my_alias"); |
| assertThat(targetRule.getRuleInputList()).doesNotContain("//test:my_child"); |
| assertThat(targetRule.getConfiguredRuleInputList().stream().map(s -> s.getLabel())) |
| .contains("//test:my_alias"); |
| assertThat(targetRule.getConfiguredRuleInputList().stream().map(s -> s.getLabel())) |
| .doesNotContain("//test:my_child"); |
| } |
| |
| private CqueryNode getKeyedTargetByLabel(Set<CqueryNode> keyedTargets, String label) { |
| return Iterables.getOnlyElement( |
| keyedTargets.stream() |
| .filter(t -> label.equals(t.getLabel().getCanonicalForm())) |
| .collect(toImmutableSet())); |
| } |
| |
| private Configuration getConfigurationForId(List<Configuration> configurations, int id) { |
| return configurations.stream().filter(c -> c.getId() == id).findAny().orElseThrow(); |
| } |
| |
| private AnalysisProtosV2.ConfiguredTarget getRuleProtoByName( |
| List<AnalysisProtosV2.ConfiguredTarget> resultsList, String s) { |
| return resultsList.stream() |
| .filter(result -> s.equals(result.getTarget().getRule().getName())) |
| .findAny() |
| .orElseThrow(); |
| } |
| |
| @Test |
| public void testAlias() throws Exception { |
| ConfiguredRuleClassProvider ruleClassProvider = setRuleClassProviders(getSimpleRule()).build(); |
| helper.useRuleClassProvider(ruleClassProvider); |
| |
| writeFile( |
| "test/BUILD", |
| """ |
| simple_rule(name = "my_rule") |
| |
| alias( |
| name = "my_alias", |
| actual = ":my_rule", |
| ) |
| """); |
| |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| AnalysisProtosV2.ConfiguredTarget alias = |
| Iterables.getOnlyElement( |
| getProtoOutput("//test:my_alias", AnalysisProtosV2.CqueryResult.parser()) |
| .getResultsList()); |
| |
| assertThat(alias.getTarget().getRule().getName()).isEqualTo("//test:my_alias"); |
| assertThat(alias.getTarget().getRule().getRuleInputCount()).isEqualTo(1); |
| assertThat(alias.getTarget().getRule().getRuleInput(0)).isEqualTo("//test:my_rule"); |
| } |
| |
| /* See b/209787345 for context. */ |
| @Test |
| public void testAlias_withSelect() throws Exception { |
| ConfiguredRuleClassProvider ruleClassProvider = setRuleClassProviders(getSimpleRule()).build(); |
| helper.useRuleClassProvider(ruleClassProvider); |
| |
| writeFile( |
| "test/BUILD", |
| """ |
| alias( |
| name = "my_alias_rule", |
| actual = select({ |
| ":config1": ":target1", |
| "//conditions:default": ":target2", |
| }), |
| ) |
| |
| config_setting( |
| name = "config1", |
| values = {"foo": "woof"}, |
| ) |
| |
| simple_rule(name = "target1") |
| |
| simple_rule(name = "target2") |
| """); |
| getHelper().useConfiguration("--foo=woof"); |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| List<AnalysisProtosV2.ConfiguredTarget> myAliasRuleProto = |
| getProtoOutput( |
| "deps(//test:my_alias_rule)", |
| AnalysisProtosV2.CqueryResult.parser()) |
| .getResultsList(); |
| |
| List<String> depNames = new ArrayList<>(myAliasRuleProto.size()); |
| myAliasRuleProto.forEach( |
| configuredTarget -> depNames.add(configuredTarget.getTarget().getRule().getName())); |
| assertThat(depNames) |
| // The alias also includes platform info since aliases with select() trigger toolchain |
| // resolution. We're not interested in those here. |
| .containsAtLeast("//test:my_alias_rule", "//test:config1", "//test:target1"); |
| } |
| |
| @Test |
| public void testAllOutputFormatsEquivalentToProtoOutput() throws Exception { |
| MockRule depsRule = |
| () -> |
| MockRule.define( |
| "my_rule", |
| (builder, env) -> |
| builder.add(attr("deps", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))); |
| ConfiguredRuleClassProvider ruleClassProvider = setRuleClassProviders(depsRule).build(); |
| helper.useRuleClassProvider(ruleClassProvider); |
| |
| writeFile( |
| "test/BUILD", |
| """ |
| my_rule( |
| name = "my_rule", |
| deps = [ |
| "lasagna.java", |
| "naps.java", |
| ], |
| ) |
| """); |
| AnalysisProtosV2.CqueryResult prototype = AnalysisProtosV2.CqueryResult.getDefaultInstance(); |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| AnalysisProtosV2.CqueryResult protoOutput = |
| getProtoOutput("//test:*", prototype.getParserForType()); |
| |
| AnalysisProtosV2.CqueryResult textprotoOutput = |
| getProtoFromTextprotoOutput("//test:*", prototype); |
| |
| AnalysisProtosV2.CqueryResult jsonprotoOutput = |
| getProtoFromJsonprotoOutput("//test:*", prototype); |
| |
| ImmutableList<AnalysisProtosV2.CqueryResult> streamedProtoOutput = |
| getStreamedProtoOutput("//test:*", prototype.getParserForType()); |
| AnalysisProtosV2.CqueryResult.Builder combinedStreamedProtoBuilder = |
| AnalysisProtosV2.CqueryResult.newBuilder(); |
| for (AnalysisProtosV2.CqueryResult result : streamedProtoOutput) { |
| if (!result.getResultsList().isEmpty()) { |
| combinedStreamedProtoBuilder.addAllResults(result.getResultsList()); |
| } |
| if (!result.getConfigurationsList().isEmpty()) { |
| combinedStreamedProtoBuilder.addAllConfigurations(result.getConfigurationsList()); |
| } |
| } |
| |
| assertThat(textprotoOutput).ignoringRepeatedFieldOrder().isEqualTo(protoOutput); |
| assertThat(jsonprotoOutput).ignoringRepeatedFieldOrder().isEqualTo(protoOutput); |
| assertThat(combinedStreamedProtoBuilder.build()) |
| .ignoringRepeatedFieldOrder() |
| .isEqualTo(protoOutput); |
| } |
| |
| @Test |
| public void testAllOutputFormatsEquivalentToProtoOutput_noIncludeConfigurations() |
| throws Exception { |
| options.protoIncludeConfigurations = false; |
| MockRule depsRule = |
| () -> |
| MockRule.define( |
| "my_rule", |
| (builder, env) -> |
| builder.add(attr("deps", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))); |
| ConfiguredRuleClassProvider ruleClassProvider = setRuleClassProviders(depsRule).build(); |
| helper.useRuleClassProvider(ruleClassProvider); |
| |
| writeFile( |
| "test/BUILD", |
| """ |
| my_rule( |
| name = "my_rule", |
| deps = [ |
| "lasagna.java", |
| "naps.java", |
| ], |
| ) |
| """); |
| Build.QueryResult prototype = Build.QueryResult.getDefaultInstance(); |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| Build.QueryResult protoOutput = getProtoOutput("//test:*", prototype.getParserForType()); |
| |
| Build.QueryResult textprotoOutput = getProtoFromTextprotoOutput("//test:*", prototype); |
| |
| Build.QueryResult jsonprotoOutput = getProtoFromJsonprotoOutput("//test:*", prototype); |
| |
| ImmutableList<Build.QueryResult> streamedProtoOutput = |
| getStreamedProtoOutput("//test:*", prototype.getParserForType()); |
| Build.QueryResult.Builder combinedStreamedProtoBuilder = Build.QueryResult.newBuilder(); |
| for (Build.QueryResult result : streamedProtoOutput) { |
| if (!result.getTargetList().isEmpty()) { |
| combinedStreamedProtoBuilder.addAllTarget(result.getTargetList()); |
| } |
| } |
| |
| assertThat(textprotoOutput).ignoringRepeatedFieldOrder().isEqualTo(protoOutput); |
| assertThat(jsonprotoOutput).ignoringRepeatedFieldOrder().isEqualTo(protoOutput); |
| assertThat(combinedStreamedProtoBuilder.build()) |
| .ignoringRepeatedFieldOrder() |
| .isEqualTo(protoOutput); |
| } |
| |
| private MockRule getSimpleRule() { |
| return () -> MockRule.define("simple_rule"); |
| } |
| |
| private <T extends Message> T getProtoOutput(String queryExpression, Parser<T> parser) |
| throws Exception { |
| InputStream in = queryAndGetInputStream(queryExpression, OutputType.BINARY); |
| return parser.parseFrom(in, ExtensionRegistry.getEmptyRegistry()); |
| } |
| |
| private <T extends Message> ImmutableList<T> getStreamedProtoOutput( |
| String queryExpression, Parser<T> parser) throws Exception { |
| InputStream in = queryAndGetInputStream(queryExpression, OutputType.DELIMITED_BINARY); |
| ImmutableList.Builder<T> builder = new ImmutableList.Builder<>(); |
| T result; |
| while ((result = parser.parseDelimitedFrom(in, ExtensionRegistry.getEmptyRegistry())) != null) { |
| builder.add(result); |
| } |
| return builder.build(); |
| } |
| |
| private <T extends Message> T getProtoFromTextprotoOutput(String queryExpression, T prototype) |
| throws Exception { |
| InputStream in = queryAndGetInputStream(queryExpression, OutputType.TEXT); |
| Message.Builder builder = prototype.newBuilderForType(); |
| TextFormat.getParser().merge(new InputStreamReader(in, UTF_8), builder); |
| @SuppressWarnings("unchecked") |
| T message = (T) builder.build(); |
| return message; |
| } |
| |
| private <T extends Message> T getProtoFromJsonprotoOutput(String queryExpression, T prototype) |
| throws Exception { |
| InputStream in = queryAndGetInputStream(queryExpression, OutputType.JSON); |
| Message.Builder builder = prototype.newBuilderForType(); |
| JsonFormat.parser().merge(new InputStreamReader(in, UTF_8), builder); |
| @SuppressWarnings("unchecked") |
| T message = (T) builder.build(); |
| return message; |
| } |
| |
| private InputStream queryAndGetInputStream(String queryExpression, OutputType outputType) |
| throws Exception { |
| QueryExpression expression = QueryParser.parse(queryExpression, getDefaultFunctions()); |
| Set<String> targetPatternSet = new LinkedHashSet<>(); |
| expression.collectTargetPatterns(targetPatternSet); |
| PostAnalysisQueryEnvironment<CqueryNode> env = |
| ((ConfiguredTargetQueryHelper) helper).getPostAnalysisQueryEnvironment(targetPatternSet); |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| ProtoOutputFormatterCallback callback = |
| new ProtoOutputFormatterCallback( |
| reporter, |
| options, |
| out, |
| getHelper().getSkyframeExecutor(), |
| env.getAccessor(), |
| options.aspectDeps.createResolver( |
| getHelper().getPackageManager(), NullEventHandler.INSTANCE), |
| outputType, |
| LabelPrinter.legacy()); |
| env.evaluateQuery(expression, callback); |
| return new ByteArrayInputStream(out.toByteArray()); |
| } |
| } |