blob: 43748b45ad5fe0fa7877960b0788bf2eddbab0fe [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.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.FileValue;
import com.google.devtools.build.lib.actions.InconsistentFilesystemException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.syntax.BuildFileAST;
import com.google.devtools.build.lib.syntax.Mutability;
import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.syntax.StarlarkSemantics;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.io.IOException;
import javax.annotation.Nullable;
/**
* A SkyFunction for {@link ASTFileLookupValue}s.
*
* <p> Given a {@link Label} referencing a Skylark file, loads it as a syntax tree
* ({@link BuildFileAST}). The Label must be absolute, and must not reference the special
* {@code external} package. If the file (or the package containing it) doesn't exist, the
* function doesn't fail, but instead returns a specific {@code NO_FILE} {@link ASTFileLookupValue}.
*/
public class ASTFileLookupFunction implements SkyFunction {
private final RuleClassProvider ruleClassProvider;
public ASTFileLookupFunction(RuleClassProvider ruleClassProvider) {
this.ruleClassProvider = ruleClassProvider;
}
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException,
InterruptedException {
Label fileLabel = (Label) skyKey.argument();
PathFragment filePathFragment = fileLabel.toPathFragment();
// Determine whether the package designated by fileLabel exists.
// TODO(bazel-team): After --incompatible_disallow_load_labels_to_cross_package_boundaries is
// removed and the new behavior is unconditional, we can instead safely assume the package
// exists and pass in the Root in the SkyKey and therefore this dep can be removed.
SkyKey pkgSkyKey = PackageLookupValue.key(fileLabel.getPackageIdentifier());
PackageLookupValue pkgLookupValue = null;
try {
pkgLookupValue = (PackageLookupValue) env.getValueOrThrow(
pkgSkyKey, BuildFileNotFoundException.class, InconsistentFilesystemException.class);
} catch (BuildFileNotFoundException e) {
throw new ASTLookupFunctionException(
new ErrorReadingSkylarkExtensionException(e), Transience.PERSISTENT);
} catch (InconsistentFilesystemException e) {
throw new ASTLookupFunctionException(e, Transience.PERSISTENT);
}
if (pkgLookupValue == null) {
return null;
}
if (!pkgLookupValue.packageExists()) {
return ASTFileLookupValue.forBadPackage(fileLabel, pkgLookupValue.getErrorMsg());
}
// Determine whether the file designated by fileLabel exists.
Root packageRoot = pkgLookupValue.getRoot();
RootedPath rootedPath = RootedPath.toRootedPath(packageRoot, filePathFragment);
SkyKey fileSkyKey = FileValue.key(rootedPath);
FileValue fileValue = null;
try {
fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class);
} catch (InconsistentFilesystemException e) {
throw new ASTLookupFunctionException(e, Transience.PERSISTENT);
} catch (IOException e) {
throw new ASTLookupFunctionException(
new ErrorReadingSkylarkExtensionException(e), Transience.PERSISTENT);
}
if (fileValue == null) {
return null;
}
if (!fileValue.exists()) {
return ASTFileLookupValue.forMissingFile(fileLabel);
}
if (!fileValue.isFile()) {
return ASTFileLookupValue.forBadFile(fileLabel);
}
StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
if (starlarkSemantics == null) {
return null;
}
// Both the package and the file exist; load the file and parse it as an AST.
BuildFileAST ast = null;
Path path = rootedPath.asPath();
try {
long astFileSize = fileValue.getSize();
try (Mutability mutability = Mutability.create("validate")) {
com.google.devtools.build.lib.syntax.Environment validationEnv =
ruleClassProvider
.createSkylarkRuleClassEnvironment(
fileLabel,
mutability,
starlarkSemantics,
env.getListener(),
// the three below don't matter for extracting the ValidationEnvironment:
/*astFileContentHashCode=*/ null,
/*importMap=*/ null,
/*repoMapping=*/ ImmutableMap.of())
.setupDynamic(Runtime.PKG_NAME, Runtime.NONE)
.setupDynamic(Runtime.REPOSITORY_NAME, Runtime.NONE);
byte[] bytes = FileSystemUtils.readWithKnownFileSize(path, astFileSize);
ast =
BuildFileAST.parseSkylarkFile(
bytes, path.getDigest(), path.asFragment(), env.getListener());
ast = ast.validate(validationEnv, env.getListener());
}
} catch (IOException e) {
throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException(e),
Transience.TRANSIENT);
}
return ASTFileLookupValue.withFile(ast);
}
@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}
private static final class ASTLookupFunctionException extends SkyFunctionException {
private ASTLookupFunctionException(ErrorReadingSkylarkExtensionException e,
Transience transience) {
super(e, transience);
}
private ASTLookupFunctionException(InconsistentFilesystemException e, Transience transience) {
super(e, transience);
}
}
}