Understanding gradle multi project build.gradle execution order - gradle

I'm doing some experiments on below gradle multi project structure
I have added println in all the build.gradle & settings.gradle files to see in what order they execute. I'm seeing that projectA -> build.gradle file is executing after its subproject pA1->build.gradle as seen in the output below
I'm not understanding why projectA->build.gradle is executing after its sub projects ? Should it not execute before its subproject, just like the build.gradle file on root.
location on project Multi project sample

First off, putting a naked println line in your build.gradle files does not give you execution order. The println will be invoked in the configuration phase, so if anything you're looking at the configuration order.
If you want to investigate execution order add a task with the same name to all your build.gradle files, maybe something like this:
task action << {
println("In project: ${project.name}")
}
and then run gradle action from the root folder.

Related

Conditionally skip subproject in multi-module Gradle build

Consider the following Gradle project structure:
- root
|- build.gradle
|- barProject
|- build.gradle
|- fooProject
|- build.gradle
where the root build.gradle configures its subprojects like so:
subprojects {
apply plugin: 'java'
//bunch of other stuff
}
Now when I call gradlew build on the root project it automatically configures both subprojects and then builds them - all well and good.
I also know that I can skip a specific task with various means (onlyIf(), [taskName].enabled = false, etc.) but when I utilize any of those on build Gradle still runs all the dependent tasks (compileJava, processResources, classes, etc.) until finally hitting build which it then skips.
My question is, is there any way to have Gradle stop and go to the next subproject right after the configuration phase?
EDIT:
To clarify; each subproject has a property called skip that is evaluated in the configuration phase.
Now when I call gradlew build on the root project I want Gradle to, ideally, check that property and if it's true then to completely skip the corresponding project.
Executing external task 'build'...
//check project.skip -> IF true -> skip project, go to :foo ELSE continue
:bar:compileJava
:bar:processResources UP-TO-DATE
:bar:classes
:bar:jar
:bar:startScripts
:bar:distTar
:bar:distZip
:bar:assemble
:bar:compileTestJava UP-TO-DATE
:bar:processTestResources UP-TO-DATE
:bar:testClasses UP-TO-DATE
:bar:test UP-TO-DATE
:bar:check UP-TO-DATE
:bar:build
//check project.skip -> IF true -> skip project, go to end ELSE continue
:foo:compileJava
:foo:processResources UP-TO-DATE
:foo:classes
:foo:jar
:foo:startScripts
:foo:distTar
:foo:distZip
:foo:assemble
:foo:compileTestJava UP-TO-DATE
:foo:processTestResources UP-TO-DATE
:foo:testClasses UP-TO-DATE
:foo:test UP-TO-DATE
:foo:check UP-TO-DATE
:foo:build
BUILD SUCCESSFUL
I hope this makes more sense
Well, first of all: An easy solution would be calling exactly the task(s) you need. In simple Gradle builds, task names are unique. However, in multi-project builds, each project can have a task with a specific name. This is the reason why Gradle introduced task paths. Task paths combine the unique project paths with the intra-project unique task names: :projectX:taskY.
Using project paths, you can easily specify the project-specific task you want to execute: :build for the build task in the root project and :<subproject>:build for the build task in a subproject. If a task name, e.g. build, is provided for a multi-project build, Gradle will search through any project (root and subs) for a task with the specified name and execute them all. This explains the current behaviour.
The task names for execution are managed by a StartParameter object of the Gradle Settings. These Settings can be modified in your settings.gradle file, where you also include subprojects:
include ':foo', ':bar'
startParameter.excludedTaskNames += ':foo:build'
This line excludes the build task of the foo subproject from the execution. You could add the subproject task path (even independent from the task name) to the excluded task names, if a specific condition is met. But, I did not find a way to access the Settings in your build file, so this solution can not be used during configuration phase. However, if your condition is solely based on project / system properties, they can be accessed from settings.gradle.
Another solution for the configuration phase came to my mind, but it's basically what you already mentioned, because it simply skips the tasks:
if (project.skip) {
project.tasks.all { task -> task.enabled = false }
}
This way, all tasks from the project (not only the build task) will be skipped.
Please consider, that tasks can also be executed, because they create a dependency for another project. Even gradle :bar:build will execute the task :foo:jar and its task dependencies, if foo is a project dependency of bar. You can disable this behaviour with the Gradle command line options -a or --no-rebuild or the following entry in your settings.gradle:
startParameter.buildProjectDependencies = false

Gradle multi-project custom build.gradle file name

I have a multi-project Gradle build, which is currently configured through a single build.gradle file.
There are over 70 modules in this project, and the single (gigantic) build.gradle file has become cumbersome to use, so I'd like to split it into small per-module buildscript files.
Now, I don't want to have 70 small build.gradle files (one in each module), as that would make navigating to a specific build.gradle a pain in the IDE (the only difference between the files is their path).
What I want is my per-module buildscript files to be named after the module name.
Instead of this:
root
|--foo\
|--| build.gradle
|--bar\
|--| build.gradle
I want this:
root
|--foo\
|--| foo.gradle
|--bar\
|--| bar.gradle
Since this doesn't seem to be officially supported, I tried hacking around the root build.gradle a bit, but it seems that applying a .gradle file happens before the projects are configured, so this gives an error for projects that depend on other projects:
in root build.gradle:
subprojects { subProject ->
rootProject.apply from: "${subProject.name}/${subProject.name}.gradle"
}
foo.gradle, which is not a standard build.gradle file:
project('foo') {
dependencies {
compile project(':bar')
}
}
Is there any way of making it work like this?
A web search for "gradle rename build.gradle" rendered the below example settings.gradle file:
rootProject.buildFileName = 'epub-organizer.gradle'
rootProject.children.each { project ->
String fileBaseName = project.name.replaceAll("\p{Upper}") { "-${it.toLowerCase()}" }
project.buildFileName = "${fileBaseName}.gradle"
}
Note that the author is here also renaming the root project's build script, which you may or may not want.
One of the authors of Gradle, Hans Dockter, has said somewhere (I believe it was in his "Rocking the Gradle" demo from 2012), that he felt one of their biggest mistakes was using build.gradle as the default file name.
You can customize name of your build scripts in settings.gradle file. Check recent presentation from Ben Muschko about multi-project builds or look at Gradle sources where similar customization is done.
rootProject.children.each {
it.buildFileName = it.name + '.gradle'
}
You can find this content in Gradle in action, manning

Gradle-built project structure

I see a lot of examples of Gradle-built Java/Groovy projects that have the following structure:
some-app/
src/
main/
test/
docs/
README.md
build.gradle
gradlew
gradlew.bat
settings.gradle
gradle.properties
gradle/
*.gradle
I understand that build.gradle is the main buildscript and that gradle.properties is its properties file. But settings.gradle really throws me. Inside it I see:
rootProject.name = "someApp"
But this seems like it belongs in gradle.properties. I'm also wondering where the gradlew and gradlew.bat files come from, they seem to be generated.
Finally, I'm wondering why there are so many *.gradle files under the gradle/ dir: are these plugins, or extension scripts of some sort. They are all pulled in from the main build.gradle like so:
apply "gradle/fizz.gradle"
apply "gradle/buzz.gradle"
etc.
So:
What properties are supposed to go in settings.gradle that are not supposed to go in gradle.properties?
How are the gradlew/gradlew.bat files generated?
Why would someone have so many disparate *.gradle files? Why not just 1 big build.gradle buildscript?
1) gradle.properties is normal properties file, while settings.gradle is also a build script. You can add there some code that will be executed during build. Typically this file is needed when You have a multi-module project.
2) When You type gradle tasks in project build directory (empty build.gradle is enough to see it) You'll see wrapper task. This task is used to generate scripts You're asking about. More info.
3) The reason is that all these files have different responsibilities that are cleanly separated.

project name in gradle must be a directory?

Is it mandatory to have a directory with same name as project in gradle ? My code is like this:
.
|-- build.gradle
`-- suites
`-- lrgabc.gradle
1 directory, 2 files
content of suites/lrgabc.gradle
$ cat suites/lrgabc.gradle
project(':lrgabc') {
task block1 << {
println "Hello from lrgabc.block1"
}
}
Content of build.gradle
apply from: 'suites/lrgabc.gradle'
When I run, gradle -q :lrgabc:block1 I get
* Where:
Script '/home/skgupta/gradle-examples/multiproject/suites/lrgabc.gradle' line: 1
* What went wrong:
A problem occurred evaluating script.
> Project with path ':lrgabc' could not be found in root project 'multiproject'.
Is it mandatory to have 'lrgabc' as a directory under 'multiproject' ?
What I'm trying is under one single project, create multiple test suites using gradle. Each test suite is one gradle file, where each task in that gradle represent one test [These are non java tests, like perl, python..etc]
How do I solve this ?
Each subproject in a multi-project Gradle build needs to have a separate project directory. However, directory names do not have to correspond to project names (this is configurable in settings.gradle).
You really want to take a look at the documentation for multi-project builds - http://www.gradle.org/docs/current/userguide/multi_project_builds.html There are also samples in Gradle distribution that can help you.
You don't need to create new projects to create more test tasks. You can have these tests in one project. It is quite common to define multiple test suite like unit and functional tests this way. http://blog.safaribooksonline.com/2013/08/22/gradle-test-organization/ shows a possible implementation.

gradle: avoid same custom task in multiple project

I have following folder structure...
project/
project/library
project/app1
project/app2
Now I have build one custom task publish (copy my android apks to a server folder). This task is in /app1/build.gradle and /app2/build.gradle.
My task is in both app same, except I have set project.ext variables. How can I put this task in /build.gradle(in root), because I don't want to change every single build.gradle in every app. The problem is, that /library should not have this task.
Any ideas?
Have a look at this document section: 14.3. Configuring the project using an external build script
UPDATE
Structure
p1
build.gradle - empty file
p2
build.gradle
build.gradle - empty file
settings.gradle
custom.gradle
custom.gradle:
task hello << {
println "hello!"
println project.sample_prop
}
settings.gradle:
include 'p1', 'p2'
p2/build.gradle
apply from: '../custom.gradle'
project.ext.sample_prop = '1'

Resources