How do I use gradle plugin tasks with different configurations? - java

I would like to have the default configuration from task "jar" from the java plugin for gradle, but I would also like to add several different configurations for different "jar" tasks. The way I am doing it now is just commenting out the jar tasks that I am not using.
Example:
//jar {
// archiveName = 'a.jar'
// include 'path/to/package/1/*'
//}
jar {
archiveName = 'b.jar'
include include 'path/to/package/2/*'
}
Is there a better way?
I am aware that I can create new tasks of type "Jar", but then I don't get that default configuration for free; I would have to provide it myself. Which is troublesome, since I don't know what that configuration is.
I am looking for something like:
task myJar(type: Jar, dependsOn: classes) {
extends "jar"
...include additional configuration here
}
I have the same issue for other types of default tasks, not just "jar".

I found the way, simple, yet I found it very hard to find information about it.
Using the keyword "with" I can include another task's configs with the current task:
task(testJar, type: Jar) {
archiveName = 'test.jar'
with jar
}
Yields the same result as
jar {
archiveName = 'test.jar'
}

Related

gradle: tar task not creating a tar.gz

Hi I have a tar task that I made after looking at numerous methods and some SO posts.
task buildDist(type: Tar, dependsOn: jar) {
print 'here'
archiveName = 'xyz-' + version
destinationDir = file('build/dist')
extension = 'tar.gz'
compression = Compression.GZIP
from 'build/libs'
include 'xyz.jar'
}
buildDist.mustRunAfter jar
I have the java plugin applied and the jar task makes the xyz.jar file available under build/libs. The build/dist directory does not exist yet, but I tried new File("build/dist") as well. That did not work either - I even pointed it to the build directory that exists - doesn't work. I run the entire script with /gradlew clean build. The print in the above code does print.
I am making a few assumptions here as you didn't post the output from running Gradle.
The build task is just a normal Gradle task that doesn't do anything by itself. Instead, it depends on other tasks. If you create your own custom task and you like to have it included when executing build, you have to add a dependency to it. If this is not the problem and you have actually done this, please give some more details as to what makes it "not work" when you run build.
If you want to test your task in isolation (e.g. to make sure it works correctly without running unit tests or whatever else that is unrelated), just run gradlew cleanBuildDist buildDist.
A note about the 'print' statement - it executes doing the configuration phase, but this doesn't mean you can use it to test if the task actually executes. In fact, it will most likely print no matter what task you execute. If you wanted to print something on execution time, you would have to put it in a doLast block.
There is a few other things you should change as well:
It is not a good practice to use relative references. Instead, use the buildDir property to get an absolute reference to the build directory.
Don't use deprecated methods like archiveName and destinationDir. Use archiveFileName and destinationDirectory instead.
The extension property is also deprecated, but it is ignored if you set the full name of the archive yourself. So just remove it. This also means you are missing the extension on the full name.
The from and include is a little fragile. Just use from jar.archivePath if you only want to gzip your application jar.
Example:
task buildDist(type: Tar, dependsOn: jar) {
archiveFileName = "${jar.baseName}-${version}.tar.gz"
destinationDirectory = file("$buildDir/dist")
compression = Compression.GZIP
from jar.archivePath
}
build.dependsOn buildDist
Lastly, if your intention is to create a distribution of your application that is runnable on its own (with all required dependencies), you should consider using the distribution plugin and perhaps also the application plugin.

Gradle Multi Module jar

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

execute gradle shadowjar task twice in same build file

I am trying to create two 'fatJars' using ShadowJar plugin as part of the same build file. I am trying to run the shadowJar task twice inside the build by declaring two tasks of ShadowJar type
So far, I have defined two tasks like so:
task shadowjar_one (type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar)
task shadowjar_two (type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar)
and now trying to create my jars like so:
shadowjar_one {
mergeServiceFiles()
exclude 'somefile.txt'
archiveName = 'jar1.jar'
appendManifest {
attributes 'Main-Class': 'some.package.someClass'
}
}
shadowjar_two {
mergeServiceFiles()
exclude 'someOtherfile.txt'
archiveName = 'jar2.jar'
appendManifest {
attributes 'Main-Class': 'some.package.someOtherClass'
}
}
The problem I am facing is that the jars are created, but they do not contain any of the other dependencies (packages, files etc) from 'other' jars. The jars only contain the META-INF and current project's package directories.
Any idea what could be the issue?
Note:
I am expecting two slightly different jar files to be produced. Both must have the same project codebase with differences in the Main-Class attribute of manifest (and a couple of other small differences)
Many Thanks!
The author gave a very good solution (in that it is both short and working) here:
https://github.com/johnrengelman/shadow/issues/108
I'm actually using a tweak to that solution, appearing at the bottom of that page (I've added remarks to explain it a little):
task bootstrapNodeJar(type: ShadowJar) {
group = "shadow" // Not a must have, but it's always good to have a group, you can chose whichever - this is the one shadowJar belongs to
description = "Builds a Bitsquare bootstrap node executable jar" // Same as the above
manifest.attributes 'Main-Class': 'io.bitsquare.app.cli.BootstrapNodeMain' // The main attraction! Be sure to update this line
classifier = 'bootstrapNode' // General jar task property - see more about it in the Gradle manual
from(project.convention.getPlugin(JavaPluginConvention).sourceSets.main.output) // Leave as is
configurations = [project.configurations.runtime] // Same as the above
exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA') // This one is actually really important!
// Here you can add other Jar properties like destinationDir, for example
}
Shadow plugin author here - I was just made aware of this question here. What you are encountering is the fact that the Shadow plugin creates and configures a shadowJar task using a set of defined conventions for that task.
When you are creating your own tasks using that type, you'll need to manually define a number of those configuration options since there is no way for the plugin to know what your intent is with those tasks.
You can reference the configuration that is being applied to the built in task here: https://github.com/johnrengelman/shadow/blob/master/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy#L38-L63
A workaround that I've used is to have a single shadowJar task, but pass parameters.
In your case, something like:
shadowJar {
mergeServiceFiles()
exclude System.properties.getProperty('exclude')
archiveName = System.properties.getProperty('archiveName')
appendManifest {
attributes 'Main-Class': System.properties.getProperty('mainClass')
}
}
Then, when starting your application:
gradlew shadowJar -Dexclude=... -DarchiveName=... -DmainClass=...

How do you create additional jar artifacts from compiled classes using Gradle?

I can't tell if this is a bug with Gradle 1.0m7, or if we are just doing this wrong.
We have some classes that get compiled as apart of a project, that we want to individually jar into it's own artifact. These are for example standalone domain model objects, that we want to share with another project.
I'd prefer not to go the multi-project build route, so how do we tell Gradle to create another jar for these?
Currently we are doing this:
task modelJar(type: Jar) {
classifier = 'model'
from fileTree(dir: sourceSets.main.classesDir).matching { include 'com/foo/bar/model/**' }
}
artifacts {
archives modeljar
}
The issue here, is the modeljar task runs before the classes are compiled. At first we didn't realise this and thought this was working. Turns out, the artifact was picking up classes from the previous run, not the current run. Doing clean before the build results in a jar with no classes in it, and reveals the problem.
I was looking at custom configuration, but it seems pretty complex and I didn't want to overly complicate the build file.
Appreciate any advice.
Thanks.
the most convenient way to do this is
task modelJar(type: Jar) {
classifier = 'model'
from sourceSets.main.output
include 'com/foo/bar/model/**'
}
Some background:
sourceSets.main.output is a buildable filecollection. This means that if a task works with this file collection, gradle knows that this file collection must be created before another task can use it. in this particular case, sourcesets.main.classes is wired to the classes task of the java plugin. Therefore you your modelJar task does not need to depend on the classes task explicitly.
How about making modelJar task depend on classes (built-in) task? This should make sure compilation is done before modelJar task.
task modelJar(dependsOn: classes, type: Jar){
...

How can I import one Gradle script into another?

I have a complex Gradle script that wraps up a load of functionality around building and deploying a number of NetBeans projects to a number of environments.
The script works very well, but in essence it is all configured through half a dozen maps holding project and environment information.
I want to abstract the tasks away into another file, so that I can simply define my maps in a simple build file, and import the tasks from the other file. In this way, I can use the same core tasks for a number of projects and configure those projects with a simple set of maps.
Can anyone tell me how I can import one Gradle file into another, in a similar manner to Ant's task? I've trawled Gradle's docs to no avail so far.
Additional Info
After Tom's response below, I thought I'd try and clarify exactly what I mean.
Basically I have a Gradle script which runs a number of subprojects. However, the subprojects are all NetBeans projects, and come with their own ant build scripts, so I have tasks in Gradle to call each of these.
My problem is that I have some configuration at the top of the file, such as:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
I then generate tasks such as:
projects.each({
task "checkout_$it.shortname" << {
// Code to for example check module out from cvs using config from 'it'.
}
})
I have many of these sort of task generation snippets, and all of them are generic - they entirely depend on the config in the projects list.
So what I want is a way to put this in a separate script and import it in the following sort of way:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
import("tasks.gradle") // This will import and run the script so that all tasks are generated for the projects given above.
So, in this example, tasks.gradle will have all the generic task generation code in, and will get run for the projects defined in the main build.gradle file. In this way, tasks.gradle is a file that can be used by all large projects that consist of a number of sub-projects with NetBeans ant build files.
There is a new feature in 0.9. You can use apply from: 'other.gradle' command.
Read my question about same thing at: Is there a way to split/factor out common parts of Gradle build
The answer to the question turned out to be in the Plugins system, where you can add the desired functionality in a set of plugins which can be groovy files located in the directory buildSrc/src/main/groovy. Plugins can also be bundled as a Jar though I haven't tried this.
Details here: Custom Plugins
Well, it is hard to tell what serves you best without actually seeing your build file.
I could assume that stetting up your environment as multi-project build should provide you the abstraction you are looking for.
In your project root build.gradle you define all your domain specific stuff as well as the things that apply to all your subprojects:
repositories {
add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
name = 'destRepo'
addIvyPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/ivys/ivy(-[revision]).xml')
addArtifactPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/[type]s/[artifact](-[revision]).[ext]')
descriptor = 'optional'
checkmodified = true
}
...
}
...
subprojects {
sourceCompatibility = 1.5
targetCompatibility = 1.5
group = 'my.group'
version = '1.0'
uploadArchives {
uploadDescriptor = true
repositories {
add rootProject.repositories.destRepo
}
}
apply{ type my.group.gradle.api.plugins.MyPlugin }
...
}
dependsOnChildren()
The project root directory might also contain a gradle.properties file where you define properties used by your projects:
buildDirName=staging
repo.dest.dir=/var/repo
...
Then in an additional file from your project root named settings.gradle you actually point to your subprojects:
include 'my-first-component',
'my-second-component'
...
project(':my-first-component').projectDir = new File(rootDir, 'path/to/first/component')
project(':my-second-component').projectDir = new File(rootDir, 'path/to/second/component')
...
Each sub-project directory contains a build.gradle file containing the sub-project specific stuff only.
No matter if you invoke gradle from your project root or sub-project directory, gradle will automatically consider all your definitions done in the various files.
Also note that no compile task will be executed for your project root as long as you don't load any plugin beyond the default plugin at the root level.
This is an example for Kotlin DSL (build.gradle.kts).
apply(from = "scripts/my-script.gradle.kts")
scripts/my-script.gradle.kts:
println(
"""
I am defined at the top level of the script and
executed at the configuration phase of build process
"""
)
tasks.create("MyTask") {
println(
"""
I am defined in a task and
run at the configration phase of build process"""
)
doLast {
// ...
}
}
See this answer and this answer for how to import a function from another script in Kotlin DSL.
Based off this similar question/answer, the easiest solution I've found after searching for days is using buildscript.sourceFile. It correctly gives the file being run rather than the pwd/cwd/parent-file of said process. I feel like this would solve your issue.

Categories