Skip to content

Commit

Permalink
Track repo rule label attributes after the first non-existent one
Browse files Browse the repository at this point in the history
Even with this commit, the fact that a particular label in a repository
rule's label attributes does not resolve to a regular file is not
tracked, which means that there is still a potential for incorrect
incremental fetches.

Work towards bazelbuild#13441
  • Loading branch information
fmeum committed May 9, 2023
1 parent b1af8fc commit d0fb723
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.google.devtools.build.lib.packages.StructProvider;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
import com.google.devtools.build.lib.repository.RepositoryFetchProgress;
import com.google.devtools.build.lib.rules.repository.NeedsSkyframeRestartException;
import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException;
import com.google.devtools.build.lib.rules.repository.WorkspaceAttributeMapper;
import com.google.devtools.build.lib.runtime.ProcessWrapper;
Expand Down Expand Up @@ -516,26 +517,42 @@ public String toString() {
*/
// TODO(wyv): somehow migrate this to the base context too.
public void enforceLabelAttributes() throws EvalException, InterruptedException {
// TODO: If a labels fails to resolve to an existing regular file, we do not add a dependency on
// that fact - if the file is created later or changes its type, it will not trigger a rerun of
// the repository function.
StructImpl attr = getAttr();
for (String name : attr.getFieldNames()) {
Object value = attr.getValue(name);
if (value instanceof Label) {
getPathFromLabel((Label) value);
dependOnLabelIgnoringErrors((Label) value);
}
if (value instanceof Sequence) {
for (Object entry : (Sequence) value) {
if (entry instanceof Label) {
getPathFromLabel((Label) entry);
dependOnLabelIgnoringErrors((Label) entry);
}
}
}
if (value instanceof Dict) {
for (Object entry : ((Dict) value).keySet()) {
if (entry instanceof Label) {
getPathFromLabel((Label) entry);
dependOnLabelIgnoringErrors((Label) entry);
}
}
}
}
}

private void dependOnLabelIgnoringErrors(Label label) throws InterruptedException, EvalException {
try {
getPathFromLabel(label);
} catch (EvalException e) {
if (e instanceof NeedsSkyframeRestartException) {
throw e;
}
// EvalExceptions indicate labels not referring to existing files. This is fine,
// as long as they are never resolved to files in the execution of the rule; we allow
// non-strict rules.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,6 @@ public RepositoryDirectoryValue.Builder fetch(
} catch (NeedsSkyframeRestartException e) {
// Missing values are expected; just restart before we actually start the rule
return null;
} catch (EvalException e) {
// EvalExceptions indicate labels not referring to existing files. This is fine,
// as long as they are never resolved to files in the execution of the rule; we allow
// non-strict rules. So now we have to start evaluating the actual rule, even if that
// means the rule might get restarted for legitimate reasons.
}

// This rule is mainly executed for its side effect. Nevertheless, the return value is
Expand Down
6 changes: 3 additions & 3 deletions src/test/shell/bazel/external_integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2461,7 +2461,7 @@ EOF

mkdir main
cd main
cat > foo.bzl <<'EOF'
cat > foo.bzl <<EOF
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
def foo():
Expand All @@ -2471,7 +2471,7 @@ def foo():
build_file = "@b//:a.BUILD",
)
EOF
cat > bar.bzl <<'EOF'
cat > bar.bzl <<EOF
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
def bar():
Expand All @@ -2493,7 +2493,7 @@ EOF
touch BUILD

bazel build //... > "${TEST_log}" 2>&1 && fail "expected failure" || :
inplace-sed -e 's?$(pwd)/?PWD/?g' "${TEST_log}"
inplace-sed -e "s?$(pwd)/?PWD/?g" "${TEST_log}"

expect_not_log '[iI]nternal [eE]rror'
expect_not_log 'IllegalStateException'
Expand Down
44 changes: 44 additions & 0 deletions src/test/shell/bazel/starlark_prefetching_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,48 @@ EOF
bazel build @ext//:foo || fail "expected success"
}

# Regression test for https://github.com/bazelbuild/bazel/issues/13441
function test_files_tracked_with_non_existing_files() {
cat > rules.bzl <<'EOF'
def _repo_impl(ctx):
ctx.symlink(ctx.path(Label("@//:WORKSPACE")).dirname, "link")
print("b.txt: " + ctx.read("link/b.txt"))
print("c.txt: " + ctx.read("link/c.txt"))
ctx.file("BUILD")
ctx.file("WORKSPACE")
repo = repository_rule(
_repo_impl,
attrs = {"_files": attr.label_list(
default = [
Label("@//:a.txt"),
Label("@//:b.txt"),
Label("@//:c.txt"),
],
)},
)
EOF

cat > WORKSPACE <<'EOF'
load(":rules.bzl", "repo")
repo(name = "ext")
EOF
touch BUILD

# a.txt is intentionally not created
echo "bbbb" > b.txt
echo "cccc" > c.txt

# The missing file dependency is tolerated.
bazel build @ext//:all &> "$TEST_log" || fail "Expected repository rule to build"
expect_log "b.txt: bbbb"
expect_log "c.txt: cccc"

echo "not_cccc" > c.txt
bazel build @ext//:all &> "$TEST_log" || fail "Expected repository rule to build"
expect_log "b.txt: bbbb"
expect_log "c.txt: not_cccc"
}

run_suite "Starlark repo prefetching tests"

0 comments on commit d0fb723

Please sign in to comment.