From a1a57a519d49fcbda35f3df0e68e04ba09db3362 Mon Sep 17 00:00:00 2001 From: twerth Date: Wed, 18 Jul 2018 05:34:13 -0700 Subject: Add aquery as proper command to Bazel. RELNOTES: Add aquery command to get analysis time information about the action graph. PiperOrigin-RevId: 205064145 --- .../build/lib/buildtool/AqueryBuildTool.java | 57 ++++ .../ActionGraphProtoOutputFormatterCallback.java | 73 ++++++ .../lib/query2/ActionGraphQueryEnvironment.java | 291 +++++++++++++++++++++ .../build/lib/query2/AqueryThreadsafeCallback.java | 45 ++++ .../query2/ConfiguredTargetQueryEnvironment.java | 10 + .../lib/query2/PostAnalysisQueryEnvironment.java | 23 +- .../build/lib/query2/output/AqueryOptions.java | 30 +++ .../build/lib/runtime/BuiltinCommandModule.java | 2 + .../build/lib/runtime/commands/AqueryCommand.java | 121 +++++++++ .../devtools/build/lib/runtime/commands/aquery.txt | 8 + .../lib/skyframe/actiongraph/ActionGraphDump.java | 5 + 11 files changed, 656 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/buildtool/AqueryBuildTool.java create mode 100644 src/main/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallback.java create mode 100644 src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java create mode 100644 src/main/java/com/google/devtools/build/lib/query2/AqueryThreadsafeCallback.java create mode 100644 src/main/java/com/google/devtools/build/lib/query2/output/AqueryOptions.java create mode 100644 src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java create mode 100644 src/main/java/com/google/devtools/build/lib/runtime/commands/aquery.txt (limited to 'src/main/java/com') diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/AqueryBuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/AqueryBuildTool.java new file mode 100644 index 0000000000..ef6721b43f --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/buildtool/AqueryBuildTool.java @@ -0,0 +1,57 @@ +// 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.buildtool; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.query2.ActionGraphQueryEnvironment; +import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction; +import com.google.devtools.build.lib.query2.engine.QueryExpression; +import com.google.devtools.build.lib.query2.output.AqueryOptions; +import com.google.devtools.build.lib.runtime.CommandEnvironment; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; +import com.google.devtools.build.skyframe.WalkableGraph; + +/** A version of {@link BuildTool} that handles all aquery work. */ +public class AqueryBuildTool extends PostAnalysisQueryBuildTool { + + public AqueryBuildTool(CommandEnvironment env, QueryExpression queryExpression) { + super(env, queryExpression); + } + + @Override + protected PostAnalysisQueryEnvironment getQueryEnvironment( + BuildRequest request, + BuildConfiguration hostConfiguration, + BuildConfiguration targetConfig, + WalkableGraph walkableGraph) { + ImmutableList extraFunctions = + new ImmutableList.Builder() + .addAll(ActionGraphQueryEnvironment.AQUERY_FUNCTIONS) + .addAll(env.getRuntime().getQueryFunctions()) + .build(); + AqueryOptions aqueryOptions = request.getOptions(AqueryOptions.class); + return new ActionGraphQueryEnvironment( + request.getKeepGoing(), + env.getReporter(), + extraFunctions, + targetConfig, + hostConfiguration, + env.getRelativeWorkingDirectory().getPathString(), + env.getPackageManager().getPackagePath(), + () -> walkableGraph, + aqueryOptions); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallback.java b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallback.java new file mode 100644 index 0000000000..d18ee1852d --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphProtoOutputFormatterCallback.java @@ -0,0 +1,73 @@ +// 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.common.annotations.VisibleForTesting; +import com.google.devtools.build.lib.actions.CommandLineExpansionException; +import com.google.devtools.build.lib.analysis.AnalysisProtos; +import com.google.devtools.build.lib.analysis.AnalysisProtos.ActionGraphContainer; +import com.google.devtools.build.lib.events.Reporter; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; +import com.google.devtools.build.lib.query2.output.AqueryOptions; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; +import com.google.devtools.build.lib.skyframe.SkyframeExecutor; +import com.google.devtools.build.lib.skyframe.actiongraph.ActionGraphDump; +import java.io.IOException; +import java.io.OutputStream; + +/** Default output callback for aquery, prints proto output. */ +public class ActionGraphProtoOutputFormatterCallback extends AqueryThreadsafeCallback { + + final ActionGraphDump actionGraphDump; + + ActionGraphProtoOutputFormatterCallback( + Reporter reporter, + AqueryOptions options, + OutputStream out, + SkyframeExecutor skyframeExecutor, + TargetAccessor accessor) { + super(reporter, options, out, skyframeExecutor, accessor); + // TODO(twerth): Allow users to include action command lines. + actionGraphDump = new ActionGraphDump(/* includeActionCmdLine */ false); + } + + @Override + public String getName() { + return "proto"; + } + + @Override + public void processOutput(Iterable partialResult) throws IOException { + try { + for (ConfiguredTargetValue configuredTargetValue : partialResult) { + actionGraphDump.dumpConfiguredTarget(configuredTargetValue); + } + } catch (CommandLineExpansionException e) { + throw new IOException(e.getMessage()); + } + } + + @Override + public void close(boolean failFast) throws IOException { + if (!failFast && printStream != null) { + ActionGraphContainer actionGraphContainer = actionGraphDump.build(); + actionGraphContainer.writeTo(printStream); + } + } + + @VisibleForTesting + public AnalysisProtos.ActionGraphContainer getProtoResult() { + return actionGraphDump.build(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java new file mode 100644 index 0000000000..e3dd353a0b --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/ActionGraphQueryEnvironment.java @@ -0,0 +1,291 @@ +// 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.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.TargetParsingException; +import com.google.devtools.build.lib.cmdline.TargetPattern; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.events.Reporter; +import com.google.devtools.build.lib.packages.RuleTransitionFactory; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.pkgcache.PackageManager; +import com.google.devtools.build.lib.pkgcache.PathPackageLocator; +import com.google.devtools.build.lib.query2.engine.Callback; +import com.google.devtools.build.lib.query2.engine.KeyExtractor; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment; +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.QueryUtil.ThreadSafeMutableKeyExtractorBackedSetImpl; +import com.google.devtools.build.lib.query2.output.AqueryOptions; +import com.google.devtools.build.lib.rules.AliasConfiguredTarget; +import com.google.devtools.build.lib.skyframe.BuildConfigurationValue; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; +import com.google.devtools.build.lib.skyframe.SkyframeExecutor; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.WalkableGraph; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +/** + * {@link QueryEnvironment} that is specialized for running action graph queries over the configured + * target graph. + */ +public class ActionGraphQueryEnvironment + extends PostAnalysisQueryEnvironment { + + public static final ImmutableList AQUERY_FUNCTIONS = ImmutableList.of(); + public static final ImmutableList FUNCTIONS = populateFunctions(); + AqueryOptions aqueryOptions; + private final KeyExtractor + configuredTargetKeyExtractor; + + public ActionGraphQueryEnvironment( + boolean keepGoing, + ExtendedEventHandler eventHandler, + Iterable extraFunctions, + BuildConfiguration defaultTargetConfiguration, + BuildConfiguration hostConfiguration, + String parserPrefix, + PathPackageLocator pkgPath, + Supplier walkableGraphSupplier, + Set settings) { + super( + keepGoing, + eventHandler, + extraFunctions, + defaultTargetConfiguration, + hostConfiguration, + parserPrefix, + pkgPath, + walkableGraphSupplier, + settings, + new ConfiguredTargetValueAccessor(walkableGraphSupplier.get())); + this.configuredTargetKeyExtractor = + configuredTargetValue -> { + try { + ConfiguredTarget element = configuredTargetValue.getConfiguredTarget(); + return ConfiguredTargetKey.of( + element, + element.getConfigurationKey() == null + ? null + : ((BuildConfigurationValue) graph.getValue(element.getConfigurationKey())) + .getConfiguration()); + } catch (InterruptedException e) { + throw new IllegalStateException("Interruption unexpected in configured query"); + } + }; + } + + public ActionGraphQueryEnvironment( + boolean keepGoing, + ExtendedEventHandler eventHandler, + Iterable extraFunctions, + BuildConfiguration defaultTargetConfiguration, + BuildConfiguration hostConfiguration, + String parserPrefix, + PathPackageLocator pkgPath, + Supplier walkableGraphSupplier, + AqueryOptions aqueryOptions) { + this( + keepGoing, + eventHandler, + extraFunctions, + defaultTargetConfiguration, + hostConfiguration, + parserPrefix, + pkgPath, + walkableGraphSupplier, + aqueryOptions.toSettings()); + this.aqueryOptions = aqueryOptions; + } + + private static ImmutableList populateFunctions() { + return ImmutableList.copyOf(QueryEnvironment.DEFAULT_QUERY_FUNCTIONS); + } + + @Override + public ImmutableList> + getDefaultOutputFormatters( + TargetAccessor accessor, + Reporter reporter, + SkyframeExecutor skyframeExecutor, + BuildConfiguration hostConfiguration, + @Nullable RuleTransitionFactory trimmingTransitionFactory, + PackageManager packageManager) { + OutputStream out = reporter.getOutErr().getOutputStream(); + return ImmutableList.of( + new ActionGraphProtoOutputFormatterCallback( + reporter, aqueryOptions, out, skyframeExecutor, accessor)); + } + + @Override + public String getOutputFormat() { + return aqueryOptions.outputFormat; + } + + @Override + protected KeyExtractor + getConfiguredTargetKeyExtractor() { + return configuredTargetKeyExtractor; + } + + @Override + public Label getCorrectLabel(ConfiguredTargetValue configuredTargetValue) { + ConfiguredTarget target = configuredTargetValue.getConfiguredTarget(); + if (target instanceof AliasConfiguredTarget) { + return ((AliasConfiguredTarget) target).getOriginalLabel(); + } + return target.getLabel(); + } + + @Nullable + @Override + protected ConfiguredTargetValue getHostConfiguredTarget(Label label) throws InterruptedException { + return this.getConfiguredTargetValue(ConfiguredTargetValue.key(label, hostConfiguration)); + } + + @Nullable + @Override + protected ConfiguredTargetValue getTargetConfiguredTarget(Label label) + throws InterruptedException { + return this.getConfiguredTargetValue( + ConfiguredTargetValue.key(label, defaultTargetConfiguration)); + } + + @Nullable + @Override + protected ConfiguredTargetValue getNullConfiguredTarget(Label label) throws InterruptedException { + return this.getConfiguredTargetValue( + ConfiguredTargetValue.key(label, /* configuration= */ null)); + } + + @Nullable + @Override + protected ConfiguredTargetValue getValueFromKey(SkyKey key) throws InterruptedException { + return getConfiguredTargetValue(key); + } + + @Nullable + @Override + protected RuleConfiguredTarget getRuleConfiguredTarget( + ConfiguredTargetValue configuredTargetValue) { + ConfiguredTarget configuredTarget = configuredTargetValue.getConfiguredTarget(); + if (configuredTarget instanceof RuleConfiguredTarget) { + return (RuleConfiguredTarget) configuredTarget; + } + return null; + } + + @Nullable + @Override + protected BuildConfiguration getConfiguration(ConfiguredTargetValue configuredTargetValue) { + ConfiguredTarget target = configuredTargetValue.getConfiguredTarget(); + try { + return target.getConfigurationKey() == null + ? null + : ((BuildConfigurationValue) graph.getValue(target.getConfigurationKey())) + .getConfiguration(); + } catch (InterruptedException e) { + throw new IllegalStateException("Unexpected interruption during aquery"); + } + } + + @Override + protected ConfiguredTargetKey getSkyKey(ConfiguredTargetValue configuredTargetValue) { + ConfiguredTarget target = configuredTargetValue.getConfiguredTarget(); + return ConfiguredTargetKey.of(target, getConfiguration(configuredTargetValue)); + } + + @Override + public QueryTaskFuture getTargetsMatchingPattern( + QueryExpression owner, String pattern, Callback callback) { + TargetPattern patternToEval; + try { + patternToEval = getPattern(pattern); + } catch (TargetParsingException tpe) { + try { + reportBuildFileError(owner, tpe.getMessage()); + } catch (QueryException qe) { + return immediateFailedFuture(qe); + } + return immediateSuccessfulFuture(null); + } + AsyncFunction reportBuildFileErrorAsyncFunction = + exn -> { + reportBuildFileError(owner, exn.getMessage()); + return Futures.immediateFuture(null); + }; + return QueryTaskFutureImpl.ofDelegate( + Futures.catchingAsync( + patternToEval.evalAdaptedForAsync( + resolver, + ImmutableSet.of(), + ImmutableSet.of(), + (Callback) + partialResult -> { + List transformedResult = new ArrayList<>(); + for (Target target : partialResult) { + ConfiguredTargetValue configuredTargetValue = + getConfiguredTargetValue(target.getLabel()); + if (configuredTargetValue != null) { + transformedResult.add(configuredTargetValue); + } + } + callback.process(transformedResult); + }, + QueryException.class), + TargetParsingException.class, + reportBuildFileErrorAsyncFunction, + MoreExecutors.directExecutor())); + } + + private ConfiguredTargetValue getConfiguredTargetValue(Label label) throws InterruptedException { + // Try with target configuration. + ConfiguredTargetValue configuredTargetValue = getTargetConfiguredTarget(label); + if (configuredTargetValue != null) { + return configuredTargetValue; + } + // Try with host configuration (even when --nohost_deps is set in the case that top-level + // targets are configured in the host configuration so we are doing a host-configuration-only + // query). + configuredTargetValue = getHostConfiguredTarget(label); + if (configuredTargetValue != null) { + return configuredTargetValue; + } + // Last chance: source file. + return getNullConfiguredTarget(label); + } + + @Override + public ThreadSafeMutableSet createThreadSafeMutableSet() { + return new ThreadSafeMutableKeyExtractorBackedSetImpl<>( + configuredTargetKeyExtractor, + ConfiguredTargetValue.class, + SkyQueryEnvironment.DEFAULT_THREAD_COUNT); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/query2/AqueryThreadsafeCallback.java b/src/main/java/com/google/devtools/build/lib/query2/AqueryThreadsafeCallback.java new file mode 100644 index 0000000000..efbecd524b --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/AqueryThreadsafeCallback.java @@ -0,0 +1,45 @@ +// 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.events.Reporter; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; +import com.google.devtools.build.lib.query2.output.AqueryOptions; +import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue; +import com.google.devtools.build.lib.skyframe.SkyframeExecutor; +import java.io.OutputStream; +import java.io.PrintStream; + +/** Base class for aquery output callbacks. */ +public abstract class AqueryThreadsafeCallback + extends NamedThreadSafeOutputFormatterCallback { + protected final Reporter reporter; + protected final AqueryOptions options; + protected final PrintStream printStream; + protected final SkyframeExecutor skyframeExecutor; + protected final ConfiguredTargetValueAccessor accessor; + + AqueryThreadsafeCallback( + Reporter reporter, + AqueryOptions options, + OutputStream out, + SkyframeExecutor skyframeExecutor, + TargetAccessor accessor) { + this.reporter = reporter; + this.options = options; + this.printStream = out == null ? null : new PrintStream(out); + this.skyframeExecutor = skyframeExecutor; + this.accessor = (ConfiguredTargetValueAccessor) accessor; + } +} 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 f48c384d9c..2ab78be533 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 @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.MoreExecutors; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.TargetParsingException; import com.google.devtools.build.lib.cmdline.TargetPattern; @@ -326,6 +327,15 @@ public class ConfiguredTargetQueryEnvironment return getValueFromKey(ConfiguredTargetValue.key(label, null)); } + @Nullable + @Override + protected RuleConfiguredTarget getRuleConfiguredTarget(ConfiguredTarget configuredTarget) { + if (configuredTarget instanceof RuleConfiguredTarget) { + return (RuleConfiguredTarget) configuredTarget; + } + return null; + } + @Nullable @Override protected BuildConfiguration getConfiguration(ConfiguredTarget target) { diff --git a/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java index df61da858a..fb17eac07d 100644 --- a/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java @@ -341,19 +341,24 @@ public abstract class PostAnalysisQueryEnvironment extends AbstractBlazeQuery .collect(Collectors.toList()); } } - if (settings.contains(Setting.NO_IMPLICIT_DEPS) && target instanceof RuleConfiguredTarget) { - Set implicitDeps = ((RuleConfiguredTarget) target).getImplicitDeps(); - deps = - deps.stream() - .filter( - dep -> - !implicitDeps.contains( - ConfiguredTargetKey.of(getCorrectLabel(dep), getConfiguration(dep)))) - .collect(Collectors.toList()); + if (settings.contains(Setting.NO_IMPLICIT_DEPS)) { + RuleConfiguredTarget ruleConfiguredTarget = getRuleConfiguredTarget(target); + if (ruleConfiguredTarget != null) { + Set implicitDeps = ruleConfiguredTarget.getImplicitDeps(); + deps = + deps.stream() + .filter( + dep -> + !implicitDeps.contains( + ConfiguredTargetKey.of(getCorrectLabel(dep), getConfiguration(dep)))) + .collect(Collectors.toList()); + } } return deps; } + protected abstract RuleConfiguredTarget getRuleConfiguredTarget(T target); + protected Map> targetifyValues( Map> input) throws InterruptedException { Map> result = new HashMap<>(); diff --git a/src/main/java/com/google/devtools/build/lib/query2/output/AqueryOptions.java b/src/main/java/com/google/devtools/build/lib/query2/output/AqueryOptions.java new file mode 100644 index 0000000000..157b3b4f8e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/output/AqueryOptions.java @@ -0,0 +1,30 @@ +// 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.output; + +import com.google.devtools.build.lib.query2.CommonQueryOptions; +import com.google.devtools.common.options.Option; +import com.google.devtools.common.options.OptionDocumentationCategory; +import com.google.devtools.common.options.OptionEffectTag; + +/** Options class for aquery specific query options. */ +public class AqueryOptions extends CommonQueryOptions { + @Option( + name = "output", + defaultValue = "proto", + documentationCategory = OptionDocumentationCategory.QUERY, + effectTags = {OptionEffectTag.TERMINAL_OUTPUT}, + help = "The format in which the aquery results should be printed.") + public String outputFormat; +} 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 e94f2f5be2..63896d3ccf 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 @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.lib.runtime; +import com.google.devtools.build.lib.runtime.commands.AqueryCommand; 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; @@ -58,6 +59,7 @@ public class BuiltinCommandModule extends BlazeModule { new ShutdownCommand(), new TestCommand(), new VersionCommand(), + new AqueryCommand(), new CqueryCommand()); // Only enable the "license" command when this binary has an embedded LICENSE file. if (LicenseCommand.isSupported()) { diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java new file mode 100644 index 0000000000..ec8ca19f47 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java @@ -0,0 +1,121 @@ +// 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.AqueryBuildTool; +import com.google.devtools.build.lib.buildtool.BuildRequest; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.query2.ActionGraphQueryEnvironment; +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.query2.output.AqueryOptions; +import com.google.devtools.build.lib.runtime.BlazeCommand; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; +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 'aquery' command on the Blaze command line. */ +@Command( + name = "aquery", + builds = true, + inherits = {BuildCommand.class}, + options = {AqueryOptions.class}, + usesConfigurationOptions = true, + shortDescription = "Analyzes the given targets and queries the action graph.", + allowResidue = true, + completion = "label", + help = "resource:aquery.txt") +public final class AqueryCommand implements BlazeCommand { + + @Override + public void editOptions(OptionsParser optionsParser) { + try { + optionsParser.parse( + PriorityCategory.COMPUTED_DEFAULT, + "Option required by aquery", + ImmutableList.of("--nobuild")); + } catch (OptionsParsingException e) { + throw new IllegalStateException("Aquery's known options failed to parse", e); + } + } + + @Override + public BlazeCommandResult exec(CommandEnvironment env, OptionsProvider options) { + // TODO(twerth): Reduce overlap with CqueryCommand. + env.getReporter() + .handle( + Event.warn( + "Note that the aquery command is still experimental " + + "and its API will change in the future.")); + if (options.getResidue().isEmpty()) { + env.getReporter() + .handle( + Event.error( + "Missing query expression. Use the 'help aquery' command for syntax and help.")); + return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); + } + String query = Joiner.on(' ').join(options.getResidue()); + HashMap functions = new HashMap<>(); + for (QueryFunction queryFunction : ActionGraphQueryEnvironment.FUNCTIONS) { + functions.put(queryFunction.getName(), queryFunction); + } + for (QueryFunction queryFunction : env.getRuntime().getQueryFunctions()) { + 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 BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR); + } + + List topLevelTargets = options.getOptions(AqueryOptions.class).universeScope; + Set 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()); + ExitCode exitCode = + new AqueryBuildTool(env, expr).processRequest(request, null).getExitCondition(); + return BlazeCommandResult.exitCode(exitCode); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/aquery.txt b/src/main/java/com/google/devtools/build/lib/runtime/commands/aquery.txt new file mode 100644 index 0000000000..d8585f4a33 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/aquery.txt @@ -0,0 +1,8 @@ +Usage: %{product} %{command} + +Queries the specified targets, taking options into account and returns analysis +time information about the action graph. +The aquery command works on the post analysis phase configured target graph. It +understands and takes into account configurations and configuration transitions. + +%{options} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java b/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java index acd5b3bd1b..5ade7a91ea 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/ActionGraphDump.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.skyframe.actiongraph; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -59,6 +60,10 @@ public class ActionGraphDump { private final KnownRuleConfiguredTargets knownRuleConfiguredTargets; private final boolean includeActionCmdLine; + public ActionGraphDump(boolean includeActionCmdLine) { + this(/* actionGraphTargets= */ ImmutableList.of("..."), includeActionCmdLine); + } + public ActionGraphDump(List actionGraphTargets, boolean includeActionCmdLine) { this.actionGraphTargets = ImmutableSet.copyOf(actionGraphTargets); this.includeActionCmdLine = includeActionCmdLine; -- cgit v1.2.3