How do I configure a dependency in a gradle project such that it also works as a submodule? - gradle

I want to use a project in my project as a git submodule, ideally without changing the upstream project (or if it makes sense, changing it in a way that will get merged there).
The problem is that the project consists of modules, 2 of which I want to directly use, with the one also depending on the other.
myProject
├ submodule
| ├ first
| | ├ build.gradle: implementation project(":second") <---- this fails if I don't change it to :submodule:second
| ├ second
| ├ settings.gradle: include ':first', ':second'
├ app
| ├ build.gradle: implementation project(":submodule:first"), implementation project(":submodule:second")
| ├ settings.gradle: include ':submodule:first', ':submodule:second', ':app'
As my project's settings.gradle replaces the submodule's settings.gradle, the submodule's absolute paths suddenly change. Can I somehow `include ':submodule:second as :second'?
I know I can use implementation project(somePrefix + ":second") and define this somePrefix accordingly but that would involve touching the submodule.

Gradle project paths (e.g. ':submodule:second')
actually represent project hierarchies. This means that using include ':submodule:second' in your settings.gradle will implicitly create a project called submodule, even if not relevant for the build at all.
do not need to match the directory structure (this is just a convention). You may set the project directory independent from the project path (and the project name).
Check out the following example from the documentation:
// include two projects, 'foo' and 'foo:bar'
// directories are inferred by replacing ':' with '/'
include 'foo:bar'
// include one project whose project dir does not match the logical project path
include 'baz'
project(':baz').projectDir = file('foo/baz')
However, I would recommend to not use include at all. Instead use includeBuild and setup a composite build. Composite builds differ from classical multi-project builds in their coupling. Since your Git submodule indicates a build that may be used as a standalone project (with its own settings.gradle), the strong coupling of a multi-project builds seems inappropriate. Multiple settings.gradle files may also cause problems in a multi-project build, but are fine for composite builds.

Related

Gradle composite build throwing "Failed to get Gradle name for :module"

We've a collection of libraries developed by different teams and individually pushed to different Git repositories. To minimize the hassle of publishing to local Maven repositories or publishing snapshot versions, we've put together a root project to include all those libraries with Gradle's dependencies substitutions.
The structure is as follows:
- root (Git-root)
...
- modules
- module-a (Git-module-a)
- a-core
- build.gradle.kts
- a-data
- build.gradle.kts
- settings.gradle.kts
- module-b (Git-module-b)
- b-core
- build.gradle.kts
- b-data
- build.gradle.kts
- settings.gradle.kts
- settings.gradle.kts
- settings.gradle.kts
It's a bit confusing but module-a and module-b are included as git submodules inside a modules folder of the root project.
The Gradle files are as follow:
settings.gradle.kts
rootProject.name = "project-root"
includeBuild("modules")
modules/settings.gradle.kts
rootProject.name = "modules"
includeBuild("module-a")
includeBuild("module-b")
modules/module-a/settings.gradle.kts
rootProject.name = "module-a"
include("a-core")
include("a-data")
modules/module-b/settings.gradle.kts
rootProject.name = "module-b"
include("b-core")
include("b-data")
Just to illustrate the reason for this, lets say that the module-b:b-core depends on the module-a:a-core library.
The problem is that when I run this build it comes back with the following message:
Multiple build operations failed.
Failed to get Gradle name for :a-core
Failed to get Gradle name for :a-data
Failed to get Gradle name for :b-core
Failed to get Gradle name for :b-data
I don't know if what I'm trying to achieve is possible, but I couldn't find anything in the documentation. It looks like we're always very close to what we desire, but it's almost impossible to get there.
Do you guys know what I'm missing?
Error messsages come from AGP ModelBuilder.getBuildeName()
The reason is that most "bottom" projects in composite build ("a-core", "a-data", "b-core" and "b-data") have gradle.rootProject.parent set to "topmost" project ("project-root"), instead of their immediate parents ("module-a" and "module-b").
You can check it putting
println("${project.name} parent is ${project.gradle.parent?.rootProject?.name}")
in build.gradle.kts of all the projects and running sync
It whether Gradle or AGP problem, depending on is such a root behavior correct or a gradle bug.
I have created the issue about this case in gradle project. Will look what gradle team answer :)
As temporary solution add includeBuild("a-core") and other bottom level projects in modules/settings.gradle.kts. If still will got error, try add it to the topmost "project-root" settings.gradle.kts instead.

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.

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.

Can Gradle handle local dependencies to other than sub-directories?

I don't know if I totally got the concept wrong, but I want to create several projects with dependencies to other projects which are not part of the directory structure of a parent project. I know that the normal way of doing this would be to use an external dependency which fetches from some external repository. But in this case, where let's say in project called 'F' a framework is developed, which is used in project 'P'., then P uses F, but F should IMO not necessarily be a sub-project of P as P is only used to test-drive the development of F (but it's not only a unit test). Later in the process, when F is stable, F is separated and can be consumed by other projects via a repository. But during development of F with P as it's test case, it would be nice if that round-trip through the repository could be omitted.
To make matters worse, for the initial development there is more than one test-driving consumer project, which all need to have a dependency to F, but not via an external repository.
My idea is to develop F in some place on the disk with it's own git reposity. The other P like projects reside somewhere else on the disk and have a local file system based dependency to F. Would such a construct be possible in Gradle? If so, where do I start? I scanned the Java examples but couldn't find an appropriate example.
Any ideas?
The Gradle project hierarchy is fully virtual. It just has the default that the physical location corresponds to the virtual hierarchy. But you have complete control over this. See: http://gradle.org/0.9-rc-1/docs/userguide/build_lifecycle.html#sec:settings_file
Regarding your other ideas have a look at the following Jira: http://jira.codehaus.org/browse/GRADLE-1014
You could consider a folder hierarchy like this one:
Main folder
|- F folder
| |- .git
| |- sources
| |- build.gradle (with parts specific to F)
|- P folder
| |- sources
| |- build.gradle (with part specific to P)
|- build.gradle (with common parts)
|- settings.gradle
So you can always decide to run gradle on either the F project, the P project or the two alltoegether. It will also allows you to promote you F project alone without the P or any other side projects.
For more up-to-date information, check the Multi Project Builds chapter of the Gradle documentation.

Gradle multiprojects with same name, different paths

I have a monolith Gradle project that contains multiple subprojects, each with its own subprojects, like so:
project
|
|-- subprojectA
| |-- models
|
|-- subprojectB
| |-- models
This compiles fine but the issue is when I try to add a dependency on :subprojectB:models to :subprojectA:models, Gradle thinks :subprojectA:models is trying to add a dependency on itself and complains of a circular dependency, even though I specify the fully qualified path like so (in subprojectA's build.gradle):
compile project(':subprojectB:models')
How can I avoid this? Can subprojects not have the same name even if their paths are unique?
Project identity for dependency resolution is based on the group:name:version or GAV coordinates, as explained in the linked Gradle issue.
So you need to make sure your different models project have different GAVs.
One way to make this happen is to make the subprojectA (or B) part of the group.
Another way is to assign names that are not based on the containing folder.
That's currently a known Gradle issue as Gradle by default uses the parent directory name as the project name. You can work around the issue as described here by assigning unique subproject names in the root project's settings.gradle like so:
include ':subprojectA:models', ':subprojectB:models'
project(':subprojectA:models-a').projectDir = file('subprojectA/models')
project(':subprojectB:models-b').projectDir = file('subprojectA/models')

Resources