-
Notifications
You must be signed in to change notification settings - Fork 5k
document how to make ad-hoc tasks cacheable #6691
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
document how to make ad-hoc tasks cacheable #6691
Conversation
Signed-off-by: jnizet <[email protected]>
@jnizet Thank you for your contribution! The example is really good and yes, we should have an example how to make custom tasks cacheable, like, as you a custom I don't think that we should have a guide how to make ad-hoc tasks cacheable. The runtime API for making tasks cacheable is meant to be used for tasks where you cannot change the implementation. For the use-case you described above I think a custom task implementation would be much better. And then we don't need to use the runtime API. Luckily, we already have a guide which describes how to get a cacheable webpack task: https://guides.gradle.org/running-webpack-with-gradle/#step_4_leverage_gradle_build_cache Instead of doing the changes here, could you improve the Webpack guide and link to there from here? |
@wolfs I understand your argument. My main concern is that when looking for how to use the build cache, few people would go to a guide about webpack. But I also understand that I shouldn't encourage the usage of cacheable ad-hoc tasks. How about the following improvements?
|
@jnizet Your suggestions sound good. IMO, it would be enough to link to the webpack example from the build cache chapter. If you think that is not enough, then the rest of your suggestions are fine with me, too. Thanks again! |
Signed-off-by: jnizet <[email protected]>
} | ||
main = "org.gradle.sample.Bundle" | ||
classpath = project.sourceSets.main.runtimeClasspath | ||
args projectDir.toPath().relativize(scripts.toPath()).toString(), projectDir.toPath().relativize(bundle.toPath()).toString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should not promote using of relative paths for the JavaExec task. We have CommandLineArgumentProvider
and Exec.argumentProviders.add
exactly for that reason. I think we should leverage CommandLineArgumentProvider
in this example.
Actually, we need to ignore the path to the executable as well (at least as long as something like #6711 has not been merged). If not, people are forced to install Java in the same directory. So why not declare a custom webpack task which uses Project.javaExec
? The other option would be to subclass the Exec
task and override the getExecutable()
method annotating it with @Internal
.
And the final option is to override the executable property via the runtime API by doing something like inputs.property("executable", "webpack")
.
I would prefer using the custom task class and Project.javaExec
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll go with a Webpack task.
For the purpose of this example, it will not be cacheable (of course), and will take two files as arguments, but these arguments won't be inputs, otherwise I will have the same problem as for the JavaExec task: the task will have inputs that I can't remove (AFAIK), and that will be absolute paths, and thus make the task not relocatable.
Given that the task won't even appear in the guide, there is no need to use javaExec, right? I can just use groovy/kotlin code directly in the task as I did in my first attempt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, to be more realistic, I think it would be more logical to use a very generic NpmTask, that would just take a list of arguments. A specific WebpackTask would probably define inputs and outputs, making the example look weird.
Signed-off-by: jnizet <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That looks pretty good already! Thank you!
I added some comments.
Regarding where to place this content: Looking at it, it seems more suited for the build cache guide, since this guide is targeted more at build authors.
Would it be OK for you to add it as a chapter there? If you want to, I can also take care of moving the content you provided over there.
At some point, we wanted to move the build cache guide back to the userguide, so it would only be a different chapter instead of a whole new repository.
WDYT?
// Fake NPM task that would normally execute npm with its provided arguments | ||
open class NpmTask : DefaultTask() { | ||
|
||
lateinit var args: List<String> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A ListProvider<String>
would be better.
|
||
As we have seen, built-in tasks, or tasks provided by plugins, are cacheable if their class is annotated with the `Cacheable` annotation. But what if you want to make cacheable a task whose class is not cacheable? Let's take a concrete example: your build script uses a generic `NpmTask` task to create a JavaScript bundle by delegating to NPM (and running `npm run bundle`). This process is similar to a complex compilation task, but `NpmTask` is too generic to be cacheable by default: it just takes arguments and runs npm with those arguments. | ||
|
||
The inputs and outputs of this task are obvious. The input is the directory containing the JavaScript files. The output is the bundle file generated by this task. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the configuration files for the bundle task?
include::sample[dir="buildCache/non-cacheable-bundle/kotlin",files="build.gradle.kts[tags=bundle-task]"] | ||
==== | ||
|
||
If you're a modern JavaScript developer, you know that bundling can be quite long, and is worth caching. To achieve that, we need to tell Gradle when it's allowed to cache the output of that task, using the link:{javadocPath}/org/gradle/api/tasks/TaskOutputs.html#cacheIf-org.gradle.api.specs.Spec-[TaskOutputs.cacheIf()] method. In this case, it's always allowed. We also need to assign a property name to each input and output, used by the cache to identify them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: We (try to) use one sentence per line style in the userguide.
|
||
If you're a modern JavaScript developer, you know that bundling can be quite long, and is worth caching. To achieve that, we need to tell Gradle when it's allowed to cache the output of that task, using the link:{javadocPath}/org/gradle/api/tasks/TaskOutputs.html#cacheIf-org.gradle.api.specs.Spec-[TaskOutputs.cacheIf()] method. In this case, it's always allowed. We also need to assign a property name to each input and output, used by the cache to identify them. | ||
|
||
This is sufficient to make the task cacheable on your own machine. However, input files are identified by default by their absolute path. So if the cache needs to be shared between several developers or machines using different paths, that won't work as expected. So we also need to set the "path sensitivity". In this case, the relative path of the input files can be used to identify them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's link to path sensitivity in the build cache guide: https://guides.gradle.org/using-build-cache/#relocatability
// Fake NPM task that would normally execute npm with its provided arguments | ||
open class NpmTask : DefaultTask() { | ||
|
||
lateinit var args: List<String> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use a ListProperty<String>
.
- one sentence per line - NPM config files as input - ListProperty<String> - pass a string rather than a file to outputs.file() - link to guide for relocatability Signed-off-by: jnizet <[email protected]>
Thanks for the review @wolfs. Regarding the location of this new section, if you think it should be in the build cache guide rather than here, that's fine with me. I'm not too familiar with Spock though, so I'd be more comfortable if you moved it there (and rewrote the exemplar test as a Spock integration test). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good from my side! I will merge this soon.
for adding documentation for caching. PR: #6691
Context
I was experimenting with the build cache, and couldn't find any information in the gradle documentation on how to make ad-hoc tasks cacheable, although I feel it's a common use-case. I even thought, reading the build cache chapter of the documentation, that this wasn't supported.
But then I googled and found this document. So I thought adding a section with an example in the documentation could be useful.
Contributor Checklist
<subproject>/src/integTest
) to verify changes from a user perspective<subproject>/src/test
) to verify logic./gradlew <changed-subproject>:check
Gradle Core Team Checklist