blob: 7b09b08a24e7492482e5360f08efd91b6afab776 [file]
// 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.skyframe;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.FileValue;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.DigestHashFunction;
import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.FileSystem;
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.lib.vfs.inmemoryfs.InMemoryFileSystem;
import com.google.devtools.build.skyframe.ErrorInfo;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyKey;
import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
import net.starlark.java.eval.Module;
import net.starlark.java.eval.Mutability;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkSemantics;
import net.starlark.java.eval.StarlarkThread;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests of specific functionality of BzlCompileFunction. */
@RunWith(JUnit4.class)
public class BzlCompileFunctionTest extends BuildViewTestCase {
private static class MockFileSystem extends InMemoryFileSystem {
PathFragment throwIOExceptionFor = null;
MockFileSystem() {
super(DigestHashFunction.SHA256);
}
@Override
public FileStatus statIfFound(PathFragment path, boolean followSymlinks) throws IOException {
if (path.equals(throwIOExceptionFor)) {
throw new IOException("bork");
}
return super.statIfFound(path, followSymlinks);
}
}
private MockFileSystem mockFS;
@Override
protected FileSystem createFileSystem() {
mockFS = new MockFileSystem();
return mockFS;
}
@Test
public void testIOExceptionOccursDuringReading() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("/workspace/tools/test_build_rules/BUILD");
scratch.file(
"foo/BUILD",
"""
genrule(
name = "foo",
outs = ["out.txt"],
cmd = "echo hello >@",
)
""");
mockFS.throwIOExceptionFor = PathFragment.create("/workspace/foo/BUILD");
invalidatePackages(/*alsoConfigs=*/ false); // We don't want to fail early on config creation.
SkyKey skyKey = PackageIdentifier.createInMainRepo("foo");
EvaluationResult<PackageValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /*keepGoing=*/ false, reporter);
assertThat(result.hasError()).isTrue();
ErrorInfo errorInfo = result.getError(skyKey);
Throwable e = errorInfo.getException();
assertThat(e).isInstanceOf(NoSuchPackageException.class);
assertThat(e).hasMessageThat().contains("bork");
}
@Test
public void testLoadFromFileInRemoteRepo() throws Exception {
Path repoPath = scratch.dir("/a_remote_repo");
scratch.file("/a_remote_repo/REPO.bazel");
scratch.file("/a_remote_repo/remote_pkg/BUILD");
scratch.file("/a_remote_repo/remote_pkg/foo.bzl", "load(':bar.bzl', 'CONST')");
scratch.file("/a_remote_repo/remote_pkg/bar.bzl", "CONST = 17");
invalidatePackages(/*alsoConfigs=*/ false); // Repository shuffling messes with toolchains.
SkyKey skyKey =
BzlCompileValue.key(
Root.fromPath(repoPath),
Label.parseCanonicalUnchecked("@a_remote_repo//remote_pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
List<String> loads =
BzlLoadFunction.getLoadsFromProgram(result.get(skyKey).getProgram()).stream()
.map(Pair::getFirst)
.collect(toImmutableList());
assertThat(loads).containsExactly(":bar.bzl");
}
@Test
public void testLoadOfNonexistentFile() throws Exception {
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertThat(result.get(skyKey).getError()).contains("cannot load '//pkg:foo.bzl': no such file");
}
@Test
public void testBigIntegerLiterals() throws Exception {
// This test ensures that numerical literals with values that can't be expressed as Java longs
// can be compiled. Regression test for b/217548647.
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:bigint.bzl"));
scratch.file("pkg/BUILD");
scratch.file(
"pkg/bigint.bzl",
String.format(
"[%s, %s]",
BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE),
BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE)));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /*keepGoing=*/ false, reporter);
BzlCompileValue bzlCompileValue = result.get(skyKey);
assertThat(bzlCompileValue.lookupSuccessful()).isTrue();
try (Mutability mu = Mutability.create()) {
Object val =
Starlark.execFileProgram(
bzlCompileValue.getProgram(),
Module.withPredeclared(StarlarkSemantics.DEFAULT, ImmutableMap.of()),
StarlarkThread.createTransient(mu, StarlarkSemantics.DEFAULT));
assertThat(val.toString()).isEqualTo("[-9223372036854775809, 9223372036854775808]");
}
}
@Test
public void testInvalidUtf8_enforcementOff() throws Exception {
setBuildLanguageOptions("--noincompatible_enforce_starlark_utf8");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", new byte[] {'#', ' ', (byte) 0x80});
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isTrue();
assertNoEvents();
}
@Test
public void testInvalidUtf8_enforcementWarning() throws Exception {
setBuildLanguageOptions("--incompatible_enforce_starlark_utf8=warning");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", new byte[] {'#', ' ', (byte) 0x80});
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isTrue();
assertContainsEvent(
"WARNING /workspace/pkg/foo.bzl: not a valid UTF-8 encoded file; this can lead to"
+ " inconsistent behavior and will be disallowed in a future version of Bazel");
}
@Test
public void testInvalidUtf8_enforcementError() throws Exception {
reporter.removeHandler(failFastHandler);
setBuildLanguageOptions("--incompatible_enforce_starlark_utf8");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", new byte[] {'#', ' ', (byte) 0x80});
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertThat(result.get(skyKey).getError())
.isEqualTo("compilation of '/workspace/pkg/foo.bzl' failed");
assertContainsEvent(
"ERROR /workspace/pkg/foo.bzl: not a valid UTF-8 encoded file; this can lead to"
+ " inconsistent behavior and will be disallowed in a future version of Bazel");
}
@Test
public void testBzlFileSize_withinLimits() throws Exception {
setBuildLanguageOptions("--max_bzl_file_size=1000", "--soft_max_bzl_file_size=500");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isTrue();
assertNoEvents();
}
@Test
public void testBzlFileSize_exceedsSoftLimitWithoutOptOut_fails() throws Exception {
reporter.removeHandler(failFastHandler);
setBuildLanguageOptions("--max_bzl_file_size=1000", "--soft_max_bzl_file_size=4");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertThat(result.get(skyKey).getError())
.contains("File '//pkg:foo.bzl' size (6 bytes) exceeds the soft maximum size (4 bytes)");
assertContainsEvent(
"ERROR /workspace/pkg/foo.bzl: File '//pkg:foo.bzl' size (6 bytes) exceeds the soft"
+ " maximum size (4 bytes). Split this file or move data out of Starlark. To opt out"
+ " up to the hard limit, set `_KNOWN_OVERSIZED_BZL_FILE =");
}
@Test
public void testBzlFileSize_exceedsSoftLimitWithValidOptOut_succeeds() throws Exception {
setBuildLanguageOptions("--max_bzl_file_size=1000", "--soft_max_bzl_file_size=4");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "_KNOWN_OVERSIZED_BZL_FILE = \"b/564114066\"", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isTrue();
assertNoEvents();
}
@Test
public void testBzlFileSize_exceedsSoftLimitWithNonStringOptOut_fails() throws Exception {
reporter.removeHandler(failFastHandler);
setBuildLanguageOptions("--max_bzl_file_size=1000", "--soft_max_bzl_file_size=4");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "_KNOWN_OVERSIZED_BZL_FILE = True", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertContainsEvent("exceeds the soft maximum size (4 bytes)");
}
@Test
public void testBzlFileSize_exceedsMaxLimitEvenWithSoftOptOut_fails() throws Exception {
reporter.removeHandler(failFastHandler);
setBuildLanguageOptions("--max_bzl_file_size=10", "--soft_max_bzl_file_size=4");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "_KNOWN_OVERSIZED_BZL_FILE = \"b/564114066\"", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertThat(result.get(skyKey).getError())
.contains(
"File '//pkg:foo.bzl' size (48 bytes) exceeds the maximum allowed size (10 bytes)");
assertContainsEvent(
"ERROR /workspace/pkg/foo.bzl: File '//pkg:foo.bzl' size (48 bytes) exceeds the maximum"
+ " allowed size (10 bytes).");
}
@Test
public void testSclFileSize_exceedsSoftAndHardLimits() throws Exception {
reporter.removeHandler(failFastHandler);
setBuildLanguageOptions("--max_bzl_file_size=1000", "--soft_max_bzl_file_size=4");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.scl", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.scl"));
EvaluationResult<BzlCompileValue> softResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(softResult.get(skyKey).lookupSuccessful()).isFalse();
assertContainsEvent(
"ERROR /workspace/pkg/foo.scl: File '//pkg:foo.scl' size (6 bytes) exceeds the soft"
+ " maximum size (4 bytes).");
scratch.overwriteFile("pkg/foo.scl", "_KNOWN_OVERSIZED_BZL_FILE = \"b/564114066\"", "x = 1");
invalidatePackages(/* alsoConfigs= */ false);
EvaluationResult<BzlCompileValue> optOutResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(optOutResult.get(skyKey).lookupSuccessful()).isTrue();
}
@Test
public void testBzlFileSize_zeroLimitDisabled() throws Exception {
setBuildLanguageOptions("--max_bzl_file_size=0", "--soft_max_bzl_file_size=0");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isTrue();
assertNoEvents();
}
@Test
public void testBzlFileSize_allowlistedExempt() throws Exception {
scratch.file("tools/allowlist/BUILD");
scratch.file(
"tools/allowlist/allowlist.allowlist.scl",
"\"\"\"Oversized bzl allowlist.\"\"\"",
"ALLOWED = {",
" \"other/file.bzl\": True,",
" \"pkg/foo.bzl\": {\"reason\": \"b/564114066\", \"tags\": [\"legacy\", 1]},",
"}");
setBuildLanguageOptions(
"--max_bzl_file_size=1000",
"--soft_max_bzl_file_size=500",
"--bzl_file_size_limit_allowlist=//tools/allowlist:allowlist.allowlist.scl");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = '" + "a".repeat(2000) + "'");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isTrue();
assertNoEvents();
}
@Test
public void testBzlFileSize_allowlistFileModified_invalidatesSkyframeCache() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("tools/allowlist/BUILD");
scratch.file("tools/allowlist/allowlist.allowlist.scl", "ALLOWED = {\"other/file.bzl\": True}");
setBuildLanguageOptions(
"--max_bzl_file_size=1000",
"--soft_max_bzl_file_size=500",
"--bzl_file_size_limit_allowlist=//tools/allowlist:allowlist.allowlist.scl");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = '" + "a".repeat(2000) + "'");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> initialResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(initialResult.get(skyKey).lookupSuccessful()).isFalse();
// Add pkg/foo.bzl to the allowlist file; Skyframe must invalidate the failed node and succeed.
scratch.overwriteFile(
"tools/allowlist/allowlist.allowlist.scl",
"ALLOWED = {\"other/file.bzl\": True, \"pkg/foo.bzl\": True}");
invalidatePackages(/* alsoConfigs= */ false);
eventCollector.clear();
EvaluationResult<BzlCompileValue> allowlistedResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(allowlistedResult.get(skyKey).lookupSuccessful()).isTrue();
assertNoEvents();
// Remove pkg/foo.bzl from the allowlist file; Skyframe must invalidate the cached success.
scratch.overwriteFile("tools/allowlist/allowlist.allowlist.scl", "ALLOWED = {}");
invalidatePackages(/* alsoConfigs= */ false);
EvaluationResult<BzlCompileValue> removedResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(removedResult.get(skyKey).lookupSuccessful()).isFalse();
}
@Test
public void testBzlFileSize_allowlistFileOversized_failsWithoutDependencyCycle()
throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("tools/allowlist/BUILD");
scratch.file(
"tools/allowlist/allowlist.allowlist.scl",
"# Long comment that makes allowlist.allowlist.scl itself exceed 15 bytes in file size",
"ALLOWED = {\"pkg/foo.bzl\": True}");
setBuildLanguageOptions(
"--max_bzl_file_size=15",
"--soft_max_bzl_file_size=5",
"--bzl_file_size_limit_allowlist=//tools/allowlist:allowlist.allowlist.scl");
SkyKey allowlistKey =
BzlCompileValue.key(
root, Label.parseCanonicalUnchecked("//tools/allowlist:allowlist.allowlist.scl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), allowlistKey, /* keepGoing= */ false, reporter);
assertThat(result.get(allowlistKey).lookupSuccessful()).isFalse();
assertThat(result.get(allowlistKey).getError())
.contains("The allowlist file itself cannot be oversized to avoid dependency cycles.");
}
@Test
public void testBzlFileSize_allowlistInvalidEntries_failsCompilation() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("tools/allowlist/BUILD");
scratch.file("tools/allowlist/other.scl", "X = 1");
setBuildLanguageOptions(
"--max_bzl_file_size=1000",
"--soft_max_bzl_file_size=500",
"--bzl_file_size_limit_allowlist=//tools/allowlist:allowlist.allowlist.scl");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = '" + "a".repeat(2000) + "'");
assertAllowlistError(
"cannot contain load statements",
"load(':other.scl', 'X')",
"ALLOWED = {\"pkg/foo.bzl\": True}");
assertAllowlistError(
"must only define a single top-level 'ALLOWED' symbol",
"X = 1",
"ALLOWED = {\"pkg/foo.bzl\": True}");
assertAllowlistError(
"'ALLOWED' must be a dict with string keys", "ALLOWED = [\"pkg/foo.bzl\"]");
assertAllowlistError("all keys in 'ALLOWED' must be string literals", "ALLOWED = {1: True}");
assertAllowlistError(
"values in 'ALLOWED' must only contain literals (key \"pkg/foo.bzl\" at"
+ " /workspace/tools/allowlist/allowlist.allowlist.scl:1:27)",
"ALLOWED = {\"pkg/foo.bzl\": len(\"abc\")}");
assertAllowlistError(
"duplicate key \"pkg/foo.bzl\" in 'ALLOWED' at /workspace/tools/allowlist/"
+ "allowlist.allowlist.scl:3:5",
"ALLOWED = {",
" \"pkg/foo.bzl\": \"b/1\",",
" \"pkg/foo.bzl\": \"b/2\",",
"}");
assertAllowlistError(
"invalid key \"//pkg:foo.bzl\" in 'ALLOWED'", "ALLOWED = {\"//pkg:foo.bzl\": True}");
assertAllowlistError(
"invalid key \"./pkg/foo.bzl\" in 'ALLOWED'", "ALLOWED = {\"./pkg/foo.bzl\": True}");
assertAllowlistError(
"invalid key \"@@ext/pkg/foo.bzl\" in 'ALLOWED'",
"ALLOWED = {\"@@ext/pkg/foo.bzl\": True}");
assertAllowlistError(
"invalid key \"@@//pkg/foo.bzl\" in 'ALLOWED'", "ALLOWED = {\"@@//pkg/foo.bzl\": True}");
}
private void assertAllowlistError(String expectedError, String... allowlistLines)
throws Exception {
scratch.overwriteFile("tools/allowlist/allowlist.allowlist.scl", allowlistLines);
invalidatePackages(/* alsoConfigs= */ false);
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertThat(result.get(skyKey).getError()).contains(expectedError);
}
@Test
public void testBzlFileSize_allowlistMissing_failsCompilation() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("tools/allowlist/BUILD");
setBuildLanguageOptions(
"--max_bzl_file_size=1000",
"--soft_max_bzl_file_size=500",
"--bzl_file_size_limit_allowlist=//tools/allowlist:allowlist.allowlist.scl");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = '" + "a".repeat(2000) + "'");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertThat(result.get(skyKey).getError())
.contains(
"Failed to compile allowlist file '//tools/allowlist:allowlist.allowlist.scl':"
+ " cannot load '//tools/allowlist:allowlist.allowlist.scl': no such file");
}
@Test
public void testBzlFileSize_allowlistCrossesPackageBoundary_failsCompilation() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("tools/allowlist/BUILD");
scratch.file(
"tools/allowlist/sub/allowlist.allowlist.scl", "ALLOWED = {\"pkg/foo.bzl\": True}");
setBuildLanguageOptions(
"--max_bzl_file_size=1000",
"--soft_max_bzl_file_size=500",
"--bzl_file_size_limit_allowlist=//tools/allowlist/sub:allowlist.allowlist.scl");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = '" + "a".repeat(2000) + "'");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> result =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(result.get(skyKey).lookupSuccessful()).isFalse();
assertThat(result.get(skyKey).getError())
.contains(
"Failed to load allowlist file '//tools/allowlist/sub:allowlist.allowlist.scl': Label"
+ " '//tools/allowlist/sub:allowlist.allowlist.scl' is invalid because"
+ " 'tools/allowlist/sub' is not a package; perhaps you meant to put the colon"
+ " here: '//tools/allowlist:sub/allowlist.allowlist.scl'?");
}
@Test
public void testBzlFileSize_allowlistKeysAreRepoQualified() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("tools/allowlist/BUILD");
scratch.file("tools/allowlist/allowlist.allowlist.scl", "ALLOWED = {\"pkg/foo.bzl\": True}");
setBuildLanguageOptions(
"--max_bzl_file_size=1000",
"--soft_max_bzl_file_size=500",
"--bzl_file_size_limit_allowlist=//tools/allowlist:allowlist.allowlist.scl");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = '" + "a".repeat(2000) + "'");
// Both keys read the same file under the same root, but belong to different repositories.
SkyKey mainKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
SkyKey extKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("@@ext//pkg:foo.bzl"));
// A main-repo key does not exempt the same path in another repository.
EvaluationResult<BzlCompileValue> mainResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), mainKey, /* keepGoing= */ false, reporter);
assertThat(mainResult.get(mainKey).lookupSuccessful()).isTrue();
EvaluationResult<BzlCompileValue> extResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), extKey, /* keepGoing= */ false, reporter);
assertThat(extResult.get(extKey).lookupSuccessful()).isFalse();
// A repo-qualified key exempts only the file in that repository.
scratch.overwriteFile(
"tools/allowlist/allowlist.allowlist.scl",
"ALLOWED = {\"@@ext//pkg/foo.bzl\": True, \"root_file.bzl\": True,"
+ " \"@@ext//root_file.bzl\": True}");
invalidatePackages(/* alsoConfigs= */ false);
mainResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), mainKey, /* keepGoing= */ false, reporter);
assertThat(mainResult.get(mainKey).lookupSuccessful()).isFalse();
extResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), extKey, /* keepGoing= */ false, reporter);
assertThat(extResult.get(extKey).lookupSuccessful()).isTrue();
// Root-package files use "root_file.bzl" in the main repo and "@@ext//root_file.bzl" in
// external repos.
scratch.file("BUILD");
scratch.file("root_file.bzl", "x = '" + "a".repeat(2000) + "'");
SkyKey mainRootKey =
BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//:root_file.bzl"));
SkyKey extRootKey =
BzlCompileValue.key(root, Label.parseCanonicalUnchecked("@@ext//:root_file.bzl"));
EvaluationResult<BzlCompileValue> mainRootResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), mainRootKey, /* keepGoing= */ false, reporter);
assertThat(mainRootResult.get(mainRootKey).lookupSuccessful()).isTrue();
EvaluationResult<BzlCompileValue> extRootResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), extRootKey, /* keepGoing= */ false, reporter);
assertThat(extRootResult.get(extRootKey).lookupSuccessful()).isTrue();
}
@Test
public void testBzlFileSize_allowlistChange_onlyInvalidatesOversizedBzlFiles() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file("tools/allowlist/BUILD");
scratch.file(
"tools/allowlist/allowlist.allowlist.scl", "ALLOWED = {\"pkg/oversized.bzl\": True}");
setBuildLanguageOptions(
"--max_bzl_file_size=1000",
"--soft_max_bzl_file_size=500",
"--bzl_file_size_limit_allowlist=//tools/allowlist:allowlist.allowlist.scl");
scratch.file("pkg/BUILD");
scratch.file("pkg/normal.bzl", "x = 1"); // normal: size < 500
scratch.file("pkg/oversized.bzl", "x = '" + "a".repeat(2000) + "'"); // oversized: size > 1000
SkyKey normalKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:normal.bzl"));
SkyKey oversizedKey =
BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:oversized.bzl"));
// Both compile successfully initially.
EvaluationResult<BzlCompileValue> initialNormalResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), normalKey, /* keepGoing= */ false, reporter);
EvaluationResult<BzlCompileValue> initialOversizedResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), oversizedKey, /* keepGoing= */ false, reporter);
assertThat(initialNormalResult.get(normalKey).lookupSuccessful()).isTrue();
assertThat(initialOversizedResult.get(oversizedKey).lookupSuccessful()).isTrue();
// Verify Skyframe dependency graph:
Label allowlistLabel =
Label.parseCanonicalUnchecked("//tools/allowlist:allowlist.allowlist.scl");
SkyKey allowlistFileKey =
FileValue.key(RootedPath.toRootedPath(root, allowlistLabel.toPathFragment()));
SkyKey allowlistCompileKey = BzlCompileValue.key(root, allowlistLabel);
Iterable<SkyKey> normalDeps =
getSkyframeExecutor()
.getEvaluator()
.getExistingEntryAtCurrentlyEvaluatingVersion(normalKey)
.getDirectDeps();
Iterable<SkyKey> oversizedDeps =
getSkyframeExecutor()
.getEvaluator()
.getExistingEntryAtCurrentlyEvaluatingVersion(oversizedKey)
.getDirectDeps();
assertThat(normalDeps).doesNotContain(allowlistFileKey);
assertThat(normalDeps).doesNotContain(allowlistCompileKey);
assertThat(oversizedDeps).doesNotContain(allowlistFileKey);
assertThat(oversizedDeps).contains(allowlistCompileKey);
// Modify allowlist.allowlist.scl: remove pkg/oversized.bzl
scratch.overwriteFile("tools/allowlist/allowlist.allowlist.scl", "ALLOWED = {}");
invalidatePackages(/* alsoConfigs= */ false);
// Evaluate both again.
EvaluationResult<BzlCompileValue> secondNormalResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), normalKey, /* keepGoing= */ false, reporter);
EvaluationResult<BzlCompileValue> secondOversizedResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), oversizedKey, /* keepGoing= */ false, reporter);
// Oversized bzl was invalidated by the allowlist change and now fails compilation:
assertThat(secondOversizedResult.get(oversizedKey).lookupSuccessful()).isFalse();
// Normal bzl has no dependency on allowlist, so it remains valid in Skyframe:
assertThat(secondNormalResult.get(normalKey).lookupSuccessful()).isTrue();
}
@Test
public void testBzlFileSize_overrideInvalidatesSkyframeCache() throws Exception {
reporter.removeHandler(failFastHandler);
setBuildLanguageOptions("--max_bzl_file_size=4", "--soft_max_bzl_file_size=2");
scratch.file("pkg/BUILD");
scratch.file("pkg/foo.bzl", "x = 1");
SkyKey skyKey = BzlCompileValue.key(root, Label.parseCanonicalUnchecked("//pkg:foo.bzl"));
EvaluationResult<BzlCompileValue> initialResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(initialResult.get(skyKey).lookupSuccessful()).isFalse();
// Now supply larger limits: Skyframe must invalidate cached failure.
setBuildLanguageOptions("--max_bzl_file_size=1000", "--soft_max_bzl_file_size=1000");
EvaluationResult<BzlCompileValue> retryResult =
SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), skyKey, /* keepGoing= */ false, reporter);
assertThat(retryResult.get(skyKey).lookupSuccessful()).isTrue();
}
}