gradle generated file from a task not available in final jar - gradle

I have set up some tasks compiling a libGdx project in Android Studio. What I want is, that a dynamic file "version.txt" is created in the assets folder. This file must be available in the final jar-output.
Here is how I did the setup:
libGdx provides me with a dist task.
I created two tasks on top of that: buildDebugVersion and buildReleaseVersion.
Both shall create a version.txt, one containing debug information and the other no debug info.
libGdx' original dist task
task dist(type: Jar) {
manifest {
attributes 'Main-Class': project.mainClassName
}
//processResources.dependsOn tasks.updateVersionFileRelease
dependsOn configurations.runtimeClasspath
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
}
my debug/release tasks
task createDebugVersion {
outputs.upToDateWhen { false }
doFirst {
modifyVersionFile(1)
}
}
task createReleaseVersion {
outputs.upToDateWhen { false }
doFirst {
modifyVersionFile(0)
}
}
task dependencies
To put that all in the right running order, I created
createDebugVersion.finalizedBy(tasks.dist)
createReleaseVersion.finalizedBy(tasks.dist)
dist.mustRunAfter(tasks.createDebugVersion, tasks.createReleaseVersion)
My expectation was:
createdebug/release runs and creates the file
THEN dist runs
This seems to happen, when I look at the output. You can see, what file is generated:
Here is the output when I run it:
> Task :desktop:createReleaseVersion
Generated version file is:
--------------------------
Version=0.1.113
BuildDate=2020-04-24
--------------------------
BUILD SUCCESSFUL in 5s
10 actionable tasks: 9 executed, 1 up-to-date
...but when I run the program, I see this version information:
The file contained in the jar is alway 1 or 2 versions behind, as if it would've been taken out of some cache folder.
What scares me is the question: How many of the other assets are now 1 or 2 versions behind, too?
From where does gradle take this file?
I was not able to find it out so far.
what I already tried
I run this through a .cmd script. Before starting the gradle task, I already delete all build folders (and tested it, everything is removed before the build starts)
(the variable %TASK% contains either createDebugVersion or createReleaseVersion)
ECHO Forcing resources rebuild
RD /S /Q .\desktop\build
RD /S /Q .\android\build
ECHO Compiling distribution version of %GAMENAME%...
CALL gradlew desktop:clean desktop:build %TASK% --rerun-tasks
But still, out of some "ghost-galaxy-space", a file that is two builds old is taken from somewhere and put into the jar...
Any help greatly appreciated!

Ok, I figured it out - maybe it helps others if they run into a similar issue.
The error was here:
CALL gradlew desktop:clean desktop:build %TASK% --rerun-tasks
If you look closely, you see FIRST desktop:clean runs, THEN desktop:build runs, which causes a full-rebuild due to the deleted caches and build folders, and THEN my %TASK% runs - as third in order!
So, my task runs after the build. Even when I put up the build chain with
createDebugVersion.finalizedBy(tasks.dist)
createReleaseVersion.finalizedBy(tasks.dist)
this all only runs after the build is already done. And the dist task has no clean in this build chain so it uses the output of the build generated by the second parameter of my gradlew call!
The solution
I simply changed how I run gradlew:
my command script now calls
call gradlew desktop:clean %TASK% --rerun-tasks
so a clean is still forced, but the build only happens as a reaction from dist because it can't find any binaries, so it has to build them right now.
And then the build finally runs after my file has been written to the assets folder.
Hope this helps someone some day!
cheers, gris

Related

Running Gradle root project task from subproject

I am facing the following problem. I have multi module Gradle project. One module is my root project and the second module are integration tests.
In order for my integration tests to run, a copy task needs to run first in order to move some assets to a folder on the root directory of the project.
I already have such a task defined on my root project which when I try to invoke does nothing (I have tried several different way of calling it).
Since this was failing I went ahead and created the following task on the subproject itself:
task prepareTestAssets(type: Copy) {
description = "Copies the needed jars from the root project output build dir"
copy {
from rootProject.configurations.compileClasspath
into ("${rootProject.rootDir}/classes")
rename { fileName -> fileName.replace '-internal', '' }
rename 'ads-(.+).jar', 'ads.jar'
fileMode 0755
}
copy {
from ("${rootProject.buildDir}/libs")
into ("${rootProject.rootDir}/classes")
fileMode 0755
}
}
Which I by having another task depend on it. My goal for this is to have it copy the root project classes as well as the jar generated under build/libs into a single directory (needed by the integration tests to run).
My problem is that when this runs, it seemingly finds no source and keeps failing.
Can anyone help me troubleshoot why I cannot copy the root project's assets from the context of the subproject
It seems you should leave out the copy{} closure and specify its contents directly.
The configuration intended for the task is otherwise given to the closure, so the task thinks it has no configuration (it doesn't know to look for a closure called "copy").

Execute gradle tasks on sub projects in order

we have a project structure such that
ParentProject
projA
projB
projC
projD
...
We have a bunch of tasks that we run on these projects from the parent project, calling gradle clean build make tag push remove
clean: removes the build directory for each project
build: compiles the source code
make: uses the compiled code to create a docker image (custom task)
tag: tags the docker image (custom task)
push: pushes the tagged docker image to our nexus repo (custom task)
remove: removes the local docker image (custom task)
all the tasks work as intended, but not exactly in the order we want.
our issue is disk space. creating all the docker images (we have many to make) takes several gigabytes of space. Gradle fails because it runs out of disk space before reaching the end. We thought we could solve this issue by simply removing the image after it is pushed and free the disk space, but the way gradle runs, that does not work.
Current, gradle executes in the following manner:
projA:clean
projB:clean
projC:clean
...
projA:build
projB:build
projC:build
...
projA:make
projB:make
projC:make
...
going through like this, everything tries to build before anything gets removed. The way we would like to run gradle is as follows:
projA:clean
projA:build
projA:make
projA:tag
projA:push
projA:remove
projB:clean
projB:build
projB:make
projB:tag
projB:push
projB:remove
...
this way the project cleans itself up and frees disk space before the next project starts building.
is there any way to do this?
edit: There may be times where not every task needs to be run. The image may already built and simply needs to be retagged. Or may need to be built for local testing, but not tagged or pushed.
Looks like tasks make, tag, push, remove are custom tasks.
If you want dependency among tasks, for example taskX should be executed whenever taskY is started, then you should use dependsOn property of task.It is also called as task dependencies.
below shows the build.gradle on parent project that make task of projA dependsOn clean task of projA. you should do this for remaining tasks of projA.
project('projA') {
task make(dependsOn: ':projA:build') {
doLast {
println 'make'
}
}
}
project('projA') {
task build {
doLast {
println 'build'
}
}
}
...
...
Then you should continue for remaining projects and remaining tasks. For example, ProjB:clean should dependOn projA:remove
project('projB') {
task clean(dependsOn: ':projA:remove') {
doLast {
println 'clean'
}
}
}
continue for other tasks in project B and remaining projects.
For more details please follow this link.
We found we can create a task of type 'GradleBuild' where we can specify what tasks we want to run within a single task.
task deploy(type: GradleBuild){
tasks = ['clean','build','make','tag','push','remove']
}
this solution prevents linking of tasks with dependencies, so we can still build sub projects individually without worrying about other tasks being run unnecessarily.

Gradle - Error Copying Files To Project Root

I’m getting the following error whenever I attempt to use a Copy task to copy a file into the root of a project (the same folder I’m running gradle from):
Failed to create MD5 hash for file content.
I thought this was related to the artifacts I was pulling from Artifactory, but that seems to be unrelated. I was able to get the same results with a minimal script.
Is there something obviously wrong with what I’m doing, or does Gradle intentionally disallow such things?
task fails(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath
}
task works(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath + '/output'
}
Short Answer: Don't copy into the project directory, you are best to use into "$buildDir/someFolder" so that the folder is isolated to this single task, and also so that it will be cleaned by gradle clean
Long Answer: At it's core, Gradle has the concept of an "UP-TO-DATE" check for every single task. If Gradle sees that nothing has changed since last time a task was executed it will use the old result instead of executing again.
UP-TO-DATE checking is implemented by taking a "hash" of the task inputs and task outputs. Since you are using into '.' that means that the entire contents of the project directory is considered a task output (bad)
Gradle uses the .gradle folder for temp files (eg task hashes) It's likely some of these files are locked for writing as Gradle is trying to also read the same files (to calculate the "hash" of the task outputs) causing the error you are seeing
* EDIT *
If you need to copy into the project directory for legacy reasons, you might use Project.copy(...) directly instead of a Copy task. You could manually manage the task inputs/outputs in this case
Eg
task customCopy {
inputs.file "$buildDir/someFile.txt"
outputs.file 'someFile.txt'
doLast {
copy {
from "$buildDir/someFile.txt"
into '.'
}
}
}
Can you believe it, the following works
task myCopy(type: Copy) {
from "$rootDir/app1/src/main/resources/db"
into "$rootDir/app2/src/test/resources/db"
}
test.dependsOn myCopy
and the following doesn't 🤦
task myCopy(type: Copy) {
from '$rootDir/app1/src/main/resources'
into '$rootDir/app2/src/test/resources'
}
test.dependsOn myCopy

Getting a list of all Gradle tasks from build script

So I'm trying to write a list of all gradle tasks to a file. I of course could use the tasks command for this, but I want cache it to a file every time any other gradle command is called. So whenever I run ./gradlew build for example, I want the available tasks to be written to a file.
This seemed simple enough, and I wrote the below task to try it out:
task cacheTasks() {
doLast {
allprojects { p ->
p.tasks*.each { t ->
println(p.name + ":" + t.name)
}
}
}
}
The problem is, that I only get a sub-set of all the tasks available. When I run the ./gradlew tasks --all command, many more are printed. It seems that none of the built-in tasks (like build, clean or help) are in the tasks* List when I loop over it, but oddly enough I can reference them directly:
tasks.build { t ->
println("DEBUG:" + t.name)
}
It seems so simple, yet I've been searching in vain for a solution. I even tried looking in the gradle source code to see how the tasks Task works, but I couldn't find any clue as to why this doesn't work.
rootProject.getAllTasks(true) looks like it's retrieving more tasks than rootProject.tasks.
I highly doubt there is a task class in container with name build.
This is what I get when I debug your task:
I am not saying gradle build does not run, but it can be in other forms maybe an instance of org.gradle.api.tasks.GradleBuild. (I am not very sure because the gradle source code is very hard for me to compile and run).
When using
tasks.build { t ->
println("DEBUG:" + t.name)
}
You actually call org.gradle.api.tasks.TaskContainer#create(java.util.Map<java.lang.String,?>, groovy.lang.Closure) and create a new task named build.

Zip task marked as up-to-date

Hi I try to collect plugins from sub-folder, zip them and copy to my export folder.
task buildPlugin {
dependsOn ':empty-plugin:build'
}
task exportPlugin(type: Zip) {
dependsOn buildPlugin
// create new export folder as destination for nightly build
def folder = '/export';
def file = "${project.name}-sdk-${project.version}";
// collect all plugins into cwc-sdk zip file
baseName = file
fileTree("cwc-plugin").each({
if (it.name.endsWith(".zip")) {
from it.absolutePath
}
})
// move cwc-sdk zip file into export destination folder
copy { into folder from file }
delete file
}
I run clean task first. The gradle logs:
:api:compileJava
:api:processResources
:api:classes
:api:jar
:empty-plugin:compileJava
:empty-plugin:processResources
:empty-plugin:classes
:empty-plugin:jar
:empty-plugin:assemble
:empty-plugin:compileTestJava UP-TO-DATE
:empty-plugin:processTestResources UP-TO-DATE
:empty-plugin:testClasses UP-TO-DATE
:empty-plugin:test UP-TO-DATE
:empty-plugin:check UP-TO-DATE
:api:javadoc
:empty-plugin:zip
:empty-plugin:build
:buildPlugin
:exportPlugin UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.097 secs
While first run :exportPlugin is marked as UP-TO-DATE and I don't get the zipped file from build. When I run :exportPlugin again everything is fine. It's also fine when I chain both tasks manually (rungradle clean, next run gradle buildPlugin, run gradle exportPlugin by doublclick to tasks at IDEA)
I think the order of tasks are still ok. I don't need to work with mustRunAfter.
I also played around with copySpec, buildplugin.outputs.files. But nothing helps.
Can anybody help me to solve this issue for initial build run?
Thanks!
Update:
A Zip task is an abstracted Copy task
AbstractCopyTask is the base class for all copy tasks. (Docu)
I found this comment from Peter Niederwieser
A Copy task only gets executed if it has something to copy. Telling it what to copy is part of configuring the task, and therefore needs to be done in the configuration phase, rather than the execution phase.
How do I change from it.absolutePath code line inside fileTree loop to be part during configuration phase?

Resources