Execute Gradle task after subprojects are configured - gradle

I have a multi-project Gradle build where subprojects are assigned version numbers independent of the root project. I'd like to inject this version number into a few resource files in each subproject. Normally, I'd do this by configuring the processResources task for each subproject in the root build. However, the problem is that Gradle appears to be executing the processResources task before loading the subprojects' build files and is injecting "unspecified" as the version.
Currently, my project looks like this:
/settings.gradle
include 'childA' // ... and many others
/build.gradle
subprojects {
apply plugin: 'java'
apply plugin: 'com.example.exampleplugin'
}
subprojects {
// This has to be configured before processResources
customPlugin {
baseDir = "../common"
}
processResources {
// PROBLEM: version is "unspecified" here
inputs.property "version", project.version
// Inject the version:
from(sourceSets.main.resources.srcDirs) {
include 'res1.txt', 'res2.txt', 'res3.txt'
expand 'version':project.version
}
// ...
}
}
/childA/build.gradle
version = "0.5.424"
I looked into adding evaluationDependsOnChildren() at the beginning of root's build.gradle, but that causes an error because childA/build.gradle runs before customPlugin { ... }. I've tried using dependsOn, mustRunAfter, and other techniques, but none seem have the desired effect. (Perhaps I don't fully understand the lifecycle, but it seems like the root project is configured and executed before the subprojects. Shouldn't it configure root, then configure subprojects, and then execute?)
How can I get inject the version of each subproject into the appropriate resource files without a lot of copy/paste or boilerplate?

You could try using this method, with a hook:
gradle.projectsEvaluated({
// your code
})

I got this figured out for myself. I'm using a init.gradle file to apply something to the rootProject, but I need data from a subproject.
First option was to evaluate each subproject before I modified it:
rootProject {
project.subprojects { sub ->
sub.evaluate()
//Put your code here
But I wasn't sure what side effects forcing the sub project to evaluate would have so I did the following:
allprojects {
afterEvaluate { project ->
//Put your code here

Try doing it like this:
subprojects { project ->
// your code
}
Otherwise project will refer to your root project where no version has been specified.

Related

Use build.finalizedBy on each subproject with Gradle kotlin

I want to run a specific task after EVERY build of my subprojects. I can go into each of my subprojects build.gradle.kts file and add the following
tasks.build {
finalizedBy("afterbuildtask")
}
However, this should be possible to do in my root project build.gradle.kts file right? I tried it by doing the following:
subprojects {
this.tasks.findByName("build")?.dependsOn("afterbuildtask")
}
But nothing happens. How can I achieve this?
You can't programatically execute tasks from other tasks in newer versions of Gradle.
Instead, you are supposed to declare task dependencies and Gradle will ensure they get executed in the correct order. But I think it's not what you want
Alternatively, you could move your logic into the doLast block in the build task. eg:
build {
doLast {
println("Copying...")
copy {
from 'source'
into 'target'
include '*.war'
}
println("completed!")
}
}
good coding! ¯_(ツ)_/¯

How to copy files between sub-projects in Gradle

How can I make a sub-project copy a file that is produced by a sibling sub-project? All this with proper dependency management, and without assuming that any language-specific plugins (like the JavaPlugin) are used.
I have looked at the updated Gradle 6 draft Sharing artifacts between projects but it does not really answer that question.
My multi-project structure is something like:
top/
build.gradle
settings.gradle
producer/
build.gradle
myFile_template.txt
consumer/
build.gradle
I want a Copy-task in producer/build.gradle to copy+transform myFile_template.txt into $buildDir/target/myFile.txt and another Copy-task in consumer/build.gradle should further copy+transform that myFile.txt to a finalFile.txt.
Presumably a proper solution would be able to use task outputs.files or some such so that consumer/build.gradle does not need to explicitly mention the location of $buildDir/target/myFile.txt.
(I'm completely new to Gradle).
Gradle gives you lots of freedom but I prefer that projects only "share" with each other by Configurations and/or Artifacts. I feel that one project should never concern itself with another project's tasks and feel that the tasks are private to each project.
With this principle in mind you could do something like
project(':producer') {
configurations {
transformed
}
task transformTemplate(type: Copy) {
from 'src/main/template'
into "$buildDir/transformed"
filter(...) // transformation goes here
}
dependencies {
// file collection derived from a task.
// Any task which uses this as a task input will depend on the transformTemplate task
transformed files(transformTemplate)
}
}
project(':consumer') {
configurations {
producerTransformed
}
dependencies {
producerTransformed project(path: ':producer', configuration: 'transformed')
}
task transformProducer(type:Copy) {
from configurations.producerTransformed // this will create a task dependency
into ...
filter ...
}
}

Can't define task dependencies between multiple projects using gradle

I need to build 2 projects using Gradle.
I have the 2 gradle files for each project and a parent gradle file.
In the settings.gradle I define the projects:
include 'loadRemote'
include 'load'
rootProject.name = 'EquipLoad'
project(':loadRemote').buildFileName = 'buildRemote.gradle'
project(':load').buildFileName = 'buildLoad.gradle'
Each of the subprojects has their own defined compile and stage tasks.
I need the loadRemote project to run first then the load project.
How to I create this dependency?
I tried adding the dependency to the build.gradle file like this:
tasks.getByPath(":load:cleanCompileStage").dependsOn(":loadRemote:cleanCompileStage")
But the load project compiles first.
I found these syntax:
project(':load') {
dependencies {
compile project (':remoteLoad')
}
}
But need to replace the Gradle compile task with the one that I created in the subproject. I am not sure if it is allowed.
Does anyone have any ideas how to define the dependencies of tasks between 2 subprojects?
You can modify your script like this:
project(':load') {
war.dependsOn project(":loadRemote").tasks.compileJava
}
The above answer did not work for me. I'm sure it is unique to my project. I have to create 2 ear files using 1 code base.
What I did was create a parent gradle file, build.gradle, and add tasks in there that used both projects like this:
//This task builds load and loadRemote ear using 1 command, buildAll
gradle.projectsEvaluated {
task compileAll (dependsOn: [project(':loadRemote').remoteLoadCleanCompileStage]) {
compileAll.finalizedBy project(':load').loadCleanCompileStage
}
task packageAll (dependsOn: [project(':loadRemote').remoteLoadPackage]) {
packageAll.finalizedBy project(':load').loadPackage
}
task buildAll (dependsOn: [compileAll]) {
buildAll.finalizedBy packageAll
}
}

Gradle - Make existing android project a child of other project and run "gradlew tasks"

Part 1.
I have an existing Android project which is built with gradle.
settings.gradle looks like this:
include ':android', ':androidTest', ':androidTestApp'
build.gradle looks like this:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.4'
}
task customClean(dependsOn: [':android:clean', ':androidTest:clean', ':androidTestApp:clean']) {
}
allprojects {
repositories {
mavenCentral()
mavenLocal()
}
}
What I need to do is a create a project that is a parent of this android project and runs this android project tasks. I add to settings.gradle of new root project only include 'android-sdk:android'which is all I need for now. And everything works fine, I can run tasks of android project like this gradlew androidBuild:
task androidBuild(dependsOn: 'android-sdk:android:assemble') << {
group unitySdkGroupName
}
Problems arise when I run gradlew tasks on the new root projects. I get
Caused by: org.gradle.api.UnknownTaskException: Task with path ':android:clean' not found in project ':android-sdk'.
I know I can fix this by removing colons like this, but I want to somehow make tasks command work without changing android project if possible.
task customClean(dependsOn: ['android:clean', 'androidTest:clean', 'androidTestApp:clean']) {
}
Part 2.
When I add to settings.gradle of root project other nested projects
include 'android-sdk:androidTest'
include 'android-sdk:androidTestApp'
I get errors here in dependencies sections of child projects that are dependent on android module (this is androidTest module):
dependencies {
compile project(path: ":android")
compile 'com.google.android.gms:play-services:7.3.0'
}
and getting this when I refresh projects (again seems like relative paths issue):
Error:(30, 0) Project with path ':android' could not be found in project ':android-sdk:androidTest'.
So the question is - can I make gradlew tasks command work properly without modifying the underlying android project and how can it be done the most elegant way?
Unfortunately: no. Nested multiproject builds are not possible. I should add yet, but although intention to support them has been shown, there is absolutely no indication of when this feature can be expected.
See https://discuss.gradle.org/t/nested-multimodule-builds-with-a-flat-directory-layout/7509/13
I could not even find a feature request at https://discuss.gradle.org/c/bugs

Gradle: Common Zip task for every subproject

I have a multi-project gradle build, where all subprojects of root project have common zip task. They should zip some source, depending of the subproject's settings.
First, I configured it in parent build.gradle:
subprojects { subproject ->
apply plugin: 'java'
...
task submission(type: Zip) {
destinationDir = subproject.buildDir
archiveName = subproject.name
from subproject.ext.submissionFiles
}
}
But this does not work with Cannot get property 'submissionFiles' on extra properties extension as it does not exist. So i moved the task in each subproject's build.gradle, which breaks DRY principle.
I think the problem is that the configuration phase takes place before subprojects are configured. How can I "defer" configuration of this task, so that I only configure ext.submissionFiles in subprojects?
I think you're saying that submissionFiles is set in the subproject's build.gradle?
There are a few ways of doing it.
Option 1. You could defer the evaluation of subproject.ext.submissionFiles by wrapping it in a closure:
task submission(type: Zip) {
destinationDir = subproject.buildDir
archiveName = subproject.name
from ({ subproject.ext.submissionFiles })
}
Option 2. I think it could be better if you didn't use the intermediate variable (submissionFiles) and directly configured the submission task in the children:
// In root
task submission(type: Zip) {
destinationDir = subproject.buildDir
archiveName = subproject.name
}
// In child
submission {
from("path/to/files")
}
Using an intermediate variable like that to move configuration around can make your builds harder to understand connections between tasks.
Option 3. In some cases, it makes sense to use evaluationDependsOnChildren(), but I would generally not recommend it.

Resources