Better to use task dependencies or task.doLast in Gradle? - gradle

After building my final output file with Gradle I want to do 2 things. Update a local version.properties file and copy the final output final to some specific directory for archiving. Let's assume I already have 2 methods implemented that do exactly what I just described, updateVersionProperties() and archiveOutputFile().
I'm know wondering what's the best way to do this...
Alternative A:
assembleRelease.doLast {
updateVersionProperties()
archiveOutputFile()
}
Alternative B:
task myBuildTask(dependsOn: assembleRelease) << {
updateVersionProperties()
archiveOutputFile()
}
And here I would call myBuildTask instead of assembleRelease as in alternative A.
Which one is the recommended way of doing this and why? Is there any advantage of one over the other? Would like some clarification please... :)

Whenever you can, model new activities as separate tasks. (In your case, you might add two more tasks.) This has many advantages:
Better feedback as to which activity is currently executing or failed
Ability to declare task inputs and outputs (reaping all benefits that come from this)
Ability to reuse existing task types
More possibilities for Gradle to execute tasks in parallel
Etc.
Sometimes it isn't easily possible to model an activity as a separate task. (One example is when it's necessary to post-process the outputs of an existing task in-place. Doing this in a separate task would result in the original task never being up-to-date on subsequent runs.) Only then the activity should be attached to an existing task with doLast.

Related

Troubleshooting Gradle Performance Issue

Recently our build times have been fluxuating wildly. Our baseline was between 1-2mins, but now it is sometimes taking up to 20 mins. I have been trying to track down the cause, but am having some trouble. It feels like the changes that trigger fluxuation in the build time are completely arbitrary, or at least I can not find the underlying connection between them.
Here is what the current build time looks like:
Now this obviously looks like the issue lies in Task Execution time, so lets look at that in more detail:
This indicates that the test task for the umlgen-transformer project is the culprit. The only difference between this project and the one that built in ~1min, is one tiny xml file. That xml file is read in by each of the unit tests as input. Here is what a typical test looks like:
#Test
def void testComponentNamesUpdated() {
loadModel("/testData/ProducerConsumer.uml")
val trafo = new ComponentTransformation
trafo.execute(model)
val componentNames = model.allOwnedElements.filter(Component).map[name]
assertThat(componentNames).contains(#{"ProducerComponent", "ConsumerComponent"})
}
(the .uml file is just a UML model stored as xml). There are several reasons why I don't think this is the underlying cause of our increases in build times: 1) the xml file is incredily small, ~200kb 2) the behavior in the unit tests is extremly simple and executes quite fast 3) when the unit tests are run through eclipse they execute in seconds 4) the build times of unrelated aspects, such as the time to compile kotlin code, have also seen a significant increase 5) this is not the only project we are having this issue with.
What I have tried:
Setting org.gradle.parallel = true
Increasing the memory of gradle deamon to 3gb
Updating to gradle 5
Clearing all gradle caches
Building without the gradle daemon
Simplifying the build.gradle scripts. I have removed several plugins and tasks to rule them out as possible causes
I have only been using Gradle for a couple months now, so I am hoping to get feedback on more conclusive ways to find out what gradle is doing (or to find some way to cross it off the list of possible causes). Please let me know if any additional information would be helpful.

Gradle: task's configuration depends on another task's execution

My Gradle build has two task:
findRevision(type: SvnInfo)
buildWAR(type: MavenExec, dependsOn: findRevision)
Both tasks are configuration based, but the buildWAR task depends on a project property that is only defined in the execution phase of the findRevision task.
This breaks the process, as Gradle cannot find said property at the time it tries to configure the buildWAR task.
Is there any way to delay binding or configuration until another task has executed?
In this specific case I can make use of the mavenexec method instead of the MavenExec task type, but what should be done in similar scenarios where no alternative method exists?
Depending on what configuration option exactly you want to change, you might change it in the execution phase of the task with buildWAR.doFirst { }. But generally this is a really bad idea. If you e. g. change something that influences the result of the UP-TO-DATE checks like input files for example, the task might execute though it would not be necessary or even worse do not execute thoug it would be necessary. You can of course make the task always execute to overcome this with outputs.upToDateWhen { false }, but there might be other problems and also this way you disable one of Gradles biggest strenghts.
It is a much better idea to redesign your build so that this is not necessary. For example determining the revision at configuration time already. Depending on whether the task needs much time this might be a viable solution or not. Also depending on what you want to do with the revision, you might consider the suggestion of #LanceJava and make your findRevision task generate a file with the revision in it that is then packaged into the WAR and used at runtime.

"Up To Date" Gradle task status when it has no output

How can you correctly mark a Gradle task as being "up to date" when the task doesn't produce any output? The task should remain "up to date" provided the last run was successful and the inputs haven't changed since then. The Gradle guide states just before section 15.9.2, the following:
"A task with no defined outputs will never be considered up-to-date."
How is it possible to mark tasks as up to date in this case? It appears that Gradle needs to know the time of the last successful run and then compare that to the last modified time of the inputs. As a workaround the script could create / touch an empty file to mark the task as complete? Are there any other suggested workarounds?
To just think through the different scenarios...
Tasks without any inputs or outputs. These run all the time. This might be just wrapping an existing "do something" executable.
Tasks with inputs and outputs. These run when either the inputs or outputs change. This might be a compiler.
Tasks with just outputs. These run only when the outputs have changed/don't exist. This might be something that downloads something. (I think these are pretty rare in reality, I'd count the URL to download as an input.)
Tasks with inputs and no outputs. I haven't run into these in practice.
Like you've said, you could cheat the up-to-date checks with an output file that is just empty. The built-in Gradle Test task is most similar to what you're describing and it has a "report" as its output. I think you would probably have something similar too. It could be as simple as capturing the stdout/stderr of the task and putting that into a file. That's not too useful for when everything passes, but it would be useful for when things fail.
Of course, any of these could be supplemented with a custom upToDateWhen bit of code. e.g., you have a task that starts a webserver and it's "up-to-date" when the webserver is already running. I don't think that's a good fit with what you're describing here.
To start out with, I'd try:
outputs.files file("${buildDir}/reports/${name}.out")
I think that'd work with or without actually putting something in the file.
You can override the tasks upToDateWhen method.
task createDist(type: Zip) {
outputs.upToDateWhen {
return true
}
}
So with true it is always up-of-date and false always out-of-date. You can define any kind of custom logic to determine if it is up to date.
Tasks with inputs and no outputs
Unless you explicitly declare the output, Gradle will decide the output is out-of-date to stay build safe.
You should always patently declare both inputs and outputs.
Use outputs.upToDateWhen { true } to simultaneously declare outputs and indicate that your outputs are always up-to-date.
For example (in Kotlin):
task ("dataFileHandler") {
inputs.file ("data-file.txt")
outputs.upToDateWhen { true }
doLast {
// do something with "data-file.txt"
}
}
This task will be executed once (on the first build) and will remain “UP-TO-DATA” until “data-file.txt” is changed.

Optionally build multiple jobs in a row

We currently have created several jobs for our components. These components all depend on each other like the following:
A -> B -> C
Currently it is possible to run these jobs separately independent from each other. If someone is running C the build uses A and B artifacts from a previous build.
Now it should be possible to optionally build these jobs in a row. My first thought was some kind of a BuildAll-job which starts the other jobs in the right order, but it does not seem to be possible to start other jobs in a buildstep.
Solving this by using the Build other projects-option is not a solution, because this would always trigger the other builds if someone e.g. starts A.
So anyone got an idea on how to solve this? Is something like this possible? Perhaps I missed an option/plugin to use other jobs as buildsteps?
I would look at using the Parameterized Trigger plugin:
https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Trigger+Plugin
It allows you to trigger another job as a build step, with parameters if you need them. This would allow you to create BuildAll job that calls A, then B, then C in sequence.
Have you considered:
https://wiki.jenkins-ci.org/display/JENKINS/Join+Plugin
This can help you with the "Build-All" step if you want to go down that path.
However, one part that I do not understand is that,
if A -> B -> C,
how are any optional? If you can clarify, might be able to help you better.

Design to distribute work when generating task oriented input for legacy dos application?

I'm attempting to automate a really old dos application. I've decided the best way to do this is via input redirection. The legacy app (menu driven) has many tasks within tasks with branching logic. In order to easily understand and reuse the input for these tasks, I'd like to break them into bit size pieces. Since I'll need to start a fresh app on each run, repeating a context to consume a bit might be messy.
I'd like to create an object model that:
allows me to concentrate on the task at hand
allows me to reuse common tasks from different start points
prevents me from calling a task from the wrong start point
To be more explicit, given I have the following task hierarchy:
START
A
A1
A1a
A1b
A2
A2a
B
B1
B1a
I'd like an object model that lets me generate an input file for task "A1b" buy using building blocks like:
START -> do_A, do_A1, do_A1b
but prevents me from:
START -> do_A1 // because I'm assuming a different call chain from above
This will help me write "do_A1b" because I can always assume the same starting context and will simplify writing "do_A1a" because it has THE SAME starting context. What patterns will help me out here? I'm using ruby at the moment so if dynamic language features can help, I'm game.
EDIT: after re-reading your question, I realized I misunderstood it. Let me answer what you actually asked...
I would create a hierarchy of classes. The simplest ones would be have functions like "do task A1b" that would output the appropriate steps to accomplish this. On top of that, I would build functions that would call the sub-tasks in specific orders to accomplish specific goals.
Pretending VIM was the program being controlled, the first level tasks would be things like 'Enter insert mode' 'Enter command mode' 'write the file' or 'input this arbitrary set of inputs'. On top of this I would build functions like 'insert "foobar" into the open file at the start of line 5' which would call the lower-level tasks.

Resources