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

Java tutorial

Introduction

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

Source

// Copyright 2016 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 static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.devtools.build.lib.query2.SkyQueryEnvironment.IS_TTV;
import static com.google.devtools.build.lib.query2.SkyQueryEnvironment.SKYKEY_TO_LABEL;

import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.concurrent.MultisetSemaphore;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.query2.engine.Callback;
import com.google.devtools.build.lib.query2.engine.QueryException;
import com.google.devtools.build.lib.query2.engine.Uniquifier;
import com.google.devtools.build.skyframe.SkyKey;
import java.util.Map;
import java.util.Set;

/**
 * A helper class that computes unbounded 'deps(<expr>)' via BFS. Re-use logic from {@link
 * AbstractEdgeVisitor} to grab relevant semaphore locks when processing nodes as well as batching
 * visits by packages.
 */
class DepsUnboundedVisitor extends AbstractEdgeVisitor<SkyKey> {
    /**
     * A {@link Uniquifier} for valid deps. Only used prior to visiting the deps. Deps filtering is
     * done in the {@link DepsUnboundedVisitor#getVisitResult} stage.
     */
    private final Uniquifier<SkyKey> validDepUniquifier;

    private final boolean depsNeedFiltering;
    private final Callback<Target> errorReporter;

    DepsUnboundedVisitor(SkyQueryEnvironment env, Uniquifier<SkyKey> validDepUniquifier, Callback<Target> callback,
            MultisetSemaphore<PackageIdentifier> packageSemaphore, boolean depsNeedFiltering,
            Callback<Target> errorReporter) {
        super(env, callback, packageSemaphore);
        this.validDepUniquifier = validDepUniquifier;
        this.depsNeedFiltering = depsNeedFiltering;
        this.errorReporter = errorReporter;
    }

    /**
     * A {@link Factory} for {@link DepsUnboundedVisitor} instances, each of which will be used to
     * perform visitation of the DTC of the {@link SkyKey}s passed in a single {@link
     * Callback#process} call. Note that all the created instances share the same {@link Uniquifier}
     * so that we don't visit the same Skyframe node more than once.
     */
    static class Factory implements ParallelVisitor.Factory {
        private final SkyQueryEnvironment env;
        private final Uniquifier<SkyKey> validDepUniquifier;
        private final Callback<Target> callback;
        private final MultisetSemaphore<PackageIdentifier> packageSemaphore;
        private final boolean depsNeedFiltering;
        private final Callback<Target> errorReporter;

        Factory(SkyQueryEnvironment env, Callback<Target> callback,
                MultisetSemaphore<PackageIdentifier> packageSemaphore, boolean depsNeedFiltering,
                Callback<Target> errorReporter) {
            this.env = env;
            this.validDepUniquifier = env.createSkyKeyUniquifier();
            this.callback = callback;
            this.packageSemaphore = packageSemaphore;
            this.depsNeedFiltering = depsNeedFiltering;
            this.errorReporter = errorReporter;
        }

        @Override
        public ParallelVisitor<SkyKey, Target> create() {
            return new DepsUnboundedVisitor(env, validDepUniquifier, callback, packageSemaphore, depsNeedFiltering,
                    errorReporter);
        }
    }

    @Override
    protected Visit getVisitResult(Iterable<SkyKey> keys) throws InterruptedException {
        if (depsNeedFiltering) {
            // We have to targetify the keys here in order to determine the allowed dependencies.
            Multimap<SkyKey, SkyKey> packageKeyToTargetKeyMap = env.makePackageKeyToTargetKeyMap(keys);
            Set<PackageIdentifier> pkgIdsNeededForTargetification = packageKeyToTargetKeyMap.keySet().stream()
                    .map(SkyQueryEnvironment.PACKAGE_SKYKEY_TO_PACKAGE_IDENTIFIER).collect(toImmutableSet());
            packageSemaphore.acquireAll(pkgIdsNeededForTargetification);
            Iterable<Target> deps;
            try {
                deps = env.getFwdDeps(env.makeTargetsFromSkyKeys(keys).values());
            } finally {
                packageSemaphore.releaseAll(pkgIdsNeededForTargetification);
            }

            return new Visit(/*keysToUseForResult=*/ keys,
                    /*keysToVisit=*/ Iterables.transform(deps, Target::getLabel));
        }

        // We need to explicitly check that all requested TTVs are actually in the graph.
        Map<SkyKey, Iterable<SkyKey>> depMap = env.graph.getDirectDeps(keys);
        checkIfMissingTargets(keys, depMap);
        Iterable<SkyKey> deps = Iterables.filter(Iterables.concat(depMap.values()), IS_TTV);
        return new Visit(keys, deps);
    }

    private void checkIfMissingTargets(Iterable<SkyKey> keys, Map<SkyKey, Iterable<SkyKey>> depMap) {
        if (depMap.size() != Iterables.size(keys)) {
            Iterable<Label> missingTargets = Iterables.transform(
                    Iterables.filter(keys, Predicates.not(Predicates.in(depMap.keySet()))), SKYKEY_TO_LABEL);
            env.getEventHandler().handle(Event.warn("Targets were missing from graph: " + missingTargets));
        }
    }

    @Override
    protected Iterable<SkyKey> preprocessInitialVisit(Iterable<SkyKey> keys) {
        return keys;
    }

    @Override
    protected void processPartialResults(Iterable<SkyKey> keysToUseForResult, Callback<Target> callback)
            throws QueryException, InterruptedException {
        errorReporter.process(processResultsAndReturnTargets(keysToUseForResult, callback));
    }

    @Override
    protected SkyKey getNewNodeFromEdge(SkyKey visit) {
        return visit;
    }

    @Override
    protected ImmutableList<SkyKey> getUniqueValues(Iterable<SkyKey> keysToVisit) {
        // Legit deps are already filtered using env.getFwdDeps().
        return validDepUniquifier.unique(keysToVisit);
    }
}