Running task after only if another task is not up-to-date - gradle

I have a task that I want to run in gradle after the basic 'classes' task runs, but only if it was not UP-TO-DATE.
How can I model this in gradle?
The problem I'm trying to solve is that I have a wsgen task that I use to generate a wsdl file. The source that is used as a basis of the wsdl has changed, but the wsgen task still says it's up-to-date since the output file exists. I want to delete the wsdl file if the build/compile task ran and was not up-to-date.
Let's say I have my task as follows:
task deleteGeneratedWsdl() {
// delete file here.
}
// I Only want this to run if classes was not UP-TO-DATE
deleteGeneratedWsdl.dependsOn classes
Here is my wsgen task:
task wsgen() {
def inputDir = file('build/classes/main')
def dstDir = file('build/wsdl')
def outputWsdl = file('build/wsdl/MyWSDL.wsdl')
inputs.dir inputDir
outputs.file outputWsdl
doLast{
dstDir.mkdirs()
ant {
taskdef(name:'wsgen',
classname:'com.sun.tools.ws.ant.WsGen',
classpath:configurations.jaxws.asPath)
wsgen(keep:true,
destdir: dstDir,
genwsdl:'true',
classpath:'build/classes/main;../UTIL_PROJ/build/classes/main',
sei:'my.path.SourceFile')
}
}
}
wsgen.dependsOn classes
build.dependsOn wsgen

Related

Error using dependsOn to call task from another gradle file

I am working with a build.gradle file that has multiple ways to specify executions for a task - setup. To call a task from another gradle file - runtests.gradle, I created a task - testTask and added task dependency using dependsOn, but this implementation does not seem to work and giving out an error like :
Could not find property 'testTask' on root project 'GradleFile
My build file looks like this :
build.gradle
task setup(dependsOn: testTask) <<
{
println "In main execution"
}
// new task
task testTask(type: GradleBuild) {
if (getEnvironmentVariable('RUN_TEST').equalsIgnoreCase("true")) {
buildFile = "../Behave/runtests.gradle"
tasks = ['mainTask']
}
else {
println "Exiting runTests Task"
}
}
setup.doFirst {
println "In first execution"
}
setup.doLast {
println "In last execution"
}
D:\>gradle -q GradleFile/build.gradle setup
I am not looking to make much changes to existing tasks, so is there any other workaround I should try?
I have been through many links but could not find anything that suits this scenario. Looking for suggestions please.
Gradle is sensitive to the ordering of tasks in the build script if a task instance is given in the dependsOn. The task setup depends on task (instance) testTask which, at the moment the build script is compiled, doesn't exist yet. The most common options to solve the issue are:
Define task setup below testTask:
task testTask(type: GradleBuild) {
}
task setup(dependsOn: testTask) {
}
Use a relative path to the task, i.e. the task's name, in the dependsOn
task setup(dependsOn: 'testTask') {
}
task testTask(type: GradleBuild) {
}
Please find more details in Javadoc of Task.

How can I get Gradle to build dependencies before running my task?

Background: I am trying to hook the compiler for my own domain-specific language into Gradle. The DSL is compiled to Java source code, so I have built a task that runs before the Java compiler. The compiler cannot currently handle multiple projects with dependencies, so I'm trying to add that.
My DSL has packages like Java that get mapped to identical Java packages. The same should be true for projects. In that case, for each project, the DSL sources get compiled to Java source code, as well as meta-data (a JSON file per compiled class, containing information from the DSL's type system that cannot be mapped to Java types). When project A depends on B, the DSL compilation process for A needs the meta-data files from B. That meta-data should be packaged as resources into the JAR file together with the generated and compiled Java code, as well as possibly hand-written and compiled Java code.
FoobarPlugin.groovy:
class FoobarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
// create the compileFoobar task
CompileFoobarTask task = project.getTasks().create('compileFoobar', CompileFoobarTask.class);
task.group = 'build';
task.setDescription('Compiles Foobar to Java code.');
task.sourceDirectory = new File(project.projectDir, "src/main/foobar");
task.outputDirectory = new File(project.getBuildDir(), "foobar-java");
// compileFoobar must run before compiling Java code
project.tasks.compileJava.dependsOn(task);
// add the task's output folders as Java source folders
project.sourceSets.main.java.srcDirs += task.outputDirectory;
project.sourceSets.main.resources.srcDirs += task.outputDirectory;
project.sourceSets.test.java.srcDirs += task.outputDirectory;
project.sourceSets.test.resources.srcDirs += task.outputDirectory;
// Turn project dependencies into task dependencies. We have to delay this until the end of the configuration
// phase because project dependencies are not fully known until then.
project.gradle.addBuildListener(new BuildAdapter() {
#Override
void projectsEvaluated(Gradle gradle) {
project.configurations.compile.each {
task.dependencyOutputs += it
}
}
});
}
}
CompileFoobarTask.groovy:
class CompileFoobarTask extends DefaultTask {
#InputDirectory
File sourceDirectory;
#InputFiles
List<File> dependencyOutputs = new ArrayList<>();
#OutputDirectory
File outputDirectory;
#TaskAction
void run() {
FileUtils.write(new File(outputDirectory, "timestamp"), "" + System.currentTimeMillis(), StandardCharsets.UTF_8);
}
}
build.gradle from project A:
apply plugin: 'java'
apply plugin: foobar.gradle.FoobarPlugin
repositories {
mavenCentral()
}
dependencies {
compile project(':b')
}
build.gradle from project B:
apply plugin: 'java'
apply plugin: foobar.gradle.FoobarPlugin
repositories {
mavenCentral()
}
dependencies {
compile 'org.apache.commons:commons-lang3:3.0'
}
Test runs and output:
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean a:compileFoobar
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :a:compileFoobar
running task ':a:compileFoobar'
BUILD SUCCESSFUL in 1s
4 actionable tasks: 2 executed, 2 up-to-date
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean b:compileFoobar
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :b:compileFoobar
running task ':b:compileFoobar'
BUILD SUCCESSFUL in 469ms
4 actionable tasks: 2 executed, 2 up-to-date
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean a:compileJava
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :a:compileFoobar
running task ':a:compileFoobar'
> Task :b:compileFoobar
running task ':b:compileFoobar'
BUILD SUCCESSFUL in 487ms
7 actionable tasks: 5 executed, 2 up-to-date
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean b:compileJava
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :b:compileFoobar
running task ':b:compileFoobar'
BUILD SUCCESSFUL in 471ms
4 actionable tasks: 3 executed, 1 up-to-date
As you can see, even though I add b.jar as a dependency to a:compileFoobar, Gradle won't build that JAR before running a:compileFoobar. The Java plugin seems to do something different because running a:compileJava WILL build b.jar first. What do I have to do to achieve the same for my task?
What you need to do is to explicitly create a Task dependency between consumer project's compileFoobar task and the producer project's jar task (in your example where project a depends on project b, you need to create task dependency a:compileFoobar -> b.jar)
You can achieve this in your custom plugin, by checking if the current project has dependencies of type ProjectDependency: if so you create the task dependency accordingly.
Code sample (in your plugin apply() method):
// Turn project dependencies into task dependencies. We have to delay this until the end of the configuration
// phase because project dependencies are not fully known until then.
project.gradle.addBuildListener(new BuildAdapter() {
#Override
void projectsEvaluated(Gradle gradle) {
project.configurations.each { config ->
config.dependencies.each { dep ->
if (dep instanceof ProjectDependency) {
def producerProject = ((ProjectDependency) dep).dependencyProject
def producerJarTask = producerProject.tasks.jar
println " **** Project $project.name depends on $producerProject.name"
println " => create dependency between $task to $producerJarTask"
task.dependsOn(producerJarTask)
}
}
}
}
})
Build execution:
$ ./gradlew clean a:compileFoobar
**** Project a depends on b
=> create dependency between task ':a:compileFoobar' to task ':b:jar'
> Task :a:clean
> Task :b:clean
> Task :b:compileFoobar
> Task :b:compileJava NO-SOURCE
> Task :b:processResources NO-SOURCE
> Task :b:classes UP-TO-DATE
> Task :b:jar
> Task :a:compileFoobar

gradle zip task only creates zip in configuration phase

When I try to create a zip file in the execution phase of a Zip typed gradle task no zip file is created.
If I perform the same in the configuration phase (leaving out the doLast statement), the zip file is created without problems.
The doLast block is called since the println statement is shown in the output logging.
The reason that we need to generate the zip in the execution phase is because the file which needs to be compressed is the result of the compile execution.
I have also tried to solve this with a jar task, but this gives me similar problems.
Here's the code:
task createClassPathJar(type: Zip) {
dependsOn("createManifest")
from("${projectRoot}") {
include "MANIFEST.MF"
}
archiveName = "dummy.jar"
doLast {
destinationDir(file("${projectRoot}"))
archiveName = "zipfile.jar"
println "executing phase createClassPathJar. archiveName: " + archiveName
}
}
Can someone help me here ? I'm using Gradle v6.4.1.
You cannot use doLast to configure the action of your task, because it will be executed after the particular action (in this case the zipping) has run. Either use a doFirst closure or setup your task configuration in a way that it does not depend on other configurations:
As an example, depending on how properly your task createManifest defines its output, you may use it directly to define the Zip task content using "from createManifest".
I guess your reason for using a doLast closure is the call to destinationDir that is based on a variable. Instead, you may just use a closure that evaluates the variable lazily:
task createManifest {
outputs.file('path/to/MANIFEST.MF')
}
task createClassPathJar(type: Zip) {
from createManifest
archiveName = 'zipfile.jar'
destinationDir = file({ "${projectRoot}" }) // or just file({ projectRoot })
}
Try with doFirst instead of doLast:
task createClassPathJar(type: Zip) {
dependsOn("createManifest")
from("${projectRoot}") {
include "MANIFEST.MF"
}
archiveName = "dummy.jar"
doFirst {
destinationDir(file("${projectRoot}"))
archiveName = "zipfile.jar"
println "executing phase createClassPathJar. archiveName: " + archiveName
}
}
I don't know about your case but I tried myself by changing the archive name to the current time in the doFirst block, and the archive name was effectively matching the time of execution and not of configuration.

How do I get a task to execute of type Zip using 'dependsOn'?

I am trying to make my task zipGui execute on build target, but the only way I seem to be able to execute a task is if I remove the (type: Zip) from the task definition.
This snippet fails to execute the zipGui task at all:
task zipGui(type: Zip) {
doFirst {
println "==================== Zipping GUI components"
}
doLast {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
}
}
build.dependsOn zipGui
And this executes the zipGui but it doesn't know anything about how to zip files:
task zipGui {
doFirst {
println "==================== Zipping GUI components"
}
doLast {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
}
}
build.dependsOn zipGui
This is a stripped down fragment of the overall build.gradle.
How can I get zipGui to execute as a dependency of the build?
EDIT: here is more of the real build.gradle without me stripping things out to simplify the question:
task copyCert(type: Copy) {
from '../../../install'
into 'assets/certs/root'
include 'ca.pem'
doFirst {
println "==================== Copying root cert into assets"
}
}
task copyGui(dependsOn: copyCert, type: Copy) {
from '../../web/gui'
into 'assets/htdocs'
include '**/*.html'
include '**/*.css'
include '**/*.js'
include '**/*.wav'
include '**/*.tmpl'
include '**/*.png'
include '**/*.gif'
include '**/*.jpg'
exclude '**/*.DS_Store'
exclude '**/.gitignore'
exclude '**/.thumb'
exclude '**/build'
doFirst {
println "==================== Copying gui components into assets"
}
}
task zipGui(dependsOn: copyGui, type: Zip) {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
doFirst {
println "==================== Zipping GUI components"
}
}
Check details of zip task here: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html
task zipGui(type: Zip) {
archiveName = 'gui.zip'
from("$projectDir/../../../install") {
into 'assets/certs/root'
include 'ca.pem'
}
from("$projectDir/../../web/gui"){
into 'assets/htdocs'
exclude '**/*.DS_Store'
exclude '**/.gitignore'
exclude '**/.thumb'
exclude '**/build'
} // Just include or exclude is enough here as you indicated.
destinationDir(file("$buildDir/libs"))
}
Your task simply has nothing to do.
Gradle tasks are only executed, if they have something to do. If there is nothing to do (e.g. no files to zip), a task is skipped. There are multiple reasons for a task to have nothing to do. The main reason are up-to-date checks to prevent Gradle from doing the same thing what was done during the last invocation, at least as long as the task outputs are still available and the task inputs did not change.
However, in your specific case, the task has nothing to do, because at the time the task executes, no input files are specified at all. Files to include into the zip file can be added via from, which you do use. But, you use it inside a doLast closure, which is executed after the task actions (where the zipping takes place) were executed. Therefor, when running the zip process, the configuration did not take place.
You could use the regular configuration closure of the task or even the doFirst closure to configure your task:
task zipGui(type: Zip) {
from 'assets/htdocs'
}
// OR
task zipGui(type: Zip) {
doFirst {
from 'assets/htdocs'
}
}
Some more remarks:
The line FileTree zip = zipTree('assets/htdocs/gui.zip') does absolutely nothing, because the created file tree is not queried. You need to either access the files of the file tree manually or pass it to a task, e.g. your zip task: from zipTree('assets/htdocs/gui.zip')
In the second example, the task is executed because for normal tasks there are no automatic checks if there is any work to do, since a normal task can basically do everything the user makes it do.
I'm not quite sure, but I think the second example should throw an exception, because a normal task without a type does not provide a from method.
To get the status of each executed task and more insight generally, use the command line parameter --console=plain. The parameters -d and --stacktrace / --full-stacktrace may be used to get more information on errors.

Run gradle task multiple times

I have a gradle project containing two modules in subdirectories. The directory structure is as below.
root
module1
build.gradle
module2
build.gradle
build.gradle
settings.gradle
The top level settings.gradle includes the two modules. The top level build.gradle contains the following.
task runScript(type: Exec) {
workingDir 'scripts'
commandLine 'python3', 'myscript.py'
}
project(':module1') {
evaluationDependsOn(':module1')
final test = tasks.findByName('test')
test.dependsOn(runScript)
}
project(':module2') {
evaluationDependsOn(':module2')
final test = tasks.findByName('test')
test.dependsOn(runScript)
}
Task runScript sets the database to a known state and must be run before each module test task.
When I run the test task my script only executes once. How can I ensure it executes multiple times?
$ ./gradlew test
... some output
:runScript
RUNNING MY SCRIPT
:module1:test
RUNNING MODULE1 TESTS
... some output
:module2:test
RUNNING MODULE2 TESTS
Things I Tried
I tried adding outputs.upToDateWhen {false} to the task runScript so Gradle never thinks it is up to date. This didn't make any difference; I assume because the task still only occurs once in the task graph?
I tried replacing the lines containing dependsOn for each module with test.doFirst {runScript.execute()}. This changes when the task gets executed but does not result in multiple executions.
$ ./gradlew test
... some output
:module1:test
RUNNING MY SCRIPT
RUNNING MODULE1 TESTS
... some output
:module2:test
RUNNING MODULE2 TESTS
I tried creating a new task for each module. This works but it's duplicating code.
project(':module1') {
evaluationDependsOn(':module1')
final test = tasks.findByName('test')
task runScript(type: Exec) {
workingDir '../scripts'
commandLine 'python3', 'myscript.py'
}
test.dependsOn(runScript)
}
project(':module2') {
evaluationDependsOn(':module2')
final test = tasks.findByName('test')
task runScript(type: Exec) {
workingDir '../scripts'
commandLine 'python3', 'myscript.py'
}
test.dependsOn(runScript)
}
If your script is necessary for each run of each Test task, simply assure its execution before each Test task. Why even use a task then?
subprojects {
tasks.withType(Test) {
doFirst {
exec {
workingDir 'scripts'
commandLine 'python3', 'myscript.py'
}
}
}
}

Resources