gradle - copy file after its generation - java

I try to build jar and after that copy it to another folder.
task createJar(type: Jar) {
archiveName = "GradleJarProject.jar"
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'me.test.Test'
}
baseName = project.name
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
task copyJarToBin {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
task buildApp (dependsOn: [clean, createJar, copyJarToBin])
But I can't figure out one problem.
copyJarToBin task try to copy old jar. If I delete /build folder in the project and run buildApp() task, task createJar() will generate .jar file, but copyJarToBin() won't find that .jar file.
Could you help me?
Thanks.

The culprit is your copyJarToBin task. when doing
task copyJarToBin {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
you copy the jar during the configuration time by using the copy method. (see the gradle user guide at https://docs.gradle.org/current/userguide/userguide_single.html#sec:build_phases to understand the build lifecycle)
You want to run the actual copy operation during the execution phase (the execution of the task).
One way to solve that is to move the call of the copy method into a doLast block:
task copyJarToBin {
doLast {
copy {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
}
}
The problem with this approach is that you won't benefit of gradles incremental build feature and copy that file every single time you execute the task even though the file hasn't changed.
A better and more idiomatic way of writing your copyJarToBin task is to change your task implementation to use the Copy task type:
task copyJarToBin(type: Copy) {
from 'build/libs/GradleJarProject.jar'
into "d:/tmp"
}
We can even improve this snippet by taking advantage of gradle's autowiring feature. You can declare the output of one task as input to another. So instead of writing `build/libs/GradleJarProject.jar' you can simply do:
task copyJarToBin(type: Copy) {
from createJar // shortcut for createJar.outputs.files
into "d:/tmp"
}
Now you don't need to bother about task ordering as gradle know that the createJar task must be executed before the copyJarToBin task can be executed.

I think the above answer is somehow old. Here is an answer using gradle 3.3
jar {
baseName = 'my-app-name'
version = '0.0.1'
}
task copyJar(type: Copy) {
from jar // here it automatically reads jar file produced from jar task
into 'destination-folder'
}
build.dependsOn copyJar

Just made few corrections to above Answers:
jar {
baseName = "$artifactId"
version = '0.0.1'
}
task copyJar(type: Copy) {
from jar // copies output of file produced from jar task
into 'destination-folder'
}
build.finalizedBy copyJar

You probably need to ensure they are run in the right order,
task copyJarToBin(type:Copy,dependsOn:[createJar]) {
copy {
from "${buildDir}/GradleJarProject.jar" // needs to be gstring
into "d:/tmp"
}
}

In my use case I needed projectA to consume (unzip) contents from projectB's jar output. In this case the embedded doLast { copy { .. }} was required.
configurations {
consumeJarContents
}
dependencies {
consumeJarContents project(':projectB')
}
task copyFromJar() {
dependsOn configurations.consumeJarContents
doLast {
copy {
configurations.consumeJarContents.asFileTree.each {
from( zipTree(it) )
}
into "$buildDir/files"
}
}
}
The problem with task copyFromJar(type: Copy) in this scenario is that the Copy configuration phase checks for the jar file, which it does not find because it has not yet been created, and so declares NO-INPUT for the task at execution time.

Related

Combining multiple gradle zip tasks into a single one

I have a gradle task to zip the .yaml files into a zip folder, which looks like this:
task gradleTask1(type: Zip) {
from 'src/test/resources/source_one/'
include '**/*'
archiveName 'file-collect.zip'
destinationDir(file("${buildDir}/resources/test/staging/target_one"))
}
Similarly, I have other tasks that look the same, but the source and target directories are different.
task gradleTask2(type: Zip) {
from 'src/test/resources/source_two/'
include '**/*'
archiveName 'file-collect.zip'
destinationDir(file("${buildDir}/resources/test/staging/target_two"))
}
task gradleTask3(type: Zip) {
from 'src/test/resources/source_three/'
include '**/*'
archiveName 'file-collect.zip'
destinationDir(file("${buildDir}/resources/test/staging/target_three"))
}
And the main issue is I have to add dependency every time I add a new zip task as follows:
compileJava.finalizedBy gradleTask1
compileJava.finalizedBy gradleTask2
compileJava.finalizedBy gradleTask3
Is there any way I can achieve these steps dynamically? Can I have a single zip task (something like zipAll) and finally the task dependency can be
compileJava.finalizedBy zipAll
Consider the following (example here):
tasks.withType(Zip).all { task ->
def taskName = task.name
if (taskName ==~ /gradleTask.*/) {
println "TRACER adding dependency on ${taskName}"
compileJava.finalizedBy taskName
}
}
This will dynamically find tasks of type Zip with name matching gradleTask* and add it to the list of tasks for compileJava.finalizedBy.
There is no zipAll task, but gradle compileJava will work as desired.

Task in gradle to include java class before compile

I need to create Gradle task which can replace .java files before Gradle build.
Gradle build package app in .war file. And I need to have replaced bytecode there after build.
I tried sourceSets Gradle task but it can only exclude files.
sourceSets {
main {
java {
exclude 'com/myapp/example/resource/impl/ResourceBundleImpl.java'
}
}
}
But I need to also include file in the same place. How I can do it with Gradle?
The directory to file that I need to exclude: com/myapp/example/resource/impl/ResourceBundleImpl.java
The directory to file that I need to include: src/main/webapp/WEB-INF/my/ResourceBundleImpl.java
To copy file content it is also posible solution.But How can I do it before compile time?
The below task didn't helped, becouse in build file have .java files instead of .classe file.
task prepareSources(type: Copy) {
from('src/main/webapp/WEB-INF/my')
into('build/classes/java/main/com/myapp/example/resource/impl')com/myapp/example
}
// Prepare sources, before compile
compileJava {
dependsOn prepareSources
}
The below task throws :
Task :cdx-war:compileJava FAILED
error: package com.myapp.example.util does not exist
sourceSets {
main {
java {
srcDir "$projectDir"
exclude 'com/medtronic/diabetes/carelink/rbps/resource/impl/ResourceBundleImpl.java'
include 'src/main/webapp/WEB-INF/my/ResourceBundleImpl.java'
}
}
I put classes that I need to replace together and add suffix to one of them and replace this suffix during compile time :
sourceSets {
main {
java {
srcDirs = ["$buildDir\\generated-src"]
}
}
}
task copySourceSet(type: Copy) {
println 'Change java class in patient-svc-war'
from "$rootDir\\patient-svc-war\\src\\main\\java"
into "$buildDir\\generated-src"
from file("$rootDir\\patient-svc-war\\**_suffix.java")
into "$buildDir\\generated-src"
rename { String fileName ->
fileName.replace("_suffix", "")
}
filter { line -> line.replaceAll('_suffix', '')}
}
compileJava.source "$buildDir\\generated-src"
compileJava {
dependsOn copySourceSet
}
compileJava.doLast {
delete "$buildDir\\generated-src"
}

Delombok'ing source code with added jar dependencies

I am unable to delombok my Java source code, apparently due to jar dependencies that the project has, and I don't understand why. There are two jar files that have to be committed to the repo to tag along, and they are added to the project in the dependencies node of the build.gradle file by adding the line compile files('myproj1.jar'). So, the relevant part of the build.gradle file looks like this:
dependencies {
compile files('myproj1.jar')
compile files('myproj2.jar')
.....
}
When I run the delombok task I get the following error:
Execution failed for task ':delombok'.
> taskdef class lombok.delombok.ant.Tasks$Delombok cannot be found
using the classloader AntClassLoader[/path/to/repo/myproj1.jar:/path/to/repo/myproj2.jar]
Why would delombok task be using the AntClassLoader from the jar files?
I have tried the delombok'ing code from this post
Here is the task from my build.gradle file
def srcJava = 'src/main/java'
def srcDelomboked = 'build/src-delomboked'
task delombok {
// delombok task may depend on other projects already being compiled
dependsOn configurations.compile.getTaskDependencyFromProjectDependency(true, "compileJava")
// Set up incremental build, must be made in the configuration phase (not doLast)
inputs.files file(srcJava)
outputs.dir file(srcDelomboked)
doLast {
FileCollection collection = files(configurations.compile)
FileCollection sumTree = collection + fileTree(dir: 'bin')
ant.taskdef(name: 'delombok', classname: 'lombok.delombok.ant.Tasks$Delombok', classpath: configurations.compile.asPath)
ant.delombok(from:srcJava, to:srcDelomboked, classpath: sumTree.asPath)
}
}
I expect to be able to delombok my Java source code as part of the build process so that when the project is compiled there are no dependencies on Lombok.
So after continued trial an error, I have a working implementation. To answer my own question, the problem has nothing to do with the additional Jar files. Rather, when gradle runs the delombok task, the classes in the lombok jar are not in the classpath of the org.gradle.api.AntBuilder (ie, the ant task), and so it does not have a reference to lombok.delombok.ant.Tasks$Delombok anywhere (which seems obvious at this point, but not at the time).
The solution thus far has been to add those references in from configurations.compile
Combining code snippits from this post and this post you can do it with something like this:
def srcDelomboked = 'build/src-delomboked'
task delombok {
description 'Delomboks the entire source code tree'
def srcJava = 'src/main/java'
inputs.files files( srcJava )
outputs.dir file( srcDelomboked )
doFirst {
ClassLoader antClassLoader = org.apache.tools.ant.Project.class.classLoader
def collection = files( configurations.compile + configurations.testCompile )
def sumTree = collection + fileTree( dir: 'bin' )
sumTree.forEach { File file ->
antClassLoader.addURL(file.toURI().toURL())
}
ant.taskdef( name: 'delombok', classname: 'lombok.delombok.ant.Tasks$Delombok',
classpath: configurations.compile.asPath + configurations.testCompile.asPath )
ant.delombok( from: srcJava, to: srcDelomboked, classpath: sumTree.asPath )
}
}
sourceSets {
main {
java { srcDirs = [ srcDelomboked ] } //blow away the old source sets so that we only use the delomboked source sets
}
test {
java { srcDirs += [ srcDelomboked ] } //but add those new source sets to the tests so that their references are available at test time
}
}
compileJava.dependsOn(delombok)
bootJar {
mainClassName = 'com.myproj.MyMainClass' // you will need this if its a Spring Boot project
}
Hope this helps whomever else needs to delombok their code.

Gradle: Task with path not found in project

I have a gradle project with the following structure:
rootDir
|--agent-v1.0.0
|--agent.jar
|--proj1
|-- // other project files
|--build.gradle
|--proj2
|-- // other project files
|--build.gradle
|--build.gradle
I would like to run test.jvmArgs = ['javaagent:agent-v1.0.0/agent.jar'] for all subprojects, so I wrote the following task in the root build.gradle:
subprojects {
task cs {
outputs.upToDateWhen { false }
dependsOn test.jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar']
}
}
But this fails with:
Could not determine the dependencies of task ':proj1'.
Task with path 'javaagent:../agent-v1.0.0/agent.jar' not found in project ':proj1'.
I've tried this by putting the agent-v1.0.0 in both the root, and in each project, and it still fails. What am I missing?
Why are you wrapping that logic in a new task? And then passing the return from jvmArgs to dependsOn?
Just configure the test tasks correctly:
subprojects {
tasks.withType(Test) {
jvmArgs "-javaagent:${project.rootDir}/agent-v1.0.0/agent.jar"
}
}
A task can depend on another task. So dependsOn expects a task as argument. test.jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar'] is not a task.
If you want to configure all the test tasks of all subprojects to have additional jvm args, then the syntax would be
subprojects {
// this block of code runs for every subproject
afterEvaluate {
// this block of code runs after the subproject has been evaluated, and thus after
// the test task has been added by the subproject build script
test {
// this block of code is used to configure the test task of the subproject
// this configures the jvmArgs property of the test task
jvmArgs = ['javaagent:../agent-v1.0.0/agent.jar']
}
}
}
But just don't copy and paste this code. Read the grade user guide, and learn its fundamental concepts.

Gradle task replace string in .java file

I want to replace few lines in my Config.java file before the code gets compiled. All I was able to find is to parse file through filter during copying it. As soon as I have to copy it I had to save it somewhere - thats why I went for solution: copy to temp location while replacing lines > delete original file > copy duplicated file back to original place > delete temp file. Is there better solution?
May be you should try something like ant's replaceregexp:
task myCopy << {
ant.replaceregexp(match:'aaa', replace:'bbb', flags:'g', byline:true) {
fileset(dir: 'src/main/java/android/app/cfg', includes: 'TestingConfigCopy.java')
}
}
This task will replace all occurances of aaa with bbb. Anyway, it's just an example, you can modify it under your purposes or try some similar solution with ant.
To complement lance-java's answer, I found this idiom more simple if there's only one value you are looking to change:
task generateSources(type: Copy) {
from 'src/replaceme/java'
into "$buildDir/generated-src"
filter { line -> line.replaceAll('xxx', 'aaa') }
}
Caveat: Keep in mind that the Copy task will only run if the source files change. If you want your replacement to happen based on other conditions, you need to use Gradle's incremental build features to specify that.
I definitely wouldn't overwrite the original file
I like to keep things directory based rather than filename based so if it were me, I'd put Config.java in it's own folder (eg src/replaceme/java)
I'd create a generated-src directory under $buildDir so it's deleted via the clean task.
You can use the Copy task and ReplaceTokens filter. Eg:
apply plugin: 'java'
task generateSources(type: Copy) {
from 'src/replaceme/java'
into "$buildDir/generated-src"
filter(ReplaceTokens, tokens: [
'xxx': 'aaa',
'yyy': 'bbb'
])
}
// the following lines are important to wire the task in with the compileJava task
compileJava.source "$buildDir/generated-src"
compileJava.dependsOn generateSources
If you do wish to overwrite the original file, using a temp file strategy, the following will create the filtered files, copy them over the original, and then delete the temp files.
task copyAtoB(dependsOn: [existingTask]) {
doLast {
copy {
from("folder/a") {
include "*.java"
}
// Have to use a new path for modified files
into("folder/b")
filter {
String line ->
line.replaceAll("changeme", "to this")
}
}
}
}
task overwriteFilesInAfromB(dependsOn: [copyAtoB]) {
doLast {
copy {
from("folder/b") {
include "*.java"
}
into("folder/a")
}
}
}
// Finally, delete the files in folder B
task deleteB(type: Delete, dependsOn: overwriteFilesInAfromB) {
delete("folder/b")
}
nextTask.dependsOn(deleteB)

Categories

Resources