From 78d4b99db948da68acd64b553081bd5908ba5b47 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 15 Jun 2026 13:33:43 -0700 Subject: [PATCH] =?UTF-8?q?Catching=C2=A0exceptions=C2=A0during=C2=A0prope?= =?UTF-8?q?rty=C2=A0evaluation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The eval task, unlike other tasks, creates the evaluator when the property is evaluated, not just when the task is executed (in the `getEffective*` properties). The problem here is that property evaluation can and will happen before the task is executed, because Gradle needs property values for caching and sometimes task dependency information. Therefore, if due to misconfiguration or due to intentional configuration the evaluator cannot be created - for example, when the `PklProject` module doesn't exist, but the project directory is configured - then the task will fail with an exception which looks _very_ similar to a regular execution exception, but which actually is not, because it is thrown not during task execution, but during preparation for the execution. This distinction matters a lot when you rely on conditional task execution. If you use the `onlyIf` predicate on tasks to define a condition for the task execution, this predicate will be evaluated before the task actions are run, but *after* properties are evaluated. Thus, if property evaluation fails with an exception, it will *look* as if the `onlyIf` predicate is completely ignored and not even evaluated, and the task action is run regardless of the predicate. This error mode is extremely confusing and is actually wrong: if I use `onlyIf` to gate the task execution, I don't want *any* of its logic to run. In fact, in my case I specifically use `onlyIf` on the evaluation task to only execute it if a prerequisite is met, and the same predicate guards a bunch of other tasks which prepare the Pkl project, so my Pkl project isn't even generated if the predicate fails. But due to this behavior of properties evaluation which are not controlled by `onlyIf`, the project is still attempted to be evaluated, and this results in a very unexpected build failure. The solution is to ignore the evaluation exception, because for all intents and purposes, if the project can't be properly evaluated, it will fail during the task execution as it should, so the values of output properties don't really matter as the task will never be run. --- .../java/org/pkl/gradle/task/EvalTask.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/task/EvalTask.java b/pkl-gradle/src/main/java/org/pkl/gradle/task/EvalTask.java index bfcc9d535..7f486f711 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/task/EvalTask.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/task/EvalTask.java @@ -20,6 +20,7 @@ import java.io.File; import java.util.Collections; import java.util.Set; +import java.util.concurrent.Callable; import javax.annotation.Nullable; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.FileCollection; @@ -74,7 +75,12 @@ private CliEvaluator createCliEvaluator() { public FileCollection getEffectiveOutputFiles() { return getObjects() .fileCollection() - .from(getProviders().provider(() -> nullToEmpty(createCliEvaluator().getOutputFiles()))); + .from( + getProviders() + .provider( + () -> + exceptionToEmpty( + () -> nullToEmpty(createCliEvaluator().getOutputFiles())))); } @OutputDirectories @@ -84,13 +90,24 @@ public FileCollection getEffectiveOutputDirs() { .fileCollection() .from( getProviders() - .provider(() -> nullToEmpty(createCliEvaluator().getOutputDirectories()))); + .provider( + () -> + exceptionToEmpty( + () -> nullToEmpty(createCliEvaluator().getOutputDirectories())))); } private static Set nullToEmpty(@Nullable Set set) { return set == null ? Collections.emptySet() : set; } + private static Set exceptionToEmpty(Callable> provider) { + try { + return provider.call(); + } catch (Exception e) { + return Collections.emptySet(); + } + } + @Override protected void doRunTask() { //noinspection ResultOfMethodCallIgnored