Having difficulty setting up Gradle multiproject build for existing repository layout - gradle

I'm trying to craft a Gradle multiproject build for a situation in which my project layout is already dictated to me. I have something like this:
-->Shared\
---->SharedComponent1\
------>build.gradle
------>src\
...
---->SharedComponent2\
------>build.gradle
...
-->Product1\
---->ProductComponent1\
------>build.gradle
---->ProductComponent2\
------>build.gradle
...
---->build\
------>settings.gradle
My settings.gradle looks like this:
rootProject.name = 'Product1'
rootProject.projectDir = new File( "${ProjectsRoot}" )
include 'Shared:SharedComponent1'
include 'Shared:SharedComponent2'
include 'Product1:ProductComponent1'
include 'Product1:ProductComponent2'
When I run Gradle in the build folder like this:
gradle -PProjectsRoot=c:\my\project\root\dir projects
I get:
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'build'
No sub-projects
To see a list of the tasks of a project, run gradle <project-path>:tasks
For example, try running gradle :tasks
BUILD SUCCESSFUL
i.e. it doesn't find the projects I'm trying to build.
Is what I'm trying to do possible with Gradle's multiproject support? Or am I barking up the wrong tree?

A couple of pointers:
Gradle strictly separates the logical project hierarchy (the way Gradle organizes your build into a logical hierarchy of projects) from the physical directory layout. Just about any mapping is possible. (One exception that comes to mind is that you can't have two projects sharing the same project directory.)
To implement a custom directory layout, you'll have to set projectDir for all projects, not just the root project. You should use relative paths, e.g. rootProject.projectDir = new File(settingsDir, "../foo") and project(":sub1").projectDir = new File(rootDir, "bar"). Here, settingsDir refers to the directory containing settings.gradle, and rootDir is a shorthand for rootProject.projectDir.
To configure projects generically, you can recursively walk (root)Project.children. Note that settings.gradle and build.gradle use different types to represent a project - ProjectDescriptor and Project, respectively.
Gradle has to be invoked from the directory containing settings.gradle, or a subdirectory thereof. From a usability perspective, it is therefore best to put settings.gradle into the root of the directory hierarchy.
For more information, see Settings in the Gradle Build Language Reference, and the Multi-Project Builds chapter in the Gradle User Guide.

For completeness, the settings.gradle that solved my specific example above is as follows:
rootProject.name = 'Product1'
def projectTreeRootDir = new File( "${ProjectsRoot}" )
// Shared components
def sharedRootDir = new File( projectTreeRootDir, 'Shared' )
include ':SharedComponent1'
project( ':SharedComponent1' ).projectDir = new File( sharedRootDir, 'SharedComponent1' )
include ':SharedComponent2'
project( ':SharedComponent2' ).projectDir = new File( sharedRootDir, 'SharedComponent2' )
// Product components
includeFlat 'ProductComponent1', 'ProductComponent2'
Clearly this doesn't scale to large numbers of subprojects and it could be done significantly better using the hints provided by Peter above.

Related

How to manage composite builds between gradle modules projects and applications

I'm facing this limitation / issue with Gradle.
I have two multi-project builds with Gradle, let's call them modules-technical, and modules-business.
This is my setting.gradle for modules-business:
rootProject.name = 'modules-business'
include 'business-core'
.. snip business-* modules ..
includeBuild '../modules-technical/'
This means, that modules-business depends on modules-technical.
Now, I want to develop an app that depends on modules-business.
rootProject.name = 'my-app'
includeBuild '../modules-business'
includeBuild '../modules-technical'
Due to the current limitations of Gradle 4.2.1 I would need to comment the includeBuild statement on my modules-business project. Like this:
rootProject.name = 'modules-business'
include 'business-core'
.. snip business-* modules ..
// includeBuild '../modules-technical/'
Is there a better way to manage this limitation for now? Meaning, how can I develop multi-modules and applications using Gradle composite builds?
You should be able to do
if (settings.hasProperty('doCompositeBuilds') && doCompositeBuilds.toBoolean()) {
includeBuild '../modules-technical/'
}
and set doCompositeBuilds to true when working with modules-business and to false or leave unset when working with app.
Or you could have two branches of modules-business, one with the includeBuild, one without and then use the one you actually need.

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 creating build directory in subfolder

I have a subfolder api which holds multiple api related subprojects. I have included an api subproject in my settings.gradle as
include "api:invenio"
project(":api:invenio").name = 'metadata-api-invenio'
folder structure looks like this
root/api/invenio/build.gradle
For some reason gradle is treating api as a subproject as well even though this is just a folder to hold subprojects. Its creating a build directory in api folder. How can I tell gradle to exclude api folder from subprojects.
Thanks
Take a look at Gradle's settings.gradle for an example. Basically you can do somethings like
include 'invenio'
project(':invenio').projectDir = new File(settingsDir, 'api/invenio')

Gradle specify settings dir path for a sub project

In a multi-project build I have a module that in itself is composed of two sub-projects. If I just want the option of building the top-level module but also ensure both the sub-projects within it are also built, how I do achieve this?
include 'moduleA', 'moduleB', 'moduleC' (root project settings.gradle)
project(':moduleC').projectDir = new File('path to custom module that includes sub-projects)
project(':moduleC').settingsDir = ?? (gradle fails because there is no settingsDir path)
but moduleC has a settings.gradle in itself that has
include 'api'
include 'server'
Now I want both these to be triggered when I specify gradlew :moduleC:build, but instead it just builds moduleC root project. Is there a way? This use case does seem valid to me (i.e. for modularity, you want to keep the inclusion of sub-projects at moduleC's level and not at root level).
Thanks,
Paddy
As of Gradle 2.2, only a single settings.gradle per build is supported. If that file contains include "moduleC:api" and include "moduleC:server", then running gradle build from moduleC's project directory will also build api and server.

Gradle subproject name different than folder name

I have a couple of subprojects that are part of a multi-project build (flat hierarchy). I want to set the name on them to be different than their folder name. However, in include (settings.gradle) it has to have the folder name otherwise it won't find it (same for the compile project(':ProjectName')).
If I attempt to set project.name it tells me that is read-only. The reason is that we are converting from Ant and would like to keep the same name for Eclipse IDE. As far the artifacts go, we use jar.artifactName to set whatever name we want.
Thank you.
Project names can only be changed in settings.gradle. For example:
include "foo" // or `includeFlat`, doesn't matter
// always good to nail down the root project name, because
// the root directory name may be different in some envs (e.g. CI)
// hence the following even makes sense for single-project builds
rootProject.name = "bar"
// change subproject name
project(":foo").name = "foofoo"
Alternatively, you can use the desired project name in the include statement, and later reconfigure the project directory:
include "foofoo"
project(":foofoo").projectDir = file("foo")
To give some background, the only difference between include and includeFlat is that they use different defaults for the project's projectDir. Otherwise they are the same.
For further information, check out Settings in the Gradle Build Language Reference.
This is Kotlin DSL equivalent of Peter Niederwieser's answer.
settings.gradle.kts:
rootProject.name = "MyApp"
include(":Narengi")
project(":Narengi").name = "Bunny"
// OR
include(":Bunny")
project(":Bunny").projectDir = file("Narengi/")

Resources