Determine if a task is UP-TO-DATE / FROM-CACHE without actually executing it

I basically have the same question as here on StackOverflow, but I’m not yet convinced that “This is not possible” is the correct answer. Maybe the feature is just not implemented in Gradle yet, but it would be possible to implement.

Background: Our Travis CI builds for a GitHub project install a bunch of prerequisites which takes significant time. For changes to e.g. *.md files which are not part of the build, I’d like to skip installing even the prerequisites and not just skip the build itself. As such I’d need a way to ask Gradle if it would need to build anything for a given task. I was experimenting with the --dry-run option, but this just skips execution without any indication whether the task would be executed without --dry-run.

So, is there currently any to achieve what I want with Gradle, and if not, would it be at least possible to implement, or is it not even possible to implement in Gradle itself (with reasonable effort)?

Hi Sebastian,

for your use-case, you could try to model the installing of the prerequisites as a build service. Then the build service would only be created if a task really uses it (like during execution time). This is a new feature coming in Gradle 6.2.

Is that something which could work for you?

Or is what you are asking for more of a run mode where Gradle would stop/fail if any task needs some executing? I suppose that could work by adding a doFirst to all tasks which throws an exception. If the build fails to execute, then you need to install the prerequisites. If it passes, nothing needs to be done.

Cheers,
Stefan

Hi Stefan,

while it sounds that modelling the installation of prerequisites as a build service would work, I’d prefer to keep the installation of Travis-specific prerequisites separate from the Gradle build.

As such I’d indeed prefer a special generic “run mode” for this use case. From a user perspective it seems that --dry-run really already almost does what I want. But instead of writing just SKIPPED after every task, it could write something like SKIPPED (would be: UP-TO-DATE) to denote what the task status would be without --dry-run. Then I could basically just grep the output of --dry-run to see if there is any task that has neither would be: UP-TO-DATE nor would be: FROM-CACHE to check if a real Gradle build would do anything.

Hi Sebastian,

then how about my second suggestion. Use a command line flag to activate some build logic like this:

tasks.configureEach {
    doFirst {
        if (System.getBoolean("expect.no.work.required")) {
            throw new RuntimeException("Work required to run build")
        }
    }
}

There are some nuances, like you probably should add the doFirst only to tasks which do up-to-date checks and which you care about.

Cheers,
Stefan

@Stefan_Wolf, I guess it would be nice to be able to represent doFirst as a task.
For instance:

testTask {
    initializedBy(installPython) // this naming is in line with existing finalizedBy
}

Currently the only option is dependsOn which would unconditionally execute installPython even in case testTask is up to date (e.g. taken from the build cache).

On the other hand, it would be nice to add initializedBy kind of task dependencies that would be executed only in case the task itself would be executed.

WDYT?

Is it worth filing an issue?

@vlsi That is exactly what build services are for. And that is an upcoming feature of Gradle 6.2.

It looks like build service does not come with “up to date checking”, so I would consider build service and initializedBy different.

Right, that would probably work, too, but it’s cumbersome to add this logic manually to all relevant tasks. Do you see any chance for Gradle to provide this as a built-in feature, i.e. is it reasonable to implement in Gradle itself, and worth filing an issue on GitHub about it?

I think there is a chance for us implementing such a feature, so please open an issue for it. What we also think about doing (long term), is to have something like “coarse grained caching”, i.e. not even trying to run intermediate tasks and pull the final result from the build cache/workspace. For example, when you want to test your app, the sources would be the input and then we could directly load the JAR from the cache. That probably also would work well with the feature you are suggesting.

1 Like

Got, so initializedBy is something like finalizedBy, only that it would only run if Gradle decides that the task actions need to execute. This would mean suspending the execution of one task and running a different task.
I think it would probably be better to allow build services to schedule work which uses up-to-date checks. Similarly, items for the worker API could also have support for up-to-date checking/build cache.

None of this is supported, yet, and there aren’t plans for the near future (next 6 months) to implement those things.

You would still want the suspending behavior.
For instance, if 10 tasks ask for a single build service, then you don’t really want to stall the executor threads by the 10 tasks (or work items) that are doing nothing but waiting for the build service to come.

What you want is to temporarily suspend the tasks, and provide room for the execution of the other tasks that do not use the build service.

If task suspend-resume is implemented, then it should be easy to implement both initializedBy and build service initialization.

Thanks, I see. It is not something that bites me, and I just thought initializedBy might be a nice concept that is both useful and it seems to be in line with the way Gradle solves problems.

Done: https://github.com/gradle/gradle/issues/11942