I have a problem with gradle's task dependencies.
I have a version.properties file which must be cleared (on clean task) or filled (on war task) with a version info.
Therefore I set this dependency :
clean.dependsOn clearVersionProperties
and
processResources.dependsOn ([defaultValues,infoEnv])
compileJsps.dependsOn(classes, tomcatJasper, xmlModify)
war.dependsOn(compileJsps, svnRevision, writeVersionProperties)
My tasks have been externalized in a commons.gradle and look like this:
...
def writeVersionFile(String whatToWrite) {
File f = new File(project.webAppDirName+'/WEB-INF/version.properties');
if (f.exists()) { f.delete(); }
f = new File(project.webAppDirName+'/WEB-INF/version.properties');
FileOutputStream os = new FileOutputStream(f);
os.write(whatToWrite.getBytes());
os.flush();
os.close();
}
task clearVersionProperties() {
println "Clearing version file"
String whatToWrite = "version=#version#"
writeVersionFile(whatToWrite)
}
task writeVersionProperties(dependsOn: svnRevision) {
println "Writing version file"
String whatToWrite = "version="+project.ext.revision;
writeVersionFile(whatToWrite)
}
...
From my understanding clean will now call clearVersionProperties and war will call writeVersionProperties.
But when I execute a gradle clean, the reactor plan looks like this :
C:\devtools\....\branches\merge\Application>gradle -bbuild20.gradle clean -Ptomcat=7 -Ptarget=live
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: http://gradle.org/
docs/2.0/userguide/gradle_daemon.html.
_______________________________________________________________________________
### building LIVE system
_______________________________________________________________________________
Clearing version file
Writing version file
JSP compilation...
TC7 Sources integrated
________________________________________________________
Compiling JSPs against Tomcat 7.0.11
________________________________________________________
:clearVersionProperties UP-TO-DATE
:clean UP-TO-DATE
BUILD SUCCESSFUL
Total time: 16.803 secs
Why are the tasks clearVersionProperties and writeVersionProperties executed, since they are bound to certain build phases? for example the task infoEnv is not executed, but - and this is really a problem - the task writeVersionProperties which only should be executed by the war task.
Any help appreciated!
in the meantime I found out why these tasks were fired :
I missed << after the task statement, so changing my two taks to
task clearVersionProperties() << {
println "Clearing version file"
String whatToWrite = "version=#version#"
writeVersionFile(whatToWrite)
}
task writeVersionProperties(dependsOn: [svnRevision]) << {
println "Writing version file"
String whatToWrite = "version="+project.ext.revision;
writeVersionFile(whatToWrite)
}
writeVersionProperties.mustRunAfter clearVersionProperties
This did the job.... I am not sure, if this is a bug, feature or side effect. But it seems to me the same utterly evil trap like forgetting () after a function.
Related
I have implemented the product flavors for my project. I am building three new application in the same codebase.
After gradle sync, three different flavors have been generated, say flavor1Debug, flavor1Release, flavor2Debug, flavor2Release.
I have moved all flavor specific resources inside the flavor specific res folder. When I tried to run ./gradlew lintRelease(which is supposed to run lint for flavor1Release and flavor2Release), it's not detecting any of the errors
For testing the lint, I have introduced an unused resource inside the res folder of flavor1 and flavor2. I tried to run ./gradlew lintFlavor1Release or ./gradlew lintFlavor2Release, its detecting the error and throwing respectively.
But ./gradlew lintRelease is not throwing any errors. Where am I going wrong?
Try something like this:
make a custom gradle task that will run all those lint tasks separately.
tasks.register("runAllLinters")
afterEvaluate {
if ("lintFlavor1Release" in tasks.names) {
runAllLinters.dependsOn tasks.named("lintFlavor1Release")
}
}
of course, this will trigger only for Flavor1 - so you will need to expand both the if() condition, and the runAllLinters.dependsOn block to depend on ALL flavour-dependant lint tasks.
and finally, you should be able to run it like ./gradlew runAllLinters
Try this:
afterEvaluate {
def LINT_TASK_NAME = "lint"
def lintTask = tasks.findByName(LINT_TASK_NAME)
tasks.findAll { task ->
task.name.startsWith(LINT_TASK_NAME) &&
task.name != LINT_TASK_NAME &&
// remove some standard lint subtasks
!task.name.startsWith("lintFix") &&
!task.name.startsWith("lintAnalyze")
}.each { task -> lintTask.dependsOn.add(task) }
}
This makes task 'lint' to depend on all the other "lintXxxx" tasks except some standard.
I'm currently writing a gradle script to automate some builds. However it seems that there are 2 ways to create tasks. What should I take and why are there different types in first place?
task copy(type: Copy, group: "Custom", description: "Copies sources to the dest directory") {
from "src"
into "dest"
}
vs
tasks.register("gutenTag", Greeting) {
group = 'Welcome'
description = 'Produces a German greeting'
message = 'Guten Tag'
recipient = 'Welt'
}
The first is the (one of the) older methods of adding a task to a build
The second is using register, which enables task configuration avoidance
https://docs.gradle.org/current/userguide/task_configuration_avoidance.html
That is; the task is only configured if the task is used in the build
Consider I have a Java (or Kotlin, doesn't really matter) application which uses java.io.Console API, or any other API which manipulates the terminal state (e.g.: net.rubygrapefruit:native-platform):
System.out.println(format("Console is %s.", System.console()));
final Terminals terminals = Native.get(Terminals.class);
final Output out = Stdout;
if (terminals.isTerminal(out)) {
final TerminalOutput stdout = terminals.getTerminal(out);
stdout.bold();
System.out.println("bold text");
stdout.foreground(Color.Magenta);
System.out.println("Magenta");
stdout.reset();
final Prompter prompter = new Prompter(terminals);
prompter.askYesNo("Prompt", true);
prompter.enterText("Text", "default text");
prompter.enterPassword("Password");
prompter.select("Select", asList("foo", "bar", "baz"), 1);
} else {
System.out.println(format("%s is not a terminal.", out));
}
The above code would run just fine when launched by Maven Exec plug-in, but with Gradle (since Gradle tries to make its own output look pretty, with all those bells, whistles and progress bars) the code just prints:
$ gradle --console=plain run
> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :run
Console is null.
Stdout is not a terminal.
BUILD SUCCESSFUL in 0s
2 actionable tasks: 1 executed, 1 up-to-date
Configuring the run task as described here and here:
private val run: JavaExec by tasks
run.apply {
standardInput = System.`in`
standardOutput = System.out
errorOutput = System.err
}
or adding --no-daemon to Gradle's command line args as suggested here doesn't really help (System.in is still an instance of java.io.PipedInputStream, while System.out and System.err are instances of org.gradle.internal.io.LinePerThreadBufferingOutputStream).
Is it possible to make Gradle stop messing with the terminal and allow the application being run to access it?
You're probably assigning them wrongfully, because these both should be ByteArrayOutputStream. And probably they should not even be assigned in Gradle (the assignment of default values is redundant). Use Exec and then execute java -jar fileName, if you shouldn't get it working with JavaExec.
I've currently got a configuration task being depended on by an execution task that I am calling from command-line, i.e.
task deployTest(dependsOn: [build, assembleForTest]) << {
...
}
This task should essentially grab the files I've assembled in assembleForTest and then deploy them (ssh, etc.)
My assembleForTest code:
task assembleForTest(type: Sync) {
fileMode = 0775
from ("scripts") {
include "**/*.cgi"
filter(org.apache.tools.ant.filters.ReplaceTokens,
tokens: [programName: programName,
version: version,
dbServer: dbServerTest,
deployDir: deployDirTest])
}
from files("scripts/" + programName + ".cgi")
from files("build/libs/" + programName + "-" + version + ".jar")
into ("build/" + programName)
}
But the problem is: My project gets built AFTER this configuration task assembleForTest has run. i.e. it will try build after the assembly is finished, which means an outdated (or nonexistant) deployment is attempted.
Gradle has some of the worst documenting I've seen, I have worked with it for a while and I still don't understand the ideal setup.
Wouldn't that solve your problem?
task assembleForTest(type: Sync, dependsOn: build) {
/* configuration phase, evaluated always and before execution phase of any task */
...
}
task deployTest(dependsOn: assembleForTest) << {
/* execution phase, evaluated only if the task is invoked and after configuration phase for all tasks has been finished */
...
}
EDIT: I added comments within example. Note that the 1st task is provided with configuration while the 2nd is provided with action. The switch is done with left shift operator. Alternative syntax, especially helpful to combine both phases definition, looks as follows:
task plop() {
// some configuration
...
doLast {
// some action
...
}
}
If you put println in place of 'some configuration' it prints always regardless what task is invoked, as that's evaluated in configuration phase.
I have a typical Antlr 4.5 project with two grammar files: MyLexer.g4 and MyParser.g4. From them, Antlr generates 6 output files: MyLexer.java, MyLexer.tokens, MyParser.java, MyParser.tokens, MyParserBaseListener.java and MyParserListener.java. The gradle tasks are all working correctly so that the output files are all generated, compiled and tested as expected.
The problem is that gradle sees the 6 target files as always being out of date, so every run or debug session has to regenerate them and therefore has to recompile the main java project even if none of the source files have changed.
The gradle task which generates the file has the output spec defined as the folder into which the 6 output files are generated. I think that I need a way to define it as being the 6 specific files rather than the output folder. I just don't know the syntax to do that.
Here's the pertinent part of my build.gradle file:
ext.antlr4 = [
antlrSource: "src/main/antlr",
destinationDir: "src/main/java/com/myantlrquestion/core/antlr/generated",
grammarpackage: "com.myantlrquestion.core.antlr.generated"
]
task makeAntlrOutputDir << {
file(antlr4.destinationDir).mkdirs()
}
task compileAntlrGrammars(type: JavaExec, dependsOn: makeAntlrOutputDir) {
// Grammars are conveniently sorted alphabetically. I assume that will remain true.
// That ensures that files named *Lexer.g4 are listed and therefore processed before the corresponding *Parser.g4
// It matters because the Lexer must be processed first since the Parser needs the .tokens file from the Lexer.
// Also note that the output file naming convention for combined grammars is slightly different from separate Lexer and Parser grammars.
def grammars = fileTree(antlr4.antlrSource).include('**/*.g4')
def target = file("${antlr4.destinationDir}")
inputs.files grammars
// TODO: This output spec is incorrect, so this task is never considered up to date.
// TODO: Tweak the outputs collection so it is correct with combined grammars as well as separate Lexer and Parser grammars.
outputs.dir target
main = 'org.antlr.v4.Tool'
classpath = configurations.antlr4
// Antlr command line args are at https://theantlrguy.atlassian.net/wiki/display/ANTLR4/ANTLR+Tool+Command+Line+Options
args = ["-o", target,
"-lib", target,
//"-listener", //"-listener" is the default
//"-no-visitor", //"-no-visitor" is the default
"-package", antlr4.grammarpackage,
grammars.files
].flatten()
// include optional description and group (shown by ./gradlew tasks command)
description = 'Generates Java sources from ANTLR4 grammars.'
group = 'Build'
}
compileJava {
dependsOn compileAntlrGrammars
// this next line isn't technically needed unless the antlr4.destinationDir is not under buildDir, but it doesn't hurt either
source antlr4.destinationDir
}
task cleanAntlr {
delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr
I discovered the problem was not that the target files were out of date, but rather, due to a bug in the cleanAntlr task, they were being deleted every time that any gradle task was run. The problem was that all of the code in cleanAntlr was being run during gradle's initialization and configuration phase, even if the cleanAntlr task itself wasn't being executed.
Originally, the task was defined as:
task cleanAntlr {
delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr
The solution was to define it like this: (Note the "<<" after the task name.)
task cleanAntlr << {
delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr
... or, for additional clarity, use this more verbose, but functionally equivalent task definition:
task cleanAntlr {
doLast() {
// Be sure to wrap the execution phase code inside doLast().
// Otherwise it will run during the initialization or configuration phase, even when an unrelated task is is run.
// It would also run when the NetBeas IDE first loaded the project.
//println 'Deleting Antlr Directory: ' + antlr4.destinationDir
delete antlr4.destinationDir
}
}
clean.dependsOn cleanAntlr
With that bug fixed, the original outputs specification for the compileAntlrGrammars task works correctly. There is no need to specify each individual output file. This is explained quite well in section 15.9.2 of https://gradle.org/docs/current/userguide/more_about_tasks.html.
def grammars = fileTree(antlr4.antlrSource).include('**/*.g4')
def target = file("${antlr4.destinationDir}")
inputs.files grammars
outputs.dir target
Could you please try the following piece of code:
generatedFiles = ['MyLexer.java',] // and so on..
generatedFiles.each { f -> outputs.file("$target/$f") }