run { standardInput = System.in }
In case you are not sure what versions of things are being used:
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:
gradle jvm
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:
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:
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:
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:
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:
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:
sourceSets { main { resources { srcDirs += ['static'] } } }
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:
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 }
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' }
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).
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' }
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 }
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.
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(); } } } } }
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.
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") }
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; } }
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:
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)); } }
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"); } } }
Allows a default main class (via application
plugin) that can be overridden from the command line:
def defaultMainClassName = "mypackage.Main"; application { mainClass = project.hasProperty("mainClass") ? project.getProperty("mainClass") : defaultMainClassName; }
Override at the command line using:
gradle run -PmainClass=newpackage.NewMain
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:
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; }
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; }
Add a task that will copy the exploded WAR into the Jetty deploy location:
tasks.register("deployWar", Sync) { into "${buildDir}/deploy"; with war; }
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
:
Open the project properties and select Build > Build Actions.
Select run.single
in the Configure Action combo box.
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.