In Gradle it is easy to define tasks to run after the build.
task finalize1 << {
println('finally1!')
}
build.finalizedBy(finalize1)
This works as expected. But now I want to execute a copy task at the end.
task finalize (type: Copy) {
def zipFile = file('data/xx.zip')
def outputDir = file("data/")
println('Unzip..')
from zipTree(zipFile)
into outputDir
}
build.finalizedBy(finalize)
This does not work anymore. I see the "Unzip" output at the beginning of the build (I need the extract at the end).
Unzip..
:clean
:compileJava
:processResources
:classes
:findMainClass
:jar
:bootRepackage
:assemble
...
<< does the trick it seems but how I can I merge these two?
You don't have to. You see Unzip... at the beginning of the build, but it does not mean that Gradle is executing your task at that moment.
This message is printed in console when Gradle starts to configure your copy task, e.g. adding paths to inputs and outputs. Real execution is done after build. To verify that you can use doLast closure:
task finalize (type: Copy) {
doLast { println 'running now' }
...
}
Code inside doLast block will be executed after build.
P.S. Don't move the rest of your task code (from zipTree(zipFile), etc) inside doLast closure, it won't work.
Related
I have my grade script set up.
When I execute the Gradle build, everything is working and it runs the jUnit tests.
After that when I run the Gradle test I get the following:
C:\Users\..\..\Project>gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
When I perform gradle clean, then Gradle build works, of course...
I want to be able to reset only the tests, not build the whole project: how should I do this?
One option would be using the --rerun-tasks flag in the Forcing tasks to execute section. This would rerun all the the test task and all the tasks it depends on.
If you're only interested in rerunning the tests then another option would be to make gradle clean the tests results before executing the tests. This can be done using the cleanTest task.
Some background - the Java plugin defines a clean tasks to each of the other tasks. According to the Tasks documentation:
cleanTaskName - Deletes files created by specified task. cleanJar will delete the JAR file created by the jar task, and cleanTest will delete the test results created by the test task.
Therefore, all you need in order to re-run your tests is to also run the cleanTest task, i.e.:
gradle cleanTest test
Other option would be to add following in your build.gradle:
test.outputs.upToDateWhen {false}
This was recently the topic on Gradle's blog post Stop rerunning your tests. The author shows an example using outputs.upToDateWhen { false } and explains why it is wrong:
This doesn’t actually force reruns
What the author of this snippet probably wanted to say is “Always rerun my tests”. That’s not what this snippet does though. It will only mark the task out-of-date, forcing Gradle to recreate the output. But here’s the thing, if the build cache is enabled, Gradle doesn’t need to run the task to recreate the output. It will find an entry in the cache and unpack the result into the test’s output directory.
The same is true for this snippet:
test.dependsOn cleanTest
Gradle will unpack the test results from the build cache after the output has been cleaned, so nothing will be rerun. In short, these snippets are creating a very expensive no-op.
If you’re now thinking “Okay, I’ll deactivate the cache too”, let me tell you why you shouldn’t.
Then, the author goes on to explain why rerunning some tests is a waste of time:
The vast majority of your tests should be deterministic, i.e. given the same inputs they should produce the same result.
In the few cases where you do want to rerun tests where the code has not changed, you should model them as an input. Here are both examples from the blog post that show adding an input so the task will use it during its up-to-date checks.
task randomizedTest(type: Test) {
systemProperty "random.testing.seed", new Random().nextInt()
}
task systemIntegrationTest(type: Test) {
inputs.property "integration.date", LocalDate.now()
}
I recommend reading the entire blog post.
gradle test --rerun-tasks
Specifies that any task optimization is ignored.
Source: https://gradle.org/docs/current/userguide/gradle_command_line.html
--rerun-tasks works, but is inefficient as it reruns all tasks.
cleanTest by itself may not suffice due to build cache.
so, best way to accomplish this is:
./gradlew --no-build-cache cleanTest test
Here's a solution using the "build.gradle" file, in case you don't want to modify your command line:
test {
dependsOn 'cleanTest'
//Your previous task details (if any)
}
And here's the output. Notice 2 changes from your previous output:
1) A new 'cleanTest' task appears in the output.
2) 'test' is always cleaned (i.e. never 'UP-TO-DATE') so it gets executed every time:
$ gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:findMainClass
:jar
:bootRepackage
:assemble
:cleanTest
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
:check
:build
Also,
having to add --rerun-tasks is really redundant. Never happens. Create a --no-rerun-tasks and make --rerun-tasks default when cleanTask
TL;DR
test.dependsOn cleanTest
None of the above methods worked for me.
What worked for me was simply removing all items from the cache directory in /Users/<username>/.gradle/caches/build-cache-1/.
I try the following code:
roroco#roroco ~/Dropbox/jvs/ro-idea $ gradle test --tests "ro.idea.ToggleTest.testIsAd"
:ro:compileJava UP-TO-DATE
:ro:processResources UP-TO-DATE
:ro:classes UP-TO-DATE
:ro:jar
:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:ro:compileTestJava UP-TO-DATE
:ro:processTestResources UP-TO-DATE
:ro:testClasses UP-TO-DATE
:ro:test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':ro:test'.
> No tests found for given includes: [ro.idea.ToggleTest.testIsAd]
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
The output show "No tests found for given includes", my question is: how to list all "given tests" and how to specify "given tests"
this is my old question
I am unsure about list all the given tests prior to executing since I don't believe this is known until the testing actually executes.
What you could so is add this to your build.gradle file:
test {
beforeTest { descriptor ->
logger.lifecycle("Running test: ${descriptor}")
}
}
Then if you go:
gradle clean test
It will run all tests but will also print out the test descriptor before it executes providing the method(className) which will look like so:
:test
Running test: test testC(org.gradle.MySecondTest)
Running test: test testD(org.gradle.MySecondTest)
Running test: test testA(org.gradle.MyFirstTest)
Running test: test testB(org.gradle.MyFirstTest)
Alternatively you can just run the previous command without the build.gradle file change and look at your build/reports/tests/index.html file which will show all tests run.
So then you could specify a single test with:
gradle clean test --tests "org.gradle.MyFirstTest.testA"
Or all tests in a class:
gradle clean test --tests "org.gradle.MyFirstTest"
Or all tests in a package:
gradle clean test --tests "org.gradle.*"
You can also use:
test {
testLogging {
events "passed", "skipped", "failed"
}
}
I have simple build.gradle (or any build.gradle with task that has println)
println GradleVersion.current().prettyPrint()
task task1{
println 'task1 starting'
}
Now when I run $ gradle build I always see tasks executing or print output
task1 starting
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build
BUILD SUCCESSFUL
Total time: 1.291 secs
Why there is always output from println inside tasks?
If You have the following piece of code:
task task1 {
println 'task1 starting'
}
You're in configuration phase of a task. This phase is run during script evaluation. If You'd like to print something while task is executed You need to add an action for task.
It looks like:
task task1 << {
println 'task1 action'
}
This piece of code will be evaluated while the task is being run. << is exactly the same as invoking doLast method on Task's object. You can add many actions.
EDIT
I also highly encourage you to read this blog post.
from Chapter 55. The Build Lifecycle http://www.gradle.org/docs/current/userguide/build_lifecycle.html
// in `settings.gradle`
// println 'This is executed during the initialization phase.'
println 'This is executed during the configuration phase.'
task configure {
println 'This is also executed during the configuration phase.'
}
task execute << {
println 'This is executed during the execution phase.'
}
run with gradle help
output:
This is executed during the initialization phase.
This is executed during the configuration phase.
This is also executed during the configuration phase.
:help
Welcome to Gradle 1.10.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
BUILD SUCCESSFUL
Total time: 1.882 secs
There is simple Eclipse plugin to run Gradle, that just uses command line way to launch gradle.
What is gradle analog for maven compile and run
mvn compile exec:java -Dexec.mainClass=example.Example
This way any project with gradle.build could be run.
UPDATE: There was similar question What is the gradle equivalent of maven's exec plugin for running Java apps? asked before, but solution suggested altering every project build.gradle
package runclass;
public class RunClass {
public static void main(String[] args) {
System.out.println("app is running!");
}
}
Then executing gradle run -DmainClass=runclass.RunClass
:run FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':run'.
> No main class specified
There is no direct equivalent to mvn exec:java in gradle, you need to either apply the application plugin or have a JavaExec task.
application plugin
Activate the plugin:
plugins {
id 'application'
...
}
Configure it as follows:
application {
mainClassName = project.hasProperty("mainClass") ? project.getProperty("mainClass") : "NULL"
}
On the command line, write
$ gradle -PmainClass=Boo run
JavaExec task
Define a task, let's say execute:
task execute(type:JavaExec) {
main = project.hasProperty("mainClass") ? getProperty("mainClass") : "NULL"
classpath = sourceSets.main.runtimeClasspath
}
To run, write gradle -PmainClass=Boo execute. You get
$ gradle -PmainClass=Boo execute
:compileJava
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes
:execute
I am BOO!
mainClass is a property passed in dynamically at command line. classpath is set to pickup the latest classes.
If you do not pass in the mainClass property, both of the approaches fail as expected.
$ gradle execute
FAILURE: Build failed with an exception.
* Where:
Build file 'xxxx/build.gradle' line: 4
* What went wrong:
A problem occurred evaluating root project 'Foo'.
> Could not find property 'mainClass' on task ':execute'.
You just need to use the Gradle Application plugin:
apply plugin:'application'
mainClass = "org.gradle.sample.Main"
And then simply gradle run.
As Teresa points out, you can also configure mainClass as a system property and run with a command line argument.
Expanding on First Zero's answer, I'm guess you want something where you can also run gradle build without errors.
Both gradle build and gradle -PmainClass=foo runApp work with this:
task runApp(type:JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = project.hasProperty("mainClass") ? project.getProperty("mainClass") : "package.MyDefaultMain"
}
where you set your default main class.
You can parameterise it and pass gradle clean build -Pprokey=goodbye
task choiceMyMainClass(type: JavaExec) {
group = "Execution"
description = "Run Option main class with JavaExecTask"
classpath = sourceSets.main.runtimeClasspath
if (project.hasProperty('prokey')){
if (prokey == 'hello'){
main = 'com.sam.home.HelloWorld'
}
else if (prokey == 'goodbye'){
main = 'com.sam.home.GoodBye'
}
} else {
println 'Invalid value is enterrd';
// println 'Invalid value is enterrd'+ project.prokey;
}
Gradle, being an incremental build system, is supposed to detect when no changes to source or outputs have been made, and skip tasks when appropriate to save on build time.
However, in my build, subsequent executions of a gradle task, with no changes in between, this incremental feature is not working. compileJava, jar, etc are executing with every build rather than only when changes have been made.
Our build is pretty complex (switching to gradle from a very old, very messy ant build), so I'm just going to show a small snippet:
buildDir = 'build-server'
jar {
sourceSets.main.java.srcDirs = ['src',
'../otherProject/src']
sourceSets.main.java {
include 'com/ourCompany/pkgone/allocation/**'
include 'com/ourCompany/pkgone/authenticationmngr/**'
...
//Excludes from all VOBs
exclude 'com/ourCompany/pkgtwo/polling/**'
}
sourceSets.main.resources.srcDirs = ['cotsConfig/ejbconfig']
sourceSets.main.resources {
include 'META-INF/**'
}
}
dependencies {
compile project(':common')
}
Running gradle jar on this project twice in a row results in the following output:
P:\Project>gradlew jar
:common:compileJava
:common:processResources UP-TO-DATE
:common:classes
:common:jar
:clnt:compileJava
:clnt:processResources UP-TO-DATE
:clnt:classes
:clnt:jar
BUILD SUCCESSFUL
Total time: 1 mins 46.802 secs
P:\Project>gradlew jar
:common:compileJava
:common:processResources UP-TO-DATE
:common:classes
:common:jar
:clnt:compileJava
:clnt:processResources UP-TO-DATE
:clnt:classes
:clnt:jar
BUILD SUCCESSFUL
My question is, what might I be doing that would prevent the up-to-date detection to not work properly? Could it be related to my complicated build path?
When you run with --info, Gradle will tell you why the task isn't up-to-date.
Two things you need to do
Check if your gradle version is above 2.1
There is a way setting incremental build:
tasks.withType(JavaCompile) {
options.incremental = true // one flag, and things will get MUCH faster
}
Reference is here: https://blog.gradle.org/incremental-compiler-avoidance