[6.5.0] Restart at most once when prepopulating repository rule environment (#20667)
When a repository rule is fetch attributes are iterated over in
`enforceLabelAttributes` to prepopulate the environment, restarting the
fetch each time a new dependency is discovered.
This is faster than calling `repository_ctx.path(...)` early in the
repository rule implementation function but still has considerable
overhead.
This PR defers throwing `NeedsSkyframeRestartException` till after every
attribute has been processed, greatly reducing the number of restarts
and iterations.
In an internal project these optimisations are particularly noticeable.
Before: 2min 8s, 2min 7s
After: 1min 35s, 1min 27s
Closes #20434.
Commit
https://github.com/bazelbuild/bazel/commit/e8ac96adb860b9831a62681167295f103a51470a
PiperOrigin-RevId: 588090528
Change-Id: I7917b137d6e60b6d6a73189cf396418a85b3ec28
Co-authored-by: Jordan Mele <mele@canva.com>
Co-authored-by: Xùdōng Yáng <wyverald@gmail.com>
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java
index dd8ad14..2462288 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java
@@ -516,43 +516,56 @@
* potentially more expensive operations.
*/
// TODO(wyv): somehow migrate this to the base context too.
- public void enforceLabelAttributes() throws EvalException, InterruptedException {
+ public void enforceLabelAttributes()
+ throws EvalException, InterruptedException, NeedsSkyframeRestartException {
// 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();
+ // Batch restarts as they are expensive
+ boolean needsRestart = false;
for (String name : attr.getFieldNames()) {
Object value = attr.getValue(name);
if (value instanceof Label) {
- dependOnLabelIgnoringErrors((Label) value);
+ if (dependOnLabelIgnoringErrors((Label) value)) {
+ needsRestart = true;
+ }
}
if (value instanceof Sequence) {
for (Object entry : (Sequence) value) {
if (entry instanceof Label) {
- dependOnLabelIgnoringErrors((Label) entry);
+ if (dependOnLabelIgnoringErrors((Label) entry)) {
+ needsRestart = true;
+ }
}
}
}
if (value instanceof Dict) {
for (Object entry : ((Dict) value).keySet()) {
if (entry instanceof Label) {
- dependOnLabelIgnoringErrors((Label) entry);
+ if (dependOnLabelIgnoringErrors((Label) entry)) {
+ needsRestart = true;
+ }
}
}
}
}
+
+ if (needsRestart) {
+ throw new NeedsSkyframeRestartException();
+ }
}
- private void dependOnLabelIgnoringErrors(Label label)
- throws InterruptedException, NeedsSkyframeRestartException {
+ private boolean dependOnLabelIgnoringErrors(Label label) throws InterruptedException {
try {
getPathFromLabel(label);
} catch (NeedsSkyframeRestartException e) {
- throw e;
+ return true;
} 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.
}
+ return false;
}
}