Reading includes from idl file in custom task - gradle

I want to make my gradle build inteligent when building my model.
To acquire this I was planning to read schema files, acquire what is included and then build firstly included models (if they are not present).
I'm pretty new to Groovy and Gradle, so please that into account.
What I have:
build.gradle file on root directory, including n subdirectories (subprojects added to settings.gradle). I have only one gradle build file, because I defined tasks like:
subprojects {
task init
task includeDependencies(type: checkDependencies)
task build
task dist
(...)
}
I will return to checkDependencies shortly.
Schema files located externally, which I can see.
Each of them have from 0 to 3 lines of code, that say about dependencies and looks like that:
#include "ModelDir/ModelName.idl"
In my build.gradle I created task that should open, and read those dependencies, preferably return them:
class parsingIDL extends DefaultTask{
String idlFileName="*def file name*"
def regex = ~/#include .*\/(\w*).idl/
#Task Action
def checkDependencies(){
File idlFile= new File(idlFileName)
if(!idlFile.exists()){
logger.error("File not found)
} else {
idlFile.eachLine{ line ->
def dep = []
def matcher = regex.matcher(line)
(...)*
}
}
}
}
What should I have in (...)* to find all dependencies and how should I define, that for example
subprojectA::build.dependsOn([subprojectB::dist, subprojectC::dist])?
All I could find on internet created dep, that outputted given:
[]
[]
[modelName]
[]
[]
(...)

Related

Is there a way to access variables from an imported gradle script?

I have a build.gradle file that's fairly long, and I'd like to break some of the logic up into smaller files to make the whole thing more maintainable. After moving some tasks into a new file, I found that none of the variables I had set in the parent script were available in the child script. Below is a pair of source files I reproduced this behavior with:
build.gradle:
apply from: 'repro.gradle'
def foo = "This is a variable"
tasks.register('printFromMainScript') {
println(foo)
}
repro.gradle:
tasks.register('printFromChildScript') {
println(foo)
}
In the above example, printFromMainScript works fine, but printFromChildScript fails. Is there a way to access foo from repro.gradle?
def foo creates a variable that exists only in the scope of build.gradle. Gradle documentation explains this more in detail.
There is ext block in Gradle which is meant for extra properties.
This should work in your case:
build.gradle:
apply from: 'repro.gradle'
ext {
foo = "This is a variable"
}
repro.gradle:
task printFromChildScript {
doLast {
println(project.foo)
}
}
Note: doLast block ensures that the println function is called only when the project is fully configured and printFromChildScript is actually executed. If you put println directly in task body then it will be executed during Gradle project configuration phase.

Gradle configure task based on subproject property

I'm trying to configure a Zip task based on one of the property inside sub-projects, but the property is not yet accessible at the time of configuring the task. For instance, I want to exclude all my projects that has toexclude = true from my zip file. So, the build.gradle of the sub-projects that I want to exclude starts with this:
ext.toexclude = true;
...
And my main build.gradle has this task:
task zipContent (type: Zip){
def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
println excludedProjects
destinationDir = "/some/path"
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
The problem is that excludedProjects is always empty. Indeed, when I am executing the task, I can see []. I believe this is due to the fact that the property that I set in the subproject's build.gradle is not available at the moment the task is configured. As a proof, if I replace the first line of the task by this:
def excludedProjects = allprojects.collect{it.name}
The task prints out all of my project's name, and the zip contains nothing (which means the problem is in the p.toexclude == true).
Also, if I try this:
task zipContent (type: Zip){
def excludedProjects = []
doFirst{
excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
println "IN DOFIRST"
println excludedProjects
}
println "IN TASK CONFIG"
println excludedProjects
destinationDir = "/some/path"
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
The task prints out IN TASK CONFIG followed by an empty array, then IN DOFIRST with the array containing only the subprojects that I set ext.toexclude == true.
So, is there a way to get the properties of the sub-projects at configuration time?
Well, the crucial question is: At which point of the build is all necessary information available?
Since we want to know each project in the build, where the extra property toexclude is set to true and it is possible (and by design) that the property is set via the build script, we need each build script to be evaluated.
Now, we have two options:
By default, subprojects are evaluated after the parent (root) project. To ensure the evaluation of each project, we need to wait for the point of the build, where all projects are evaluated. Gradle provides a listener for that point:
gradle.addListener(new BuildAdapter() {
#Override
void projectsEvaluated(Gradle gradle) {
tasks.getByPath('zipContent').with {
exclude allprojects.findAll { it.toexclude }.collect{ it.name }
}
}
})
Gradle provides the method evaluationDependsOnChildren(), to turn the evaluation order around. It may be possible to use your original approach by calling this method before querying the excluded projects. Since this method only applies on child projects, you may try to call evaluationDependsOn(String) for each project in the build to also apply for 'sibling' projects. Since this solution breaks Gradle default behavior, it may have undesired side effects.
Just define excludedProjects outside the task
def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
task zipContent (type: Zip){
destinationDir = file("/some/path")
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
You can call evaluationDependsOnChildren() in the root project so that child projects are evaluated before the root
Eg
evaluationDependsOnChildren()
task zipContent (type: Zip) { ... }
Another option is to use an afterEvaluate { ... } closure to delay evaluation
Eg:
afterEvaluate {
task zipContent (type: Zip) { ... }
}

Calling the same task multiple times in a single build.gradle file

I have a custom Gradle plugin that will generate Java files from a template file. I have several such template files in different locations, and I need to "compile" all of them to generate the Java files I need. Once I have the files, I want to package them into a .jar.
One way I thought I could do this was to call the "compile template" task multiple times from within the same build file. I'd call it once in a task that compiles template files in location A, again from a task that compiles template files from location B... etc., until I have all the Java files I need.
Something like this:
task compileFromLocationA <<{
compileTemplate.execute(A)...
}
task compileFromLocationB
compileTemplate.execute(B)...
...
packageJar(depends: compileFromLocationA, compileFromLocationB, ...)
...
However, you can't programmatically call a task from within another task. I suppose I could break each compileFromLocation_ task into it's own build.gradle file, but that seems like overkill. What's the "best practice" in a case like this?
This code seems to work in build.gradle by using tasks.register() - e.g. to perform multiple source code generating steps - in my case I needed to load different pairs of files (XML schema and generation options) in two different steps:
plugins {
id 'java'
id "com.plugin" version "1.0"
}
sourceSets.main.java.srcDirs += file("${buildDir}/genSrc")
sourceSets.test.java.srcDirs += file("${buildDir}/testGenSrc")
tasks.compileJava {
dependsOn tasks.named("genMessage")
}
genMessage {
codesFile = "${projectDir}/src/main/resources/file.xml"
}
def testGenModel1 = tasks.register("testGenModel1", com.plugin.TestGenModelTask.class) {
schema = "${projectDir}/src/test/resources/file.xsd"
options = "${projectDir}/src/test/resources/file.xml"
}
def testGenModel2 = tasks.register("testGenModel2", com.plugin.TestGenModelTask.class) {
schema = "${projectDir}/src/test/resources/file2.xsd"
options = "${projectDir}/src/test/resources/file2.xml"
}
tasks.compileTestJava {
dependsOn tasks.named("testGenModel1"), tasks.named("testGenModel2")
}

include not found copySpec / Sync task Gradle

I'm trying to sync specific files from a dir with Gradle. But I get a odd error that I can't seem to solve. If there is a better (working) way to filter files while syncing that would also be welcome.
Implementation 1
def updateAbstractsContentSpec = copySpec {
from('../../base') {
includes "../../base/shared/**/*_abstract.*"
}
}
task updateAbstracts(type: Sync) {
group 'build'
with updateAbstractsContentSpec
}
Error 1
Error:(24, 0) Could not find method includes() for arguments [../../base/shared/**/*_abstract.*] on object of type org.gradle.api.internal.file.copy.CopySpecWrapper_Decorated.
Implementation 2 (Preferable)
task updateAbstracts(type: Sync) {
group 'build'
from '../../base'
includes '../../base/shared/**/*_abstract.*'
}
Error 2
Error:(23, 0) Could not find method includes() for arguments [../../base/shared/**/*_abstract.*] on task ':apps:TestApp1:updateAbstracts' of type org.gradle.api.tasks.Sync.
I assume that its clear what I try to do. I hope that somebody can help me with this.
As of Gradle 3.0 CopySpec documentation, CopySpec does not contain includes method.
You should use include instead:
task updateAbstracts(type: Sync) {
group 'build'
from '../../base'
include '../../base/shared/**/*_abstract.*'
}

How to call a task in another sub project build file with parameters

I'm working on creating a multi project build file using Gradle. Many sub projects need to execute a task which exists in another sub project by passing in certain parameters. How can this be achieved in Gradle?
for example :
root project
- project B : task X
- project A : task Y (param m, param n)
I need projectB.taskX to call projectA.taskY(m,n)
Update:
Sub-Project A has a task of type JavaExec which needs an input parameter to the location of the properties file
task generateCode(dependsOn:['classes','build'], type: JavaExec) {
main = 'jjrom.ObjectGen'
classpath = sourceSets.main.runtimeClasspath
args 'arg1', 'arg2', file(propertiesFilePath).path
}
Now, there are 10 sub projects, all of which need to call this task 'generateCode' with a parameter that contains the location to the properties file. Also, this task should be executed before building each sub-project which can be achieved using dependsOn.
My java project code organisation:
trunk/
projA/src/java/../ObjectGen.java
projB/src/java/../properties.xml
projC/src/java/../properties.xml
projD/src/java/../properties.xml
....
A task cannot call another task. Instead, the way to solve this problem is to add a generateCode task to all ten subprojects. You can do this from the root build script with code similar to the following:
subprojects {
apply plugin: 'java'
configurations {
codegen
}
dependencies {
// A contains the code for the code generator
codegen project(':A')
}
task generateCode(type: JavaExec) {
main = 'jjrom.ObjectGen'
classpath = configurations.codegen
args 'arg1', 'arg2'
}
compileJava.dependsOn(generateCode)
}
If there is no general pattern as to where the properties file is located, this information can be added in the subprojects' build scripts:
generateCode {
args file('relative/path/to/properties/file')
}

Resources