| // 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; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.devtools.build.lib.analysis.PlatformSemantics.RESOLVED_TOOLCHAINS_ATTR; |
| import static com.google.devtools.build.lib.packages.Attribute.attr; |
| import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; |
| |
| 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.AnalysisProtos; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| 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.query2.ProtoOutputFormatterCallback.OutputType; |
| import com.google.devtools.build.lib.query2.engine.ConfiguredTargetQueryHelper; |
| import com.google.devtools.build.lib.query2.engine.ConfiguredTargetQueryTest; |
| 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.output.AspectResolver.Mode; |
| import com.google.devtools.build.lib.query2.output.CqueryOptions; |
| import com.google.devtools.build.lib.query2.proto.proto2api.Build; |
| import com.google.devtools.build.lib.util.FileTypeSet; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| 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(juliexxia): 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(); |
| options.aspectDeps = Mode.OFF; |
| options.protoIncludeConfigurations = 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(RESOLVED_TOOLCHAINS_ATTR, LABEL_LIST) |
| .nonconfigurable("Used in toolchain resolution") |
| .value(ImmutableList.of())) |
| .add(attr("deps", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))); |
| helper.useRuleClassProvider(setRuleClassProviders(depsRule).build()); |
| |
| writeFile( |
| "test/BUILD", |
| "my_rule(name = 'my_rule',", |
| " deps = select({", |
| " ':garfield': ['lasagna.java', 'naps.java'],", |
| " '//conditions:default': ['mondays.java']", |
| " })", |
| ")", |
| "config_setting(", |
| " name = 'garfield',", |
| " values = {'test_arg': 'cat'}", |
| ")"); |
| |
| AnalysisProtos.ConfiguredTarget myRuleProto = |
| (Iterables.getOnlyElement(getOutput("//test:my_rule").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("--test_arg=cat"); |
| myRuleProto = Iterables.getOnlyElement(getOutput("//test:my_rule").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 |
| public void testConfigurationHash() throws Exception { |
| TestArgPatchTransition attributePatchTransition = new TestArgPatchTransition("SET BY PATCH"); |
| |
| MockRule ruleWithPatch = |
| () -> |
| MockRule.define( |
| "my_rule", |
| (builder, env) -> |
| builder |
| .add( |
| attr("deps", LABEL_LIST) |
| .allowedFileTypes(FileTypeSet.ANY_FILE) |
| .cfg(attributePatchTransition)) |
| .add( |
| attr(RESOLVED_TOOLCHAINS_ATTR, LABEL_LIST) |
| .nonconfigurable("Used in toolchain resolution") |
| .value(ImmutableList.of()))); |
| |
| helper.useRuleClassProvider(setRuleClassProviders(ruleWithPatch, getSimpleRule()).build()); |
| |
| writeFile( |
| "test/BUILD", |
| "my_rule(name = 'my_rule',", |
| " deps = [':patched'],", |
| ")", |
| "simple_rule(name = 'dep')"); |
| |
| // Assert checksum from proto is proper checksum. |
| AnalysisProtos.ConfiguredTarget myRuleProto = |
| Iterables.getOnlyElement(getOutput("//test:my_rule").getResultsList()); |
| ConfiguredTarget myRule = Iterables.getOnlyElement(eval("//test:my_rule")); |
| |
| assertThat(myRuleProto.getConfiguration().getChecksum()) |
| .isEqualTo(myRule.getConfigurationChecksum()); |
| |
| // Assert checksum for two configured targets in proto are not the same. |
| List<AnalysisProtos.ConfiguredTarget> protoDeps = |
| getOutput("deps(//test:my_rule)").getResultsList(); |
| assertThat(protoDeps).hasSize(2); |
| |
| Iterator<AnalysisProtos.ConfiguredTarget> protoDepsIterator = protoDeps.iterator(); |
| assertThat(protoDepsIterator.next().getConfiguration().getChecksum()) |
| .isNotEqualTo(protoDepsIterator.next().getConfiguration().getChecksum()); |
| } |
| |
| @Test |
| public void testAlias() throws Exception { |
| helper.useRuleClassProvider(setRuleClassProviders(getSimpleRule()).build()); |
| writeFile( |
| "test/BUILD", |
| "simple_rule(name = 'my_rule')", |
| "alias(name = 'my_alias', actual = ':my_rule')"); |
| |
| AnalysisProtos.ConfiguredTarget alias = |
| Iterables.getOnlyElement(getOutput("//test:my_alias").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 { |
| helper.useRuleClassProvider(setRuleClassProviders(getSimpleRule()).build()); |
| writeFile( |
| "test/BUILD", |
| "alias(", |
| " name = 'my_alias_rule',", |
| " actual = select({", |
| " ':config1': ':target1',", |
| " '//conditions:default': ':target2',", |
| " }),", |
| ")", |
| "config_setting(", |
| " name = 'config1',", |
| " values = {'test_arg': 'woof'},", |
| ")", |
| "simple_rule(name = 'target1')", |
| "simple_rule(name = 'target2')"); |
| getHelper().useConfiguration("--test_arg=woof"); |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| |
| List<AnalysisProtos.ConfiguredTarget> myAliasRuleProto = |
| getOutput("deps(//test:my_alias_rule)").getResultsList(); |
| |
| List<String> depNames = new ArrayList<>(myAliasRuleProto.size()); |
| myAliasRuleProto.forEach( |
| configuredTarget -> depNames.add(configuredTarget.getTarget().getRule().getName())); |
| assertThat(depNames) |
| .containsExactly("//test:my_alias_rule", "//test:config1", "//test:target1"); |
| } |
| |
| private MockRule getSimpleRule() { |
| return () -> |
| MockRule.define( |
| "simple_rule", |
| (builder, unusedEnv) -> |
| builder.add( |
| attr(RESOLVED_TOOLCHAINS_ATTR, LABEL_LIST) |
| .nonconfigurable("Used in toolchain resolution") |
| .value(ImmutableList.of()))); |
| } |
| |
| private AnalysisProtos.CqueryResult getOutput(String queryExpression) throws Exception { |
| QueryExpression expression = QueryParser.parse(queryExpression, getDefaultFunctions()); |
| Set<String> targetPatternSet = new LinkedHashSet<>(); |
| expression.collectTargetPatterns(targetPatternSet); |
| helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); |
| PostAnalysisQueryEnvironment<ConfiguredTarget> env = |
| ((ConfiguredTargetQueryHelper) helper).getPostAnalysisQueryEnvironment(targetPatternSet); |
| |
| ProtoOutputFormatterCallback callback = |
| new ProtoOutputFormatterCallback( |
| reporter, |
| options, |
| /*out=*/ null, |
| getHelper().getSkyframeExecutor(), |
| env.getAccessor(), |
| options.aspectDeps.createResolver( |
| getHelper().getPackageManager(), NullEventHandler.INSTANCE), |
| OutputType.BINARY); |
| env.evaluateQuery(expression, callback); |
| return callback.getProtoResult(); |
| } |
| } |