GitBucket
4.21.2
Toggle navigation
Snippets
Sign in
Files
Branches
1
Releases
Issues
Pull requests
Labels
Priorities
Milestones
Wiki
Forks
mark.george
/
Wiki
Compare Revisions
View Page
Back to Page History
Gradle.md
### Contents - [Pass System.in to Application](#pass-systemin-to-application) - [Dump JVM and Gradle Details](#dump-jvm-and-gradle-details) - [Working With Multiple Source Sets](#working-with-multiple-source-sets) - [Dependency Version Number Wildcards](#dependency-version-number-wildcards) - [Copy Dependencies](#copy-dependencies) - [Building Generated Code](#building-generated-code) - [Source/Target Versions](#sourcetarget-versions) - [Fat JAR](#fat-jar) - [Adding Dependencies to Manifest Classpath](#adding-dependencies-to-manifest-classpath) - [Managing Source Set Directories](#managing-source-set-directories) - [Preventing Tests from Running](#preventing-tests-from-running) - [Parallel Execution of JUnit Tests](#parallel-execution-of-junit-tests) - [Log output streams when running tests](#log-output-streams-when-running-tests) - [Opening project directories](#opening-project-directories) - [Generating .gitignore](#generating-.gitignore) - [Making NetBeans + Web Application + Embedded Jetty + Gradle play nicely](#Making NetBeans-+-Web-Application-+-Embedded-Jetty-+-Gradle-play-nicely) ### Pass System.in to Application ```groovy run { standardInput = System.in } ``` ### Dump JVM and Gradle Details In case you are not sure what versions of things are being used: ```groovy tasks.register("info") { description = "Show version and location information for Java, Gradle, and project."; doLast { println "Java Version: " + org.gradle.internal.jvm.Jvm.current(); println "Java Home: " + org.gradle.internal.jvm.Jvm.current().getJavaHome(); println "Gradle Version: ${gradle.gradleVersion}"; println "Gradle Home: ${gradle.gradleHomeDir}"; println "Gradle User Home: ${gradle.gradleUserHomeDir}"; println "Project Location: ${projectDir}"; println "Project Build Location: ${buildDir}"; } } ``` Run using: ```sh gradle jvm ``` ### Working With Multiple Source Sets All source sets are broken into two sub-trees — one called `java` which can only contain Java code, and one called `resources` for everything else. There are a couple of default source sets — `main` for the main code and `test` for the unit test code. To add a new source set: ```groovy sourceSets { // create a new source set shinyStuff { java { srcDir 'path to source dir' } } } ``` New source sets will automatically get the default `java` and `resources` sub-trees unless they are explicitly excluded: ``` sourceSets { web { resources { srcDirs = ['public'] } java{ srcDirs = [] } } } ``` This will prevent the `java` tree from being used for this source set, and also prevent the default `resource` tree from being used in addition to the new `public` one. Source sets do not see each other by default (one can't use code from another). To make this happen: ```groovy sourceSets { shinyStuff // this is all we need if source dir is in the root of the project and named shinyStuff main { // make compiled shinyStuff visible to main compileClasspath += shinyStuff.output runtimeClasspath += shinyStuff.output // make shinyStuff dependencies also visible to main source set compileClasspath += shinyStuff.compileClasspath } } ``` Dependencies are tied to source sets and prefixed with the source set name: ```groovy dependencies { // dependencies for shinyStuff shinyStuffImplementation '...' shinyStuffImplementation '...' // dependencies for main source set implementation '...' implementation '...' } ``` Alternately you can nest the dependencies with the source sets, and you don't need to prefix anymore: ```groovy sourceSets { shinyStuff { dependencies { implementation '...' } } } ``` **Note** : Only resources in the `main` source set are processed by Gradle — resources in other source sets will not be included in the build output or the generated JAR file by default. Add the following to make this happen: ```groovy processResources { // resources outside of the default 'main' sourceSet are not processed by default from(project.sourceSets.web.resources) { include "**/*.*" } } ``` If you just need an additional resources directory, then you can add a directory to the existing main source set as follows: ```groovy sourceSets { main { resources { srcDirs += ['static'] } } } ``` ### Dependency Version Number Wildcards Sometimes it is handy to just get the latest versions of everything (saves us from having to look up the version numbers manually). You can use the '+' operator as a version wild-card: ```groovy dependencies { def dorisVer='+' // use latest version of doris def borisVer='1+' // use latest 1.x version of boris implementation group: 'com.some.where', name: 'doris', version: dorisVer implementation group: 'com.who.cares', name: 'boris', version: borisVer } ``` ### Copy Dependencies Sometimes it is handy to have actual copies of the JAR files somewhere. The following task will copy the JARs for declared dependencies into the `libs` folder: ``` task copyLibs(type: Copy) { project.configurations.implementation.setCanBeResolved(true) from configurations.implementation into 'libs' } ``` ### Building Generated Code If you are using a task that generates code, then you will probably want the standard build to run the generate task before the `compileJava` task. The can be useful for resolving chicken and egg build problems in addition to removing additional steps that need to be performed to build the project: ``` compileJava.dependsOn tasks.<taskNameGoesHere> ``` This goes at the root level. The `tasks` prefix is there to resolve problems where you are using plugins that have extensions and tasks that have the same name (such as the OpenAPI Generator Gradle plugin). ### Source/Target Versions If you are building with a later version of Java, but want to build binaries that run on older versions: ``` compileJava { sourceCompatibility = '1.8' targetCompatibility = '1.8' } ``` ### Fat JAR Create a JAR that includes the dependencies. This does't always work since some dependencies may be sealed, or otherwise protected. The contents of the individual JAR files will be extracted and added to the final JAR along side the classes for your project. This results in a single self-contained JAR file. ``` task fatJAR(type: Jar) { project.configurations.implementation.setCanBeResolved(true) manifest.from jar.manifest baseName = 'fat' from { configurations.implementation.collect { duplicatesStrategy = 'exclude' it.isDirectory() ? it : zipTree(it) } } destinationDir = file('dist') with jar } ``` ### Adding Dependencies to Manifest Classpath As an alternative to creating a fat JAR, the dependencies can be added to the manifest's classpath. This produces a JAR that is runnable without the need for a script that sets the classpath. ``` jar { project.configurations.implementation.setCanBeResolved(true) manifest { attributes ( 'Main-Class': mainClassName, 'Class-Path': configurations.implementation.collect { 'lib/' + it.name }.join(' ') ) } } ``` This code with assumes the dependencies are in a subdirectory named `libs` relative to the primary JAR. ### Managing Source Set Directories Being able to create and remove a project's source set directories on the fly is very handy, and it is annoying that Gradle doesn't have built-in tasks for this already. Creating missing source folders: ``` tasks.register("createMissingSourceDirs") { group = "Project"; description = "Create all of the missing source directories for this project."; doFirst { sourceSets.each { def sourceRoot -> sourceRoot.allSource.srcDirTrees.each { def sourceDir -> if(!sourceDir.dir.exists()) { println "Creating source ${sourceDir}"; mkdir sourceDir.dir; } } } } } ``` Removing empty source folders: ``` tasks.register("deleteEmptySourceDirs") { group = "Project"; description = "Delete empty source directories."; doFirst { sourceSets.each { def sourceRoot -> sourceRoot.allSource.srcDirTrees.each { def sourceDir -> if(sourceDir.dir.exists() && sourceDir.dir.isDirectory() && sourceDir.dir.list().length == 0) { println "Removing empty source ${sourceDir}"; sourceDir.dir.delete(); } } } } } ``` ### Preventing Tests from Running You can sometimes end up with chicken and egg situations if the tests are run as part of the standard build (which is the default). In this case you can stop this by adding the following to the `test` task: ``` test { useJUnitPlatform() onlyIf { project.gradle.startParameter.taskNames.contains("test") } } ``` This will stop the tests from being executed unless you explicitly run the `test` task. ### Parallel Execution of JUnit Tests Note that this will likely cause non-deterministic test results unless you have actually created your module under test to handle concurrency. This is good for demonstrating non-determinism due to poorly implemented concurrency though. ``` test { useJUnitPlatform() systemProperty("junit.jupiter.execution.parallel.enabled", true) systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") } ``` ### Log output streams when running tests If you want to see stdout or stderr output streams, you need to add the following to the test task: ``` test { useJUnitPlatform(); testLogging { showStandardStreams = true; } } ``` ### Opening project directories If you are using an IDE or editor that lacks an "open project in file manager" feature then you can add this via a Gradle task: ```groovy tasks.register("openProjectDir") { group = "Project"; description = "Open the project directory in the system file manager."; doFirst { println("Opening: " + file(projectDir)); java.awt.Desktop.getDesktop().open(file(projectDir)); } } ``` ### Generating .gitignore ```groovy tasks.register("createGitIgnore") { group = "Project"; description = "Create the project's .gitignore file."; def gitIgnored=""" .gradle/ .nb-gradle/ .settings/ nbproject/ build/ bin/ dist/ tmp/ .classpath .project *.zip *.tgz *.tar.gz *.class *.jar .DS_Store !gradle-wrapper.jar """; doLast { def file = new File(projectDir, ".gitignore"); if ( !file.exists() ) { println("Creating .gitignore"); gitIgnored.lines().each { f -> if(!f.trim().isBlank()) { file.append(f.trim()+"\n"); } } } else { println(".gitignore already exists"); } } } ``` ### Making NetBeans + Web Application + Embedded Jetty + Gradle play nicely If you have a project that NetBeans recognises as a web application (has the typical JEE web project layout) then NetBeans will run the `war` task when you run the project. Since the embedded server is launched from a `main` method, this is not very useful. We want the normal `run` task to be called when the project is run so that the embedded server is started via the `main` method. This is fixable via the `build.gradle` by altering the dependency chain: 1. We are going to hijack the `war` task to make it call `run`, so we need a replacement task for building the WAR file. This is only necessary if we want to produce a WAR file — if we don't need a WAR file then we don't need this task since we only really care about the exploded WAR directory for deploying to Jetty: ``` tasks.register("buildWar") { dependsOn war; } ``` 1. Make the `war` task call `run` so that the embedded server will be started when you run the project. Only allow the `war` task itself to be executed if the starting task was `buildWar`. Note that the `run` dependency will still be executed when tasks other than `buildWar` trigger the task — the `dependsOn` tasks aren't affected by the `onlyIf`. ``` war { onlyIf { project.gradle.startParameter.taskNames.contains("buildWar") }; dependsOn run; } ``` 1. Add a task that will copy the exploded WAR into the Jetty deploy location: ``` tasks.register("deployWar", Sync) { into "${buildDir}/deploy"; with war; } ``` 1. Make the `run` task call `deployWar` so that the application is deployed to Jetty prior to the embedded server being launched. We use the `onlyIf` to ensure that we only run the embedded server if the `war` or `run` tasks are the starting tasks (since both `buildWar` and `build` call `war` which calls `run`): ``` run { onlyIf { project.gradle.startParameter.taskNames.intersect(["war","run"]) }; dependsOn deployWar; } ``` The `buildWar` task can be used to produce the WAR file. The `war` task will now call `run` which in turn deploys the exploded JAR. To make the *Run File* feature of NetBeans work with this configuration, you need to modify the build action for the `run.single` task so that it calls `deployWar` before calling `runSingle`: 1. Open the project properties and select Build > Build Actions. 1. Select `run.single` in the *Configure Action* combo box. 1. In the *Arguments* text box, find where the `runSingle` task is being called and add `deployWar` just before it. This adds a `gradle.properties` file to the project with the above change.
### Contents - [Pass System.in to Application](#pass-systemin-to-application) - [Dump JVM and Gradle Details](#dump-jvm-and-gradle-details) - [Working With Multiple Source Sets](#working-with-multiple-source-sets) - [Dependency Version Number Wildcards](#dependency-version-number-wildcards) - [Copy Dependencies](#copy-dependencies) - [Building Generated Code](#building-generated-code) - [Source/Target Versions](#sourcetarget-versions) - [Fat JAR](#fat-jar) - [Adding Dependencies to Manifest Classpath](#adding-dependencies-to-manifest-classpath) - [Managing Source Set Directories](#managing-source-set-directories) - [Preventing Tests from Running](#preventing-tests-from-running) - [Parallel Execution of JUnit Tests](#parallel-execution-of-junit-tests) - [Log output streams when running tests](#log-output-streams-when-running-tests) - [Opening project directories](#opening-project-directories) - [Generating .gitignore](#generating-.gitignore) - [Making NetBeans + Web Application + Embedded Jetty + Gradle play nicely](#Making NetBeans-+-Web-Application-+-Embedded-Jetty-+-Gradle-play-nicely) ### Pass System.in to Application ```groovy run { standardInput = System.in } ``` ### Dump JVM and Gradle Details In case you are sure what versions of things are being used: ```groovy tasks.register("info") { description = "Show version and location information for Java, Gradle, and project."; doLast { println "Java Version: " + org.gradle.internal.jvm.Jvm.current(); println "Java Home: " + org.gradle.internal.jvm.Jvm.current().getJavaHome(); println "Gradle Version: ${gradle.gradleVersion}"; println "Gradle Home: ${gradle.gradleHomeDir}"; println "Gradle User Home: ${gradle.gradleUserHomeDir}"; println "Project Location: ${projectDir}"; println "Project Build Location: ${buildDir}"; } } ``` Run using: ```sh gradle jvm ``` ### Working With Multiple Source Sets All source sets are broken into two sub-trees — one called `java` which can only contain Java code, and one called `resources` for everything else. There are a couple of default source sets — `main` for the main code and `test` for the unit test code. To add a new source set: ```groovy sourceSets { // create a new source set shinyStuff { java { srcDir 'path to source dir' } } } ``` New source sets will automatically get the default `java` and `resources` sub-trees unless they are explicitly excluded: ``` sourceSets { web { resources { srcDirs = ['public'] } java{ srcDirs = [] } } } ``` This will prevent the `java` tree from being used for this source set, and also prevent the default `resource` tree from being used in addition to the new `public` one. Source sets do not see each other by default (one can't use code from another). To make this happen: ```groovy sourceSets { shinyStuff // this is all we need if source dir is in the root of the project and named shinyStuff main { // make compiled shinyStuff visible to main compileClasspath += shinyStuff.output runtimeClasspath += shinyStuff.output // make shinyStuff dependencies also visible to main source set compileClasspath += shinyStuff.compileClasspath } } ``` Dependencies are tied to source sets and prefixed with the source set name: ```groovy dependencies { // dependencies for shinyStuff shinyStuffImplementation '...' shinyStuffImplementation '...' // dependencies for main source set implementation '...' implementation '...' } ``` Alternately you can nest the dependencies with the source sets, and you don't need to prefix anymore: ```groovy sourceSets { shinyStuff { dependencies { implementation '...' } } } ``` **Note** : Only resources in the `main` source set are processed by Gradle — resources in other source sets will not be included in the build output or the generated JAR file by default. Add the following to make this happen: ```groovy processResources { // resources outside of the default 'main' sourceSet are not processed by default from(project.sourceSets.web.resources) { include "**/*.*" } } ``` If you just need an additional resources directory, then you can add a directory to the existing main source set as follows: ```groovy sourceSets { main { resources { srcDirs += ['static'] } } } ``` ### Dependency Version Number Wildcards Sometimes it is handy to just get the latest versions of everything (saves us from having to look up the version numbers manually). You can use the '+' operator as a version wild-card: ```groovy dependencies { def dorisVer='+' // use latest version of doris def borisVer='1+' // use latest 1.x version of boris implementation group: 'com.some.where', name: 'doris', version: dorisVer implementation group: 'com.who.cares', name: 'boris', version: borisVer } ``` ### Copy Dependencies Sometimes it is handy to have actual copies of the JAR files somewhere. The following task will copy the JARs for declared dependencies into the `libs` folder: ``` task copyLibs(type: Copy) { project.configurations.implementation.setCanBeResolved(true) from configurations.implementation into 'libs' } ``` ### Building Generated Code If you are using a task that generates code, then you will probably want the standard build to run the generate task before the `compileJava` task. The can be useful for resolving chicken and egg build problems in addition to removing additional steps that need to be performed to build the project: ``` compileJava.dependsOn tasks.<taskNameGoesHere> ``` This goes at the root level. The `tasks` prefix is there to resolve problems where you are using plugins that have extensions and tasks that have the same name (such as the OpenAPI Generator Gradle plugin). ### Source/Target Versions If you are building with a later version of Java, but want to build binaries that run on older versions: ``` compileJava { sourceCompatibility = '1.8' targetCompatibility = '1.8' } ``` ### Fat JAR Create a JAR that includes the dependencies. This does't always work since some dependencies may be sealed, or otherwise protected. The contents of the individual JAR files will be extracted and added to the final JAR along side the classes for your project. This results in a single self-contained JAR file. ``` task fatJAR(type: Jar) { project.configurations.implementation.setCanBeResolved(true) manifest.from jar.manifest baseName = 'fat' from { configurations.implementation.collect { duplicatesStrategy = 'exclude' it.isDirectory() ? it : zipTree(it) } } destinationDir = file('dist') with jar } ``` ### Adding Dependencies to Manifest Classpath As an alternative to creating a fat JAR, the dependencies can be added to the manifest's classpath. This produces a JAR that is runnable without the need for a script that sets the classpath. ``` jar { project.configurations.implementation.setCanBeResolved(true) manifest { attributes ( 'Main-Class': mainClassName, 'Class-Path': configurations.implementation.collect { 'lib/' + it.name }.join(' ') ) } } ``` This code with assumes the dependencies are in a subdirectory named `libs` relative to the primary JAR. ### Managing Source Set Directories Being able to create and remove a project's source set directories on the fly is very handy, and it is annoying that Gradle doesn't have built-in tasks for this already. Creating missing source folders: ``` tasks.register("createMissingSourceDirs") { group = "Project"; description = "Create all of the missing source directories for this project."; doFirst { sourceSets.each { def sourceRoot -> sourceRoot.allSource.srcDirTrees.each { def sourceDir -> if(!sourceDir.dir.exists()) { println "Creating source ${sourceDir}"; mkdir sourceDir.dir; } } } } } ``` Removing empty source folders: ``` tasks.register("deleteEmptySourceDirs") { group = "Project"; description = "Delete empty source directories."; doFirst { sourceSets.each { def sourceRoot -> sourceRoot.allSource.srcDirTrees.each { def sourceDir -> if(sourceDir.dir.exists() && sourceDir.dir.isDirectory() && sourceDir.dir.list().length == 0) { println "Removing empty source ${sourceDir}"; sourceDir.dir.delete(); } } } } } ``` ### Preventing Tests from Running You can sometimes end up with chicken and egg situations if the tests are run as part of the standard build (which is the default). In this case you can stop this by adding the following to the `test` task: ``` test { useJUnitPlatform() onlyIf { project.gradle.startParameter.taskNames.contains("test") } } ``` This will stop the tests from being executed unless you explicitly run the `test` task. ### Parallel Execution of JUnit Tests Note that this will likely cause non-deterministic test results unless you have actually created your module under test to handle concurrency. This is good for demonstrating non-determinism due to poorly implemented concurrency though. ``` test { useJUnitPlatform() systemProperty("junit.jupiter.execution.parallel.enabled", true) systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") } ``` ### Log output streams when running tests If you want to see stdout or stderr output streams, you need to add the following to the test task: ``` test { useJUnitPlatform(); testLogging { showStandardStreams = true; } } ``` ### Opening project directories If you are using an IDE or editor that lacks an "open project in file manager" feature then you can add this via a Gradle task: ```groovy tasks.register("openProjectDir") { group = "Project"; description = "Open the project directory in the system file manager."; doFirst { println("Opening: " + file(projectDir)); java.awt.Desktop.getDesktop().open(file(projectDir)); } } ``` ### Generating .gitignore ```groovy tasks.register("createGitIgnore") { group = "Project"; description = "Create the project's .gitignore file."; def gitIgnored=""" .gradle/ .nb-gradle/ .settings/ nbproject/ build/ bin/ dist/ tmp/ .classpath .project *.zip *.tgz *.tar.gz *.class *.jar .DS_Store !gradle-wrapper.jar """; doLast { def file = new File(projectDir, ".gitignore"); if ( !file.exists() ) { println("Creating .gitignore"); gitIgnored.lines().each { f -> if(!f.trim().isBlank()) { file.append(f.trim()+"\n"); } } } else { println(".gitignore already exists"); } } } ``` ### Making NetBeans + Web Application + Embedded Jetty + Gradle play nicely If you have a project that NetBeans recognises as a web application (has the typical JEE web project layout) then NetBeans will run the `war` task when you run the project. Since the embedded server is launched from a `main` method, this is not very useful. We want the normal `run` task to be called when the project is run so that the embedded server is started via the `main` method. This is fixable via the `build.gradle` by altering the dependency chain: 1. We are going to hijack the `war` task to make it call `run`, so we need a replacement task for building the WAR file. This is only necessary if we want to produce a WAR file — if we don't need a WAR file then we don't need this task since we only really care about the exploded WAR directory for deploying to Jetty: ``` tasks.register("buildWar") { dependsOn war; } ``` 1. Make the `war` task call `run` so that the embedded server will be started when you run the project. Only allow the `war` task itself to be executed if the starting task was `buildWar`. Note that the `run` dependency will still be executed when tasks other than `buildWar` trigger the task — the `dependsOn` tasks aren't affected by the `onlyIf`. ``` war { onlyIf { project.gradle.startParameter.taskNames.contains("buildWar") }; dependsOn run; } ``` 1. Add a task that will copy the exploded WAR into the Jetty deploy location: ``` tasks.register("deployWar", Sync) { into "${buildDir}/deploy"; with war; } ``` 1. Make the `run` task call `deployWar` so that the application is deployed to Jetty prior to the embedded server being launched. We use the `onlyIf` to ensure that we only run the embedded server if the `war` or `run` tasks are the starting tasks (since both `buildWar` and `build` call `war` which calls `run`): ``` run { onlyIf { project.gradle.startParameter.taskNames.intersect(["war","run"]) }; dependsOn deployWar; } ``` The `buildWar` task can be used to produce the WAR file. The `war` task will now call `run` which in turn deploys the exploded JAR. To make the *Run File* feature of NetBeans work with this configuration, you need to modify the build action for the `run.single` task so that it calls `deployWar` before calling `runSingle`: 1. Open the project properties and select Build > Build Actions. 1. Select `run.single` in the *Configure Action* combo box. 1. In the *Arguments* text box, find where the `runSingle` task is being called and add `deployWar` just before it. This adds a `gradle.properties` file to the project with the above change.