blob: 234eb6c9ecd07cf34474390ab39a0f5fdce682f5 [file] [log] [blame]
// Copyright 2014 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.skyframe;
import com.google.devtools.build.lib.actions.InconsistentFilesystemException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
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.vfs.PathFragment;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import javax.annotation.Nullable;
/**
* A SkyFunction for {@link TargetMarkerValue}s. Returns a {@link
* TargetMarkerValue#TARGET_MARKER_INSTANCE} if the {@link Label} in the {@link SkyKey}
* specifies a {@link Package} that exists and a {@link Target} that exists in that package. The
* package may have errors.
*/
public final class TargetMarkerFunction implements SkyFunction {
@Override
public SkyValue compute(SkyKey key, Environment env)
throws TargetMarkerFunctionException, InterruptedException {
try {
return computeTargetMarkerValue(key, env);
} catch (NoSuchTargetException e) {
throw new TargetMarkerFunctionException(e);
} catch (NoSuchPackageException e) {
// Re-throw this exception with our key because root causes should be targets, not packages.
throw new TargetMarkerFunctionException(e);
}
}
@Nullable
static TargetMarkerValue computeTargetMarkerValue(SkyKey key, Environment env)
throws NoSuchTargetException, NoSuchPackageException, InterruptedException {
Label label = (Label) key.argument();
PathFragment pkgForLabel = label.getPackageFragment();
if (label.getName().contains("/")) {
// This target is in a subdirectory, therefore it could potentially be invalidated by
// a new BUILD file appearing in the hierarchy.
PathFragment containingDirectory = getContainingDirectory(label);
ContainingPackageLookupValue containingPackageLookupValue;
try {
PackageIdentifier newPkgId = PackageIdentifier.create(
label.getPackageIdentifier().getRepository(), containingDirectory);
containingPackageLookupValue = (ContainingPackageLookupValue) env.getValueOrThrow(
ContainingPackageLookupValue.key(newPkgId),
BuildFileNotFoundException.class, InconsistentFilesystemException.class);
} catch (InconsistentFilesystemException e) {
throw new NoSuchTargetException(label, e.getMessage());
}
if (containingPackageLookupValue == null) {
return null;
}
if (!containingPackageLookupValue.hasContainingPackage()) {
// This means the label's package doesn't exist. E.g. there is no package 'a' and we are
// trying to build the target for label 'a:b/foo'.
throw new BuildFileNotFoundException(
label.getPackageIdentifier(),
"BUILD file not found on package path for '" + pkgForLabel.getPathString() + "'");
}
if (!containingPackageLookupValue.getContainingPackageName().equals(
label.getPackageIdentifier())) {
throw new NoSuchTargetException(
label,
String.format(
"Label '%s' crosses boundary of subpackage '%s'",
label,
containingPackageLookupValue.getContainingPackageName()));
}
}
SkyKey pkgSkyKey = PackageValue.key(label.getPackageIdentifier());
PackageValue value =
(PackageValue) env.getValueOrThrow(pkgSkyKey, NoSuchPackageException.class);
if (value == null) {
return null;
}
Package pkg = value.getPackage();
Target target = pkg.getTarget(label.getName());
if (pkg.containsErrors()) {
// There is a target, but its package is in error. We rethrow so that the root cause is the
// target, not the package. Note that targets are only in error when their package is
// "in error" (because a package is in error if there was an error evaluating the package, or
// if one of its targets was in error).
throw new NoSuchTargetException(target);
}
return TargetMarkerValue.TARGET_MARKER_INSTANCE;
}
private static PathFragment getContainingDirectory(Label label) {
PathFragment pkg = label.getPackageFragment();
String name = label.getName();
return name.equals(".") ? pkg : pkg.getRelative(name).getParentDirectory();
}
@Override
public String extractTag(SkyKey skyKey) {
return Label.print((Label) skyKey.argument());
}
/**
* Used to declare all the exception types that can be wrapped in the exception thrown by
* {@link TargetMarkerFunction#compute}.
*/
private static final class TargetMarkerFunctionException extends SkyFunctionException {
public TargetMarkerFunctionException(NoSuchTargetException e) {
super(e, Transience.PERSISTENT);
}
public TargetMarkerFunctionException(NoSuchPackageException e) {
super(e, Transience.PERSISTENT);
}
}
}