Gradle multi-project build: how to specify different build files? - gradle

Gradle multi-project build: how to set different build files? e.g.
/foo/bar/build-hello.gradle
/foo/bar/build-world.gradle
settings.gradle
include 'hello'
project(':hello').projectDir = new File('/foo/bar')
project(':hello').buildFile = new File('/foo/bar/build-hello.gradle')
include 'world'
project(':world').projectDir = new File('/foo/bar')
project(':world').buildFile = new File('/foo/bar/build-world.gradle')
Error: can not set readOnly property buildFile.
How to specify a different build file other than the default build.gradle?
EDIT:
https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:projectDir
File projectDir (read-only)
The directory containing the project build file.
projectDir is also readOnly. But no error in setting its value. Why?

The settings.gradle file operates on a Settings object, whose project() method returns ProjectDescriptor instances, because the Project instances are not created at this point of the Gradle build.
The read-only buildFile property of the ProjectDescriptor instances is created from the two writable properties projectDir and buildFileName:
include 'hello'
project(':hello').projectDir = new File('/foo/bar')
project(':hello').buildFileName = 'build-hello.gradle'
include 'world'
project(':world').projectDir = new File('/foo/bar')
project(':world').buildFileName = 'build-world.gradle'

Related

How do you access gradle.ext properties in Plugin Java source code?

I need a property to (one) be available when a plugin is applied and (two) allow for a calculated override value in the settings.gradle file. A project property would be ideal as it can have a default set in gradle.properties:
# gradle.properties
myProp=originalValue
This is great because it can be overrode with a command line argument like -PmyProp=newValue, but I was not able to find a good way to override the property in the settings.gradle file before the build.gradle executes (i.e. before the plugins are applied).
For instance all of these leave rootProject.myProp unaltered at plugin application:
// settings.gradle
rootProject.getProperties.put("myProp", "overrideValue")
settings.ext.myProp = "overrideValue"
settings.extensions.myProp = "overrideValue"
gradle.startParameters.projectProperties.myProp = "overrideValue"
We cannot do any magic in the build.gradle either because no logic can exist before the plugins block:
// build.gradle
plugins {
id 'com.myCompany.myPlugin' version 1.0.0 // 'myProp' must be set by now
}
One workaround I can think of would be to use:
// settings.gradle
gradle.ext.myProp = "overrideValue"
... but there doesn't seem to be a good way to access gradle.ext properties in Java source code (for a plugin), or is there?
This seems to work for the gradle.ext.myProp use case, but it is surprising to me that the only workable approach is to cast the Gradle object to an ExtensionAware object:
// MyPlugin.java
String myProp = (String) project.getRootProject().getProperties().getOrDefault("myProp", null);
Gradle gradle = project.getRootProject().getGradle();
if ((myProp == null) && (gradle instanceof ExtensionAware)) {
ExtensionAware gradleExtensions = (ExtensionAware) gradle;
myProp = (String) gradleExtensions.getExtensions().getExtraProperties().get("myProp");
}
It seems like what I'm trying to do should be commonplace, so is there a better way like solely using project properties?
If so, then how do you change the values in the settings.gradle file?
This is probably not what you’re looking for but maybe it still helps: have you considered an initialization script? In such a script it is possible to override a project property.
Example:
$ ./gradlew -PmyProp=originalValue properties | grep myProp
myProp: originalValue
$ ./gradlew -PmyProp=originalValue -I init.gradle properties | grep myProp
myProp: overrideValue
… where init.gradle is the following:
allprojects {
project.ext.myProp = 'overrideValue'
}
Note that there are also other ways of specifying the init script.

Setting gradle system property in .gradle file

I have a "general.gradle" file that sets the common properties for all of my projects.
This file is committed to git repository and shared among many users.
I would like to add a system property to is so it will be common to all the users
such options like systemProp.http.nonProxyHosts
is there a way?
You could make another file, like general.properties, add your system properties there prefixed by systemProp and then in general.gradle load the properties from that file, like so:
FileInputStream fileInputStream = new FileInputStream(new File('{YOUR_PATH}/general.properties'))
Properties properties = new Properties()
properties.load(fileInputStream)
fileInputStream.close()
properties.stringPropertyNames().forEach({key -> ext.set(key, properties.getProperty(key))})
and then load it to your root build.gradle file in projects, like so:
apply from: '{YOUR_PATH}/general.gradle'
You can retrieve it from the ext property. Following this example, if you put general.properties in your project and add there,for example: spring=dev. Then you put the property loading code in general.gradle and apply it in your build.gradle, if you add a task like this in your build.gradle:
task testProp << {
String profile = getProperty('spring')
System.setProperty('Spring.profiles.active', profile)
String prop = System.getProperty('Spring.profiles.active');
println prop
}
then the task execution should print out dev.

Getting Gradle 'rootProject' object to honor env vars

I ran across the following configuration in a Gradle project's buildfile (build.gradle):
codenarcMain {
configFile = rootProject.file("gradle/codenarc/CodeNarcMain.groovy")
}
When I Google "Gradle rootProject" I find this link which makes it look like rootProject is a ProjectDescriptor instance.
But looking at ProjectDescriptor, I don't see any property called file. I see a buildFile and projectDir, but no file property.
Ultimately, I am trying to get Gradle to load the CodeNarc config file from outside the build directory. On my system I have an env var called $CODENARC_HOME with the following directory structure:
CODENARC_HOME/ (say this is /home/myuser/tools/codenarc/)
CodeNarcMain.groovy
CodeNarcTest.groovy
README.md
Now I would like to change the CodeNarc config in Gradle to look something like this:
codenarcMain {
configFile = rootProject.file("CODENARC_HOME/CodeNarcMain.groovy")
}
And then, no matter where CODENARC_HOME is defined, the Gradle build will still be able to locate the config file.
So my questions:
What is the file property on rootProject, and why don't I see it in the API docs (linked above)?; and
How to get rootProject.file(...) to honor system/env vars inside its file path string argument?
rootProject in settings.gradle is-a ProjectDescriptor. rootProject in build.gradle is-a Project. Environment variables and system properties can be accessed in the standard Java way:
codenarcMain {
configFile = rootProject.file("${System.getenv("CODENARC_HOME")}/CodeNarcMain.groovy")
// or: System.getProperty("codenarc.home")
}

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/")

Having difficulty setting up Gradle multiproject build for existing repository layout

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.

Resources