diff options
author | twerth <twerth@google.com> | 2018-06-11 03:09:29 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-06-11 03:10:39 -0700 |
commit | 1a6c706d7b92d2529986c814cc1c9eac2dab9a1c (patch) | |
tree | 957c7ec1365070801171ec3449b8e4ff62e98292 /src/main/java/com/google/devtools/build/lib/buildtool | |
parent | 4022bac855a790b3a42fcb44c06f62dbbe078c5a (diff) |
Introduce generic post analysis build tool.
This is the first step on the way to a proper action graph query command.
RELNOTES: None
PiperOrigin-RevId: 200026440
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/buildtool')
3 files changed, 185 insertions, 134 deletions
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 407f2f63a1..755ea82151 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 @@ -39,7 +39,7 @@ import com.google.devtools.build.lib.analysis.config.InvalidConfigurationExcepti import com.google.devtools.build.lib.buildeventstream.AbortedEvent; import com.google.devtools.build.lib.buildeventstream.BuildEventId; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Aborted.AbortReason; -import com.google.devtools.build.lib.buildtool.CqueryBuildTool.ConfiguredTargetQueryCommandLineException; +import com.google.devtools.build.lib.buildtool.PostAnalysisQueryBuildTool.PostAnalysisQueryCommandLineException; import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent; import com.google.devtools.build.lib.buildtool.buildevent.BuildInterruptedEvent; import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent; @@ -131,14 +131,10 @@ public class BuildTool { * @param result the build result that is the mutable result of this build * @param validator target validator */ - public void buildTargets( - BuildRequest request, - BuildResult result, - TargetValidator validator) + public void buildTargets(BuildRequest request, BuildResult result, TargetValidator validator) throws BuildFailedException, InterruptedException, ViewCreationFailedException, TargetParsingException, LoadingFailedException, AbruptExitException, - InvalidConfigurationException, TestExecException, - ConfiguredTargetQueryCommandLineException { + InvalidConfigurationException, TestExecException, PostAnalysisQueryCommandLineException { validateOptions(request); BuildOptions buildOptions = runtime.createBuildOptions(request); // Sync the package manager before sending the BuildStartingEvent in runLoadingPhase() @@ -311,8 +307,7 @@ public class BuildTool { AnalysisResult analysisResult, BuildConfigurationCollection configurations) throws InterruptedException, ViewCreationFailedException, - ConfiguredTargetQueryCommandLineException { - } + PostAnalysisQueryCommandLineException {} private void reportExceptionError(Exception e) { if (e.getMessage() != null) { @@ -376,7 +371,7 @@ public class BuildTool { } catch (TargetParsingException | LoadingFailedException | ViewCreationFailedException e) { exitCode = ExitCode.PARSING_FAILURE; reportExceptionError(e); - } catch (ConfiguredTargetQueryCommandLineException e) { + } catch (PostAnalysisQueryCommandLineException e) { exitCode = ExitCode.COMMAND_LINE_ERROR; reportExceptionError(e); } catch (TestExecException e) { diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java index 46ed57cd4f..2bdd2b7750 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java @@ -14,151 +14,42 @@ package com.google.devtools.build.lib.buildtool; import com.google.common.collect.ImmutableList; -import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult; -import com.google.devtools.build.lib.analysis.TargetAndConfiguration; -import com.google.devtools.build.lib.analysis.ViewCreationFailedException; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; -import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection; -import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.query2.ConfiguredTargetQueryEnvironment; -import com.google.devtools.build.lib.query2.CqueryThreadsafeCallback; import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction; -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.output.CqueryOptions; import com.google.devtools.build.lib.runtime.CommandEnvironment; -import com.google.devtools.build.lib.skyframe.SkyframeExecutorWrappingWalkableGraph; import com.google.devtools.build.skyframe.WalkableGraph; -import java.io.IOException; -import java.util.List; -import java.util.stream.Collectors; /** A version of {@link BuildTool} that handles all cquery work. */ -public class CqueryBuildTool extends BuildTool { - - private final QueryExpression queryExpression; +public class CqueryBuildTool extends PostAnalysisQueryBuildTool { public CqueryBuildTool(CommandEnvironment env, QueryExpression queryExpression) { - super(env); - this.queryExpression = queryExpression; + super(env, queryExpression); } @Override - protected void postProcessAnalysisResult( - BuildRequest request, - AnalysisResult analysisResult, - BuildConfigurationCollection configurations) - throws InterruptedException, ViewCreationFailedException, - ConfiguredTargetQueryCommandLineException { - // TODO: b/71905538 - this query will operate over the graph as constructed by analysis, but - // will also pick up any nodes that are in the graph from prior builds. This makes the results - // 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 (queryExpression != null) { - if (!env.getSkyframeExecutor().tracksStateForIncrementality()) { - throw new ConfiguredTargetQueryCommandLineException( - "Configured query is not allowed if incrementality state is not being kept"); - } - try { - doConfiguredTargetQuery( - request, - configurations.getHostConfiguration(), - analysisResult.getTopLevelTargetsWithConfigs(), - queryExpression); - } catch (QueryException | IOException e) { - if (!request.getKeepGoing()) { - throw new ViewCreationFailedException("Error doing configured target query", e); - } - env.getReporter().error(null, "Error doing configured target query", e); - } - } - } - - private void doConfiguredTargetQuery( + protected ConfiguredTargetQueryEnvironment getQueryEnvironment( BuildRequest request, BuildConfiguration hostConfiguration, - List<TargetAndConfiguration> topLevelTargetsWithConfigs, - QueryExpression queryExpression) - throws InterruptedException, QueryException, IOException { - - // 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. - // So for now, we only allow multiple targets when they all carry the same config. - // TODO: b/71508373 - fully support multiple top level targets - List<TargetAndConfiguration> nonNullTargets = - topLevelTargetsWithConfigs - .stream() - .filter(targetAndConfig -> targetAndConfig.getConfiguration() != null) - .collect(Collectors.toList()); - BuildConfiguration targetConfig = null; - if (!nonNullTargets.isEmpty()) { - targetConfig = nonNullTargets.get(0).getConfiguration(); - for (TargetAndConfiguration targAndConfig : topLevelTargetsWithConfigs) { - if (targAndConfig.getConfiguration() != null - && !targAndConfig.getConfiguration().equals(targetConfig)) { - throw new QueryException( - new TargetLiteral(queryExpression.toString()), - String.format( - "Top-level targets %s and %s have different configurations (top-level " - + "targets with different configurations is not supported)", - nonNullTargets.get(0).getLabel(), targAndConfig.getLabel())); - } - } - } - WalkableGraph walkableGraph = - SkyframeExecutorWrappingWalkableGraph.of(env.getSkyframeExecutor()); + BuildConfiguration targetConfig, + WalkableGraph walkableGraph) { ImmutableList<QueryFunction> extraFunctions = new ImmutableList.Builder<QueryFunction>() .addAll(ConfiguredTargetQueryEnvironment.CQUERY_FUNCTIONS) .addAll(env.getRuntime().getQueryFunctions()) .build(); CqueryOptions cqueryOptions = request.getOptions(CqueryOptions.class); - ConfiguredTargetQueryEnvironment configuredTargetQueryEnvironment = - new ConfiguredTargetQueryEnvironment( - request.getKeepGoing(), - env.getReporter(), - extraFunctions, - targetConfig, - hostConfiguration, - env.newTargetPatternEvaluator().getOffset(), - env.getPackageManager().getPackagePath(), - () -> walkableGraph, - cqueryOptions.toSettings()); - Iterable<CqueryThreadsafeCallback> callbacks = - configuredTargetQueryEnvironment.getDefaultOutputFormatters( - configuredTargetQueryEnvironment.getAccessor(), - cqueryOptions, - env.getReporter(), - env.getSkyframeExecutor(), - hostConfiguration, - runtime.getRuleClassProvider().getTrimmingTransitionFactory(), - cqueryOptions.aspectDeps.createResolver(env.getPackageManager(), env.getReporter())); - CqueryThreadsafeCallback callback = - CqueryThreadsafeCallback.getCallback(cqueryOptions.outputFormat, callbacks); - if (callback == null) { - env.getReporter() - .handle( - Event.error( - String.format( - "Invalid output format '%s'. Valid values are: %s", - cqueryOptions.outputFormat, - CqueryThreadsafeCallback.callbackNames(callbacks)))); - return; - } - QueryEvalResult result = - configuredTargetQueryEnvironment.evaluateQuery(queryExpression, callback); - if (result.isEmpty()) { - env.getReporter().handle(Event.info("Empty query results")); - } - } - - /** Cquery specific command line exception. */ - protected static class ConfiguredTargetQueryCommandLineException extends Exception { - ConfiguredTargetQueryCommandLineException(String message) { - super(message); - } + return new ConfiguredTargetQueryEnvironment( + request.getKeepGoing(), + env.getReporter(), + extraFunctions, + targetConfig, + hostConfiguration, + env.newTargetPatternEvaluator().getOffset(), + env.getPackageManager().getPackagePath(), + () -> walkableGraph, + cqueryOptions); } } diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/PostAnalysisQueryBuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/PostAnalysisQueryBuildTool.java new file mode 100644 index 0000000000..df5205eb70 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/buildtool/PostAnalysisQueryBuildTool.java @@ -0,0 +1,165 @@ +// 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.devtools.build.lib.analysis.BuildView.AnalysisResult; +import com.google.devtools.build.lib.analysis.TargetAndConfiguration; +import com.google.devtools.build.lib.analysis.ViewCreationFailedException; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.query2.ConfiguredTargetQueryEnvironment; +import com.google.devtools.build.lib.query2.CqueryThreadsafeCallback; +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.runtime.CommandEnvironment; +import com.google.devtools.build.lib.skyframe.SkyframeExecutorWrappingWalkableGraph; +import com.google.devtools.build.skyframe.WalkableGraph; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Version of {@link BuildTool} that handles all work for queries based on results from the analysis + * phase. + */ +public abstract class PostAnalysisQueryBuildTool extends BuildTool { + + private final QueryExpression queryExpression; + + public PostAnalysisQueryBuildTool(CommandEnvironment env, QueryExpression queryExpression) { + super(env); + this.queryExpression = queryExpression; + } + + @Override + protected void postProcessAnalysisResult( + BuildRequest request, + AnalysisResult analysisResult, + BuildConfigurationCollection configurations) + throws InterruptedException, ViewCreationFailedException, + PostAnalysisQueryCommandLineException { + // TODO: b/71905538 - this query will operate over the graph as constructed by analysis, but + // will also pick up any nodes that are in the graph from prior builds. This makes the results + // 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 (queryExpression != null) { + if (!env.getSkyframeExecutor().tracksStateForIncrementality()) { + throw new PostAnalysisQueryCommandLineException( + "Queries based on analysis results are not allowed " + + "if incrementality state is not being kept"); + } + try { + doPostAnalysisQuery( + request, + configurations.getHostConfiguration(), + analysisResult.getTopLevelTargetsWithConfigs(), + queryExpression); + } catch (QueryException | IOException e) { + if (!request.getKeepGoing()) { + throw new ViewCreationFailedException("Error doing post analysis query", e); + } + env.getReporter().error(null, "Error doing post analysis query", e); + } + } + } + + // TODO(twerth): Make this more generic when introducting a PostAnalysisQueryEnvironment. + protected abstract ConfiguredTargetQueryEnvironment getQueryEnvironment( + BuildRequest request, + BuildConfiguration hostConfiguration, + BuildConfiguration targetConfig, + WalkableGraph walkableGraph); + + private BuildConfiguration getBuildConfiguration( + List<TargetAndConfiguration> topLevelTargetsWithConfigs, QueryExpression queryExpression) + throws QueryException { + // 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. + // So for now, we only allow multiple targets when they all carry the same config. + // TODO: b/71508373 - fully support multiple top level targets + List<TargetAndConfiguration> nonNullTargets = + topLevelTargetsWithConfigs + .stream() + .filter(targetAndConfig -> targetAndConfig.getConfiguration() != null) + .collect(Collectors.toList()); + BuildConfiguration targetConfig = null; + if (!nonNullTargets.isEmpty()) { + targetConfig = nonNullTargets.get(0).getConfiguration(); + for (TargetAndConfiguration targAndConfig : topLevelTargetsWithConfigs) { + if (targAndConfig.getConfiguration() != null + && !targAndConfig.getConfiguration().equals(targetConfig)) { + throw new QueryException( + new TargetLiteral(queryExpression.toString()), + String.format( + "Top-level targets %s and %s have different configurations (top-level " + + "targets with different configurations is not supported)", + nonNullTargets.get(0).getLabel(), targAndConfig.getLabel())); + } + } + } + return targetConfig; + } + + private void doPostAnalysisQuery( + BuildRequest request, + BuildConfiguration hostConfiguration, + List<TargetAndConfiguration> topLevelTargetsWithConfigs, + QueryExpression queryExpression) + throws InterruptedException, QueryException, IOException { + BuildConfiguration targetConfig = + getBuildConfiguration(topLevelTargetsWithConfigs, queryExpression); + + WalkableGraph walkableGraph = + SkyframeExecutorWrappingWalkableGraph.of(env.getSkyframeExecutor()); + + ConfiguredTargetQueryEnvironment configuredTargetQueryEnvironment = + getQueryEnvironment(request, hostConfiguration, targetConfig, walkableGraph); + Iterable<CqueryThreadsafeCallback> callbacks = + configuredTargetQueryEnvironment.getDefaultOutputFormatters( + configuredTargetQueryEnvironment.getAccessor(), + env.getReporter(), + env.getSkyframeExecutor(), + hostConfiguration, + runtime.getRuleClassProvider().getTrimmingTransitionFactory(), + env.getPackageManager()); + String outputFormat = configuredTargetQueryEnvironment.getOutputFormat(); + CqueryThreadsafeCallback callback = + CqueryThreadsafeCallback.getCallback(outputFormat, callbacks); + if (callback == null) { + env.getReporter() + .handle( + Event.error( + String.format( + "Invalid output format '%s'. Valid values are: %s", + outputFormat, CqueryThreadsafeCallback.callbackNames(callbacks)))); + return; + } + QueryEvalResult result = + configuredTargetQueryEnvironment.evaluateQuery(queryExpression, callback); + if (result.isEmpty()) { + env.getReporter().handle(Event.info("Empty query results")); + } + } + + /** Post analysis query specific command line exception. */ + protected static class PostAnalysisQueryCommandLineException extends Exception { + PostAnalysisQueryCommandLineException(String message) { + super(message); + } + } +} |