com.google.devtools.build.lib.query2.ConfiguredTargetQueryEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.query2.ConfiguredTargetQueryEnvironment.java

Source

// 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.LabelAndConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
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.cmdline.TargetPattern.Type;
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.packages.Rule;
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.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.RecursivePackageProviderBackedTargetPatternResolver;
import com.google.devtools.build.lib.skyframe.SkyFunctions;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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>Currently no edges are filtered out, in contrast to query as implemented on the target graph
 * (host_deps and implicit_deps are important ones). Because of the higher fidelity that users of
 * the configured target graph presumably want, this may be ok, but also may not be.
 *
 * <p>This object can theoretically be used for multiple queries, but currently is only ever used
 * for one over the course of its lifetime.
 *
 * <p>There is currently no way to specify a configuration in the query syntax. Instead, the default
 * configuration that will be used for any raw labels is provided in the constructor of this
 * environment. That will probably have to change.
 *
 * <p>On the other end, recursive target patterns are not supported.
 *
 * <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 final ConfiguredTargetAccessor accessor = new ConfiguredTargetAccessor();
    protected WalkableGraph graph;

    private static final Function<ConfiguredTarget, SkyKey> CT_TO_SKYKEY = target -> ConfiguredTargetValue
            .key(target.getLabel(), target.getConfiguration());
    private static final Function<SkyKey, LabelAndConfiguration> SKYKEY_TO_LANDC = skyKey -> {
        ConfiguredTargetKey key = (ConfiguredTargetKey) skyKey.argument();
        return LabelAndConfiguration.of(key.getLabel(), key.getConfiguration());
    };
    private static final ImmutableList<TargetPatternKey> ALL_PATTERNS;
    private static final KeyExtractor<ConfiguredTarget, LabelAndConfiguration> CONFIGURED_TARGET_KEY_EXTRACTOR = LabelAndConfiguration::of;

    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) {
        super(keepGoing, true, Rule.ALL_LABELS, eventHandler,
                // TODO(janakr): decide whether to support host and implicit dep filtering.
                EnumSet.noneOf(Setting.class), extraFunctions);
        this.defaultTargetConfiguration = defaultTargetConfiguration;
        this.hostConfiguration = hostConfiguration;
        this.parserPrefix = parserPrefix;
        this.pkgPath = pkgPath;
        this.walkableGraphSupplier = walkableGraphSupplier;
    }

    private void beforeEvaluateQuery() throws InterruptedException {
        graph = walkableGraphSupplier.get();
        GraphBackedRecursivePackageProvider graphBackedRecursivePackageProvider = new GraphBackedRecursivePackageProvider(
                graph, ALL_PATTERNS, pkgPath);
        resolver = new RecursivePackageProviderBackedTargetPatternResolver(graphBackedRecursivePackageProvider,
                eventHandler, FilteringPolicies.NO_FILTER, MultisetSemaphore.unbounded());
    }

    @Nullable
    private ConfiguredTarget getConfiguredTarget(SkyKey key) throws InterruptedException {
        ConfiguredTargetValue value = ((ConfiguredTargetValue) walkableGraphSupplier.get().getValue(key));
        return value == null ? null : value.getConfiguredTarget();
    }

    @Override
    public void close() {
    }

    @Override
    public QueryEvalResult evaluateQuery(QueryExpression expr,
            ThreadSafeOutputFormatterCallback<ConfiguredTarget> callback)
            throws QueryException, InterruptedException, IOException {
        beforeEvaluateQuery();
        return super.evaluateQuery(expr, callback);
    }

    private TargetPattern getPattern(String pattern) throws TargetParsingException, InterruptedException {
        TargetPatternKey targetPatternKey = ((TargetPatternKey) TargetPatternValue
                .key(pattern, TargetPatternEvaluator.DEFAULT_FILTERING_POLICY, parserPrefix).argument());
        return targetPatternKey.getParsedPattern();
    }

    @Override
    public Collection<ConfiguredTarget> getSiblingTargetsInPackage(ConfiguredTarget target) {
        throw new UnsupportedOperationException("siblings() not supported");
    }

    @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()));
    }

    @Override
    public ConfiguredTarget getOrCreate(ConfiguredTarget target) {
        return target;
    }

    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;
    }

    @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(CT_TO_SKYKEY.apply(target), target);
        }
        Map<SkyKey, Collection<ConfiguredTarget>> directDeps = targetifyValues(
                graph.getDirectDeps(targetsByKey.keySet()));
        if (targetsByKey.keySet().size() != directDeps.keySet().size()) {
            Iterable<LabelAndConfiguration> missingTargets = Sets
                    .difference(targetsByKey.keySet(), directDeps.keySet()).stream().map(SKYKEY_TO_LANDC)
                    .collect(Collectors.toList());
            eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets));
        }
        ThreadSafeMutableSet<ConfiguredTarget> result = createThreadSafeMutableSet();
        for (Entry<SkyKey, Collection<ConfiguredTarget>> entry : directDeps.entrySet()) {
            result.addAll(entry.getValue());
        }
        return result;
    }

    @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(CT_TO_SKYKEY.apply(target), target);
        }
        Map<SkyKey, Collection<ConfiguredTarget>> reverseDeps = targetifyValues(
                graph.getReverseDeps(targetsByKey.keySet()));
        if (targetsByKey.keySet().size() != reverseDeps.keySet().size()) {
            Iterable<LabelAndConfiguration> missingTargets = Sets
                    .difference(targetsByKey.keySet(), reverseDeps.keySet()).stream().map(SKYKEY_TO_LANDC)
                    .collect(Collectors.toList());
            eventHandler.handle(Event.warn("Targets were missing from graph: " + missingTargets));
        }
        ThreadSafeMutableSet<ConfiguredTarget> result = createThreadSafeMutableSet();
        for (Entry<SkyKey, Collection<ConfiguredTarget>> entry : reverseDeps.entrySet()) {
            result.addAll(entry.getValue());
        }
        return result;
    }

    @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, LabelAndConfiguration::of);
    }

    @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 {
        ConfiguredTarget configuredTarget = getConfiguredTarget(label);
        return configuredTarget == null ? null : configuredTarget.getTarget();
    }

    private ConfiguredTarget getConfiguredTarget(Label label) throws InterruptedException {
        // Try with host configuration.
        ConfiguredTarget configuredTarget = getConfiguredTarget(
                ConfiguredTargetValue.key(label, hostConfiguration));
        if (configuredTarget != null) {
            return configuredTarget;
        }
        configuredTarget = getConfiguredTarget(ConfiguredTargetValue.key(label, defaultTargetConfiguration));
        if (configuredTarget != null) {
            return configuredTarget;
        }
        // Last chance: source file.
        return getConfiguredTarget(ConfiguredTargetValue.key(label, null));
    }

    @Override
    public ThreadSafeMutableSet<ConfiguredTarget> createThreadSafeMutableSet() {
        return new ThreadSafeMutableKeyExtractorBackedSetImpl<>(CONFIGURED_TARGET_KEY_EXTRACTOR,
                ConfiguredTarget.class, SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
    }

    @Override
    public <V> MutableMap<ConfiguredTarget, V> createMutableMap() {
        return new MutableKeyExtractorBackedMapImpl<>(CONFIGURED_TARGET_KEY_EXTRACTOR);
    }

    @Override
    public Uniquifier<ConfiguredTarget> createUniquifier() {
        return new UniquifierImpl<>(CONFIGURED_TARGET_KEY_EXTRACTOR, SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
    }

    @Override
    public MinDepthUniquifier<ConfiguredTarget> createMinDepthUniquifier() {
        return new MinDepthUniquifierImpl<>(CONFIGURED_TARGET_KEY_EXTRACTOR,
                SkyQueryEnvironment.DEFAULT_THREAD_COUNT);
    }

    @Override
    public ThreadSafeMutableSet<ConfiguredTarget> getBuildFiles(QueryExpression caller,
            ThreadSafeMutableSet<ConfiguredTarget> nodes, boolean buildFiles, boolean subincludes, boolean loads)
            throws QueryException, InterruptedException {
        throw new QueryException("buildfiles() doesn't make sense for the configured target graph");
    }

    @Override
    protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns)
            throws QueryException, TargetParsingException, InterruptedException {
        for (String pattern : patterns) {
            if (TargetPattern.defaultParser().parse(pattern).getType().equals(Type.TARGETS_BELOW_DIRECTORY)) {
                // TODO(bazel-team): allow recursive patterns if the pattern is present in the graph? We
                // could do a mini-eval here to update the graph to contain the necessary nodes for
                // GraphBackedRecursivePackageProvider, since all the package loading and directory
                // traversal should already be done.
                throw new QueryException(
                        "Recursive pattern '" + pattern + "' is not supported in configured target query");
            }
        }
    }
}