Gradle - Write Task Output Into A File - java

I am working with Gradle 7.1, and I am trying to write some of the tasks resuts into a file.
Specifically, I would like to write the output of dependencies task into a file after each jar task execution.
Looking for some solutions, I understand that at first I need to have jar.finalizedBy(dependencies) in order fot it to run.
However, I can't find how to redirect the dependencies's specific output into a file. All the solutions that I have found discuss Exec tasks, which dependencies isn't.
I am looking for somehing like dependencies.doFirst(///REDIRECT HERE).

You can make dependencies task write to file by attaching a StandardOutputListener:
tasks.named('dependencies').configure {
it.logging.addStandardOutputListener(new StandardOutputListener() {
#Override
void onOutput(CharSequence charSequence) {
project.file("$buildDir/dependencies_task_output.txt") << charSequence
}
})
}
This can also be done with any other Gradle task.

Related

Create makefile-like wildcard targets in Gradle

Use case: I have a bunch of images that have to be processed by a script before I build my app. In makefile I can simply define:
processed/%.png: original/%.png
script/process.sh $< $#
How do I implement this in Gradle? Specifically, I want it to work like in Makefile, that is only the modified original images will be processed again.
You can implement this behaviour as an incremental task, using IncrementalTaskInputs as its input parameter. This API docs contain an example how to use it and here is an example in another the documentation. Both of them do almost exactly what you need.
An incremental task action is one that accepts a single
IncrementalTaskInputs parameter. The task can then provide an action
to execute for all input files that are out of date with respect to
the previous execution of the task, and a separate action for all
input files that have been removed since the previous execution.
In the case where Gradle is unable to determine which input files need
to be reprocessed, then all of the input files will be reported as
IncrementalTaskInputs.outOfDate(org.gradle.api.Action).
Inside your task, call the script using an exec task. Your Gradle script could then look like this:
task processRawFiles(type: ProcessRawFiles)
class ProcessRawFiles extends DefaultTask {
#InputDirectory
File inputDir = project.file('src/raw')
#OutputDirectory
File outputDir = project.file('build/processed')
#TaskAction
void execute(IncrementalTaskInputs inputs) {
if (!inputs.incremental)
project.delete(outputDir.listFiles())
inputs.outOfDate { InputFileDetails change ->
File saveTo = new File(outputDir, change.file.name)
project.exec {
commandLine 'script/process.sh', change.file.absolutePath, saveTo.absolutePath
}
}
inputs.removed { InputFileDetails change ->
File toDelete = new File(outputDir, change.file.name)
if (toDelete.exists())
toDelete.delete()
}
}
}
This task looks for the images in src/raw. It will removed files from build directory and call your script on any files that are out of date or newly added.
Your specific case might be more complicated if you have the images scattered across multiple directories. In that case you will have to use #InputFiles instead of #InputDirectory. But the incremental task should still work.

Empty source jar with 'doLast', screwed up subproject dependencies without

I have a task in build.gradle, looking like this:
task sourceJar(type: Jar, dependsOn: classes) << {
classifier = 'sources'
from sourceSets.main.allSource
}
Running gradle sourceJar creates a jar file in libs/ but it is empty (does not include any sources, just the manifest).
Removing << fixes it for some reason, the jar is created properly, but screws up other things (the subprojects now lose the compile dependencies, that are defined for them specifically).
So, three (maybe, four?) questions here:
(1) what's wrong? Why sourceSets are empty when the task is defined with <<?
(2) why does removing << fix it? My understanding is that it just makes the insides of the block to be executed "inline", every time, not just when the task is specifically exacuted.
(3) How to fix this? I can't just remove <<, because, like I said, it screws up other things (but see question #4).
(4) Why does removing << screws up subprojects? Is this expected?
To clarify, here is what I am talking about:
subprojects {
apply plugin: 'java'
dependencies {
compile project(':a')
}
task cp << {
println ("PROJECT " + project.name + ">> " + sourceSets.main.runtimeClasspath.collect { it.absolutePath }.join(':'))
}
}
project(':b') {
dependencies {
compile project(':c')
}
}
Running gradle -q b:cp prints out
PROJECT b>> b/build/classes/main:b/build/resources/main:a/build/libs/a.jar:c/build/libs/c.jar
(I removed the absolute paths). This is what I want.
Now, if I remove << from the file, and run gradle -q b:cp again, I get this
PROJECT a>> a/build/classes/main:a/build/resources/main:/a/build/libs/a.jar
PROJECT b>> b/build/classes/main:b/build/resources/main:a/build/libs/a.jar
PROJECT c>> c/build/classes/main:c/build/resources/main:a/build/libs/a.jar
This is wrong in two ways: first, I did not ask it to be run for all three subprojects, just for b, and second, notice that b does not have c in its classpath any more.
Can someone with a clue please help me figure out what's going on here ... I am really about to give up and switch to sbt (yes, it is a threat!).
When you declare a task of type:Jar, you do not have to use <<, because you're effectively extending a jar task which already has all the required << declared correctly. But first it sounds like you need to read up on what << means and about gradle's configuration and execution phases.
Please see Peter's answer here: Why is my Gradle task always running?
(<< is gradle shorthand for doLast any task code that isn't enclosed in doLast or isn't annotated by << is executed in the configuration phase and not execution phase. This can cause your printlns for example to be executed when the task isn't explicitly invoked, because all tasks are configured even if they are not executed)
Second, your cp task isn't extending a task type. so this needs << in its definition.
task cp << { ... }

Gradle strange behavior while extending sourceSets with Map variable

We are developing a Java project that is able to instrument (change) class files at build time. We defined a Gradle task that invokes a java based Ant task which takes an inputDir (e.g. build/classes) and an outputDir (e.g. build/classes-instrumented) and possible other parameters. The task gets invoked separately for main and test class files after compilation. Since the "normal" java sourceSet is not a good fit, our first thought was to implement our own sourceSet but couldn't find an easy way. A reasonable alternative, similar to ANTLR etc, seemed to be extra variables. Since I needed several, I went for a Map.
sourceSets.all { ext.instrumentation = [:] }
sourceSets.all {
instrumentation.inputDir = null
instrumentation.outputDir = null
instrumentation.classPath = null
}
def postfix = '-instrumented'
Below you see how we initialize the variables.
sourceSets {
main {
instrumentation.inputDir = sourceSets.main.output.classesDir
instrumentation.outputDir = instrumentation.inputDir + postfix
instrumentation.classPath = sourceSets.main.output + configurations.compile
}
test {
instrumentation.inputDir = sourceSets.test.output.classesDir
instrumentation.outputDir = instrumentation.inputDir + postfix
}
}
However it fails with "Could not find method main() for arguments [build_f2cvmoa3v4hnjefifhpuk6ira$_run_closure5_closure23#12a14b74] on root
project 'Continuations'."
We are using Gradle 2.1
I have the following questions:
any idea why the first one fails?
Is the extra variable a reasonable solution to approach the problem?
Thanks a lot for your help
solution: install last version.
I had the same problem, I read gradle documentation of gradle 3, but gradle 2.7 was installed.
checked gradle version 2.7
then read gradle 2.7 doc https://docs.gradle.org/2.7/userguide/tutorial_java_projects.html#N103CD , but found no info about sourceSet in java plugin for that version
installed gradle 3 --> problem solved

Gradle java project replace single line in file during build

I have a simple Gradle build script to compile and package (similar to the application plugin) my Java application. The only thing I do not accomplish is to replace the current version number in a simple .properties file.
I have created a file 'src/main/resources/app-info.properties' with a single line 'application.version = #version#'. No I want to replace this version string whenever the file is copied to the build folder (think this happens during the build task).
I already tried a simple solution with ants ReplaceTokens. This one replaced the version but also broke my .png files in the resources..
So is there a simple solution to just replace tokens in one single file during the build task (or whatever task handles the copy to the build folder)?
Thank you for any help!
Ben
====== Edit based on the comment from Opal =====
Based on the hint I have added the following:
import org.apache.tools.ant.filters.ReplaceTokens
// ...
build {
from('src/main/resources') {
include '*.properties'
filter(ReplaceTokens, tokens: [version : project.version])
}
}
Which throws this error:
Could not find method from() for arguments [src/main/resources, build_vbjud9ah7v3pj5e7c5bkm490b$_run_closure6_closure12#43ead1a8] on root project
Seems like I am on the wrong task?
====== Edit for completeness adding the solution based on Opals suggest =====
Thanks man, the following is the working solution!
processResources {
from('src/main/resources') {
include '*.properties'
filter(ReplaceTokens, tokens: [version : project.version])
}
}
Books and blogs alike, including the answer from Opal all recommend using a vivid mixture of exclude/include, from() and filter(). And of course, so did I on my first attempt to replace the text {{app javascript library}} in a index.html file to the path of a JavaScript library which depended on a simple project property setting.
The problem that hit me was that my 'war' task produced duplicated index.html files in the war archive and getting rid of the problem, using the pattern described previously, resulted in one huge unreadable hack.
Then I found a really straight forward solution. The following example is from my own build script and you have to customize it a bit to suite your needs:
war {
eachFile { copyDetails ->
if (copyDetails.path == 'index.html') {
filter { line ->
line.replace('{{app javascript library}}', "lib/someLib.js")
}
}
}
}
Paste sample code. What You need to do is to include file for replacement and exclude other files from replacement. Here is sample usage. Search for ReplaceTokens and You'll see what am I talking about.
You need to add filtering to processResources task. Sample code:
processResources {
def profile = project.properties['profile']
def replace_tokens = profile ? filter_tokens[profile] : filter_tokens['default']
exclude '**/log4j-test.xml'
from('src/main/resources') {
exclude '**/*.ttf'
filter(ReplaceTokens, tokens: replace_tokens)
}
from('src/main/resources') {
include '**/*.ttf'
}
}
Above ttf (binary) files are excluded from filtering but copied. replace_tokens is a filter taken from map defined in other part of the script.

Gradle build results

I'm using Gradle to build my software. However, I find the output it proceduces a bit to minimal. I don't want to use --debug or --info, since that logging is much to verbose. I just want to know what the result in terms of artifacts (zip, jar, dmg, etc) of the Gradle buid is. For example, when I run 'gradle jar', I'd like to print where the jar is created.
I did that using:
jar {
doLast {
println "Jar has been created in ${archivePath}"
}
}
And it nicely prints that the jar has been created in the build/lib directory. However, when I run 'gradle distZip', the artifact is not created in the lib dir, but in the distributions directory. The above however is still printed, but I'd rather not have that: when I run the distZip, I'd like to know where I can find the output of that command, not of every step the distZip depends on.
Never mind, the following will work just nicely:
def artifacts = []
addListener(new TaskExecutionListener() {
void afterExecute(Task task, TaskState state) {
if(task in AbstractArchiveTask) {
artifacts << task.outputs.files.singleFile
}
}
void beforeExecute(Task task) { }
})
addBuildListener(new BuildAdapter() {
void buildFinished(BuildResult result) {
if(artifacts) {
println "\nOutput location: ${artifacts.last()}\n"
}
}
})
This is also available as a gist here.

Categories