blob: 7578aa875ce005f729b11c13e910f5c4bb87cc15 [file] [log] [blame]
// 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.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.QueryExpressionContext;
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 QueryExpressionContext<Target> context;
DepsUnboundedVisitor(
SkyQueryEnvironment env,
Uniquifier<SkyKey> validDepUniquifier,
Callback<Target> callback,
MultisetSemaphore<PackageIdentifier> packageSemaphore,
boolean depsNeedFiltering,
QueryExpressionContext<Target> context) {
super(env, callback, packageSemaphore);
this.validDepUniquifier = validDepUniquifier;
this.depsNeedFiltering = depsNeedFiltering;
this.context = context;
}
/**
* 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 QueryExpressionContext<Target> context;
Factory(
SkyQueryEnvironment env,
Callback<Target> callback,
MultisetSemaphore<PackageIdentifier> packageSemaphore,
boolean depsNeedFiltering,
QueryExpressionContext<Target> context) {
this.env = env;
this.validDepUniquifier = env.createSkyKeyUniquifier();
this.callback = callback;
this.packageSemaphore = packageSemaphore;
this.depsNeedFiltering = depsNeedFiltering;
this.context = context;
}
@Override
public ParallelVisitor<SkyKey, Target> create() {
return new DepsUnboundedVisitor(
env,
validDepUniquifier,
callback,
packageSemaphore,
depsNeedFiltering,
context);
}
}
@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 =
SkyQueryEnvironment.makePackageKeyToTargetKeyMap(keys);
Set<PackageIdentifier> pkgIdsNeededForTargetification =
SkyQueryEnvironment.getPkgIdsNeededForTargetification(packageKeyToTargetKeyMap);
packageSemaphore.acquireAll(pkgIdsNeededForTargetification);
Iterable<SkyKey> depsAsSkyKeys;
try {
Iterable<Target> depsAsTargets =
env.getFwdDeps(
env.getTargetKeyToTargetMapForPackageKeyToTargetKeyMap(
packageKeyToTargetKeyMap).values(),
context);
depsAsSkyKeys = Iterables.transform(depsAsTargets, Target::getLabel);
} finally {
packageSemaphore.releaseAll(pkgIdsNeededForTargetification);
}
return new Visit(
/*keysToUseForResult=*/ keys,
/*keysToVisit=*/ depsAsSkyKeys);
}
// 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 SkyKey getNewNodeFromEdge(SkyKey visit) {
return visit;
}
@Override
protected ImmutableList<SkyKey> getUniqueValues(Iterable<SkyKey> keysToVisit)
throws QueryException {
// Legit deps are already filtered using env.getFwdDeps().
return validDepUniquifier.unique(keysToVisit);
}
}