accessing fields from non-root project in gradle - gradle

this seems like it should be very simple, but I'm stuck! I have a gradle task that zips some files and creates an archive. I want to suffix the name of the archive with the version string from the project I'm in (this is a subproject in a multi-project build)
there's another task in this same build.gradle file that accesses this version field, that I want to access too. it looks like this:
task releasePublishAuto(dependsOn: [tasks.cppApiVersion, rootProject.tasks.releasePublish]) {
doLast {
println("############################################################")
println("# PUBLISHED CPP API WITH VERSION: $version")
println("############################################################")
}
}
you can see it accessing the version variable and printing it using $version. it prints something like # PUBLISHED CPP API WITH VERSION: 1.256.4-SNAPSHOT
now, my task is a zipping task, so it's a bit different:
task zipGeneratedCppClientApi(type: Zip, group: "code generator", dependsOn: [tasks.cppApiVersion, "generateCormorantCPP"]) {
from(fileTree(dir: "$buildDir/generated/cormorant_cpp", include: "**/*"))
archiveFileName = "$buildDir/distributions/generated-cpp-api-$version"
}
I would expect this to create a zipfile named generated-cpp-api-1.256.4-SNAPSHOT but instead i get one named generated-cpp-api-null. As far as I can tell this is because in the context of my zipping task, version refers to the version of the zipping library (or somesuch), not the version of the current project. I've tried instead accessing rootproject.version but that's also null - this isn't the root project but a subproject thereof!
How can I access the version field in this non-root project in gradle?
to clarify: if I remove type:zip from my task, version points to the correct value, but without type:zip it doesnt create a zipfile and so isnt very helpful.

In the context of a Zip task, the version property refers to the AbstractArchiveTask.version property, as you have somehow guessed, and not to the current project version property. As you can see, this version property has been deprecated on AbstractArchiveTask class, maybe to avoid such confusion.
To use project version instead, simply use ${project.version} as in following example.
in your subproject build script:
version="1.0.0"
tasks.register('packageDistribution', Zip) {
println("version from AbstractArchiveTask : $version") // => here, version will be null
println("version from project: ${project.version}") // => here, version will be "1.0.0" as expected
archiveFileName = "my-distribution-${project.version}.zip" // => my-distribution-1.0.0.zip as expected
destinationDirectory = layout.buildDirectory.dir('dist')
from layout.projectDirectory.dir("src/dist")
}

Related

WIthin nebula/gradle, how can I inject the version being released into the jar being published?

We have a tool that runs from the command line. One of the commands is -version.
Before we converted to the nebula release plugin, the version was in the gradle.properties file, and as part of the build we copied it from there to a src/main/resources/version.txt file, that was later read by the tool to output the version.
But now the version is never in a file that's checked into git. Instead, it is only known during the nebula release process.
We want to obtain that version during the nebula release process and inject it into the jar that nebula is about to publish. For example, it could be added to the manifest.
We've tried to figure out how to do this, but don't see any examples online, and nothing about it in the documentation.
Simply create a task that caches the version that is dynamically inferred by Nebula.
Since you originally copied/created src/main/resources/version.txt, we'll use that that model our task.
Assuming a simple/standard Java project, using the Kotlin DSL:
val cacheNebulaVersion by tasks.registering {
mustRunAfter(tasks.named("release"))
doLast {
val sourceSets = project.extensions.getByName("sourceSets") as SourceSetContainer
sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).output.resourcesDir?.let {
// If there are not existing resources in your project then you must create
// the resources dir otherwise a FileNotFoundException will be thrown.
if (!it.exists()) {
it.mkdirs()
}
File(it, "version.txt").printWriter().use { out ->
out.println(project.version)
}
}
}
}
When I invoke ./gradlew clean build snapshot cacheNebulaVersion, the version produced by Nebula is cached/created at src/main/resources/version.txt in the build output. The task above does not bundle it with the jar.
Hopefully that gives you an idea what to do.

Include dependencies in Zip file created with Gradle's Zip task using 'into' (before v4 it worked fine)

I am working in an old project which was using Gradle Wrapper v3.2.1, but I want to update it to the latest version (currently v5.4.1).
I have tried updating it to v4.10.2 first but it fails too, so I guess it is something that was not backwards compatible between v3->v4.
The code we have in our build.gradle is:
task buildZip(type: Zip) {
group 'Build'
description 'Assembles a zip archive containing the main classes.'
baseName = "someName"
from compileJava
from processResources
into('lib') {
from configurations.runtime
}
}
Using gradle v3 it included all the libraries (as .jar files) in the .zip file under "lib/" folder, but if I use v4 or later, it does not fail, but it does not include the libraries neither. I have achieved to get the .class files of the dependencies, but that does not work for what I need (AWS Lambda function).
Any idea on how to get the .jar dependencies into the .zip file?
Cheers!
Francisco Robles Martin
So, thanks to Opal comment, I kept looking for a bit more and got a solution, but it seems to not be very correct as I am forcing implementation to allow be resolved:
configurations.implementation.setCanBeResolved(true)
task buildZip(type: Zip) {
group 'Build'
description 'Assembles a zip archive containing the main classes.'
baseName = "someName"
from compileJava
from processResources
into('lib') {
from configurations.implementation
}
}
It works, but I guess there should be a better way to do it without the first line.

Compile task is running successful but not generating any class files

I want to implement a gradle build script which compiles some java classes and copy it to to a tomcat directory. I dont want to use Gradle Java plugin since it does many things which are not relevant. I want to define my own compile & deploy tasks which does it. I have implemented it as below -
task compile (type: JavaCompile) {
source = fileTree('$srcdir')
destinationDir = file('$builddir')
classpath = files('lib')
sourceCompatibility = '1.8'
}
task deploy (type: Copy) {
dependsOn compile
from fileTree('build') {
include fileTree('classes')
}
from fileTree('lib') {
include '*'
}
into '${tomcathome}//${projectname}'
}
I have not touched deploy task yet. When i am running compile tasks it is running successful but not generating any class files. I am expecting it to be generated under /build directory.
Please suggest.
Thanks
To summarise the comments in the answer, you need to use GString like #lu.koerfer stated
this way it will always be interpreted as the literal location (a subfolder called $srcdir in this case)
This is needed when using variables inside a string, if don't need to use it in a string then don't (then you don't need a dollar sign).
Not sure how your variables are defined but for build and source directories you should ideally use Gradle provided variables
buildDir to point the build directory
sourceSets.main.java.getSrcDirs() to get source directories (depending on the project structure)
or sourceSets.main.java.srcDirs but note this is going to return the collection of your source directories, depending how you specified your sourceSets, or if you haven't at all then by default is going to return a maven convention structure src/main/java
For some global variables please read about Ext variables

Gradle/Groovy refer to "file-wide/global" variable from inside block

In my build file I have the following line:
version = '1.1.2'
I want to make an executable "fat jar" using Shadow Jar (see my answer here) and then copy this to another directory (see answer here). But I want to incorporate the project's version number into the name of the jar file so as not to overwrite the existing version.
The Shadow Plugin User Guide explains how to set the name:
shadowJar {
baseName = 'shadow'
classifier = null
version = "7.7.7"
}
... but how might I set the variable version here to use the outer (file-wide/global...) variable version?
In a gradle script, there's a Project instance in scope which everything delegates to. When you say version = '1.1.2' it's actually invoking
getProject().setVersion('1.1.2')
In your shadowJar closure there's a version higher up in the scope which is hiding the project version. So you can do
shadowJar {
version = project.version
...
}

Creating a post build copy task with Gradle

I am struggling with the Gradle build lifecycle; specifically with the split between the configuration and execution phases. I have read a number of sections in the Gradle manual and have seen a number of ideas online, but have not found a solution to the following problem:
I want to run a specific task to produce an artifact at the end of my java-library-distribution build that is a flattened version of the runtime configuration jars. That is, I only want to produce the artifact when I run the specific task to create the artifact.
I have created the following task:
task packageSamplerTask(type: Tar, dependsOn: distTar) {
description "Packages the build jars including dependencies as a flattened tar file. Artifact: ${distsDir}/${archivesBaseName}-${version}.tar"
from tarTree("${distsDir}/${archivesBaseName}-${version}.tar").files
classifier = 'dist'
into "${distsDir}/${archivesBaseName}-dist-${version}.tar"
}
Although this task does produce the required artifact, the task runs during gradle's configuration phase. This behavior has the following consequences:
Irrespective of which task I run from the command line, this packageSamplerTask task is always run, often unnecessarily; and
If I clean the project, then the build fails on the next run because $distsDir doesn't exist during the configuration phase (obviously).
It appears that if I extend the Copy task in this manner I'm always going to get this kind of premature behavior.
Is there a way to use the << closure / doLast declarations to get what I want? Or is there something else I'm missing / should be doing?
Update
After further work I have clarified my requirements, and resolved my question as follows (specifically):
"I want to package my code and my code's dependencies as a flat archive of jars that can be deployed as a jMeter plugin. The package can then be installed by unpacking into the jMeter lib/ext directory, as is. The package, therefore, must not include the jMeter jars (and their dependencies) which are used for building and testing"
Because Gradle doesn't appear to support the Maven-like provided dependency management, I created a new configuration for my package which excludes the jMeter jars.
configurations {
jmpackage {
extendsFrom runtime
exclude group: 'org.apache.jmeter', name: 'ApacheJMeter_core', version: '2.11'
exclude group: 'org.apache.jmeter', name: 'ApacheJMeter_java', version: '2.11'
}
}
And then created the following task (using the closure recommendation from Peter Niederwieser):
task packageSamplerTask(type: Tar, dependsOn: assemble) {
from { libsDir }
from { configurations.jmpackage.getAsFileTree() }
classifier = 'dist'
}
This solution appears to work, and it allows me to use just theGradle java plugin, too.
The task declaration is fine, but the flattening needs to be deferred too:
...
from { tarTree("${distsDir}/${archivesBaseName}-${version}.tar").files }
Also, the Tar file should be referred to in a more abstract way. For example:
from { tarTree(distTar.archivePath).files }
First your task isn't executed in the configuration phase but like EVERY task it is configured in that phase. And your closure is just a configuration of your task (a Configuration closure, not an Action closure). That is why your code is "executed" in the configuration phase".
If you want your code to be executed in the execution phase have to write it in a doLastclosure or doFirst. But in your case it is better to keep it in a configuration closure, because you are configuring your task.
To make sure your build doesn't fail because of the missing folder, you can create it with distsDir.mkdirs().

Resources