blob: f004f830a62cf763d15bf23debf566503476dd0c [file] [log] [blame]
// 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.aquery;
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.config.transitions.TransitionFactory;
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.packages.Rule;
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.ConfiguredTargetValueAccessor;
import com.google.devtools.build.lib.query2.NamedThreadSafeOutputFormatterCallback;
import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment;
import com.google.devtools.build.lib.query2.SkyQueryEnvironment;
import com.google.devtools.build.lib.query2.aquery.ActionGraphProtoOutputFormatterCallback.OutputType;
import com.google.devtools.build.lib.query2.engine.Callback;
import com.google.devtools.build.lib.query2.engine.InputsFunction;
import com.google.devtools.build.lib.query2.engine.KeyExtractor;
import com.google.devtools.build.lib.query2.engine.MnemonicFunction;
import com.google.devtools.build.lib.query2.engine.OutputsFunction;
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.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<ConfiguredTargetValue> {
public static final ImmutableList<QueryFunction> AQUERY_FUNCTIONS = populateAqueryFunctions();
public static final ImmutableList<QueryFunction> FUNCTIONS = populateFunctions();
AqueryOptions aqueryOptions;
private AqueryActionFilter actionFilters;
private final KeyExtractor<ConfiguredTargetValue, ConfiguredTargetKey>
configuredTargetKeyExtractor;
private final ConfiguredTargetValueAccessor accessor;
public ActionGraphQueryEnvironment(
boolean keepGoing,
ExtendedEventHandler eventHandler,
Iterable<QueryFunction> extraFunctions,
TopLevelConfigurations topLevelConfigurations,
BuildConfiguration hostConfiguration,
String parserPrefix,
PathPackageLocator pkgPath,
Supplier<WalkableGraph> walkableGraphSupplier,
Set<Setting> settings) {
super(
keepGoing,
eventHandler,
extraFunctions,
topLevelConfigurations,
hostConfiguration,
parserPrefix,
pkgPath,
walkableGraphSupplier,
settings);
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", e);
}
};
this.accessor =
new ConfiguredTargetValueAccessor(
walkableGraphSupplier.get(), this.configuredTargetKeyExtractor);
}
public ActionGraphQueryEnvironment(
boolean keepGoing,
ExtendedEventHandler eventHandler,
Iterable<QueryFunction> extraFunctions,
TopLevelConfigurations topLevelConfigurations,
BuildConfiguration hostConfiguration,
String parserPrefix,
PathPackageLocator pkgPath,
Supplier<WalkableGraph> walkableGraphSupplier,
AqueryOptions aqueryOptions) {
this(
keepGoing,
eventHandler,
extraFunctions,
topLevelConfigurations,
hostConfiguration,
parserPrefix,
pkgPath,
walkableGraphSupplier,
aqueryOptions.toSettings());
this.aqueryOptions = aqueryOptions;
}
private static ImmutableList<QueryFunction> populateFunctions() {
return ImmutableList.copyOf(QueryEnvironment.DEFAULT_QUERY_FUNCTIONS);
}
private static ImmutableList<QueryFunction> populateAqueryFunctions() {
return ImmutableList.of(new InputsFunction(), new OutputsFunction(), new MnemonicFunction());
}
@Override
public ConfiguredTargetValueAccessor getAccessor() {
return accessor;
}
@Override
public ImmutableList<NamedThreadSafeOutputFormatterCallback<ConfiguredTargetValue>>
getDefaultOutputFormatters(
TargetAccessor<ConfiguredTargetValue> accessor,
ExtendedEventHandler eventHandler,
OutputStream out,
SkyframeExecutor skyframeExecutor,
BuildConfiguration hostConfiguration,
@Nullable TransitionFactory<Rule> trimmingTransitionFactory,
PackageManager packageManager) {
return aqueryOptions.protoV2
? ImmutableList.of(
new ActionGraphProtoV2OutputFormatterCallback(
eventHandler,
aqueryOptions,
out,
skyframeExecutor,
accessor,
ActionGraphProtoV2OutputFormatterCallback.OutputType.BINARY,
actionFilters),
new ActionGraphProtoV2OutputFormatterCallback(
eventHandler,
aqueryOptions,
out,
skyframeExecutor,
accessor,
ActionGraphProtoV2OutputFormatterCallback.OutputType.TEXT,
actionFilters),
new ActionGraphTextOutputFormatterCallback(
eventHandler, aqueryOptions, out, skyframeExecutor, accessor, actionFilters))
: ImmutableList.of(
new ActionGraphProtoOutputFormatterCallback(
eventHandler,
aqueryOptions,
out,
skyframeExecutor,
accessor,
OutputType.BINARY,
actionFilters),
new ActionGraphProtoOutputFormatterCallback(
eventHandler,
aqueryOptions,
out,
skyframeExecutor,
accessor,
OutputType.TEXT,
actionFilters),
new ActionGraphTextOutputFormatterCallback(
eventHandler, aqueryOptions, out, skyframeExecutor, accessor, actionFilters));
}
@Override
public String getOutputFormat() {
return aqueryOptions.outputFormat;
}
@Override
protected KeyExtractor<ConfiguredTargetValue, ConfiguredTargetKey>
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 {
if (topLevelConfigurations.isTopLevelTarget(label)) {
return this.getConfiguredTargetValue(
ConfiguredTargetValue.key(
label, topLevelConfigurations.getConfigurationForTopLevelTarget(label)));
} else {
ConfiguredTargetValue toReturn;
for (BuildConfiguration configuration : topLevelConfigurations.getConfigurations()) {
toReturn = this.getConfiguredTargetValue(ConfiguredTargetValue.key(label, configuration));
if (toReturn != null) {
return toReturn;
}
}
return null;
}
}
@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", e);
}
}
@Override
protected ConfiguredTargetKey getSkyKey(ConfiguredTargetValue configuredTargetValue) {
ConfiguredTarget target = configuredTargetValue.getConfiguredTarget();
return ConfiguredTargetKey.of(target, getConfiguration(configuredTargetValue));
}
@Override
public QueryTaskFuture<Void> getTargetsMatchingPattern(
QueryExpression owner, String pattern, Callback<ConfiguredTargetValue> 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<TargetParsingException, Void> reportBuildFileErrorAsyncFunction =
exn -> {
reportBuildFileError(owner, exn.getMessage());
return Futures.immediateFuture(null);
};
try {
return QueryTaskFutureImpl.ofDelegate(
Futures.catchingAsync(
patternToEval.evalAdaptedForAsync(
resolver,
getBlacklistedPackagePrefixesPathFragments(),
/* excludedSubdirectories= */ ImmutableSet.of(),
(Callback<Target>)
partialResult -> {
List<ConfiguredTargetValue> 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()));
} catch (InterruptedException e) {
return immediateCancelledFuture();
}
}
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 --notool_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<ConfiguredTargetValue> createThreadSafeMutableSet() {
return new ThreadSafeMutableKeyExtractorBackedSetImpl<>(
configuredTargetKeyExtractor,
ConfiguredTargetValue.class,
SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
}
public void setActionFilters(AqueryActionFilter actionFilters) {
this.actionFilters = actionFilters;
}
}