From c5b0c79864d3940f8fca6ae60e2a59637011e0b0 Mon Sep 17 00:00:00 2001 From: gregce Date: Fri, 3 May 2019 14:13:34 -0700 Subject: [PATCH] cquery: support --output=build This outputs rules as they would appear in BUILD files, except selects() are replaced with the actual paths that get chosen. More details: https://docs.bazel.build/versions/master/query.html#output-build RELNOTES[NEW]: cquery supports --output=build PiperOrigin-RevId: 246571183 --- .../lib/query2/ConfiguredTargetAccessor.java | 14 + .../ConfiguredTargetQueryEnvironment.java | 61 ++--- .../CqueryBuildOutputFormatterCallback.java | 113 ++++++++ .../query2/output/BuildOutputFormatter.java | 203 ++++++++++++++ .../lib/query2/output/OutputFormatter.java | 184 ++----------- .../BuildOutputFormatterCallbackTest.java | 259 ++++++++++++++++++ 6 files changed, 647 insertions(+), 187 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/query2/CqueryBuildOutputFormatterCallback.java create mode 100644 src/main/java/com/google/devtools/build/lib/query2/output/BuildOutputFormatter.java create mode 100644 src/test/java/com/google/devtools/build/lib/query2/BuildOutputFormatterCallbackTest.java diff --git a/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetAccessor.java b/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetAccessor.java index 2063b57e844902..bdbd902cb4ef39 100644 --- a/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetAccessor.java +++ b/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetAccessor.java @@ -26,6 +26,7 @@ import com.google.devtools.build.lib.analysis.ToolchainContext; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; +import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.ConfiguredAttributeMapper; @@ -40,6 +41,7 @@ import com.google.devtools.build.lib.rules.AliasConfiguredTarget; import com.google.devtools.build.lib.skyframe.BuildConfigurationValue; import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; import com.google.devtools.build.lib.skyframe.PackageValue; import com.google.devtools.build.lib.skyframe.UnloadedToolchainContext; import com.google.devtools.build.skyframe.WalkableGraph; @@ -189,6 +191,18 @@ public static Target getTargetFromConfiguredTarget( return target; } + /** Returns the rule that generates the given output file. */ + public RuleConfiguredTarget getGeneratingConfiguredTarget(OutputFileConfiguredTarget oct) + throws InterruptedException { + return (RuleConfiguredTarget) + ((ConfiguredTargetValue) + walkableGraph.getValue( + ConfiguredTargetValue.key( + oct.getGeneratingRule().getLabel(), + queryEnvironment.getConfiguration(oct)))) + .getConfiguredTarget(); + } + @Nullable public ToolchainContext getToolchainContext(Target target, BuildConfiguration config) { return getToolchainContext(target, config, walkableGraph); diff --git a/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java index fcbc15f046196e..cbcdea158bde6e 100644 --- a/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java @@ -164,38 +164,35 @@ private static ImmutableList getCqueryFunctions() { PackageManager packageManager) { AspectResolver aspectResolver = cqueryOptions.aspectDeps.createResolver(packageManager, eventHandler); - return new ImmutableList.Builder>() - .add( - new LabelAndConfigurationOutputFormatterCallback( - eventHandler, cqueryOptions, out, skyframeExecutor, accessor)) - .add( - new TransitionsOutputFormatterCallback( - eventHandler, - cqueryOptions, - out, - skyframeExecutor, - accessor, - hostConfiguration, - trimmingTransitionFactory)) - .add( - new ProtoOutputFormatterCallback( - eventHandler, - cqueryOptions, - out, - skyframeExecutor, - accessor, - aspectResolver, - OutputType.BINARY)) - .add( - new ProtoOutputFormatterCallback( - eventHandler, - cqueryOptions, - out, - skyframeExecutor, - accessor, - aspectResolver, - OutputType.TEXT)) - .build(); + return ImmutableList.of( + new LabelAndConfigurationOutputFormatterCallback( + eventHandler, cqueryOptions, out, skyframeExecutor, accessor), + new TransitionsOutputFormatterCallback( + eventHandler, + cqueryOptions, + out, + skyframeExecutor, + accessor, + hostConfiguration, + trimmingTransitionFactory), + new ProtoOutputFormatterCallback( + eventHandler, + cqueryOptions, + out, + skyframeExecutor, + accessor, + aspectResolver, + OutputType.BINARY), + new ProtoOutputFormatterCallback( + eventHandler, + cqueryOptions, + out, + skyframeExecutor, + accessor, + aspectResolver, + OutputType.TEXT), + new CqueryBuildOutputFormatterCallback( + eventHandler, cqueryOptions, out, skyframeExecutor, accessor)); } public String getOutputFormat() { diff --git a/src/main/java/com/google/devtools/build/lib/query2/CqueryBuildOutputFormatterCallback.java b/src/main/java/com/google/devtools/build/lib/query2/CqueryBuildOutputFormatterCallback.java new file mode 100644 index 00000000000000..9f02ae5fd6fddb --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/CqueryBuildOutputFormatterCallback.java @@ -0,0 +1,113 @@ +// Copyright 2019 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 com.google.common.base.Verify; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget; +import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.ConfiguredAttributeMapper; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; +import com.google.devtools.build.lib.query2.output.BuildOutputFormatter; +import com.google.devtools.build.lib.query2.output.BuildOutputFormatter.AttributeReader; +import com.google.devtools.build.lib.query2.output.BuildOutputFormatter.TargetOutputter; +import com.google.devtools.build.lib.query2.output.CqueryOptions; +import com.google.devtools.build.lib.query2.output.OutputFormatter; +import com.google.devtools.build.lib.query2.output.OutputFormatter.PossibleAttributeValues; +import com.google.devtools.build.lib.rules.AliasConfiguredTarget; +import com.google.devtools.build.lib.skyframe.SkyframeExecutor; +import com.google.devtools.build.skyframe.BuildDriver; +import java.io.OutputStream; + +/** Cquery implementation of BUILD-style output. */ +public class CqueryBuildOutputFormatterCallback extends CqueryThreadsafeCallback { + CqueryBuildOutputFormatterCallback( + ExtendedEventHandler eventHandler, + CqueryOptions options, + OutputStream out, + SkyframeExecutor skyframeExecutor, + TargetAccessor accessor) { + super(eventHandler, options, out, skyframeExecutor, accessor); + } + + @Override + public String getName() { + return "build"; + } + + /** {@link AttributeReader} implementation that returns the exact value an attribute takes. */ + private static class CqueryAttributeReader implements AttributeReader { + private final ConfiguredAttributeMapper attributeMap; + + CqueryAttributeReader(ConfiguredAttributeMapper attributeMap) { + this.attributeMap = attributeMap; + } + + /** + * Cquery knows which select path is taken so it knows the exact value the attribute takes. Note + * that null values are also possible - these are represented as an empty value list. + */ + @Override + public PossibleAttributeValues getPossibleValues(Rule rule, Attribute attr) { + Object actualValue = attributeMap.get(attr.getName(), attr.getType()); + return new PossibleAttributeValues( + actualValue == null ? ImmutableList.of() : ImmutableList.of(actualValue), + OutputFormatter.getAttributeSource(rule, attr)); + } + } + + private ConfiguredAttributeMapper getAttributeMap(ConfiguredTarget ct) + throws InterruptedException { + Rule associatedRule = accessor.getTargetFromConfiguredTarget(ct).getAssociatedRule(); + if (associatedRule == null) { + return null; + } else if (ct instanceof AliasConfiguredTarget) { + return ConfiguredAttributeMapper.of( + associatedRule, ((AliasConfiguredTarget) ct).getConfigConditions()); + } else if (ct instanceof OutputFileConfiguredTarget) { + return ConfiguredAttributeMapper.of( + associatedRule, + accessor + .getGeneratingConfiguredTarget((OutputFileConfiguredTarget) ct) + .getConfigConditions()); + } else { + Verify.verify(ct instanceof RuleConfiguredTarget); + return ConfiguredAttributeMapper.of( + associatedRule, ((RuleConfiguredTarget) ct).getConfigConditions()); + } + } + + @Override + public void processOutput(Iterable partialResult) throws InterruptedException { + BuildOutputFormatter.TargetOutputter outputter = + new TargetOutputter( + printStream, + // This tells TargetOutputter which attributes to print as selects without resolving + // those selects. For cquery we never have to do this since we can always resolve + // selects. Going forward we could expand this to show both the complete select + // and which path is chosen, which people may find even more informative. + (rule, attr) -> false, + System.lineSeparator()); + for (ConfiguredTarget configuredTarget : partialResult) { + Target target = accessor.getTargetFromConfiguredTarget(configuredTarget); + outputter.output(target, new CqueryAttributeReader(getAttributeMap(configuredTarget))); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/query2/output/BuildOutputFormatter.java b/src/main/java/com/google/devtools/build/lib/query2/output/BuildOutputFormatter.java new file mode 100644 index 00000000000000..b6552b3a1568b6 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/output/BuildOutputFormatter.java @@ -0,0 +1,203 @@ +// Copyright 2014 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.output; + +import com.google.common.base.Ascii; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.BuildType; +import com.google.devtools.build.lib.packages.License; +import com.google.devtools.build.lib.packages.RawAttributeMapper; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.packages.TriState; +import com.google.devtools.build.lib.query2.engine.OutputFormatterCallback; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment; +import com.google.devtools.build.lib.query2.engine.SynchronizedDelegatingOutputFormatterCallback; +import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback; +import com.google.devtools.build.lib.query2.output.OutputFormatter.AbstractUnorderedFormatter; +import com.google.devtools.build.lib.syntax.EvalUtils; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.BiPredicate; + +/** + * An output formatter that prints the generating rules using the syntax of the BUILD files. If + * multiple targets are generated by the same rule, it is printed only once. + */ +public class BuildOutputFormatter extends AbstractUnorderedFormatter { + + /** + * Generic interface for determining the possible values for a given attribute on a given rule, as + * precisely as the implementation knows. For example, cquery knows which branch a select + * takes, while query doesn't. + */ + public interface AttributeReader { + PossibleAttributeValues getPossibleValues(Rule rule, Attribute attr); + } + + /** + * Generic logic for formatting a target BUILD-style. + * + *

This logic is implementation-agnostic. So it can be shared across query, cquery, and other + * implementations. + */ + public static class TargetOutputter { + private final PrintStream printStream; + private final BiPredicate preserveSelect; + private final String lineTerm; + private final Set

Since query doesn't know which select path should be chosen, this doesn't try to - * resolve the final value. Instead it just reconstructs the select. - */ - private String outputConfigurableAttrValue( - Rule rule, RawAttributeMapper attributeMap, Attribute attr) { - List selectors = new ArrayList<>(); - for (BuildType.Selector selector : - ((BuildType.SelectorList) attributeMap.getRawAttributeValue(rule, attr)) - .getSelectors()) { - if (selector.isUnconditional()) { - selectors.add( - outputAttrValue( - Iterables.getOnlyElement(selector.getEntries().entrySet()).getValue())); - } else { - selectors.add(String.format("select(%s)", outputAttrValue(selector.getEntries()))); - } - } - return String.join(" + ", selectors); - } - - @Override - public void processOutput(Iterable partialResult) throws InterruptedException { - - for (Target target : partialResult) { - Rule rule = target.getAssociatedRule(); - if (rule == null || printed.contains(rule.getLabel())) { - continue; - } - outputRule(rule, printStream); - printed.add(rule.getLabel()); - } - } - }; - } - @Override - public ThreadSafeOutputFormatterCallback createStreamCallback( - OutputStream out, QueryOptions options, QueryEnvironment env) { - return new SynchronizedDelegatingOutputFormatterCallback<>( - createPostFactoStreamCallback(out, options)); - } - } private static class RankAndLabel implements Comparable { private final int rank; @@ -732,14 +606,12 @@ int rank(Node>> node) { } } - /** - * Helper class for {@link #getPossibleAttributeValues}. - */ - static class PossibleAttributeValues implements Iterable { + /** Helper class for {@link #getPossibleAttributeValues}. */ + public static class PossibleAttributeValues implements Iterable { final Iterable values; final AttributeValueSource source; - PossibleAttributeValues(Iterable values, AttributeValueSource source) { + public PossibleAttributeValues(Iterable values, AttributeValueSource source) { this.values = values; this.source = source; } @@ -750,6 +622,22 @@ public Iterator iterator() { } } + public static AttributeValueSource getAttributeSource(Rule rule, Attribute attr) { + if (attr.getName().equals("visibility")) { + if (rule.isVisibilitySpecified()) { + return AttributeValueSource.RULE; + } else if (rule.getPackage().isDefaultVisibilitySet()) { + return AttributeValueSource.PACKAGE; + } else { + return AttributeValueSource.DEFAULT; + } + } else { + return rule.isAttributeValueExplicitlySpecified(attr) + ? AttributeValueSource.RULE + : AttributeValueSource.DEFAULT; + } + } + /** * Returns the possible values of the specified attribute in the specified rule. For simple * attributes, this is a single value. For configurable and computed attributes, this may be a @@ -771,28 +659,13 @@ public Iterator iterator() { * * which loses track of which label appears in which branch. * - *

This avoids the memory overruns that can happen be iterating over every possible value - * for an attr = select(...) + select(...) + select(...) + ... expression. Query + *

This avoids the memory overruns that can happen be iterating over every possible value for + * an attr = select(...) + select(...) + select(...) + ... expression. Query * operations generally don't care about specific attribute values - they just care which labels * are possible. */ - protected static PossibleAttributeValues getPossibleAttributeValues(Rule rule, Attribute attr) - throws InterruptedException { - AttributeValueSource source; - - if (attr.getName().equals("visibility")) { - if (rule.isVisibilitySpecified()) { - source = AttributeValueSource.RULE; - } else if (rule.getPackage().isDefaultVisibilitySet()) { - source = AttributeValueSource.PACKAGE; - } else { - source = AttributeValueSource.DEFAULT; - } - } else { - source = rule.isAttributeValueExplicitlySpecified(attr) - ? AttributeValueSource.RULE : AttributeValueSource.DEFAULT; - } - + protected static PossibleAttributeValues getPossibleAttributeValues(Rule rule, Attribute attr) { + AttributeValueSource source = getAttributeSource(rule, attr); AggregatingAttributeMapper attributeMap = AggregatingAttributeMapper.of(rule); Iterable list; if (attr.getType().equals(BuildType.LABEL_LIST) @@ -839,7 +712,8 @@ protected static String getLocation(Target target, boolean relative) { : location.print(); } - private static class LabelPrinter extends Printer.BasePrinter { + /** Prints labels in their canonical form. */ + public static class LabelPrinter extends Printer.BasePrinter { @Override public LabelPrinter repr(Object o) { if (o instanceof Label) { diff --git a/src/test/java/com/google/devtools/build/lib/query2/BuildOutputFormatterCallbackTest.java b/src/test/java/com/google/devtools/build/lib/query2/BuildOutputFormatterCallbackTest.java new file mode 100644 index 00000000000000..d9340d4fe46c7d --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/query2/BuildOutputFormatterCallbackTest.java @@ -0,0 +1,259 @@ +// Copyright 2019 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.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; +import static com.google.devtools.build.lib.packages.BuildType.OUTPUT; + +import com.google.common.eventbus.EventBus; +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.Reporter; +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.util.FileTypeSet; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; + +/** Tests cquery's BUILD output format. */ +public class BuildOutputFormatterCallbackTest extends ConfiguredTargetQueryTest { + + private CqueryOptions options; + private Reporter reporter; + private final List events = new ArrayList<>(); + + private static MockRule.State simpleRule() { + return MockRule.define( + "my_rule", + (builder, env) -> + builder + .add(attr("deps", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) + .add(attr("out", OUTPUT))); + } + + @Before + public final void setUpCqueryOptions() throws Exception { + this.options = new CqueryOptions(); + options.aspectDeps = Mode.OFF; + this.reporter = new Reporter(new EventBus(), events::add); + helper.useRuleClassProvider( + setRuleClassProviders(BuildOutputFormatterCallbackTest::simpleRule).build()); + } + + private List getOutput(String queryExpression) throws Exception { + QueryExpression expression = QueryParser.parse(queryExpression, getDefaultFunctions()); + Set targetPatternSet = new LinkedHashSet<>(); + expression.collectTargetPatterns(targetPatternSet); + helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS); + PostAnalysisQueryEnvironment env = + ((ConfiguredTargetQueryHelper) helper).getPostAnalysisQueryEnvironment(targetPatternSet); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + CqueryBuildOutputFormatterCallback callback = + new CqueryBuildOutputFormatterCallback( + reporter, + options, + new PrintStream(output), + getHelper().getSkyframeExecutor(), + env.getAccessor()); + env.evaluateQuery(expression, callback); + return Arrays.asList(output.toString().split(System.lineSeparator())); + } + + @Test + public void selectInAttribute() throws Exception { + 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'}", + ")"); + + getHelper().useConfiguration("--test_arg=cat"); + assertThat(getOutput("//test:my_rule")) + .containsExactly( + "# /workspace/test/BUILD:1:1", + "my_rule(", + " name = \"my_rule\",", + " deps = [\"//test:lasagna.java\", \"//test:naps.java\"],", + ")") + .inOrder(); + + getHelper().useConfiguration("--test_arg=hound"); + assertThat(getOutput("//test:my_rule")) + .containsExactly( + "# /workspace/test/BUILD:1:1", + "my_rule(", + " name = \"my_rule\",", + " deps = [\"//test:mondays.java\"],", + ")") + .inOrder(); + } + + @Test + public void alias() throws Exception { + 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'}", + ")", + "alias(", + " name = 'my_alias',", + " actual = ':my_rule'", + ")"); + + assertThat(getOutput("//test:my_alias")) + .containsExactly( + "# /workspace/test/BUILD:12:1", + "alias(", + " name = \"my_alias\",", + " actual = \"//test:my_rule\",", + ")") + .inOrder(); + } + + @Test + public void aliasWithSelect() throws Exception { + writeFile( + "test/BUILD", + "my_rule(", + " name = 'my_first_rule',", + " deps = ['penne.java'],", + ")", + "my_rule(", + " name = 'my_second_rule',", + " deps = ['linguini.java'],", + ")", + "config_setting(", + " name = 'garfield',", + " values = {'test_arg': 'cat'}", + ")", + "alias(", + " name = 'my_alias',", + " actual = select({", + " ':garfield': ':my_first_rule',", + " '//conditions:default': ':my_second_rule'", + " })", + ")"); + + getHelper().useConfiguration("--test_arg=cat"); + assertThat(getOutput("//test:my_alias")) + .containsExactly( + "# /workspace/test/BUILD:13:1", + "alias(", + " name = \"my_alias\",", + " actual = \"//test:my_first_rule\",", + ")") + .inOrder(); + + getHelper().useConfiguration("--test_arg=hound"); + assertThat(getOutput("//test:my_alias")) + .containsExactly( + "# /workspace/test/BUILD:13:1", + "alias(", + " name = \"my_alias\",", + " actual = \"//test:my_second_rule\",", + ")") + .inOrder(); + } + + @Test + public void sourceFile() throws Exception { + 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'}", + ")"); + + assertThat(getOutput("//test:lasagna.java")).containsExactly(""); + } + + @Test + public void outputFile() throws Exception { + writeFile( + "test/BUILD", + "my_rule(", + " name = 'my_rule',", + " deps = select({", + " ':garfield': ['lasagna.java', 'naps.java'],", + " '//conditions:default': ['mondays.java']", + " }),", + " out = 'output.txt'", + ")", + "config_setting(", + " name = 'garfield',", + " values = {'test_arg': 'cat'}", + ")"); + + getHelper().useConfiguration("--test_arg=cat"); + assertThat(getOutput("//test:output.txt")) + .containsExactly( + "# /workspace/test/BUILD:1:1", + "my_rule(", + " name = \"my_rule\",", + " deps = [\"//test:lasagna.java\", \"//test:naps.java\"],", + " out = \"//test:output.txt\",", + ")") + .inOrder(); + + getHelper().useConfiguration("--test_arg=hound"); + assertThat(getOutput("//test:output.txt")) + .containsExactly( + "# /workspace/test/BUILD:1:1", + "my_rule(", + " name = \"my_rule\",", + " deps = [\"//test:mondays.java\"],", + " out = \"//test:output.txt\",", + ")") + .inOrder(); + } +}