bazel syntax: delete StarlarkFile.contentHashCode
It has moved to skyframe.ASTFileLookupValue.
Also, improve error messages related to ASTFileLookupValue.
PiperOrigin-RevId: 310543093
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
index 6a37406..5b45bda 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
@@ -95,7 +95,8 @@
return null;
}
if (!pkgLookupValue.packageExists()) {
- return ASTFileLookupValue.forBadPackage(fileLabel, pkgLookupValue.getErrorMsg());
+ return ASTFileLookupValue.noFile(
+ "cannot load '%s': %s", fileLabel, pkgLookupValue.getErrorMsg());
}
// Determine whether the file designated by fileLabel exists.
@@ -112,10 +113,13 @@
return null;
}
if (!fileValue.exists()) {
- return ASTFileLookupValue.forMissingFile(fileLabel);
+ return ASTFileLookupValue.noFile("cannot load '%s': no such file", fileLabel);
}
if (!fileValue.isFile()) {
- return ASTFileLookupValue.forBadFile(fileLabel);
+ return fileValue.isDirectory()
+ ? ASTFileLookupValue.noFile("cannot load '%s': is a directory", fileLabel)
+ : ASTFileLookupValue.noFile(
+ "cannot load '%s': not a regular file (dangling link?)", fileLabel);
}
StarlarkSemantics semantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
if (semantics == null) {
@@ -130,16 +134,16 @@
// Both the package and the file exist; load and parse the file.
Path path = rootedPath.asPath();
- StarlarkFile file = null;
+ StarlarkFile file;
+ byte[] digest;
try {
byte[] bytes =
fileValue.isSpecialFile()
? FileSystemUtils.readContent(path)
: FileSystemUtils.readWithKnownFileSize(path, fileValue.getSize());
- byte[] digest =
- getDigestFromFileValueOrFromKnownFileContents(fileValue, bytes, digestHashFunction);
+ digest = getDigestFromFileValueOrFromKnownFileContents(fileValue, bytes, digestHashFunction);
ParserInput input = ParserInput.create(bytes, path.toString());
- file = StarlarkFile.parseWithDigest(input, digest, options);
+ file = StarlarkFile.parse(input, options);
} catch (IOException e) {
throw new ErrorReadingSkylarkExtensionException(e, Transience.TRANSIENT);
}
@@ -148,7 +152,7 @@
Resolver.resolveFile(file, Module.createForBuiltins(ruleClassProvider.getEnvironment()));
Event.replayEventsOn(env.getListener(), file.errors()); // TODO(adonovan): fail if !ok()?
- return ASTFileLookupValue.withFile(file);
+ return ASTFileLookupValue.withFile(file, digest);
}
private static byte[] getDigestFromFileValueOrFromKnownFileContents(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java
index c35ada0..7b67b80 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java
@@ -23,37 +23,48 @@
import com.google.devtools.build.skyframe.AbstractSkyKey;
import com.google.devtools.build.skyframe.NotComparableSkyValue;
import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.errorprone.annotations.FormatMethod;
/**
* A value that represents an AST file lookup result. There are two subclasses: one for the case
* where the file is found, and another for the case where the file is missing (but there are no
* other errors).
*/
-// In practice, if a ASTFileLookupValue is re-computed (i.e. not changed pruned), then it will
-// almost certainly be unequal to the previous value. This is because of (i) the change-pruning
-// semantics of the PackageLookupValue dep and the FileValue dep; consider the latter: if the
-// FileValue for the bzl file has changed, then the contents of the bzl file probably changed and
-// (ii) we don't currently have Starlark-semantic-equality in StarlarkFile, so two StarlarkFile
-// instances representing two different contents of a bzl file will be different.
-// TODO(bazel-team): Consider doing better here. As a pre-req, we would need
-// Starlark-semantic-equality in StarlarkFile, rather than equality naively based on the contents of
-// the bzl file. For a concrete example, the contents of comment lines do not currently impact
-// Starlark semantics.
+// In practice, almost any change to a .bzl causes the ASTFileLookupValue to be recomputed.
+// We could do better with a finer-grained notion of equality for StarlarkFile than "the source
+// files differ". In particular, a trivial change such as fixing a typo in a comment should not
+// cause invalidation. (Changes that are only slightly more substantial may be semantically
+// significant. For example, inserting a blank line affects subsequent line numbers, which appear
+// in error messages and query output.)
+//
+// Comparing syntax trees for equality is complex and expensive, so the most practical
+// implementation of this optimization will have to wait until Starlark files are compiled,
+// at which point byte-equality of the compiled representation (which is simple to compute)
+// will serve. (At that point, ASTFileLookup should be renamed CompileStarlark.)
+//
public abstract class ASTFileLookupValue implements NotComparableSkyValue {
+
+ // TODO(adonovan): flatten this hierarchy into a single class.
+ // It would only cost one word per Starlark file.
+ // Eliminate lookupSuccessful; use getAST() != null.
+
public abstract boolean lookupSuccessful();
public abstract StarlarkFile getAST();
- public abstract String getErrorMsg();
+ public abstract byte[] getDigest();
+
+ public abstract String getError();
/** If the file is found, this class encapsulates the parsed AST. */
@AutoCodec.VisibleForSerialization
public static class ASTLookupWithFile extends ASTFileLookupValue {
private final StarlarkFile ast;
+ private final byte[] digest;
- private ASTLookupWithFile(StarlarkFile ast) {
- Preconditions.checkNotNull(ast);
- this.ast = ast;
+ private ASTLookupWithFile(StarlarkFile ast, byte[] digest) {
+ this.ast = Preconditions.checkNotNull(ast);
+ this.digest = Preconditions.checkNotNull(digest);
}
@Override
@@ -67,7 +78,12 @@
}
@Override
- public String getErrorMsg() {
+ public byte[] getDigest() {
+ return this.digest;
+ }
+
+ @Override
+ public String getError() {
throw new IllegalStateException(
"attempted to retrieve unsuccessful lookup reason for successful lookup");
}
@@ -93,32 +109,29 @@
}
@Override
- public String getErrorMsg() {
+ public byte[] getDigest() {
+ throw new IllegalStateException("attempted to retrieve digest for successful lookup");
+ }
+
+ @Override
+ public String getError() {
return this.errorMsg;
}
}
- static ASTFileLookupValue forBadPackage(Label fileLabel, String reason) {
- return new ASTLookupNoFile(
- String.format("Unable to load package for '%s': %s", fileLabel, reason));
+ /** Constructs a value from a failure before parsing a file. */
+ @FormatMethod
+ static ASTFileLookupValue noFile(String format, Object... args) {
+ return new ASTLookupNoFile(String.format(format, args));
}
- static ASTFileLookupValue forMissingFile(Label fileLabel) {
- return new ASTLookupNoFile(
- String.format("Unable to load file '%s': file doesn't exist", fileLabel));
+ /** Constructs a value from a parsed file. */
+ public static ASTFileLookupValue withFile(StarlarkFile ast, byte[] digest) {
+ return new ASTLookupWithFile(ast, digest);
}
- static ASTFileLookupValue forBadFile(Label fileLabel) {
- return new ASTLookupNoFile(
- String.format("Unable to load file '%s': it isn't a regular file", fileLabel));
- }
-
- public static ASTFileLookupValue withFile(StarlarkFile ast) {
- return new ASTLookupWithFile(ast);
- }
-
- public static Key key(Label astFileLabel) {
- return ASTFileLookupValue.Key.create(astFileLabel);
+ public static Key key(Label label) {
+ return ASTFileLookupValue.Key.create(label);
}
@AutoCodec.VisibleForSerialization
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index 9e97667..c6d3118 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -833,6 +833,7 @@
"//src/main/java/com/google/devtools/build/lib/syntax:frontend",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
+ "//third_party:error_prone_annotations",
"//third_party:guava",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/StarlarkImportLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/StarlarkImportLookupFunction.java
index 501cd38..721ddc9 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/StarlarkImportLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/StarlarkImportLookupFunction.java
@@ -398,8 +398,8 @@
@Nullable InliningState inliningState)
throws InconsistentFilesystemException, StarlarkImportFailedException, InterruptedException {
if (!astLookupValue.lookupSuccessful()) {
- // Starlark import files have to exist.
- throw new StarlarkImportFailedException(astLookupValue.getErrorMsg());
+ // Starlark import files must exist.
+ throw new StarlarkImportFailedException(astLookupValue.getError());
}
StarlarkFile file = astLookupValue.getAST();
if (!file.ok()) {
@@ -445,8 +445,7 @@
// Compute a digest of the file itself plus the transitive hashes of the modules it directly
// loads. Loop iteration order matches the source order of load statements.
Fingerprint fp = new Fingerprint();
- // TODO(adonovan): save file.getContentHashCode in ASTFileLookupValue, not the syntax tree.
- fp.addBytes(file.getContentHashCode());
+ fp.addBytes(astLookupValue.getDigest());
Map<String, Module> loadedModules = Maps.newHashMapWithExpectedSize(loadMap.size());
ImmutableList.Builder<StarlarkFileDependency> fileDependencies =
ImmutableList.builderWithExpectedSize(loadMap.size());
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFile.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFile.java
index 8fed417..e7858f8 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFile.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFile.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.lib.syntax;
import com.google.common.collect.ImmutableList;
-import java.io.IOException;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
@@ -31,7 +30,6 @@
private final FileOptions options;
private final ImmutableList<Comment> comments;
final List<SyntaxError> errors; // appended to by Resolver
- @Nullable private final byte[] contentHashCode;
// set by resolver
@Nullable Resolver.Function resolved;
@@ -51,14 +49,12 @@
ImmutableList<Statement> statements,
FileOptions options,
ImmutableList<Comment> comments,
- List<SyntaxError> errors,
- byte[] contentHashCode) {
+ List<SyntaxError> errors) {
super(locs);
this.statements = statements;
this.options = options;
this.comments = comments;
this.errors = errors;
- this.contentHashCode = contentHashCode;
}
// Creates a StarlarkFile from the given effective list of statements,
@@ -67,15 +63,9 @@
FileLocations locs,
ImmutableList<Statement> statements,
FileOptions options,
- Parser.ParseResult result,
- byte[] contentHashCode) {
+ Parser.ParseResult result) {
return new StarlarkFile(
- locs,
- statements,
- options,
- ImmutableList.copyOf(result.comments),
- result.errors,
- contentHashCode);
+ locs, statements, options, ImmutableList.copyOf(result.comments), result.errors);
}
/** Extract a subtree containing only statements from i (included) to j (excluded). */
@@ -85,8 +75,7 @@
this.statements.subList(i, j),
this.options,
/*comments=*/ ImmutableList.of(),
- errors,
- /*contentHashCode=*/ null);
+ errors);
}
/**
* Returns an unmodifiable view of the list of scanner, parser, and (perhaps) resolver errors
@@ -133,14 +122,7 @@
stmts.addAll(prelude);
stmts.addAll(result.statements);
- return create(result.locs, stmts.build(), options, result, /*contentHashCode=*/ null);
- }
-
- // TODO(adonovan): move the digest into skyframe and delete this.
- public static StarlarkFile parseWithDigest(ParserInput input, byte[] digest, FileOptions options)
- throws IOException {
- Parser.ParseResult result = Parser.parseFile(input, options);
- return create(result.locs, ImmutableList.copyOf(result.statements), options, result, digest);
+ return create(result.locs, stmts.build(), options, result);
}
/**
@@ -159,12 +141,7 @@
*/
public static StarlarkFile parse(ParserInput input, FileOptions options) {
Parser.ParseResult result = Parser.parseFile(input, options);
- return create(
- result.locs,
- ImmutableList.copyOf(result.statements),
- options,
- result,
- /*contentHashCode=*/ null);
+ return create(result.locs, ImmutableList.copyOf(result.statements), options, result);
}
/** Parse a Starlark file with default options. */
@@ -176,13 +153,4 @@
public FileOptions getOptions() {
return options;
}
-
- /**
- * Returns the digest of the source file, if this StarlarkFile was constructed by parseWithDigest,
- * null otherwise.
- */
- @Nullable
- public byte[] getContentHashCode() {
- return contentHashCode;
- }
}
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
index 82e7136..3d6ff85 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
@@ -174,9 +174,9 @@
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /*keepGoing=*/ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
- assertThat(result.get(skyKey).getErrorMsg())
- .contains("Unable to load package for '@a_remote_repo//remote_pkg:BUILD'");
- assertThat(result.get(skyKey).getErrorMsg())
+ assertThat(result.get(skyKey).getError())
+ .contains("cannot load '@a_remote_repo//remote_pkg:BUILD':");
+ assertThat(result.get(skyKey).getError())
.contains("The repository '@a_remote_repo' could not be resolved");
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageErrorMessageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageErrorMessageFunctionTest.java
index 110e89e..262866c 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageErrorMessageFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageErrorMessageFunctionTest.java
@@ -57,9 +57,7 @@
getPackageErrorMessageValue(/*keepGoing=*/ true);
assertThat(packageErrorMessageValue.getResult()).isEqualTo(Result.NO_SUCH_PACKAGE_EXCEPTION);
assertThat(packageErrorMessageValue.getNoSuchPackageExceptionMessage())
- .isEqualTo(
- "error loading package 'a': Unable to load file "
- + "'//a:does_not_exist.bzl': file doesn't exist");
+ .isEqualTo("error loading package 'a': cannot load '//a:does_not_exist.bzl': no such file");
}
private PackageErrorMessageValue getPackageErrorMessageValue(boolean keepGoing)
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
index 3f7988f..71ebab7 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
@@ -615,7 +615,7 @@
ErrorInfo errorInfo = result.getError(skyKey);
String expectedMsg =
"error loading package 'test/skylark': "
- + "Unable to load file '//test/skylark:bad_extension.bzl': file doesn't exist";
+ + "cannot load '//test/skylark:bad_extension.bzl': no such file";
assertThat(errorInfo.getException()).hasMessageThat().isEqualTo(expectedMsg);
}
@@ -645,7 +645,7 @@
.isEqualTo(
"error loading package 'test/skylark': "
+ "in /workspace/test/skylark/extension.bzl: "
- + "Unable to load file '//test/skylark:bad_extension.bzl': file doesn't exist");
+ + "cannot load '//test/skylark:bad_extension.bzl': no such file");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java
index c852456..fa87b2d 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java
@@ -1146,7 +1146,7 @@
} catch (ViewCreationFailedException e) {
// expect to fail.
}
- assertContainsEvent("Unable to load file '//test:aspect.bzl': file doesn't exist");
+ assertContainsEvent("cannot load '//test:aspect.bzl': no such file");
}
@Test
diff --git a/src/test/shell/integration/loading_phase_test.sh b/src/test/shell/integration/loading_phase_test.sh
index 102d625..b4a9d1b 100755
--- a/src/test/shell/integration/loading_phase_test.sh
+++ b/src/test/shell/integration/loading_phase_test.sh
@@ -108,7 +108,7 @@
rm -f $pkg/y/rules.bzl
bazel query --noshow_progress "buildfiles(//$pkg/x)" 2>$TEST_log &&
fail "Expected error"
- expect_log "Unable to load file '//$pkg/y:rules.bzl'"
+ expect_log "cannot load '//$pkg/y:rules.bzl'"
}
# Regression test for: