| // Copyright 2017 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.collect.Iterables; |
| import com.google.common.collect.Sets; |
| 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.collect.compacthashset.CompactHashSet; |
| import com.google.devtools.build.lib.concurrent.MultisetSemaphore; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.ExtendedEventHandler; |
| import com.google.devtools.build.lib.events.Reporter; |
| import com.google.devtools.build.lib.packages.DependencyFilter; |
| import com.google.devtools.build.lib.packages.NoSuchTargetException; |
| import com.google.devtools.build.lib.packages.Rule; |
| import com.google.devtools.build.lib.packages.RuleTransitionFactory; |
| import com.google.devtools.build.lib.packages.Target; |
| import com.google.devtools.build.lib.pkgcache.FilteringPolicies; |
| import com.google.devtools.build.lib.pkgcache.PathPackageLocator; |
| import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; |
| 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.MinDepthUniquifier; |
| import com.google.devtools.build.lib.query2.engine.QueryEnvironment; |
| 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.QueryUtil.MinDepthUniquifierImpl; |
| import com.google.devtools.build.lib.query2.engine.QueryUtil.MutableKeyExtractorBackedMapImpl; |
| import com.google.devtools.build.lib.query2.engine.QueryUtil.ThreadSafeMutableKeyExtractorBackedSetImpl; |
| import com.google.devtools.build.lib.query2.engine.QueryUtil.UniquifierImpl; |
| import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback; |
| import com.google.devtools.build.lib.query2.engine.Uniquifier; |
| import com.google.devtools.build.lib.query2.output.AspectResolver; |
| import com.google.devtools.build.lib.query2.output.CqueryOptions; |
| import com.google.devtools.build.lib.query2.output.QueryOptions; |
| 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.GraphBackedRecursivePackageProvider; |
| import com.google.devtools.build.lib.skyframe.PackageValue; |
| import com.google.devtools.build.lib.skyframe.RecursivePackageProviderBackedTargetPatternResolver; |
| import com.google.devtools.build.lib.skyframe.RecursivePkgValueRootPackageExtractor; |
| import com.google.devtools.build.lib.skyframe.SkyFunctions; |
| import com.google.devtools.build.lib.skyframe.SkyframeExecutor; |
| import com.google.devtools.build.lib.skyframe.TargetPatternValue; |
| import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey; |
| import com.google.devtools.build.skyframe.SkyKey; |
| import com.google.devtools.build.skyframe.WalkableGraph; |
| import com.google.devtools.common.options.OptionsParser; |
| import com.google.devtools.common.options.OptionsParsingException; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.Function; |
| import java.util.function.Supplier; |
| import java.util.stream.Collectors; |
| import javax.annotation.Nullable; |
| |
| /** |
| * {@link QueryEnvironment} that runs queries over the configured target (analysis) graph. |
| * |
| * <p>This environment can theoretically be used for multiple queries, but currently is only ever |
| * used for one over the course of its lifetime. If this ever changed to be used for multiple, the |
| * {@link ConfiguredTargetAccessor} field should be initialized on a per-query basis not a |
| * per-environment basis. |
| * |
| * <p>There is currently a limited way to specify a configuration in the query syntax via {@link |
| * ConfigFunction}. This currently still limits the user to choosing the 'target', 'host', or null |
| * configurations. It shouldn't be terribly difficult to expand this with {@link |
| * OptionsDiffForReconstruction} to handle fully customizable configurations if the need arises in |
| * the future. |
| * |
| * <p>Aspects are also not supported, but probably should be in some fashion. |
| */ |
| public class ConfiguredTargetQueryEnvironment |
| extends AbstractBlazeQueryEnvironment<ConfiguredTarget> { |
| private final BuildConfiguration defaultTargetConfiguration; |
| private final BuildConfiguration hostConfiguration; |
| private final String parserPrefix; |
| protected final PathPackageLocator pkgPath; |
| private final Supplier<WalkableGraph> walkableGraphSupplier; |
| private ConfiguredTargetAccessor accessor; |
| protected WalkableGraph graph; |
| |
| private static final Function<SkyKey, ConfiguredTargetKey> SKYKEY_TO_CTKEY = |
| skyKey -> (ConfiguredTargetKey) skyKey.argument(); |
| private static final ImmutableList<TargetPatternKey> ALL_PATTERNS; |
| private final KeyExtractor<ConfiguredTarget, ConfiguredTargetKey> configuredTargetKeyExtractor; |
| |
| /** Common query functions and cquery specific functions. */ |
| public static final ImmutableList<QueryFunction> FUNCTIONS = populateFunctions(); |
| /** Cquery specific functions. */ |
| public static final ImmutableList<QueryFunction> CQUERY_FUNCTIONS = getCqueryFunctions(); |
| |
| static { |
| TargetPattern targetPattern; |
| try { |
| targetPattern = TargetPattern.defaultParser().parse("//..."); |
| } catch (TargetParsingException e) { |
| throw new IllegalStateException(e); |
| } |
| ALL_PATTERNS = |
| ImmutableList.of( |
| new TargetPatternKey( |
| targetPattern, FilteringPolicies.NO_FILTER, false, "", ImmutableSet.of())); |
| } |
| |
| private RecursivePackageProviderBackedTargetPatternResolver resolver; |
| |
| public ConfiguredTargetQueryEnvironment( |
| boolean keepGoing, |
| ExtendedEventHandler eventHandler, |
| Iterable<QueryFunction> extraFunctions, |
| BuildConfiguration defaultTargetConfiguration, |
| BuildConfiguration hostConfiguration, |
| String parserPrefix, |
| PathPackageLocator pkgPath, |
| Supplier<WalkableGraph> walkableGraphSupplier, |
| Set<Setting> settings) { |
| super(keepGoing, true, Rule.ALL_LABELS, eventHandler, settings, extraFunctions); |
| this.defaultTargetConfiguration = defaultTargetConfiguration; |
| this.hostConfiguration = hostConfiguration; |
| this.parserPrefix = parserPrefix; |
| this.pkgPath = pkgPath; |
| this.walkableGraphSupplier = walkableGraphSupplier; |
| this.accessor = new ConfiguredTargetAccessor(walkableGraphSupplier.get()); |
| this.configuredTargetKeyExtractor = |
| element -> { |
| try { |
| 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"); |
| } |
| }; |
| } |
| |
| private static ImmutableList<QueryFunction> populateFunctions() { |
| return new ImmutableList.Builder<QueryFunction>() |
| .addAll(QueryEnvironment.DEFAULT_QUERY_FUNCTIONS) |
| .addAll(getCqueryFunctions()) |
| .build(); |
| } |
| |
| private static ImmutableList<QueryFunction> getCqueryFunctions() { |
| return ImmutableList.of(new ConfigFunction()); |
| } |
| |
| public ImmutableList<CqueryThreadsafeCallback> getDefaultOutputFormatters( |
| TargetAccessor<ConfiguredTarget> accessor, |
| CqueryOptions options, |
| Reporter reporter, |
| SkyframeExecutor skyframeExecutor, |
| BuildConfiguration hostConfiguration, |
| @Nullable RuleTransitionFactory trimmingTransitionFactory, |
| AspectResolver resolver) { |
| OutputStream out = reporter.getOutErr().getOutputStream(); |
| return new ImmutableList.Builder<CqueryThreadsafeCallback>() |
| .add( |
| new LabelAndConfigurationOutputFormatterCallback( |
| reporter, options, out, skyframeExecutor, accessor)) |
| .add( |
| new TransitionsOutputFormatterCallback( |
| reporter, |
| options, |
| out, |
| skyframeExecutor, |
| accessor, |
| hostConfiguration, |
| trimmingTransitionFactory)) |
| .add( |
| new ProtoOutputFormatterCallback( |
| reporter, options, out, skyframeExecutor, accessor, resolver)) |
| .build(); |
| } |
| |
| @Override |
| public QueryEvalResult evaluateQuery( |
| QueryExpression expr, ThreadSafeOutputFormatterCallback<ConfiguredTarget> callback) |
| throws QueryException, InterruptedException, IOException { |
| beforeEvaluateQuery(); |
| return super.evaluateQuery(expr, callback); |
| } |
| |
| private void beforeEvaluateQuery() throws InterruptedException, QueryException { |
| graph = walkableGraphSupplier.get(); |
| GraphBackedRecursivePackageProvider graphBackedRecursivePackageProvider = |
| new GraphBackedRecursivePackageProvider( |
| graph, ALL_PATTERNS, pkgPath, new RecursivePkgValueRootPackageExtractor()); |
| resolver = |
| new RecursivePackageProviderBackedTargetPatternResolver( |
| graphBackedRecursivePackageProvider, |
| eventHandler, |
| FilteringPolicies.NO_FILTER, |
| MultisetSemaphore.unbounded()); |
| checkSettings(settings); |
| } |
| |
| // Check to make sure the settings requested are currently supported by this class |
| private void checkSettings(Set<Setting> settings) throws QueryException { |
| if (settings.contains(Setting.NO_NODEP_DEPS) |
| || settings.contains(Setting.TESTS_EXPRESSION_STRICT)) { |
| settings = |
| Sets.difference( |
| settings, ImmutableSet.of(Setting.NO_HOST_DEPS, Setting.NO_IMPLICIT_DEPS)); |
| throw new QueryException( |
| String.format( |
| "The following filter(s) are not currently supported by configured query: %s", |
| settings.toString())); |
| } |
| } |
| |
| public BuildConfiguration getHostConfiguration() { |
| return hostConfiguration; |
| } |
| |
| @Override |
| public TargetAccessor<ConfiguredTarget> getAccessor() { |
| return accessor; |
| } |
| |
| // TODO(bazel-team): It's weird that this untemplated function exists. Fix? Or don't implement? |
| @Override |
| public Target getTarget(Label label) |
| throws TargetNotFoundException, QueryException, InterruptedException { |
| try { |
| return ((PackageValue) |
| walkableGraphSupplier.get().getValue(PackageValue.key(label.getPackageIdentifier()))) |
| .getPackage() |
| .getTarget(label.getName()); |
| } catch (NoSuchTargetException e) { |
| throw new TargetNotFoundException(e); |
| } |
| } |
| |
| @Override |
| public ConfiguredTarget getOrCreate(ConfiguredTarget target) { |
| return target; |
| } |
| |
| /** |
| * This method has to exist because {@link AliasConfiguredTarget#getLabel()} returns |
| * the label of the "actual" target instead of the alias target. Grr. |
| */ |
| public static Label getCorrectLabel(ConfiguredTarget target) { |
| if (target instanceof AliasConfiguredTarget) { |
| return ((AliasConfiguredTarget) target).getOriginalLabel(); |
| } |
| return target.getLabel(); |
| } |
| |
| @Override |
| public QueryTaskFuture<Void> getTargetsMatchingPattern( |
| QueryExpression owner, String pattern, Callback<ConfiguredTarget> callback) { |
| TargetPattern patternToEval; |
| try { |
| patternToEval = getPattern(pattern); |
| } catch (TargetParsingException tpe) { |
| try { |
| reportBuildFileError(owner, tpe.getMessage()); |
| } catch (QueryException qe) { |
| return immediateFailedFuture(qe); |
| } |
| return immediateSuccessfulFuture(null); |
| } catch (InterruptedException ie) { |
| return immediateCancelledFuture(); |
| } |
| AsyncFunction<TargetParsingException, Void> reportBuildFileErrorAsyncFunction = |
| exn -> { |
| reportBuildFileError(owner, exn.getMessage()); |
| return Futures.immediateFuture(null); |
| }; |
| return QueryTaskFutureImpl.ofDelegate( |
| Futures.catchingAsync( |
| patternToEval.evalAdaptedForAsync( |
| resolver, |
| ImmutableSet.of(), |
| ImmutableSet.of(), |
| (Callback<Target>) |
| partialResult -> { |
| List<ConfiguredTarget> transformedResult = new ArrayList<>(); |
| for (Target target : partialResult) { |
| ConfiguredTarget configuredTarget = getConfiguredTarget(target.getLabel()); |
| if (configuredTarget != null) { |
| transformedResult.add(configuredTarget); |
| } |
| } |
| callback.process(transformedResult); |
| }, |
| QueryException.class), |
| TargetParsingException.class, |
| reportBuildFileErrorAsyncFunction, |
| MoreExecutors.directExecutor())); |
| } |
| |
| private ConfiguredTarget getConfiguredTarget(Label label) throws InterruptedException { |
| // Try with target configuration. |
| ConfiguredTarget configuredTarget = getTargetConfiguredTarget(label); |
| if (configuredTarget != null) { |
| return configuredTarget; |
| } |
| // 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). |
| configuredTarget = getHostConfiguredTarget(label); |
| if (configuredTarget != null) { |
| return configuredTarget; |
| } |
| // Last chance: source file. |
| return getNullConfiguredTarget(label); |
| } |
| |
| @Nullable |
| private ConfiguredTarget getConfiguredTarget(SkyKey key) throws InterruptedException { |
| ConfiguredTargetValue value = |
| ((ConfiguredTargetValue) walkableGraphSupplier.get().getValue(key)); |
| return value == null ? null : value.getConfiguredTarget(); |
| } |
| |
| private TargetPattern getPattern(String pattern) |
| throws TargetParsingException, InterruptedException { |
| TargetPatternKey targetPatternKey = |
| ((TargetPatternKey) |
| TargetPatternValue.key( |
| pattern, TargetPatternEvaluator.DEFAULT_FILTERING_POLICY, parserPrefix) |
| .argument()); |
| return targetPatternKey.getParsedPattern(); |
| } |
| |
| /** |
| * Processes the targets in {@code targets} with the requested {@code configuration} |
| * |
| * @param pattern the original pattern that {@code targets} were parsed from. Used for error |
| * message. |
| * @param targets the set of {@link ConfiguredTarget}s whose labels represent the targets being |
| * requested. |
| * @param configuration the configuration to request {@code targets} in. |
| * @param callback the callback to receive the results of this method. |
| * @return {@link QueryTaskCallable} that returns the correctly configured targets. |
| */ |
| QueryTaskCallable<Void> getConfiguredTargets( |
| String pattern, |
| ThreadSafeMutableSet<ConfiguredTarget> targets, |
| String configuration, |
| Callback<ConfiguredTarget> callback) { |
| return new QueryTaskCallable<Void>() { |
| @Override |
| public Void call() throws QueryException, InterruptedException { |
| List<ConfiguredTarget> transformedResult = new ArrayList<>(); |
| for (ConfiguredTarget target : targets) { |
| Label label = getCorrectLabel(target); |
| ConfiguredTarget configuredTarget; |
| switch (configuration) { |
| case "\'host\'": |
| configuredTarget = getHostConfiguredTarget(label); |
| break; |
| case "\'target\'": |
| configuredTarget = getTargetConfiguredTarget(label); |
| break; |
| case "\'null\'": |
| configuredTarget = getNullConfiguredTarget(label); |
| break; |
| default: |
| throw new QueryException( |
| "the second argument of the config function must be 'target', 'host', or 'null'"); |
| } |
| if (configuredTarget != null) { |
| transformedResult.add(configuredTarget); |
| } |
| } |
| if (transformedResult.isEmpty()) { |
| throw new QueryException( |
| "No target (in) " |
| + pattern |
| + " could be found in the " |
| + configuration |
| + " configuration"); |
| } |
| callback.process(transformedResult); |
| return null; |
| } |
| }; |
| } |
| |
| @Nullable |
| private ConfiguredTarget getHostConfiguredTarget(Label label) throws InterruptedException { |
| return getConfiguredTarget(ConfiguredTargetValue.key(label, hostConfiguration)); |
| } |
| |
| @Nullable |
| private ConfiguredTarget getTargetConfiguredTarget(Label label) throws InterruptedException { |
| return getConfiguredTarget(ConfiguredTargetValue.key(label, defaultTargetConfiguration)); |
| } |
| |
| @Nullable |
| private ConfiguredTarget getNullConfiguredTarget(Label label) throws InterruptedException { |
| return getConfiguredTarget(ConfiguredTargetValue.key(label, null)); |
| } |
| |
| @Override |
| public ThreadSafeMutableSet<ConfiguredTarget> getFwdDeps(Iterable<ConfiguredTarget> targets) |
| throws InterruptedException { |
| Map<SkyKey, ConfiguredTarget> targetsByKey = new HashMap<>(Iterables.size(targets)); |
| for (ConfiguredTarget target : targets) { |
| targetsByKey.put(getSkyKey(target), target); |
| } |
| Map<SkyKey, Collection<ConfiguredTarget>> directDeps = |
| targetifyValues(graph.getDirectDeps(targetsByKey.keySet())); |
| if (targetsByKey.keySet().size() != directDeps.keySet().size()) { |
| Iterable<ConfiguredTargetKey> missingTargets = |
| Sets.difference(targetsByKey.keySet(), directDeps.keySet()) |
| .stream() |
| .map(SKYKEY_TO_CTKEY) |
| .collect(Collectors.toList()); |
| eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets)); |
| } |
| ThreadSafeMutableSet<ConfiguredTarget> result = createThreadSafeMutableSet(); |
| for (Map.Entry<SkyKey, Collection<ConfiguredTarget>> entry : directDeps.entrySet()) { |
| result.addAll(filterFwdDeps(targetsByKey.get(entry.getKey()), entry.getValue())); |
| } |
| return result; |
| } |
| |
| private Collection<ConfiguredTarget> filterFwdDeps( |
| ConfiguredTarget configTarget, Collection<ConfiguredTarget> rawFwdDeps) |
| throws InterruptedException { |
| if (settings.isEmpty()) { |
| return rawFwdDeps; |
| } |
| return getAllowedDeps(configTarget, rawFwdDeps); |
| } |
| |
| @Override |
| public Collection<ConfiguredTarget> getReverseDeps(Iterable<ConfiguredTarget> targets) |
| throws InterruptedException { |
| Map<SkyKey, ConfiguredTarget> targetsByKey = new HashMap<>(Iterables.size(targets)); |
| for (ConfiguredTarget target : targets) { |
| targetsByKey.put(getSkyKey(target), target); |
| } |
| Map<SkyKey, Collection<ConfiguredTarget>> reverseDepsByKey = |
| targetifyValues(graph.getReverseDeps(targetsByKey.keySet())); |
| if (targetsByKey.size() != reverseDepsByKey.size()) { |
| Iterable<ConfiguredTargetKey> missingTargets = |
| Sets.difference(targetsByKey.keySet(), reverseDepsByKey.keySet()) |
| .stream() |
| .map(SKYKEY_TO_CTKEY) |
| .collect(Collectors.toList()); |
| eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets)); |
| } |
| Map<ConfiguredTarget, Collection<ConfiguredTarget>> reverseDepsByCT = new HashMap<>(); |
| for (Map.Entry<SkyKey, Collection<ConfiguredTarget>> entry : reverseDepsByKey.entrySet()) { |
| reverseDepsByCT.put(targetsByKey.get(entry.getKey()), entry.getValue()); |
| } |
| return reverseDepsByCT.isEmpty() ? Collections.emptyList() : filterReverseDeps(reverseDepsByCT); |
| } |
| |
| private Collection<ConfiguredTarget> filterReverseDeps( |
| Map<ConfiguredTarget, Collection<ConfiguredTarget>> rawReverseDeps) { |
| Set<ConfiguredTarget> result = CompactHashSet.create(); |
| for (Map.Entry<ConfiguredTarget, Collection<ConfiguredTarget>> targetAndRdeps : |
| rawReverseDeps.entrySet()) { |
| ImmutableSet.Builder<ConfiguredTarget> ruleDeps = ImmutableSet.builder(); |
| for (ConfiguredTarget parent : targetAndRdeps.getValue()) { |
| if (parent instanceof RuleConfiguredTarget |
| && dependencyFilter != DependencyFilter.ALL_DEPS) { |
| ruleDeps.add(parent); |
| } else { |
| result.add(parent); |
| } |
| } |
| result.addAll(getAllowedDeps((targetAndRdeps.getKey()), ruleDeps.build())); |
| } |
| return result; |
| } |
| |
| /** |
| * @param target source target |
| * @param deps next level of deps to filter |
| */ |
| private Collection<ConfiguredTarget> getAllowedDeps( |
| ConfiguredTarget target, Collection<ConfiguredTarget> deps) { |
| // It's possible to query on a target that's configured in the host configuration. In those |
| // cases if --nohost_deps is turned on, we only allow reachable targets that are ALSO in the |
| // host config. This is somewhat counterintuitive and subject to change in the future but seems |
| // like the best option right now. |
| if (settings.contains(Setting.NO_HOST_DEPS)) { |
| BuildConfiguration currentConfig = getConfiguration(target); |
| if (currentConfig != null && currentConfig.isHostConfiguration()) { |
| deps = |
| deps.stream() |
| .filter( |
| dep -> |
| getConfiguration(dep) != null |
| && getConfiguration(dep).isHostConfiguration()) |
| .collect(Collectors.toList()); |
| } else { |
| deps = |
| deps.stream() |
| .filter( |
| dep -> |
| getConfiguration(dep) != null |
| && !getConfiguration(dep).isHostConfiguration()) |
| .collect(Collectors.toList()); |
| } |
| } |
| if (settings.contains(Setting.NO_IMPLICIT_DEPS) && target instanceof RuleConfiguredTarget) { |
| Set<ConfiguredTargetKey> implicitDeps = ((RuleConfiguredTarget) target).getImplicitDeps(); |
| deps = |
| deps.stream() |
| .filter( |
| dep -> |
| !implicitDeps.contains( |
| ConfiguredTargetKey.of(getCorrectLabel(dep), getConfiguration(dep)))) |
| .collect(Collectors.toList()); |
| } |
| return deps; |
| } |
| |
| private Map<SkyKey, Collection<ConfiguredTarget>> targetifyValues( |
| Map<SkyKey, ? extends Iterable<SkyKey>> input) throws InterruptedException { |
| Map<SkyKey, Collection<ConfiguredTarget>> result = new HashMap<>(); |
| for (Map.Entry<SkyKey, ? extends Iterable<SkyKey>> entry : input.entrySet()) { |
| Collection<ConfiguredTarget> value = new ArrayList<>(); |
| for (SkyKey key : entry.getValue()) { |
| if (key.functionName().equals(SkyFunctions.CONFIGURED_TARGET)) { |
| value.add(getConfiguredTarget(key)); |
| } |
| } |
| result.put(entry.getKey(), value); |
| } |
| return result; |
| } |
| |
| @Nullable |
| private BuildConfiguration getConfiguration(ConfiguredTarget target) { |
| try { |
| return target.getConfigurationKey() == null |
| ? null |
| : ((BuildConfigurationValue) graph.getValue(target.getConfigurationKey())) |
| .getConfiguration(); |
| } catch (InterruptedException e) { |
| throw new IllegalStateException("Unexpected interruption during configured target query"); |
| } |
| } |
| |
| private ConfiguredTargetKey getSkyKey(ConfiguredTarget target) { |
| return ConfiguredTargetKey.of(target, getConfiguration(target)); |
| } |
| |
| |
| @Override |
| public ThreadSafeMutableSet<ConfiguredTarget> getTransitiveClosure( |
| ThreadSafeMutableSet<ConfiguredTarget> targets) throws InterruptedException { |
| return SkyQueryUtils.getTransitiveClosure( |
| targets, this::getFwdDeps, createThreadSafeMutableSet()); |
| } |
| |
| @Override |
| public void buildTransitiveClosure( |
| QueryExpression caller, ThreadSafeMutableSet<ConfiguredTarget> targetNodes, int maxDepth) |
| throws QueryException, InterruptedException { |
| // TODO(bazel-team): implement this. Just needed for error-checking. |
| } |
| |
| @Override |
| public ImmutableList<ConfiguredTarget> getNodesOnPath(ConfiguredTarget from, ConfiguredTarget to) |
| throws InterruptedException { |
| return SkyQueryUtils.getNodesOnPath( |
| from, to, this::getFwdDeps, configuredTargetKeyExtractor::extractKey); |
| } |
| |
| @Override |
| public ThreadSafeMutableSet<ConfiguredTarget> createThreadSafeMutableSet() { |
| return new ThreadSafeMutableKeyExtractorBackedSetImpl<>( |
| configuredTargetKeyExtractor, |
| ConfiguredTarget.class, |
| SkyQueryEnvironment.DEFAULT_THREAD_COUNT); |
| } |
| |
| @Override |
| public <V> MutableMap<ConfiguredTarget, V> createMutableMap() { |
| return new MutableKeyExtractorBackedMapImpl<>(configuredTargetKeyExtractor); |
| } |
| |
| @Override |
| public Uniquifier<ConfiguredTarget> createUniquifier() { |
| return new UniquifierImpl<>( |
| configuredTargetKeyExtractor, SkyQueryEnvironment.DEFAULT_THREAD_COUNT); |
| } |
| |
| @Override |
| public MinDepthUniquifier<ConfiguredTarget> createMinDepthUniquifier() { |
| return new MinDepthUniquifierImpl<>( |
| configuredTargetKeyExtractor, SkyQueryEnvironment.DEFAULT_THREAD_COUNT); |
| } |
| |
| /** Target patterns are resolved on the fly so no pre-work to be done here. */ |
| @Override |
| protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns) {} |
| |
| public static QueryOptions parseOptions(String rawOptions) throws QueryException { |
| List<String> options = new ArrayList<>(Arrays.asList(rawOptions.split(" "))); |
| OptionsParser parser = OptionsParser.newOptionsParser(QueryOptions.class); |
| parser.setAllowResidue(false); |
| try { |
| parser.parse(options); |
| } catch (OptionsParsingException e) { |
| throw new QueryException(e.getMessage()); |
| } |
| return parser.getOptions(QueryOptions.class); |
| } |
| |
| @Override |
| public ThreadSafeMutableSet<ConfiguredTarget> getBuildFiles( |
| QueryExpression caller, |
| ThreadSafeMutableSet<ConfiguredTarget> nodes, |
| boolean buildFiles, |
| boolean loads) |
| throws QueryException, InterruptedException { |
| throw new QueryException("buildfiles() doesn't make sense for the configured target graph"); |
| } |
| |
| @Override |
| public Collection<ConfiguredTarget> getSiblingTargetsInPackage(ConfiguredTarget target) { |
| throw new UnsupportedOperationException("siblings() not supported"); |
| } |
| |
| |
| @Override |
| public void close() {} |
| } |
| |