blob: 2d31b58d652711eb0a50cce0f6ebc8012ceda2cc [file] [log] [blame]
// Copyright 2022 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.rules.genquery;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.bugreport.BugReport;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.skyframe.PackageValue;
import com.google.devtools.build.lib.skyframe.TransitiveTargetKey;
import com.google.devtools.build.lib.skyframe.TransitiveTargetValue;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyframeLookupResult;
import java.util.Collection;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Factory for {@link GenQueryPackageProvider} using {@link TransitiveTargetValue}-based Skyframe
* work to collect required information.
*/
public class GenQueryTtvPackageProviderFactory implements GenQueryPackageProviderFactory {
/**
* Precomputes the transitive closure of the scope. Returns two maps: one identifying the
* successful packages, and the other identifying the valid targets. Breaks in the transitive
* closure of the scope will cause the query to error out early.
*/
@Override
@Nullable
public GenQueryPackageProvider constructPackageMap(Environment env, ImmutableList<Label> scope)
throws InterruptedException, BrokenQueryScopeException {
// It is not necessary for correctness to construct intermediate NestedSets; we could iterate
// over individual targets in scope immediately. However, creating a composite NestedSet first
// saves us from iterating over the same sub-NestedSets multiple times.
NestedSetBuilder<Label> validTargets = NestedSetBuilder.stableOrder();
Set<SkyKey> successfulPackageKeys = Sets.newHashSetWithExpectedSize(scope.size());
Collection<SkyKey> transitiveTargetKeys =
Collections2.transform(scope, TransitiveTargetKey::of);
SkyframeLookupResult transitiveTargetValues = env.getValuesAndExceptions(transitiveTargetKeys);
if (env.valuesMissing()) {
return null;
}
for (SkyKey skyKey : transitiveTargetKeys) {
TransitiveTargetValue ttv;
try {
ttv =
(TransitiveTargetValue)
transitiveTargetValues.getOrThrow(
skyKey, NoSuchPackageException.class, NoSuchTargetException.class);
} catch (NoSuchPackageException | NoSuchTargetException e) {
throw BrokenQueryScopeException.of(e);
}
if (ttv == null) {
BugReport.sendNonFatalBugReport(
new IllegalStateException(
"TransitiveTargetValue " + skyKey + " was missing, this should never happen"));
continue;
}
if (ttv.encounteredLoadingError()) {
throw BrokenQueryScopeException.of(ttv.getErrorLoadingTarget());
}
validTargets.addTransitive(ttv.getTransitiveTargets());
for (Label transitiveLabel : ttv.getTransitiveTargets().toList()) {
successfulPackageKeys.add(transitiveLabel.getPackageIdentifier());
}
}
// Construct the package id to package map for all successful packages.
SkyframeLookupResult transitivePackages = env.getValuesAndExceptions(successfulPackageKeys);
if (env.valuesMissing()) {
// Packages from an untaken select branch could be missing: analysis avoids these, but query
// does not.
return null;
}
ImmutableMap.Builder<PackageIdentifier, Package> packageMapBuilder = ImmutableMap.builder();
for (SkyKey skyKey : successfulPackageKeys) {
PackageValue pkg = (PackageValue) transitivePackages.get(skyKey);
if (pkg == null) {
BugReport.sendNonFatalBugReport(
new IllegalStateException(
"SkyValue " + skyKey + " was missing, this should never happen"));
return null;
}
Preconditions.checkState(
!pkg.getPackage().containsErrors(),
"package %s was found to both have and not have errors.",
skyKey);
packageMapBuilder.put(pkg.getPackage().getPackageIdentifier(), pkg.getPackage());
}
ImmutableMap<PackageIdentifier, Package> packageMap = packageMapBuilder.buildOrThrow();
ImmutableMap.Builder<Label, Target> validTargetsMapBuilder = ImmutableMap.builder();
for (Label label : validTargets.build().toList()) {
try {
Target target = packageMap.get(label.getPackageIdentifier()).getTarget(label.getName());
validTargetsMapBuilder.put(label, target);
} catch (NoSuchTargetException e) {
throw new IllegalStateException(e);
}
}
return new GenQueryPackageProvider(packageMap, validTargetsMapBuilder.buildOrThrow());
}
}