I have a gradle project that contains a suit of tests, that can be launched with a command:
gradlew clean :mymodule:test
I need to build a jar, that can be executed in the same way and launch tests. I found instruction for creating a jar of test binaries here Creating a Jar of test binaries - Gradle.
task packageTests(type: Jar) {
classifier = 'tests'
from sourceSets.test.output
}
However the resulting jar contains only test classes and resources, no dependent libraries or main application source code.
How can I create an executable test jar?
If I were you, I wouldn't use classifier='tests' as that is usually reserved for a jar containing test classes without dependencies. Possibly uber-tests or fat-tests is better as uber-jar and fat-jar are common names given to this type of jar. You could do something like
task uberTestJar(type: Jar) {
dependsOn testClasses
classifier = 'uber-tests'
sourceSets.main.output.each {
from it
}
sourceSets.test.output.each {
from it
}
configurations.testRuntime.each { File f ->
if (f.name.endsWith('.jar')) {
from zipTree(f)
} else {
from f
}
}
}
There's also the shadow jar plugin
Related
I'm building a Java command line application using gradle and have it running when I use gradlew run, however I would like to generate a jar -- which I would assume I would then have users download to invoke the CLI.
However, when I run gradlew jar, nothing is produced (build/lib dir doesn't even exist) even though the build runs with no errors and finishes with BUILD_SUCCESSFUL.
Two questions:
Why is no jar being produced?
Is having users download a jar the best way to ship a CLI for Java?
Below is my full build.gradle.kts
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
application
id("com.diffplug.spotless") version "6.12.0"
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
// Use JUnit test framework.
testImplementation("junit:junit:4.13.2")
// This dependency is used by the application.
implementation("com.google.guava:guava:30.1-jre")
implementation("info.picocli:picocli:4.7.0")
annotationProcessor("info.picocli:picocli-codegen:4.7.0")
implementation("io.vavr:vavr:0.10.4")
}
application {
// Define the main class for the application.
mainClass.set("testlauncher.command.Runner")
}
subprojects {
apply {
plugin("com.diffplug.spotless")
}
}
spotless {
java {
importOrder()
removeUnusedImports()
googleJavaFormat()
}
}
project.tasks.findByName("build")?.dependsOn(project.tasks.findByName("spotlessApply"))
I'm dumb.
I thought the jar would be in ./build/libs but it's actually in ./app/build/libs.
I have a multi-module build with approx 100 modules. Is there a way to create jar files for only the changed modules and copy them to somewhere?
The task should be a standalone task without modifying existing jar tasks
So, assuming all your modules are simple java modules, all your modules will have a jar task (of type Jar) that build the JAR file of the respective module. Each jar task will only be executed, if the contents changed in any way (e.g. new files were compiled), thanks to Gradle incremental build feature.
You can simply add a doLast closure to each of these tasks, which copies the created file to your directory, because the closure will only be executed if and only if the task was executed:
Example for a single task:
jar {
doLast {
project.copy {
from archivePath
into 'path/to/my/location'
}
}
}
If this works for a single module, we can try a similar approach as what #lance-java did:
allprojects { project ->
project.tasks.withType(Jar).all { jar ->
jar.doLast {
project.copy {
from jar.archivePath
into 'path/to/my/location'
}
}
}
}
Assuming you have all your task inputs and outputs setup correctly, gradle supports up to date checking and task skipping out of the box. See up to date checks (aka incremental build)
For the copying, you could use a Sync task which will only copy (or delete) jars that are different from the target directory.
Eg
task syncJars(type: Sync) {
allprojects { p ->
from p.tasks.withType(Jar)
}
into 'path/to/target/dir'
}
Ihave a gradle project that has an automated test cases for a web application using selenium. All the tests and test cases are with in the package src/test/java. The tear down scripts (to clean up the entries in DB are written in src/main/java after test case execution). I tried to build a JAR using gradle for the same. I could get 2 different JAR's one that contains just the classes under src/main/java and the other containing src/test/java. Is there a way to build a single JAR that contains both? Let me know if there is an alternate way of doing the same.
task testJar(type: Jar) {
classifier = 'tests'
from sourceSets.test.output
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
testJar.execute()
}
Thank you!
I have the following project config (pure java):
/
/Base_module
/A_module
A_module depends on Base_module.
I want a .jar containing the A_module classes + the Base_module classes, but can't make it.
With the following config, I can only achieve different jars for each module:
settings.gradle
include 'Base_module', 'A_module'
build.gradle
....
project(':Base_module') {
}
project(':A_module') {
dependencies {
compile project(':Base_module')
}
}
....
What do I need to add to achieve the full .jar?
Thanks in advance.
You can try to make a custom task of Jar type in your root build script, which will include all the classes of all subprojects or just a number of subprojects. It could be something similar to:
//declare an array, containing subproject names, which classes you want to collect
def projectsToCollect = [':Base_Module',':A_Module']
//create a custom task, which assembles an jar-archive and depends on subproject compilation tasks,
//that causes sibprojects sources been compiled before thist task runs
task singleJar( type: Jar , dependsOn: projectsToCollect.collect{ it+":compileJava"}) {
//set new jar name
baseName = 'singleJar'
//set files, which will be included in this new jar
from files(projectsToCollect.collect{ project(it).sourceSets.main.output })
}
You can play it around, modifying sourcesets, if you have some custom in your subprojects, or if you want to add tests.
That would be fatJar aka uberJar aka shadowJar
take a look at https://github.com/johnrengelman/shadow
You would need to configure for you ':A_module' project
I have a gradle project with multiple packages. After the build, each package generates its jar files in build/libs. The external jar dependencies are pulled into ~/.gradle. I would now like to run the service locally from the commandline with the appropriate classpath. For this purpose, I am writing a script that constructs the classpath. The problem is that the script does not understand all the external dependencies and hence cannot construct the classpath. Is there a way for gradle to help with this? Ideally, I would like to dump all the dependencies into a folder at the end of the build.
Firstly, i would suggest using the application plugin if you can, since it takes care of this already.
If you want to dump the classpath to a file yourself, the simplest way is something like:
task writeClasspath << {
buildDir.mkdirs()
new File(buildDir, "classpath.txt").text = configurations.runtime.asPath + "\n"
}
If you want to actually copy all the libraries on the classpath into a directory, you can do:
task copyDependencies(type: Copy) {
from configurations.runtime
into new File(buildDir, "dependencies")
}
You could try something like this in your build script:
// add an action to the build task that creates a startup shell script
build << {
File script = file('start.sh')
script.withPrintWriter {
it.println '#!/bin/sh'
it.println "java -cp ${getRuntimeClasspath()} com.example.Main \"\$#\""
}
// make it executable
ant.chmod(file: script.absolutePath, perm: 'u+x')
}
String getRuntimeClasspath() {
sourceSets.main.runtimeClasspath.collect { it.absolutePath }.join(':')
}