![onepiece.Software by Jendrik Johannes](/img/default-banner.jpg)
- 38
- 192 733
onepiece.Software by Jendrik Johannes
Germany
Приєднався 11 бер 2021
This channel hosts the Understanding Gradle series by Jendrik Johannes, which introduces the concepts of the Gradle Build Tool one-by-one in short videos.
Understanding Gradle #36 – Task Actions
Tweak what a task is doing by adding or modifying task actions.
🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E
🏎️ Training on your Gradle topics onepiece.software/#training
✨ Support with your Gradle project onepiece.software/#consulting
💚 Support my work paypal.me/onepiecesw
💙 Follow me on Mastodon mastodon.social/@jendrik
▶️ 0:00 Tasks and Task Actions
▶️ 1:20 Implement a task action as method
▶️ 3:18 Add a task action dynamically
▶️ 5:20 Use configuration cache
▶️ 5:49 Separate configuration and execution time state
▶️ 7:33 Modify actions of registered tasks
▶️ 8:46 Add logging output
▶️ 10:16 Remove existing task action
▶️ 11:10 Re-implement removed action
▶️ 12:21 Summary
💾 Example on GitHub (Kotlin DSL): github.com/jjohannes/understanding-gradle/tree/main/36_Task_Actions
💾 Example on GitHub (Groovy DSL): github.com/jjohannes/understanding-gradle/tree/groovy-dsl/36_Task_Actions
Related Videos:
⏩ 04 Tasks: ua-cam.com/video/9tY4MFEgmgM/v-deo.html
⏩ 05 Lifecycle Tasks: ua-cam.com/video/sOo0p4Gpjcc/v-deo.html
⏩ 06 Configuring Task Inputs and Outputs: ua-cam.com/video/Pj9hSRauiQM/v-deo.html
⏩ 07 Implementing Tasks and Extensions: ua-cam.com/video/wrgyUKC7vOY/v-deo.html
⏩ 23 Caching: ua-cam.com/video/nHb0kIcTrFE/v-deo.html
Further Readings:
📕 Configuring Tasks: docs.gradle.org/current/userguide/more_about_tasks.html#sec:configuring_tasks
📕 'doFirst' / 'doLast' and Configuration Cache: docs.gradle.org/current/userguide/common_caching_problems.html#suggestions_for_authoring_your_build
🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E
🏎️ Training on your Gradle topics onepiece.software/#training
✨ Support with your Gradle project onepiece.software/#consulting
💚 Support my work paypal.me/onepiecesw
💙 Follow me on Mastodon mastodon.social/@jendrik
▶️ 0:00 Tasks and Task Actions
▶️ 1:20 Implement a task action as method
▶️ 3:18 Add a task action dynamically
▶️ 5:20 Use configuration cache
▶️ 5:49 Separate configuration and execution time state
▶️ 7:33 Modify actions of registered tasks
▶️ 8:46 Add logging output
▶️ 10:16 Remove existing task action
▶️ 11:10 Re-implement removed action
▶️ 12:21 Summary
💾 Example on GitHub (Kotlin DSL): github.com/jjohannes/understanding-gradle/tree/main/36_Task_Actions
💾 Example on GitHub (Groovy DSL): github.com/jjohannes/understanding-gradle/tree/groovy-dsl/36_Task_Actions
Related Videos:
⏩ 04 Tasks: ua-cam.com/video/9tY4MFEgmgM/v-deo.html
⏩ 05 Lifecycle Tasks: ua-cam.com/video/sOo0p4Gpjcc/v-deo.html
⏩ 06 Configuring Task Inputs and Outputs: ua-cam.com/video/Pj9hSRauiQM/v-deo.html
⏩ 07 Implementing Tasks and Extensions: ua-cam.com/video/wrgyUKC7vOY/v-deo.html
⏩ 23 Caching: ua-cam.com/video/nHb0kIcTrFE/v-deo.html
Further Readings:
📕 Configuring Tasks: docs.gradle.org/current/userguide/more_about_tasks.html#sec:configuring_tasks
📕 'doFirst' / 'doLast' and Configuration Cache: docs.gradle.org/current/userguide/common_caching_problems.html#suggestions_for_authoring_your_build
Переглядів: 803
Відео
Understanding Gradle #35 - Working with Files
Переглядів 75610 місяців тому
How to copy and package files with Gradle in an elegant way? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepiecesw 💙 Follow me on Mastodon mastodon.social/@j...
Understanding Gradle #34 - Properties and Providers
Переглядів 976Рік тому
What are the Property and Provider concepts in Gradle and why were they introduced? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepiecesw 💙 Follow me on Mast...
Understanding Gradle #33 - Classpath and Module Path in Testing
Переглядів 808Рік тому
How does modularity in Java work (Part 8): How are classpath and module path concepts used in testing? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepiecesw ...
Understanding Gradle #32 - Artifact Transforms
Переглядів 1,1 тис.Рік тому
How does modularity in Java work (Part 7): How to use Artifact Transforms to turn old Jar files into clean Java Modules? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work pay...
Understanding Gradle #31 - The Module Path
Переглядів 921Рік тому
How does modularity in Java work (Part 6): What's the Module Path and the Java Module System (JPMS, Jigsaw)? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepi...
Understanding Gradle #30 - Discover Security Vulnerabilities
Переглядів 956Рік тому
How does modularity in Java work (Part 5): Configure Gradle to Discover Security Vulnerabilities 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepiecesw 💙 Foll...
Understanding Gradle #29 - Detect and Resolve Collisions on a Classpath
Переглядів 927Рік тому
How does modularity in Java work (Part 4): Detect and make Gradle resolve class collisions on a classpath 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepiece...
Understanding Gradle #28 - Clean Compile Classpaths with the Dependency Analysis Plugin
Переглядів 1,7 тис.Рік тому
How does modularity in Java work (Part 3): Keep the Compile Classpaths of your modules clean with the Dependency Analysis plugin 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my ...
Understanding Gradle #27 - Multiple Compile Classpaths
Переглядів 1,2 тис.Рік тому
How does modularity in Java work (Part 2): Multiple Compile Classpaths and Dependency Scopes 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepiecesw 💙 Follow m...
Understanding Gradle #26 - The Classpath
Переглядів 3,3 тис.Рік тому
How does modularity in Java work (Part 1): What is the Classpath? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💚 Support my work paypal.me/onepiecesw 💙 Follow me on Mastodon mastodon.soci...
Understanding Gradle #25 - Using Java to configure builds
Переглядів 1 тис.Рік тому
No XML, no Groovy, no Kotlin required - just use Java to build your Java projects with Gradle! 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💙 Follow Jendrik on Mastodon mastodon.social/@j...
Understanding Gradle #24 - Kotlin DSL and Groovy DSL
Переглядів 2,4 тис.Рік тому
How do the Kotlin DSL and the Groovy DSL work and what are the major differences between them? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💙 Follow Jendrik on Mastodon mastodon.social/@j...
Understanding Gradle #23 - Caching
Переглядів 2,4 тис.Рік тому
What are the caching mechanisms Gradle uses? Where, and why, to configure them? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle project onepiece.software/#consulting 💙 Follow me on Mastodon mastodon.social/@jendrik ▶️ 0:00 Cachi...
Understanding Gradle #22 - The JavaCompile Task
Переглядів 1,6 тис.2 роки тому
What are the different options to perform Java compilation with the JavaCompile Task? How to control memory usage and parallelism through task configuration and gradle.properties? 🚀 Online course - Modern Gradle Fundamentals www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E 🏎️ Training on your Gradle topics onepiece.software/#training ✨ Support with your Gradle ...
Understanding Gradle #21 - Test and Code Coverage Reporting
Переглядів 3,4 тис.2 роки тому
Understanding Gradle #21 - Test and Code Coverage Reporting
Understanding Gradle #20 - Test Fixtures
Переглядів 2 тис.2 роки тому
Understanding Gradle #20 - Test Fixtures
Understanding Gradle #19 - The Test Task
Переглядів 1,5 тис.2 роки тому
Understanding Gradle #19 - The Test Task
Understanding Gradle #18 - Configuring Testing
Переглядів 3,2 тис.2 роки тому
Understanding Gradle #18 - Configuring Testing
Understanding Gradle #17 - Feature Variants
Переглядів 2,3 тис.2 роки тому
Understanding Gradle #17 - Feature Variants
Understanding Gradle #16 - Source Sets
Переглядів 5 тис.2 роки тому
Understanding Gradle #16 - Source Sets
Understanding Gradle #15.3 - Full Java Module System Project Setup
Переглядів 7832 роки тому
Understanding Gradle #15.3 - Full Java Module System Project Setup
Understanding Gradle #15.2 - Full Android Project Setup
Переглядів 7572 роки тому
Understanding Gradle #15.2 - Full Android Project Setup
Understanding Gradle #15.1 - Full Java Project Setup
Переглядів 2,2 тис.2 роки тому
Understanding Gradle #15.1 - Full Java Project Setup
Understanding Gradle #14 - Settings Plugins
Переглядів 2,4 тис.2 роки тому
Understanding Gradle #14 - Settings Plugins
Understanding Gradle #13 - Aggregating Custom Artifacts
Переглядів 3,3 тис.2 роки тому
Understanding Gradle #13 - Aggregating Custom Artifacts
Understanding Gradle #12 - Publishing Libraries
Переглядів 12 тис.2 роки тому
Understanding Gradle #12 - Publishing Libraries
Understanding Gradle #11 - Capability Conflicts
Переглядів 2,9 тис.2 роки тому
Understanding Gradle #11 - Capability Conflicts
Understanding Gradle #10 - Dependency Version Conflicts
Переглядів 7 тис.2 роки тому
Understanding Gradle #10 - Dependency Version Conflicts
Understanding Gradle #09 - Centralizing Dependency Versions
Переглядів 8 тис.2 роки тому
Understanding Gradle #09 - Centralizing Dependency Versions
Beautiful explanation
Thanks a lot, Jendrik! Your answers to each comment have also been very helpful to solve my issues.
Thanks! The information here is advanced and technically deep, when compared to the other courses in paid learning platforms which are outdated on gradle 4 or so. Demoing with a powerful typed language like Kotlin is a definite plus. Use this channel and refer to the gradle docs to gain mastery over gradle build tool. Watched 10 videos back to back 😀
Thank you for simplifying and clarifying the key points for me.
Is it a complete Gradle course?
Short answer: Yes - ua-cam.com/video/Ajs8pTbg8as/v-deo.html Long answer: If you go through the complete playlist you will learn about the fundamental Gradle concepts and certain details on how to using Gradle for Java projects. Depending on what you want to do, it might be "too much" in some places and "too abstract" in others. But it depends on what you expect and what you plan to do with Gradle. I also have: A course on Udemy (💲) going through things step-by-step in a lower pace for a typical Java project setup. www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E An example on GitHub that shows a complete project (also with links to the videos). github.com/jjohannes/gradle-project-setup-howto
Can I ask you a question? I have my custom repository for downloading plugins and dependencies (like some nexus). How can I share this maven repository between all modules (build.gradle.kts and settings.gradle.kts) not to write it every time, but for example maven(“my-nexus”).
This is a good question, because this is the one thing that is quite difficult to centralize. This is, because the repositories are needed very early. Before Gradle can load the *build configuration* itself, it must load plugins from your repository. But if repositories are defined as part of the *build configuration* - there is a chicken-egg problem. Lets first summarize what I usually do in this video series and which you can find in my example project: github.com/jjohannes/gradle-project-setup-howto We have three places: (1) *gradle/plugins/build.gradle* (or 'my-build-logic/settings.gradle' in the video examples) Here we define the repositories for Gradle to **build** the convention plugins. This is independent, because it is treated as an independent project by Gradle. (2) *pluginManagement* in *settings.gradle* Here we define the repositories for Gradle to **use** all plugins. This is usually the same as above. But because this is a different project/context the definitions are not reused. (Maybe there will be some feature in a future Gradle version...) (3) *dependencyManagement* in *settings.gradle* Here we define the repositories to find other libraries we use. This may be different than the above (e.g mavenCentral while the above are gradlePluginPortal) but in your case, it is the same again. Usually, I just accept that there is some repetition. But there is at least one trick that you can use, which is putting the information you want to share into a separate file. For instance, you can create a file that just contains your Nexus URL: *gradle/nexus.txt* (content e.g.: plugins.gradle.org/m2/) And then, in all the three places above, you get the URL from the file: *repositories.maven(providers.fileContents(layout.projectDirectory.file("../nexus.txt")).asText.get().trim())*
The best explanation about gradle on internet I found.
Excellent. Thanks
Thanks for the video. If I have multiple precompiled script plugins, is it a good practice to publish them independently with different versions? And if so, how do I simplify the task name? For example, the default is "publishCom.company.my-conventionPluginMarkerMavenPublicationToMavenRepository". I guess I could create a task with a more friendly name which is finalized by this, but I'm wondering if there is a better way.
Hi. A plugin publication consists of multiple "components" that are published by different tasks. You should not use these tasks individually as they are all connected. If you publish to your own repository (that you configured in publishing.repositories {}) usually you can call *gradle publish* to publish everything at once. That will publish: - The JAR with *all* plugins in it to *my/group/plugin-project-name/1.0/plugin-project-name-1.0.jar* - A so-called "Marker POM" for each plugin to *my/plugin/id/my.plugin.id.gradle.plugin/1.0/my.plugin.id.gradle.plugin-1.0.pom* See also the section on this in the Gradle docs: docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers The "Marker POMs" do not contain your plugins. They are only there so that Gradle can find plugins if a user does directly want to use them via the *id("my.plugin.id") version "1.0"* notation in a build file. Because Gradle does not know the *group* of the Jar in this case, it uses the marker to indirectly get to it. That's why markers and the corresponding JAR have the same version. If you want to publish plugins with different versions, they need to be packaged in separate Jars. You can achieve that by defining plugins in different subprojects that you publish individually. E.g. *gradle :my-plugin-a:publish* / *gradle :my-plugin-b:publish*
@@jjohannes Wow, great explanation, thanks a lot for taking the time to respond!
Great video and straight forward explanation. A questions I can ask is, how could we add the testFixture code into checkstyle checks? I realized it doesn’t get checked since it’s outside the src/main and src/test
Thank you! Regarding the question: It's quite straight forward. The *checkstyle* plugin is aware of *source sets* and will register a task to check *testFixtures* automatically, which is called *checkstyleTestFixtures* . After you use the plugin - *id("checkstyle")* - you can run: *gradle checkstyleTestFixtures* What the plugin does not do is automatically adding that task to the *check* lifecycle. Which is probably what you are looking for. You can do this: *tasks.check { dependsOn(tasks.named("checkstyleTestFixtures")) }* Then it runs as part of *gradle check*
@@jjohannes that worked, ended up finding the new tasks by accident while attempting a fix, so thanks for confirming it!!
I did the same thing what you did in the first 4 videos, but my output is like this: gradle :app:build --console=plain > Task :my-build-logic:java-plugins:checkKotlinGradlePluginConfigurationErrors > Task :my-build-logic:java-plugins:generateExternalPluginSpecBuilders UP-TO-DATE > Task :my-build-logic:java-plugins:extractPrecompiledScriptPluginPlugins UP-TO-DATE > Task :my-build-logic:java-plugins:compilePluginsBlocks UP-TO-DATE > Task :my-build-logic:java-plugins:generatePrecompiledScriptPluginAccessors UP-TO-DATE > Task :my-build-logic:java-plugins:generateScriptPluginAdapters UP-TO-DATE > Task :my-build-logic:java-plugins:compileKotlin UP-TO-DATE > Task :my-build-logic:java-plugins:compileJava NO-SOURCE > Task :my-build-logic:java-plugins:pluginDescriptors UP-TO-DATE > Task :my-build-logic:java-plugins:processResources UP-TO-DATE > Task :my-build-logic:java-plugins:classes UP-TO-DATE > Task :my-build-logic:java-plugins:jar UP-TO-DATE > Task :app:assemble UP-TO-DATE > Task :app:check UP-TO-DATE > Task :app:build UP-TO-DATE What is the reason for this?
The output looks like the *app* project only has the *base* plugin but no java/application plugin. Then you get the lifecycle tasks - *assemble* *check* *build* - but nothing is linked to them. Please check: - Which plugin do you apply in *my-project/app/build.gradle* ? Is it *id("my-java-application")* ? If not, add it. If you have it... - ...check your *my-build-logic/java-plugins/src/main/kotlin/my-java-application.gradle.kts* . Does it apply the *id("application")* plugin?
@@jjohannes When I applied "base" plugin to my-project/app/build.gradle and applied "java-library" and "org.jetbrains.kotlin.jvm" plugins to my-build-logic/java-plugins/src/main/kotlin/my-java-application.gradle.kts ,I got these results. Actually, I did the same things from the first video to where you applied the "base" plugin. By the way, I haven't declared anything other than the "base" plugin in the "app"'s build file yet. But somehow it produces an output as if I added dependency. By the way, if you want to take a look, I sent you the code I wrote on Udemy.
@@hsnclk1985 IIUC, your question is why you see all the *> Task :my-build-logic:java-plugins:...* tasks although you only build the app project that does not use any of the plugins in *:my-build-logic:java-plugins* . The reason is that the plugins are required at configuration time of the *whole my-project build* . In other words, *:my-build-logic:java-plugins* is a dependency of the whole build and not of individual subprojects like *app* . In other places of the build you use some of the plugins. Which are in *data-model/build.gradle.kts* and *business-logic/build.gradle.kts* . If these two files would not use any of the plugin (e.g. if you remove id("my-java-library") from these files), you will no longer see the *:my-build-logic:java-plugins* tasks running.
Thank you for complete review of Gradle's caching techniques! There is one thing, which puzzles me with "build cache" enabled is - how to --rerun integration tests (which depend on some external state (e.g. Docker-Compose)) if: 1) if those tests were already executed successfully, 2) and if the end user of the build uses a single lifecycle task to run tests? Suppose I have multiple "modules" ("Gradle projects", each having own test suites, some of them depend on Docker-compose). The regular flow for developers is to simply execute single ":testAll" task before pushing their code to CI. What if a user did some cleanup in containers by restating Docker-compose stack from scratch and wants to double check that all tests still pass? There is a '--rerun' option implicitly available for each individual task, but the problem here is that providing --rerun to lifecycle task won't affect the actual test tasks doing real job. To re-run tests in this scenario user will either have to use --no-build-cache, losing all benefits of build cache or to know what individual task on each sub-project to execute with --rerun. Would be great to know if there is a way to propagate the effect of --rerun from lifecycle task on tasks it directly dependsOn()
> but the problem here is that providing --rerun to lifecycle task won't affect the actual test tasks doing real job. Yes this is a shortcoming of Gradle. I think such a feature is missing. You have similar issues with other task-specific options like '--test'. The best "workaround" I know is to add a "-P" parameter (gradle property) for this yourself. Then users can do something like this: *./gradlew :testAll -PrerunTests* Then you would configure your test tasks to pick up the parameter. Something like: *tasks.withType<Test>().configureEach {* *if (providers.gradleProperty("rerunTests").isPresent) { inputs.property("time", Date().time) }* *}* (IntelliJ IDEA does something similar in the background when you press the test run button in the IDE.)
8:39 Inside the configurations closure, the things u put compileClasspath, runtimeClasspath. How do you know, these sub-closures will be part of configurations closure. Please reply.
Whether something will be part of the configuration of a build invocation is decided by Gradle. In general, what you configure will be used if the "thing" you configured is used. For example, if you configure the *runtimeClasspath* , the configuration of it will be performed (i.e. the closure will be executed), before the *runtimeClasspath* is used if you run a build that uses the *runtimeClasspath* .
Thanks for the concepts. I was trying to understand my companies application build.gradle. therw are so many things at the tasks, like you wrote.. Do you think I should learn groovy first to work normally, like you wrote ??
At 2:30, you are saying, all other build configuration and custom build logic should go into plugins. What is the meaning.. how other build configuration can go into plugins In my company applications' build.gradle, it is not like this
This is a forward reference to the next video in the series about *Plugins* ua-cam.com/video/N95YI-szd78/v-deo.html In that video, I continue with the same example and show how to move build configuration from all *build.gradle* files into plugins.
Here you define manual version but how we can increment this version automatically
That's a very good question. The best solution I know of is generating a timestamp yourself and set that as version. For example: *version = "1.0-${SimpleDateFormat("yyyyMMddHHmmssSSS").format(Date())}"* The tricky part is to use the same timestamp for the whole build if you publish multiple modules. The most compact way to achieve that is to set the version in the *root project* and then use that in all the subprojects. Add a *build.gradle.kts* to the root: *import java.text.SimpleDateFormat* *import java.util.Date* *version = "1.0-${SimpleDateFormat("yyyyMMddHHmmssSSS").format(Date())}"* Use that version in all the subprojects, e.g. in *my-java-base.gradle.kts* for the example in this video: *group = "org.example.my-app"* *version = rootProject.version* I shared a full example here: github.com/jjohannes/gradle-demos/tree/main/publish-with-timestamp-version
@@jjohannes thanks a lot it worked for me the only part needed is my nexus policy allow version to publish with 1.0.0-SNAPSHOT what changes need to do in your script so that every time it's incremented
Hi Jendrik! Some tasks can contain @Option's which are CLI args. Is it possible to declare that one task 'dependsOn' another task executed with specific values for those @Options? For example, I would want to have a high-level lifecycle CI task on root Gradle project which builds production-ready Docker image. Under the hood this lifecycle task should call Jib task with specific @Option on Gradle sub-project with 'main' method and id("application") plugin.
I think I can answer this question myself now. Please correct me if I am wrong, but based on what I learnt from the docs, the most idiomatic way would be to create a "configureJibForProdImagetask" where I 1) find subproject -> 2) find Jib extension on subproject -> and configure it as needed in doLast {} block of this special task. Now in my lifecycle task I should first dependsOn(configureJibForProdImagetask) and then depend on actionable Jib task. Works as expected! (but not compatible with configuration cache though :( )
@@kirillgavrilov9681 Unfortunately, there is no real "good" solution for this as far as I know. I would try to do this in the configuration of the lifecycle task directly, instead of using 'doLast' or 'doFirst'. Like this (here I use Test.debug as an example of an @Option): *tasks.register("myLifecycle") {* *dependsOn(tasks.named("test"))* *tasks.getByName<Test>("test") { this.debug = true }* *}* As far as I know, it is not really recommended to configure one task from another. But I think in this case it is still the best option. The trick is that as long as you use "lazy task configuration" (register/named), the configuration block is only executed if you call "myLifecycle". Note that *getByName* theoretically breaks "lazy task configuration" for the "test" task (in contrast to *named* which won't work here). But since you add the dependsOn anyway, it is not a problem in practice. Would be interested to know if this works for you.
@@jjohannes Hi! Thank you for your reply. > I would try to do this in the configuration of the lifecycle task directly, instead of using 'doLast' or 'doFirst' > getByName theoretically breaks "lazy task configuration" for the "test" task (in contrast to named which won't work here). But since you add the dependsOn anyway, it is not a problem in practice. Yes, this works for me and I simplified my solution with "configureXXX" task down to simply using configuration-block of 1st task to configure 2nd task. Introducing additional "configuration"-task does not seem to provide any benefits here. I need to access a target plugin's DSL in target subproject, which is not lazy and not configuration-cache friendly (which is explicitly said in docs), but I do not see a better choice here. Laziness is not a problem: as you said, there is a dependsOn anyway. CC would be nice to have, but Gradle docs explicitly say that configuring one task from another is not CC-friendly. I would be happy to work with some lazy Gradle properties providers to configure behavior of one task from another and avoid 'getByName', but such properties are not exposed by the target plugin, so I have to access subproject and plugin's extension DSL to achieve the same effect as using `--option` allows. Honestly, now I'm struggling to understand the recommendation to introduce additional `configureXXX` task instead of doing doFirst from this section of the documentation: docs.gradle.org/8.7/userguide/common_caching_problems.html#custom_actions I understood that doFirts changes are hidden for UP-TO-DATE and build cache checks. But why simply not to configure manifest attributes inside of tasks.named("test) {} lazy block? Why somebody might want to use "doFirts" for configuration, while there is a task's configuration block getting executed BEFORE actual task action begins? 🤔
@@kirillgavrilov9681 > CC would be nice to have, but Gradle docs explicitly say that configuring one task from another is not CC-friendly. Have you tested this? Is this always the case or just a general recommendation (which does not apply for this specific use case). Because I think this case should still work with CC (I very much hope). > Why somebody might want to use "doFirst" for configuration, while there is a task's configuration block getting executed BEFORE actual task action begins? I have used that for exactly the reason to hide something from UP-TO-DATE. An example is that you want to add a timestamp as attribute. But only add that if something else changed. Otherwise you are fine with reusing the previous Jar (with the previous time stamp). (If you really need timestamps in manifests is another question :) - but I have seen projects that insisted to have it.)
I'm asking this question because I'm really newbie at Gradle. I was having a problem with something but I solved it somehow. But I didn't quite understand how I solved it. I was proceeding in the order in the video. Everything was fine. I did not encounter any problems until I created the my-java-library.gradle.kts script under the kotlin folder. Even though I did "./gradlew build" via the terminal or reloaded all the gradle projects from the gradle tool window in Intellij, some code blocks in this script remained unresolved. Then I tried doing "command + f9" (i.e. build project). These code blocks were resolved in a way that I don't understand why. Why is it not enough to do "./gradlew build" or reload all gradle projects from the gradle tool window?
What you describe can happen, if: (1) You define plugins in an included build logic build - *my-build-logic/java-plugins/src/main/kotlin/*.gradle.kts* in the example (2) Do *not use any* of the plugins in your main build - non of your *build.gradle.kts* has one of the plugin in the *plugins { }* block Gradle only builds plugins if they are used, otherwise the plugins build (my-build-logic in the example) is not used. Even if you run a complete *build* of the main project (./gradlew build). As a consequence, certain files generated by Gradle may be missing that IntelliJ relies on. The "command + f9" in IntelliJ might do more. Something like the equivalent to this from command line: ./gradlew build cd ../my-build-logic ../my-project/gradlew build That is, building you main project (my-project) and *explicitly* building the build logic project (my-build-logic) as well.
@@jjohannes I understood very well what you said at the beginning, except for what you did in the command line. 1) ./gradlew build (In the first command I assume I'm in my-project. By doing this, I build all connected projects and plugins.) 2) cd ../my-build-logic (now I am in my-build-logic folder) 3) ../my-project/gradlew build (In this command, if I understand correctly, the gradlew script in the "my-project" folder is called from "my-build-logic". I don't understand what you did here.) By the way, I should point out that. I use only one gradlew script to ensure version consistency, which in turn is under my-project folder. So there is no gradlew under my-build-logic folder. I hope I understood the usage of gradlew correctly.
Dear Jendrik, Why didn't you specify version number of plugin of Kotlin (I mean id("org.jetbrains.kotlin.jvm") ) inside in the my-java-library.gradle.kts file? You specified it in the other build files.
Hi @hsnclk1985. I talk about this here: ua-cam.com/video/N95YI-szd78/v-deo.html (3:13). When you have a separate *build logic* build/project, you use Gradle's dependency management mechanisms as in any other Gradle project. External plugins, or better the Jars containing the plugins, then become dependencies of that project which you define in the *build.gradle.kts of the build logic* project. In this example I have: dependencies { implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20") } The "short notation" *id("org.jetbrains.kotlin.jvm") version "1.8.20"* that I show in the beginning of the video can be utilized to use external plugins directly in a build.gradle.kts file of your project *without a build logic* project.
@@jjohannes I'm not sure we're talking about the same thing. I had asked about the gradle script in the kotlin folder. I mean, my-java-library.gradle.kts file. Here, you wrote the Kotlin plugin declaration in the plugins code block without specifying the version number. Like this, id("org.jetbrains.kotlin.jvm"), not like this, id("org.jetbrains.kotlin.jvm") version "1.8.20"
@@hsnclk1985 Yes that's what I am talking about. You have to use *id("org.jetbrains.kotlin.jvm")* (without version) inside *java-plugins/src/main/kotin/*.gradle.kts* files. Instead, you define a dependency to the Jar containing the plugin in *java-plugins/build.gradle.kts* . In this case: *dependencies { implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20") }*
@@jjohannes My confusion is gone. I really appreciate it.
Hi Jendrik! Thanks a lot for your videos! Besides the plugins {} DSL there is also a so-called "legacy" way to apply plugins with apply(plugin = "xxx"). Should I completely avoid this legacy DSL because it will be dropped in future Gradle versions, or it won't be dropped and tehere there are valid use cases for it? One use case I can think of is conditional application of plugins. For example, I want to apply JaCoCo plugin conditionally (unless requested with a property) to avoid classes instrumentation on each build. The legacy DSL allows me to apply plugin based on some if-condition, while it does not seem to be possible with a modern plugins {} DSL. Is my understanding correct and conditional application of plugins is a valid use case for using legacy 'apply(plugin = "xxx")' syntax?
Hi. This is a very good question/topic. There are (edge) cases where the "legacy" mechanism is useful. I don't think that it will ever be removed completely. In Kotlin DSL, it is mainly discouraged because you loose most of the DSL niceness by not getting accessors generated. In general, I would say the idiomatic approach is to always apply all plugins and then conditionally let plugins not do anything. However, if a plugin is not written in a way that supports such a "no-op" state, there may be no way to achieve this. For instance, in your example of the *jacoco* plugin, you could always apply the plugin and then deactivate the instrumentation conditionally. But, while that is possible I believe, there is no simple on/off syntax for that provided by the plugin. And then there are cases where there is no other option. For example, I recently updated a build to be compatible with the Configuration Cache (CC). But the build uses the *com.google.cloud.artifactregistry.gradle-plugin* which is not CC compatible (github.com/GoogleCloudPlatform/artifact-registry-maven-tools/issues/85). So we decided to run just the publishing builds with *--no-configuration-cache* as the plugin is not needed in daily development. However, as just applying the plugin already breaks CC, I ended up doing this: *if (!serviceOf<BuildFeatures>().configurationCache.active.get()) {* *apply(plugin = "com.google.cloud.artifactregistry.gradle-plugin")* *}*
This video is great, but can you explain how to publish the plugins themselves? I'm trying to run Arquillian integration tests on project 'server' that uses the convention plugin 'com.my.server' and it complains that the plugin can't be found in the repositories. So I added 'mavenLocal()' to the 'dependencyResolutionManagement' repositories in its settings file and now I want to publish the plugin there. The problem I'm having is that declaring any publishing configuration on the plugin itself will actually configure the project for publishing as you've shown, while I want to configure the plugin for publishing. I tried to do this configuration on the build file of the plugins project itself. One problem here is that now it publishes all the plugins, including the parent plugins project, and that they will all have the same version. Another is that I end up with the groupId being 'com.my.server' and the artifactId being 'com.my.server.gradle.plugin' (the file name is 'com.my.server.gradle'), so now I have duplication of the 'com.my' part. I'm confused as to the roles of the file name vs. the 'group = "com.my"' configuration. I would like to have the following: - the file name shouldn't have the 'com.my' part, just "server.gradle", if it's even possible and correct (I thought defining the group name to 'com.my' will do the trick) - the publishing location in the local maven repo will be to '.m2/repository/com/my/server' and not '.m2/repository/com/my/server/com.my.server.gradle.plugin' or just '.m2/repository/server', which are 2 results I'm getting as I play around with this - the plugin declarations of 'server' should use 'id "com.my.server" version "1"' and not 'id "server" version "1"' - be able to define the version of each of the convention plugins and not have them share the same version As you can see, I'm confused about where to declare things and what name gets copied to where when dealing with plugins. Would be very grateful for any help.
Hi @Omega3131, it's a bit hard for me to understand your full setup from this comment. So I share a few pointers that I hope can help you figure out the issues you are having: For using plugins from a repository, you should define the repository in *pluginManagement { repositories.maven(...) }* (not dependencyResolutionManagement, which is for repositories that contain libraries you want to use in your code). Also, instead of publishing to mavenLocal(), you can directly include the plugin build using *pluginManagement { includeBuild(...) }* See also my explanation here: ua-cam.com/video/Ajs8pTbg8as/v-deo.html I wrote this guide on Gradle plugins that shows how to write and publish them (using different languages). Maybe that helps to clarify some things: github.com/jjohannes/gradle-plugins-howto Regarding the group (com.my...): This can be confusing indeed. In Gradle, plugins have two things: (1) the *Plugin ID* and (2) The *group* + *name* coordinates when you publish. (1) is a specific Gradle plugins concept, (2) is the general identification of components in Maven repositories as explained in this video (technically, Gradle plugins are Jars with class files and metadata inside like any other Java library). When you use the Kotlin DSL to write plugins, the *file name* is the *Plugin ID* . The *group* is whatever you define in the *build.gradle.kts* of your plugins build. If you want different groups/versions, you need to split up your plugins into multiple Gradle (sub)projects. When you publish, one Jar can contain multiple plugins and then these all have the same "group" and "version". In addition, Gradle publishes a so-called "Marker" for each plugin. This is only a POM file. Here the group is computed by Gradle based on the plugin ID. This is so that Gradle can find plugins in repositories only by ID. E.g., if you write *plugins { id("my.published.plugin") version "1.0" }* Gradle can find the plugin in the repository although you never give it the "group" + "name" for the Jar file that contains the plugin. See: docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers The Markers *do not* contain the plugins. They only point Gradle to the Jar that contains plugins and several markers can point to the same Jar with multiple plugins inside.
@@jjohannesThanks! The naming really is confusing. I will try to create an issue on your repo with my setup.
Would you migrate from grrovy to Kotlin?
If it is not "too expensive" in your case, I would use/migrate to Kotlin DSL. In particular, when you use IntelliJ IDEA as primary IDE. The tool support (auto-completion, suggestions, early error detection) is much better. If you already use Groovy and want to stick with it for particular reasons, you can do that. It is "just" the language on top of Gradle's API. You can use all Gradle features in both languages. See also my video on the topic: ua-cam.com/video/pKsn2eZWQK0/v-deo.html
The gradle DependencyResolutionManagement doesn't have method named includeBuild. See docs.gradle.org/current/javadoc/org/gradle/api/initialization/resolve/DependencyResolutionManagement.html So I guess the includeBuild you use inside the lambda is in invocation on the scripts context object, which is of type Settings. How can this work?
I am going through your series, you seem to be teaching very modern practices, but I am trying to understand a Groovy based Gradle project that is a couple of years old. Do you still think learning these things is helpful?
Yes, I think learning the current best practices (which I show in my videos) is helpful to understand the fundamental workings of Gradle's systems. And as it is also a goal of my videos to explain these systems, they should be helpful for you. Many of these systems exist since Gradle 1. For the "basics" I would recommend watching at least Episodes 1-10 (you might not need to understand everything in detail). For generally reading "old" Groovy scripts, the background I explain on the Groovy and Kotlin DSLs in Episode 24 should be helpful: ua-cam.com/video/pKsn2eZWQK0/v-deo.html
My other question is, if I understand correctly what I read from the gradle documentation, when we apply the kotlin dsl plugin to the java-plugins subfolder within the my-build-logic folder, we are declaring that this project will have the kotlin source tree ( that is, src/main/kotlin). Am I right? Actually, when I applied the plugin and rebuilt the wrapper, I expected this source tree to be automatically created by the plugin. I think in the video you made this source directory manually (I mean, src/main/kotlin).
> when we apply the *kotlin-dsl* plugin to the *java-plugins* subfolder within the *my-build-logic* folder, we are declaring that this project will have the Kotlin source tree (that is, *src/main/kotlin* ). Am I right? Yes that is correct. However, you observed correctly that Gradle does not create *src/...* folders automatically. This is is not clearly shown in this video. What you get however, is that IntelliJ reads the information from Gradle that *src/main/kotlin* is a folder you might want to create. If you right-click and select *New > Directory* it will offer you to create *src/main/kotlin* among others with one click.
@@jjohannes Thank you, Jendrik. I understood
Thanks for sharing these.
Hi Jendrik, Do we need to create a separate wrapper file for the convention plugin we created in a separate folder (I mean inside in my-build-logic folder)?
No you don't. In the example, I would put the wrapper into the *my-project* folder. From there you can then import into IDEA or run builds. Things in *my-build-logic* will be included in these builds. As there is no separate Gradle process for the included builds, it will always use the same Gradle version you started the main build with. In a real-life project, it is common to have the project itself together with the wrapper in the root of a git repository. And then have the build-logic in some subfolder: for example *gradle/plugins* I.e. *settings.gradle.kts* is in the root and contains a *pluginsManagement { includeBuild("gradle/plugins") }*
@@jjohannes Thanks. If I understand correctly, the wrapper in the main project (I mean, my-project) compiles other projects via the include statement. And in this way, each project included in the main project has the same version.
Genius solution to an annoying problem!
Such an underrated channel.. Amazing work..Subbed!
I want to ask a simple question.. Is a Gradle block is equivalent to a class' instance or a function? And how are those blocks suddenly exist ? Do we need to add a specific plugin to use a specific Gradle block? And why some method can be called freely without adding the plugin such as the `from()` method .
Yes. Technically, each "block" is a Java function call that is executed during Gradle's "configuration phase" where Gradle builds up a model of your project in memory. Technically, this model are Java objects. Sometimes, the "blocks" do not look like function calls, but this is only because of "syntactic sugar" from Kotlin/Groovy. For example, the following are equivalent: *setGroup("org.example")* // working in Java *group = "org.examle"* // working in Groovy/Kotlin because you can use "=" for "set" methods *tasks.named("build", t -> t.dependsOn("myTask"))* // working in Java, second argument is a function call (lambda/closure) *tasks.named("build") { dependsOn("myTask") }* // working in Groovy/Kotlin because if last method argument is a lambda, you can use *{ }* notation The Java methods on the *Project* interface are always available: docs.gradle.org/current/javadoc/org/gradle/api/Project.html And methods on other interfaces your reach from there like *TaskContainer* or *Task* etc. Then there are additional methods added by plugins through extensions ( ua-cam.com/video/wrgyUKC7vOY/v-deo.htmlm29s ). Here Gradle uses code generation to create Kotlin extension functions ( kotlinlang.org/docs/extensions.html ) to dynamically add methods which do not exist on the Java interfaces. (In the case of the Groovy DSL something similar, it's done through reflection.) I explain this more in the videos on the DSLs and using Java to configure Gradle: - ua-cam.com/video/pKsn2eZWQK0/v-deo.html - ua-cam.com/video/XnVZdMROVG8/v-deo.html
Hopefully these code implementation are not deprecated by gradle. I will try this code when i am in front of my computer again. Atm, i am just using the application plugin with kotlin-jvm plugin to set the main-class attribute and package them into .jar. Never that i know about input and output task.
Thank you for your interest. You can finde the examples from all my videos in this GitHub repository: github.com/jjohannes/understanding-gradle Specifically, the example for this video is here: github.com/jjohannes/understanding-gradle/tree/main/06_Configuring_Task_Inputs_And_Outputs My goal is to show current and future-proof APIs and language constructs in all videos. Sometimes (not here) I show very recent additions in the second part of a video where it may happen that some detail changes in a new Gradle version. In these cases, I update the corresponding examples on GitHub. They always work with the latest Gradle release.
I see the in Gradle user guide a section about the 'jvm-test-suite' plugin. It looks like it's a wider concept than source sets for test source sets specifically. Is it relevant to the topic of this video? Does it merit its own video? Do you recommend it or is it one of those inventions one should stay away from, both in the classpath and modulepath context?
Test suites are convenience introduced recently to ease the setup of new "test sets". In most scenarios, I recommend to use them as they make custom test configuration more compact. You often can do in one line, what you would otherwise have to do in several places (source set, task, dependencies). I show this in detail in this video: ua-cam.com/video/7f_gBvGQN_0/v-deo.html That being said, it's a convenience on top of otherwise public APIs. So you can do everything without it. In the context of this video, you need to use them if you also want to use *org.gradlex.java-module-testing* to ease the Module Path setup. That plugin builds on top of the test suite APIs.
Very well Explained sir...
I would be very thankful if you would anserw to my question. I cannot find the anserw on documention nor stackoverflow. Let's say I use publicated testFixtures created in project A in another project B. And in testFixtures resources directory I put application.properties file. I see that this properties file from testFixtures override application.properties from test directory of project B when running an @SpringBootTest (integration) test. It's fine for me but do You know why it works like that?
I don't know how Spring loads the *application.properties* internally. But I assume that it use the *classpath* to find the file in it. If you have multiple *application.properties* files which are not inside a package (in subfolders of resources), Java's mechanism to load resources from the classpath will return the first file it finds. The others can't be accessed at all. Which file is the first, depends on how things are ordered by Gradle when the tests are executed. In your example, the file from "testFixtures A" probably is found first. In general, you should avoid having multiple files with the same name in the same location on the classpath so that the behavior of the application does not depend on classpath order. I made a video on that topic: ua-cam.com/video/KocTqF0hO_8/v-deo.html
Thank You for the video ;)
What did the modularity gradle plugin do? From what I know now, it isn't needed anymore since some more recent gradle version, but would be nice to know how it fit into all of this.
The *org.javamodularity.moduleplugin* was started by the community before there was any support for the Module System in Gradle itself in Gradle 3, 4, 5 or 6. It re-configures all the tasks that require special treatment (JavaCompile, Test, JavaDoc, Run) to work with the *--module-path* . As Gradle 7 or 8 has *--module-path* support built-in (since 6.4) the plugin is not needed anymore in general. It has some configuration options that Gradle itself does not offer. Maybe one of these is a reason to still use it. But it requires you to turn off Gradle's built-in support and thus does not work well together with other plugins in this area - like the GradleX plugins (github.com/gradlex-org/extra-java-module-info) I am working on. From my experience by now, in most cases it only makes sense to use the Module System, if you fully use it as originally intended. Which means all Jars that you use have a *module-info.java* - and if you need Jars that do not have that, then you patch them. This is what the *extra-java-module-info* does. The *org.javamodularity.moduleplugin* does not have such functionality.
I get very confused with these tutorials. Because what you do in video not reflect the files content. But I have many difficults to understand gradle...
Sorry if you are having trouble following my videos. If the stile does not suite you, here are some other resources you could check out: - www.youtube.com/@TomGregoryTech has many good videos on Gradle from a possibly more "beginner friendly" perspective - Gradle Inc. sometimes offers free online courses: gradle.org/courses - I have a course on Udemy, that covers the beginner topics with more details and slower pace: www.udemy.com/course/modern-gradle-fundamentals/?referralCode=3AC1B96C0EBE6F4FBE6E My Udemy course is not free but there are often discounts. If you are genuinely interested, but can't afford it, please drop me a mail. Regarding the samples on GitHub (github.com/jjohannes/understanding-gradle/tree/main/05_Lifecycle_Tasks). They usually cover what I show in the video. Due to the dynamics of the video (I sometimes show alternative approaches) they may differ a bit or include alternatives in commented-out sections. They also may include additional files so that they make up a complete Gradle project. For each of these files, there is a video explaining them - in particular Episodes 1, 2 and 3 (settings file, build files, plugins). In some cases I extended the examples later after good discussions in these comments. In this case, I added the "lifecycleTasks" task after @Blundelll asked "How can you tell if a task is a Lifecycle task or an Actionable task?" (see the corresponding comment).
:business-logic:main: Could not resolve project :data-model. Correct?
Are you getting this error when importing the following project in an IDE? github.com/jjohannes/understanding-gradle/tree/main/02_The_Build_Files/my-project Could you share more details about what you are trying to achieve? I haven't seen this error in connection with the example from this video yet.
Hello Jendrik. Thank you for such a great content about Gradle! I am wondering - is it possible to resolve all the dependencies from all "configurations" with one single command? In other words: is there an alternative to `npm install`/`bundle install`/`go mod download` (which exist in NodeJs/Ruby/Go build tools correspondingly)? I miss that command when I have experiments with "dockerized" builds (I am aware of buildpacks and jib, but sometimes I need to get pure Docker/docker-compose setup for troubleshooting). Unfortunately, it seems like `gradlew :dependencies` downloads only pom files (which is enough to build the graph). I am looking for the command which would download "all at once", which would allow further dockerized steps to not make any remote calls and work much faster in "offline mode".
Hi Kirill. Thank you. That's a good question. You are right that ":dependencies" only looks at the metadata. It's a common point of confusion that the dependency resolution is done in two phases: (1) looking at metadata and resolving conflicts (2) downloading the actual artifacts (if needed). I plan to do another video on that topic at some point. To your question: There is no built-in task in Gradle that "resolves all configurations". And I think this is one of the topics where no consensus has been reached yet in the community what the best solution is. You probably also do not need to resolve "all" configurations but only the ones that you actually use (which would be "*RuntimeClasspath" and "*CompileClasspath" in a standard Java project). Two solutions I would probably use: 1. Run a build that would do everything the real build does in Dry Run mode: *gradle build --dry-run* As this computes the inputs of all tasks, it also resolves the dependencies. Unfortunately --dry-run is broken for composite builds (github.com/gradle/gradle/issues/2517). 2. Write your own task that does the resolving. It should be enough to have an "empty" task that has the configurations it needs as input. E.g.: *tasks.register("resolveAll") {* *configurations.forEach {* *if (it.isCanBeResolved) {* // or just selected configurations *inputs.files(it)* *}* *}* *doLast { }* // Have at least on action to trigger the download *}*
The (Gradle) world would be lost without your videos! 🙂 Thanks for going the extra mile and explaining things to us in detail.
I tried using tasks.named("wrapper") { ... } to configure the wrapper task, but got an error. The way it's working is: task wrapper(type: Wrapper) { ... } What syntax is this? I'm using Groovy DSL BTW.
The approach looks correct. Which error did you get? If it is only an issue in the IDE, you can also define the type of the task as part of 'named': *tasks.named('wrapper', Wrapper) { ... }* *task wrapper(type: Wrapper) { ... }* is one of the "convenience" notations introduced in Gradle 1 for Groovy DSL. It's likely to be deprecated in the future. It is the same as doing *task.getByName('wrapper', Wrapper) { ... }* or (if the task does not exist) *task.create('wrapper', Wrapper) { ... }* . In your case, it might create the task if it does not yet exists for whatever reason, but that's probably not what you want. Which is one of the reasons I think this notation is a bit "too magical".
@@jjohannesUnderstood, thanks! The problems was that I was doing this under a subproject configuration instead of the parent.
Thank you for this great video!
How should I exclude dependencies from libs.versions.toml file?
The *libs.versions.toml* file is a Catalog, which means - put simply - it is only a collection of Strings that you can conveniently access in all your *build.gradle(.kts)* files. If you want to exclude a dependency - lets say *org.example:foo* - you could add this coordinates String to the catalog: *[libraries]* *foo={module="org.example:foo"}* Then you can refer to that via *libs.foo* somewhere in your build files to exclude the dependency. Note that the notation for this is a bit complicated right now. See this feature request: github.com/gradle/gradle/issues/20274 dependencies { implementation(libs.foo) { exclude(group = libs.bar.get().group, module = libs.bar.get().name) } } In general, instead of using *exclude()* you may also consider using a *component metadata rule* to remove a dependency globally: ua-cam.com/video/5g20kbbqBFk/v-deo.htmlm18s
Thank you for this great video. Configuring scanConfigurations = [configurations.runtimeClasspath.name] raises "Could not get unknown property 'runtimeClasspath' for configuration container of type org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer." in my Android application project with multiple product flavors. Any hint how to fix this?
In Android projects, you have different runtime classpaths - one for each BuildType+Flavor combination. A single *runtimeClasspath* (as in plain Java projects) does not exist. That's what the (quite misleading) error message means to say. You could use, for example, *releaseRuntimeClasspath* and maybe also add the corresponding classpaths for other flavors to the list. If you run *gradle :app:dependencies* it should show you all the existing Configurations which you can use to figure out which RuntimeClasspaths your have. dependencyCheck { scanConfigurations = listOf("releaseRuntimeClasspath") } In a working example: github.com/jjohannes/gradle-project-setup-howto/blob/android/gradle/plugins/android-application-plugins/src/main/kotlin/org.example.android-application.gradle.kts#L43-L46
@@jjohannes Thank you. I configured the scanConfiguration by hardcoding one of my product flavor, e.g. "orange". This isn't optimal for maintenance - it could just pick the first. dependencyCheck { scanConfigurations = ["orangeReleaseRuntimeClasspath"] nvd { apiKey = "some-key" } } This made the "An NVD API Key was not provided - it is highly recommended to use an NVD API key as the update can take a VERY long time without an API Key" disappear but still the "dependencyCheckAnalyze" task runs for 30 minutes already without progress. Is it working for you? Btw. my project is open source if you want to try yourself.
@@tobiaspreuss I suspect this to be an environment issue. I remember that I sometimes had issues that it took long time on the first run, because it is downloading a lot of data from the CVE database. And the connection was not very stable sometimes. You can try setting "autoUpdate = false" temporarily to see if that speed things up. But you probably do not want that permanently. TBH I have not actively (re)configured the plugin for some time now. There are quite a lot of options.
@@jjohannes Thanks. Yes with "autoUpdate = false" the task finished successfully.
Hey Jesper - your videos are by far the best information about Gradle on the internet next to the official docs. Really awesome! I have another idea for a video, or as part of one: Custom Gradle distributions. What they are, pros and cons, how they relate to convention plugins, etc.
Thank you for the feedback and the suggestion! I put the topic "Custom Gradle Distributions" on my list.
I am not a hardcore java or kotlin developer. But, why did you package all your `.class` file into `.zip` why not package all the `.jar` file with the consumer code into .jar , can it still be run if it is inside .zip archive file.
That's a large question. :) I haven't really done anything on this topic in my videos. But it is an interesting topic, which is now on my list for future episodes! I think there was always the idea that a *JAR* is a ready-to-use software component that you can plug into your software without modifying it. It contains, for example, all meta information (like license information) and may be signed. Which are all interesting aspects if you use 3rd party components (like open source components from Maven Central). With the Java Module System (see ua-cam.com/video/X9u1taDwLSA/v-deo.html) this idea became even stronger. Now each Jar has meta-information (module-info.class) that is actively used by the Java runtime. With this in mind, it seems natural that all Jars that make up an application are delivered as they are. Without the Java Module System however, it is also a common approach to re-package everything into one *JAR* . In Gradle, the Shadow Plugin (imperceptiblethoughts.com/shadow) is quite popular that offers some convenient functionality to make sure no important metadata is lost when combining Jars. And many framework specific plugins - like Spring Boot or Micronaut - also come with their own packaging support. But in the end, there are many options to do this and my impression is that there is not the "one right way" how to package a Java-based application at the moment. With the Java Module System, the Java tools themselves contain the *jlink* and *jpackage* (docs.oracle.com/en/java/javase/21/jpackage/packaging-overview.html) commands. These are for creating a package (basically again a ZIP or an Installer) which contains a complete application for a certain Operating System - including the Java runtime. I think this is where things are heading on the long run.
@@jjohannes so basically if my code require 2 or more dependencies that I will use them in the source code, in which they came in the form of 2 separate .jar files. If it needs to be packaged for release distribution to the user it needs to be repackaged again into single .jar file otherwise it will be called fat jar if not mistaken. But the manifest file is important too, it cannot be lost because it specify the package name of that dependency. If the two jars, need to be ripped apart and merge again together the package name will change. But for now I think I want to learn how to write the `run` task for debugging. Otherwise I have to specify manually the `classpath` to the JVM. Only just recently I know that the consumer code i.e the .class code that contains the main class can be linked directly by using class path parameter. Meaning it doesn't have to be in a .jar file. And only recently, I know the `application` gradle plugin is useful to set the `Main-class` attribute for the .jar file.
Starting with Gradle 8.3, the *Configuration Cache* has been promoted to a stable feature. You can now turn it on in *gradle.properties* by setting *org.gradle.configuration-cache=true* Documentation on the Configuration Cache's performance impacts: docs.gradle.org/current/userguide/configuration_cache.html#config_cache:intro:performance_improvements
Starting with Gradle 8.3, the *Configuration Cache* has been promoted to a stable feature. You can turn it on in *gradle.properties* by setting *org.gradle.configuration-cache=true* When you turn it on, Gradle will run tasks in parallel if the task graph allows it - even if the tasks are defined in the same subproject. Thus, it adds even more parallelism than 'org.gradle.parallel=true'. Video on caching: ua-cam.com/video/nHb0kIcTrFE/v-deo.html Documentation on the Configuration Cache's performance impacts: docs.gradle.org/current/userguide/configuration_cache.html#config_cache:intro:performance_improvements
We published a GradleX plugin - *id("org.gradlex.java-ecosystem-capabilities")* - that adds Capability information for widely-used libraries that do not publish this information yet in their metadata. With the plugin applied, Gradle is able to detect more conflicts automatically as described in the video. Give it a try! github.com/gradlex-org/java-ecosystem-capabilities
Love your videos! Thank you! It would be nice to watch a video about gradle's phases like configuration, evaluation etc
Thank you! That's a good suggestion, which I did not have on my topics list yet. Might even be a good one for one of the next episodes.
@@jjohannes it would be extremely useful 😍