aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar juliexxia <juliexxia@google.com>2018-01-12 18:05:46 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2018-01-12 18:07:55 -0800
commit07c4e3648e3b7a521b6706ffc5cbcdeca1ebf4e1 (patch)
tree1c7f4fdec231146e25ef4efc71f91a7da344adc5
parentb178e89e9ec079ecde901e4fca113c88e8aad317 (diff)
Introduce the blaze cquery command as the new ui for configurable query.
before: blaze build --nobuild //foo --experimental_post_build_query="deps(//foo)" after: blaze cquery "deps(//foo)" pros of ui change: - more concise - assumes query expression targets == targets to be built (but allows for flexibility through --top_level_targets flag) - separate from build command - cquery command recognizes query options, build options, and its own unique set of options cons of ui change: - adds another command to blaze - recognizes options that don't actually work yet -> requires more option validation RELNOTES: None PiperOrigin-RevId: 181816980
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD1
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java20
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java16
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java49
-rw-r--r--src/main/java/com/google/devtools/build/lib/query2/BUILD12
-rw-r--r--src/main/java/com/google/devtools/build/lib/query2/CommonQueryOptions.java90
-rw-r--r--src/main/java/com/google/devtools/build/lib/query2/output/QueryOptions.java65
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/BuiltinCommandModule.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java110
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/commands/cquery.txt8
-rwxr-xr-xsrc/test/shell/integration/configured_query_test.sh102
11 files changed, 364 insertions, 113 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 5f5ff0a255..a350bb06a7 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -1109,6 +1109,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/profiler/memory:allocationtracker",
"//src/main/java/com/google/devtools/build/lib/query2",
"//src/main/java/com/google/devtools/build/lib/query2:abstract-blaze-query-env",
+ "//src/main/java/com/google/devtools/build/lib/query2:common-query-options",
"//src/main/java/com/google/devtools/build/lib/query2:query-engine",
"//src/main/java/com/google/devtools/build/lib/query2:query-output",
"//src/main/java/com/google/devtools/build/lib/shell",
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java
index 35328c1908..6d400c6699 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java
@@ -28,6 +28,7 @@ import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.packages.SkylarkSemanticsOptions;
import com.google.devtools.build.lib.pkgcache.LoadingOptions;
import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
+import com.google.devtools.build.lib.query2.engine.QueryExpression;
import com.google.devtools.build.lib.runtime.BlazeCommandEventHandler;
import com.google.devtools.build.lib.runtime.KeepGoingOption;
import com.google.devtools.build.lib.runtime.LoadingPhaseThreadsOption;
@@ -40,6 +41,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
+import javax.annotation.Nullable;
/**
* A BuildRequest represents a single invocation of the build tool by a user.
@@ -69,6 +71,8 @@ public class BuildRequest implements OptionsClassProvider {
private boolean runningInEmacs;
private boolean runTests;
+ private QueryExpression queryExpression = null;
+
private static final ImmutableList<Class<? extends OptionsBase>> MANDATORY_OPTIONS =
ImmutableList.of(
BuildRequestOptions.class,
@@ -167,6 +171,22 @@ public class BuildRequest implements OptionsClassProvider {
return outErr;
}
+ /**
+ * If this BuildRequest was created as part of a cquery, return the query expression, if not this
+ * will return null. TODO(juliexxia): find a better way to get this information through to
+ * BuildTool without polluting BuildRequest (like put into constructor of BuildTool or refactor to
+ * have a cquery specific BuildTool).
+ */
+ @Nullable
+ public QueryExpression getQueryExpression() {
+ return queryExpression;
+ }
+
+ /** Set this BuildRequest's query expression. */
+ public void setQueryExpression(QueryExpression expr) {
+ this.queryExpression = expr;
+ }
+
@Override
@SuppressWarnings("unchecked")
public <T extends OptionsBase> T getOptions(Class<T> clazz) {
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
index 57f0936a51..a92fbb3dc7 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
@@ -202,22 +202,6 @@ public class BuildRequestOptions extends OptionsBase {
public boolean dumpToStdout;
@Option(
- name = "experimental_post_build_query",
- defaultValue = "null",
- documentationCategory = OptionDocumentationCategory.LOGGING,
- effectTags = {OptionEffectTag.UNKNOWN}
- )
- public String queryExpression;
-
- @Option(
- name = "experimental_query_options",
- defaultValue = "null",
- documentationCategory = OptionDocumentationCategory.LOGGING,
- effectTags = {OptionEffectTag.UNKNOWN}
- )
- public String queryOptions;
-
- @Option(
name = "analyze",
defaultValue = "true",
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
index 4a7165058a..5adcbc8cc5 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
@@ -62,9 +62,11 @@ import com.google.devtools.build.lib.pkgcache.LoadingPhaseRunner;
import com.google.devtools.build.lib.pkgcache.LoadingResult;
import com.google.devtools.build.lib.profiler.ProfilePhase;
import com.google.devtools.build.lib.profiler.Profiler;
+import com.google.devtools.build.lib.query2.CommonQueryOptions;
import com.google.devtools.build.lib.query2.ConfiguredTargetQueryEnvironment;
import com.google.devtools.build.lib.query2.engine.QueryEvalResult;
import com.google.devtools.build.lib.query2.engine.QueryException;
+import com.google.devtools.build.lib.query2.engine.QueryExpression;
import com.google.devtools.build.lib.query2.engine.TargetLiteral;
import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback;
import com.google.devtools.build.lib.runtime.BlazeRuntime;
@@ -77,7 +79,6 @@ import com.google.devtools.build.skyframe.WalkableGraph;
import com.google.devtools.common.options.OptionsParsingException;
import java.io.IOException;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -237,7 +238,7 @@ public final class BuildTool {
// not reproducible at the level of a single command. Either tolerate, or wipe the analysis
// graph beforehand if this option is specified, or add another option to wipe if desired
// (SkyframeExecutor#handleConfiguredTargetChange should be sufficient).
- if (request.getBuildOptions().queryExpression != null) {
+ if (request.getQueryExpression() != null) {
if (!env.getSkyframeExecutor().tracksStateForIncrementality()) {
throw new ConfiguredTargetQueryCommandLineException(
"Configured query is not allowed if incrementality state is not being kept");
@@ -415,7 +416,7 @@ public final class BuildTool {
List<TargetAndConfiguration> topLevelTargetsWithConfigs)
throws InterruptedException, QueryException, IOException {
- String queryExpr = request.getBuildOptions().queryExpression;
+ QueryExpression expr = request.getQueryExpression();
// Currently, CTQE assumes that all top level targets take on the same default config and we
// don't have the ability to map multiple configs to multiple top level targets.
@@ -426,7 +427,7 @@ public final class BuildTool {
for (TargetAndConfiguration targAndConfig : topLevelTargetsWithConfigs) {
if (!targAndConfig.getConfiguration().equals(sampleConfig)) {
throw new QueryException(
- new TargetLiteral(queryExpr),
+ new TargetLiteral(expr.toString()),
String.format(
"Top level targets %s and %s have different configurations (top level "
+ "targets with different configurations is not supported)",
@@ -436,7 +437,6 @@ public final class BuildTool {
WalkableGraph walkableGraph =
SkyframeExecutorWrappingWalkableGraph.of(env.getSkyframeExecutor());
- String queryOptions = request.getBuildOptions().queryOptions;
ConfiguredTargetQueryEnvironment configuredTargetQueryEnvironment =
new ConfiguredTargetQueryEnvironment(
request.getKeepGoing(),
@@ -447,26 +447,25 @@ public final class BuildTool {
env.newTargetPatternEvaluator().getOffset(),
env.getPackageManager().getPackagePath(),
() -> walkableGraph,
- queryOptions == null
- ? new HashSet<>()
- : ConfiguredTargetQueryEnvironment.parseOptions(queryOptions).toSettings());
- QueryEvalResult result = configuredTargetQueryEnvironment.evaluateQuery(
- queryExpr,
- new ThreadSafeOutputFormatterCallback<ConfiguredTarget>() {
- @Override
- public void processOutput(Iterable<ConfiguredTarget> partialResult)
- throws IOException, InterruptedException {
- for (ConfiguredTarget configuredTarget : partialResult) {
- env.getReporter()
- .getOutErr()
- .printOutLn(
- configuredTarget.getLabel()
- + " ("
- + configuredTarget.getConfiguration()
- + ")");
- }
- }
- });
+ request.getOptions(CommonQueryOptions.class).toSettings());
+ QueryEvalResult result =
+ configuredTargetQueryEnvironment.evaluateQuery(
+ expr,
+ new ThreadSafeOutputFormatterCallback<ConfiguredTarget>() {
+ @Override
+ public void processOutput(Iterable<ConfiguredTarget> partialResult)
+ throws IOException, InterruptedException {
+ for (ConfiguredTarget configuredTarget : partialResult) {
+ env.getReporter()
+ .getOutErr()
+ .printOutLn(
+ configuredTarget.getLabel()
+ + " ("
+ + configuredTarget.getConfiguration()
+ + ")");
+ }
+ }
+ });
if (result.isEmpty()) {
env.getReporter().handle(Event.info("Empty query results"));
}
diff --git a/src/main/java/com/google/devtools/build/lib/query2/BUILD b/src/main/java/com/google/devtools/build/lib/query2/BUILD
index 8f143033ba..8a0d01f136 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/query2/BUILD
@@ -43,6 +43,7 @@ java_library(
srcs = glob(["output/*.java"]),
deps = [
":abstract-blaze-query-env",
+ ":common-query-options",
":fake-load-target",
":query-engine",
"//src/main/java/com/google/devtools/build/lib:events",
@@ -74,6 +75,17 @@ java_library(
)
java_library(
+ name = "common-query-options",
+ srcs = [
+ "CommonQueryOptions.java",
+ ],
+ deps = [
+ ":query-engine",
+ "//src/main/java/com/google/devtools/common/options",
+ ],
+)
+
+java_library(
name = "fake-load-target",
srcs = [
"FakeLoadTarget.java",
diff --git a/src/main/java/com/google/devtools/build/lib/query2/CommonQueryOptions.java b/src/main/java/com/google/devtools/build/lib/query2/CommonQueryOptions.java
new file mode 100644
index 0000000000..7417b697ba
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/query2/CommonQueryOptions.java
@@ -0,0 +1,90 @@
+// 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 com.google.devtools.build.lib.query2.engine.QueryEnvironment.Setting;
+import com.google.devtools.common.options.Converters;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionDocumentationCategory;
+import com.google.devtools.common.options.OptionEffectTag;
+import com.google.devtools.common.options.OptionsBase;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+/** Options shared between blaze query and blaze cquery. */
+public class CommonQueryOptions extends OptionsBase {
+ @Option(
+ name = "universe_scope",
+ defaultValue = "",
+ documentationCategory = OptionDocumentationCategory.QUERY,
+ converter = Converters.CommaSeparatedOptionListConverter.class,
+ effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+ help =
+ "A comma-separated set of target patterns (additive and subtractive). The query may "
+ + "be performed in the universe defined by the transitive closure of the specified "
+ + "targets. This option is used for the query and cquery commands. \n"
+ + "For cquery, the input to this option is the targets all answers are built under and "
+ + "so this option may affect configurations and transitions. If this option is not "
+ + "specified, the top-level targets are assumed to be the targets parsed from the "
+ + "query expression. Note: For cquery, not specifying this option may cause the build "
+ + "to break if targets parsed from the query expression are not buildable with "
+ + "top-level options."
+ )
+ public List<String> universeScope;
+
+ @Option(
+ name = "host_deps",
+ defaultValue = "true",
+ documentationCategory = OptionDocumentationCategory.QUERY,
+ effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+ help =
+ "Query: If disabled, dependencies on 'host configuration' targets will not be included in "
+ + "the dependency graph over which the query operates. A 'host configuration' "
+ + "dependency edge, such as the one from any 'proto_library' rule to the Protocol "
+ + "Compiler, usually points to a tool executed during the build (on the host machine) "
+ + "rather than a part of the same 'target' program. \n"
+ + "Cquery: If disabled, filters out all configured targets which cross a host "
+ + "transition from the top-level target that discovered this configured target. That "
+ + "means if the top-level target is in the target configuration, only configured "
+ + "targets also in the target configuration will be returned. If the top-level target "
+ + "is in the host configuration, only host configured targets will be returned."
+ )
+ public boolean includeHostDeps;
+
+ @Option(
+ name = "implicit_deps",
+ defaultValue = "true",
+ category = "query",
+ documentationCategory = OptionDocumentationCategory.QUERY,
+ effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+ help =
+ "If enabled, implicit dependencies will be included in the dependency graph over "
+ + "which the query operates. An implicit dependency is one that is not explicitly "
+ + "specified in the BUILD file but added by blaze."
+ )
+ public boolean includeImplicitDeps;
+
+ /** Return the current options as a set of QueryEnvironment settings. */
+ public Set<Setting> toSettings() {
+ Set<Setting> settings = EnumSet.noneOf(Setting.class);
+ if (!includeHostDeps) {
+ settings.add(Setting.NO_HOST_DEPS);
+ }
+ if (!includeImplicitDeps) {
+ settings.add(Setting.NO_IMPLICIT_DEPS);
+ }
+ return settings;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/query2/output/QueryOptions.java b/src/main/java/com/google/devtools/build/lib/query2/output/QueryOptions.java
index 328c8e71b8..70dca68c98 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/output/QueryOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/output/QueryOptions.java
@@ -13,21 +13,16 @@
// limitations under the License.
package com.google.devtools.build.lib.query2.output;
+import com.google.devtools.build.lib.query2.CommonQueryOptions;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Setting;
-import com.google.devtools.common.options.Converters;
import com.google.devtools.common.options.EnumConverter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionDocumentationCategory;
import com.google.devtools.common.options.OptionEffectTag;
-import com.google.devtools.common.options.OptionsBase;
-import java.util.EnumSet;
-import java.util.List;
import java.util.Set;
-/**
- * Command-line options for the Blaze query language, revision 2.
- */
-public class QueryOptions extends OptionsBase {
+/** Command-line options for the Blaze query language, revision 2. */
+public class QueryOptions extends CommonQueryOptions {
/** An enum converter for {@code AspectResolver.Mode} . Should be used internally only. */
public static class AspectResolutionModeConverter extends EnumConverter<AspectResolver.Mode> {
public AspectResolutionModeConverter() {
@@ -131,37 +126,6 @@ public class QueryOptions extends OptionsBase {
public OrderOutput orderOutput;
@Option(
- name = "host_deps",
- defaultValue = "true",
- category = "query",
- documentationCategory = OptionDocumentationCategory.QUERY,
- effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
- help =
- "If enabled, dependencies on 'host configuration' targets will be included in the "
- + "dependency graph over which the query operates. A 'host configuration' dependency "
- + "edge, such as the one from any 'proto_library' rule to the Protocol Compiler, "
- + "usually points to a tool executed during the build (on the host machine) rather "
- + "than a part of the same 'target' program. Queries whose purpose is to discover "
- + "the set of things needed during a build will typically enable this option; queries "
- + "aimed at revealing the structure of a single program will typically disable this "
- + "option."
- )
- public boolean includeHostDeps;
-
- @Option(
- name = "implicit_deps",
- defaultValue = "true",
- category = "query",
- documentationCategory = OptionDocumentationCategory.QUERY,
- effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
- help =
- "If enabled, implicit dependencies will be included in the dependency graph over "
- + "which the query operates. An implicit dependency is one that is not explicitly "
- + "specified in the BUILD file but added by blaze."
- )
- public boolean includeImplicitDeps;
-
- @Option(
name = "graph:node_limit",
defaultValue = "512",
category = "query",
@@ -236,20 +200,6 @@ public class QueryOptions extends OptionsBase {
public boolean strictTestSuite;
@Option(
- name = "universe_scope",
- converter = Converters.CommaSeparatedOptionListConverter.class,
- defaultValue = "",
- category = "query",
- documentationCategory = OptionDocumentationCategory.QUERY,
- effectTags = {OptionEffectTag.CHANGES_INPUTS},
- help =
- "A comma-separated set of target patterns (additive and subtractive). The query may "
- + "be performed in the universe defined by the transitive closure of the specified "
- + "targets."
- )
- public List<String> universeScope;
-
- @Option(
name = "relative_locations",
defaultValue = "false",
category = "query",
@@ -318,17 +268,12 @@ public class QueryOptions extends OptionsBase {
public boolean protoFlattenSelects;
/** Return the current options as a set of QueryEnvironment settings. */
+ @Override
public Set<Setting> toSettings() {
- Set<Setting> settings = EnumSet.noneOf(Setting.class);
+ Set<Setting> settings = super.toSettings();
if (strictTestSuite) {
settings.add(Setting.TESTS_EXPRESSION_STRICT);
}
- if (!includeHostDeps) {
- settings.add(Setting.NO_HOST_DEPS);
- }
- if (!includeImplicitDeps) {
- settings.add(Setting.NO_IMPLICIT_DEPS);
- }
return settings;
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuiltinCommandModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BuiltinCommandModule.java
index e8a9c394c9..499f54fdb8 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BuiltinCommandModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BuiltinCommandModule.java
@@ -17,6 +17,7 @@ import com.google.devtools.build.lib.runtime.commands.BuildCommand;
import com.google.devtools.build.lib.runtime.commands.CanonicalizeCommand;
import com.google.devtools.build.lib.runtime.commands.CleanCommand;
import com.google.devtools.build.lib.runtime.commands.CoverageCommand;
+import com.google.devtools.build.lib.runtime.commands.CqueryCommand;
import com.google.devtools.build.lib.runtime.commands.DumpCommand;
import com.google.devtools.build.lib.runtime.commands.HelpCommand;
import com.google.devtools.build.lib.runtime.commands.InfoCommand;
@@ -50,7 +51,8 @@ public final class BuiltinCommandModule extends BlazeModule {
new RunCommand(),
new ShutdownCommand(),
new TestCommand(),
- new VersionCommand());
+ new VersionCommand(),
+ new CqueryCommand());
// Only enable the "license" command when this binary has an embedded LICENSE file.
if (LicenseCommand.isSupported()) {
builder.addCommands(new LicenseCommand());
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java
new file mode 100644
index 0000000000..9c1ba0abb5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java
@@ -0,0 +1,110 @@
+// 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.runtime.commands;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.buildtool.BuildRequest;
+import com.google.devtools.build.lib.buildtool.BuildTool;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.query2.CommonQueryOptions;
+import com.google.devtools.build.lib.query2.engine.QueryEnvironment;
+import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
+import com.google.devtools.build.lib.query2.engine.QueryException;
+import com.google.devtools.build.lib.query2.engine.QueryExpression;
+import com.google.devtools.build.lib.query2.engine.QueryParser;
+import com.google.devtools.build.lib.runtime.BlazeCommand;
+import com.google.devtools.build.lib.runtime.BlazeRuntime;
+import com.google.devtools.build.lib.runtime.Command;
+import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.common.options.OptionPriority.PriorityCategory;
+import com.google.devtools.common.options.OptionsParser;
+import com.google.devtools.common.options.OptionsParsingException;
+import com.google.devtools.common.options.OptionsProvider;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/** Handles the 'cquery' command on the Blaze command line. */
+@Command(
+ name = "cquery",
+ builds = true,
+ inherits = {BuildCommand.class},
+ options = {CommonQueryOptions.class},
+ usesConfigurationOptions = true,
+ shortDescription = "Loads, analyzes, and queries the specified targets w/ configurations.",
+ allowResidue = true,
+ completion = "label",
+ help = "resource:cquery.txt"
+)
+public final class CqueryCommand implements BlazeCommand {
+
+ @Override
+ public void editOptions(OptionsParser optionsParser) {
+ try {
+ optionsParser.parse(
+ PriorityCategory.COMPUTED_DEFAULT,
+ "Options required by cquery",
+ ImmutableList.of("--nobuild"));
+ } catch (OptionsParsingException e) {
+ throw new IllegalStateException("Cquery's known options failed to parse", e);
+ }
+ }
+
+ @Override
+ public ExitCode exec(CommandEnvironment env, OptionsProvider options) {
+ if (options.getResidue().isEmpty()) {
+ env.getReporter()
+ .handle(
+ Event.error(
+ "Missing query expression. Use the 'help cquery' command for syntax and help."));
+ return ExitCode.COMMAND_LINE_ERROR;
+ }
+ String query = Joiner.on(' ').join(options.getResidue());
+ HashMap<String, QueryFunction> functions = new HashMap<>();
+ for (QueryFunction queryFunction : QueryEnvironment.DEFAULT_QUERY_FUNCTIONS) {
+ functions.put(queryFunction.getName(), queryFunction);
+ }
+ QueryExpression expr;
+ try {
+ expr = QueryParser.parse(query, functions);
+ } catch (QueryException e) {
+ env.getReporter()
+ .handle(Event.error("Error while parsing '" + query + "': " + e.getMessage()));
+ return ExitCode.COMMAND_LINE_ERROR;
+ }
+
+ List<String> topLevelTargets = options.getOptions(CommonQueryOptions.class).universeScope;
+ Set<String> targetPatternSet = new LinkedHashSet<>();
+ if (topLevelTargets.isEmpty()) {
+ expr.collectTargetPatterns(targetPatternSet);
+ topLevelTargets = new ArrayList<>(targetPatternSet);
+ }
+ BlazeRuntime runtime = env.getRuntime();
+ BuildRequest request =
+ BuildRequest.create(
+ getClass().getAnnotation(Command.class).name(),
+ options,
+ runtime.getStartupOptionsProvider(),
+ topLevelTargets,
+ env.getReporter().getOutErr(),
+ env.getCommandId(),
+ env.getCommandStartTime());
+ request.setQueryExpression(expr);
+ return new BuildTool(env).processRequest(request, null).getExitCondition();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/cquery.txt b/src/main/java/com/google/devtools/build/lib/runtime/commands/cquery.txt
new file mode 100644
index 0000000000..e631bca01b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/cquery.txt
@@ -0,0 +1,8 @@
+
+Usage: %{product} %{command} <options> <targets>
+
+Queries the specified targets, taking options into account. Unlike query,
+cquery works on the post analysis phase configured target graph. It understands
+and takes into account configurations and configuration transitions.
+
+%{options}
diff --git a/src/test/shell/integration/configured_query_test.sh b/src/test/shell/integration/configured_query_test.sh
index 240beaac76..6a45a89b0f 100755
--- a/src/test/shell/integration/configured_query_test.sh
+++ b/src/test/shell/integration/configured_query_test.sh
@@ -14,7 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-# configured_query_test.sh: integration tests for bazel configured query
+# configured_query_test.sh: integration tests for bazel configured query.
+# This tests the command line ui of configured query while
+# ConfiguredTargetQueryTest tests its internal functionality.
# Load the test setup defined in the parent directory
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -26,22 +28,48 @@ add_to_bazelrc "build --package_path=%workspace%"
#### TESTS #############################################################
function test_basic_query() {
- rm -rf maple
- mkdir -p maple
- cat > maple/BUILD <<EOF
+ rm -rf maple
+ mkdir -p maple
+ cat > maple/BUILD <<EOF
sh_library(name='maple', deps=[':japanese'])
sh_library(name='japanese')
EOF
- bazel build --nobuild //maple \
- --experimental_post_build_query='deps(//maple)' > output 2>"$TEST_log" \
- || fail "Expected success"
- cat output >> "$TEST_log"
+ bazel cquery 'deps(//maple)' > output 2>"$TEST_log" || fail "Expected success"
assert_contains "//maple:maple" output
assert_contains "//maple:japanese" output
}
+function test_respects_selects() {
+ rm -rf ash
+ mkdir -p ash
+ cat > ash/BUILD <<EOF
+sh_library(
+ name = "ash",
+ deps = select({
+ ":excelsior": [":foo"],
+ ":americana": [":bar"],
+ }),
+)
+sh_library(name = "foo")
+sh_library(name = "bar")
+config_setting(
+ name = "excelsior",
+ values = {"define": "species=excelsior"},
+)
+config_setting(
+ name = "americana",
+ values = {"define": "species=americana"},
+)
+EOF
+
+ bazel cquery 'deps(//ash)' --define species=excelsior > output \
+ 2>"$TEST_log" || fail "Excepted success"
+ assert_contains "//ash:foo" output
+ assert_not_contains "//ash:bar" output
+}
+
function test_empty_results_printed() {
rm -rf redwood
mkdir -p redwood
@@ -51,15 +79,67 @@ sh_library(name='sequoia')
sh_library(name='sequoiadendron')
EOF
- bazel build --nobuild //redwood \
- --experimental_post_build_query='somepath(//redwood:sequoia,//redwood:sequoiadendron)' \
+ bazel cquery 'somepath(//redwood:sequoia,//redwood:sequoiadendron)' \
> output 2>"$TEST_log" || fail "Expected success"
expect_log "INFO: Empty query results"
- expect_not_log "//redwood:sequoiadendreon"
+ assert_not_contains "//redwood:sequoiadendreon" output
+}
+
+function test_universe_scope_specified() {
+ write_java_library_build
+
+ # The java_library rule has a host transition on its plugins attribute.
+ bazel cquery //pine:dep+//pine:plugin --universe_scope=//pine:my_java \
+ > output 2>"$TEST_log" || fail "Excepted success"
+ # Find the lines of output for //pine:plugin and //pine:dep.
+ PINE_HOST=$(grep "//pine:plugin" output)
+ PINE_TARGET=$(grep "//pine:dep" output)
+ # Trim to just configurations.
+ HOST_CONFIG=${PINE_HOST/"//pine:plugin"}
+ TARGET_CONFIG=${PINE_TARGET/"//pine:dep"}
+ # Ensure they are are not equal.
+ assert_not_equals $HOST_CONFIG $TARGET_CONFIG
}
+# This test ensures the known buggy behavior described at b/71905538 i.e. nodes
+# lingering from previous builds.
+# TODO(juliexxia): Remove this test once b/71905538 is fixed.
+function test_ghost_nodes_bug() {
+ write_java_library_build
+
+ # Create host-configured //pine:plugin node in this cquery
+ bazel cquery "deps(//pine:my_java)" || fail "Excepted success"
+ # This cquery should return target configured //pine:plugin but returns
+ # the host-configured target generated above.
+ bazel cquery //pine:dep+//pine:plugin \
+ > output 2>"$TEST_log" || fail "Excepted success"
+
+ # Find the lines of output for //pine:plugin and //pine:dep.
+ PLUGIN=$(grep "//pine:plugin" output)
+ DEP=$(grep "//pine:dep" output)
+ # Trim to just configurations.
+ PLUGIN_CONFIG=${PLUGIN/"//pine:plugin"}
+ DEP_CONFIG=${DEP/"//pine:dep"}
+ # Ensure they are are not equal (the buggy behavior).
+ assert_not_equals $PLUGIN_CONFIG $DEP_CONFIG
+}
+
+function write_java_library_build() {
+ rm -rf pine
+ mkdir -p pine
+ cat > pine/BUILD <<EOF
+java_library(
+ name = "my_java",
+ srcs = ['foo.java'],
+ deps = [":dep"],
+ plugins = [":plugin"]
+)
+java_library(name = "dep")
+java_plugin(name = "plugin")
+EOF
+}
function tear_down() {
bazel shutdown