Request clarification of Gradle task rule syntax from documentation - groovy

Pursuant to this spot in the Gradle documentation:
https://docs.gradle.org/current/userguide/more_about_tasks.html#N10F07
tasks.addRule("Pattern: ping<ID>") { String taskName ->
if (taskName.startsWith("ping")) {
task(taskName) << {
println "Pinging: " + (taskName - 'ping')
}
}
}
It says, "Sometimes you want to have a task whose behavior depends on a large or infinite number value range of parameters. A very nice and expressive way to provide such tasks are task rules.".
It's not at all clear to me from this block of code how those parameters are defined nor how they get into the task scope so that they can be used.
It's also not clear if they have to be all the same type. I really need an example that shows these.
I'm not a Groovy guy in the first place. Gradle is my reason for learning Groovy.

Related

Is a Jenkinsfile in pure Groovy? What Groovy language construct is used in `steps` in a Jenkinsfile?

What Groovy language construct, syntax or control structure is used in the following code fragment in a Jenkinsfile?
stage('Stage 1') {
steps {
// One or more steps
}
}
i.e. What are blocks in Jenkinsfile, in terms of pure Groovy language?
what is 'steps'? or stage?
Is it calling a function? or definition? or a function call with anonymous (lambda) argument?
Inherent in this question is another question:
Question 2:
Is a Jenkinsfile, a code fragment in groovy language?
In other words, 1. Does a Jenkinsfile follow all syntax and control structures of pure Groovy? (perhaps by an implicit library import-ed or #include d silently in beginning),
As opposed to being a DSL: 2. Jenkinsfile being almost a groovy source file augmented with new Jenkins-specific constructs not originally in Groovy, e.g. Jenkins using a preprocessing.
Which of the above two hold?
Related:
https://www.jenkins.io/doc/book/pipeline/syntax/
https://www.jenkins.io/doc/book/pipeline/getting-started/#directive-generator
What is a Jenkins Stage in terms of Groovy? (despite similar title, the ask is different. The related comments are inconclusive).
In Jenkins (or Gradle) 2 main features are used:
Groovy (java) idiomatic structures like loops, switches, chain of command etc
DSLBuilder facility based on Closures to allow for nesting and invoking of domain-specific methods as they were a part of Groovy itself.
So, if you write something like
stage('Stage 1') {
steps {
// One or more steps
}
}
it translates internaly to roughly:
jenkinsContext.stage('Stage 1') {
jenkinsContext.steps {
// One or more steps
}
}
so it is way clearer to write and read. Here the Closures - the {...} blocks - represent nesting or grouping of your code.
In this block you can also see Groovy's way of calling methods where the last argument is a Closure. The code above could be rewritten as:
jenkinsContext.stage 'Stage 1', { // here no brackets around args
jenkinsContext.steps( { // here with normal java-style brackets
// One or more steps
} )
}
In jenkins you can mix and match DSL calls with Groovy structures:
[ 'Stage 1', 'Stage 2' ].each{
stage( it ) {}
}
or even have your DSL names generated dynamically:
[ 'Stage 1':'stage', 'step 2':'steps' ].each{ value, name ->
"$name"( value ) {}
}
would create the DSL (as example ONLY!):
stage( 'Stage 1') {}
steps( 'Step 2' ) {}
So, Jenkins pipeline syntax is Groovy + Jenkins DSL
Looks like it's primarily Groovy, so if you're looking for simply syntax highligting adding something like the following does the job.
<!-- language: lang-groovy -->
There are caveats however documented in the online docs, so maybe this implies it's not written in pure groovy but some specialized/constrained form of it?
The basic statements and expressions which are valid in Declarative Pipeline follow the same rules as Groovy’s syntax with the following exceptions:
The top-level of the Pipeline must be a block, specifically: pipeline { }.
No semicolons as statement separators. Each statement has to be on its own line.
Blocks must only consist of Sections, Directives, Steps, or assignment statements.
A property reference statement is treated as a no-argument method invocation. So, for example, input is treated as input().
https://www.jenkins.io/doc/book/pipeline/syntax/#declarative-pipeline

What are gradle task definitions in groovy language?

I'm completely new to both gradle and groovy and I'm having trouble to find information about what the below actually is in the groovy language
task myTask(dependsOn: 'compile') << {
println 'I am not affected'
}
AFAIK the {...} part is a closure which seems to be passed to whatever is defined before <<.
Is task myTask() a call to a constructor?
And what is the thing with the colon that looks like a parameter?
What does << do? Is it an operator that was overloaded by gradle or is it standard groovy?
dependsOn: 'compile' is a named argument. << is an overloaded operator that adds a task action to the task. (See Gradle User Guide for more information.) { ... } is a closure that implements the task action. myTask is syntactically a nested method call (task(myTask(dependsOn: 'compile') << ...)), but gets rewritten to a String using a Groovy compiler plugin (task('myTask', dependsOn: 'compile') << ...).

Aggregate tasks in Gradle

I'm working on a gradle task that depends on other gradle tasks, i.e an aggregate task. The aggregate task required the other tasks to run in a certain linear order too.
Note: The other tasks must be able to run in isolation. The dependencies between the tasks ONLY makes sense when running the aggregate task.
My first attempt was to use someTask.execute(), but then I realized that this was not the Gradle way, i.e it is not a supported feature and that the internal API might change.
task aggregate() {
doFirst {
// Need to set properties required by other tasks
project.ext.somePropRequiredByTask1 = propertyX + "-" propertyY
}
doLast {
task1.execute()
task2.execute()
task3.execute()
}
}
After reading up on creating aggregate tasks, this is my current solution:
task aggregate() {
// Only configure task if the task is exlicitly run
if (gradle.startParamter.taskNames.contains("aggregate") {
project.ext.somePropRequiredByTask1 = propertyX + "-" propertyY
chainTaskDependencies([task1, task2, task3])
}
}
// Create a strict dependsOn dependency order for the tasks in the task list
def chainTaskDependencies(List<Task> tasks) {
def taskNames = tasks.collect { it.name}
println "Run tasks in this order: " taskNames.join " -> "
def reversed = tasks.reverse()
for (int i = 0; i< reversed.size() -1 ; i ++) {
reversed[i].dependsOn(reversed[i+1])
}
}
Is this the correct way to create an aggregate task dependency where the order between tasks is linear? How can it be improved?
How about just extracting shutdownVM, waitForVMShutdown and rollback to functions/closures that your tasks call ? Then you are free to compose them in any which way you choose.
I've found this to be the most flexible option in similar cases.
B.mustRunAfter(A)
means that if both A and B run then B must run after A. B can be run by itself no problems.
So:
task A
task B
task C
task aggregate(dependsOn(['A', 'B', 'C'])
C.mustRunAfter(B)
B.mustRunAfter(A)
As Gradle team member and Spock inventor Peter Neiderweiser puts it here:
You can't, because a task (instance) will be executed at most once per build. Instead, the way to go is to declare multiple task instances. You could do this by putting the task declaration in a loop, putting it in a method and calling it multiple times, or by writing a task class and instantiating it multiple times. Then you'll add one other task that depends on all bulk tasks. Assuming the execution order between bulk tasks is irrelevant, that's it.
He also suggests using task rules for this purpose.
The OP also posts the solution he or she devised, so you can look at that as well.

build.gradle file, task using << notation, intellij warns: "Cannot infer argument type"

I'm new to groovy and gradle and was wondering whether someone knew why my scriplet wasn't working (edit actually it does work but the warning still appears). This section is taken out of the build.gradle script and intellij highlights this and complains that it: "Cannot infer argument types". any help would be nice :)
task hellofun << {
[silly:'billy'].each { k, v ->
println "$k=$v"
}
}
EDIT: I have submitted a bug request informing Intellij of this problem
EDIT: apparently this is a known bug I'll update this once the bug is fixed
If you are willing to disable the Groovy > Assignment Issues > Incompatible type assignments inspection, the warning goes away.
In a highly dynamically typed language such as groovy where names and symbols are resolved in arbitrary ways It would be quite difficult to determine the validity of any statement ahead of time without running the program. even running the program would theoretically not divulge all possible permutations of input that may change the runtime validity of a statement.
I applaud intellij's attempt at this hard problem and can live with a few incorrect syntax warnings here and there.
Untill the IntelliJ bug is fixed, there is a workaround: use doFirst instead of <<.
task hellofun() {
doFirst {
[silly:'billy'].each { k, v ->
println "$k=$v"
}
}
}
Thanks for submitting the bug :)
For the workaround, you should really call was was indicated by the << (doLast)
task hellofun() {
doLast {
[silly:'billy'].each { k, v ->
println "$k=$v"
}
}
}
In Intelij press Double shift (search all) enter "Assignment Issues" and change Assignment Issues: Incompatible type assignments inspection to "OFF"

Is there any fast tool which performs constant substitution without stripping out comments in JavaScript source code?

For example, setting MYCONST = true would lead to the transformation of
if (MYCONST) {
console.log('MYCONST IS TRUE!'); // print important message
}
to
if (true) {
console.log('MYCONST IS TRUE!'); // print important message
}
This tool ideally has a fast node.js accessible API.
A better way to achieve what you want -
Settings.js
settings = {
MYCONST = true
};
MainCode.js
if (settings.MYCONST) {
console.log('MYCONST IS TRUE!'); // print important message
}
This way, you make a change to one single file.
google's closure compiler does, among other things, inlining of constants when annotated as such, leaving string content untouched but I am not sure if it's a viable option for you.
Patch a beautifier, for example
Get the JS Beautifier https://raw.github.com/einars/js-beautify/master/beautify.js written in JS.
Replace the last line of function print_token() by something like
output.push(token_text=="MYCONST"?"true":token_text);
Call js_beautify(your_code) from within nodejs.
The Apache Ant build system supports a replace task that could be used to achieve this.
Edit: Whoops. Gotta read title first. Ignore me.
Google Closure Compiler has such a feature:
You can combine the #define tag in your code with the --define parameter to change variables at "compile" time.
The closure compiler will also remove your if-statement, which is probably what you want.
Simply write your code like this:
/** #define {boolean} */
var MYCONST = false; // default value is necessary here
if (MYCONST) {
console.log('MYCONST IS TRUE!'); // print important message
}
And call the compiler with the parameter:
java -jar closure-compiler.jar --define=MYCONST=true --js pathto/file.js
Regarding you API request: Closure Compiler has a JSON API.

Resources