Multi-Module Gradle Configuration With Git X-Modules
See how to build a multi-component app in Gradle without uploading each component to Maven each time it's updated.
Join the DZone community and get the full member experience.Join For Free
Developing an application and a few libraries in parallel could be quite painful.
On one hand, it makes sense to create a separate Git repository (and a separate Gradle project) for each library and for the app itself. Then the libraries and the app would be connected via Gradle dependencies. So if a bug in the app is caused by a bug in one of the libraries, to fix it one has to
- make a change in the library;
- build and upload the library artifacts to the Maven/Ivy repository;
- download the new versions of the library artifacts from the app project;
- build the app and ...
- ... check if the change actually fixes the app, if not, go to (1)
This cycle is annoying and slow, especially if the building process involves running all tests on the CI.
On the other hand, there's another approach: put all libraries and apps into the same Git repository (so-called "monorepo"). It would work for some projects but in general it's not a good idea.
One could put the common library into a Git submodule to the app repository, but ...
In this post, I will describe how to set up a multi-component (libraries + an app) Gradle-based project that uses Git X-Modules to glue them together. This solution has the best from both worlds: every component resides in its own repository and can be built separately. But also the app can be built together with the libraries thus avoiding the "painful cycle".
For the purpose of this project, I will use Atlassian Bitbucket Server/Data Center as the Git server. It's also convenient because there's a special Git X-Modules App for it. If you are on any other Git Server, the configuration and the steps would be similar, although the UI will look different, and you will have to use Git X-Modules as a command-line tool.
I will also use the latest version of Gradle to date, which is 6.7.1.
As an example, I will create a simple Java application that uses a simple library. The application will be named 'app' and the library will be named 'lib'.
To create a multi-component Gradle-based project I will use the "Composite builds" feature of Gradle.
I will create 3 Git repositories: one for the 'app', one for the 'lib', and the third one ('parent') to unite both 'app' and 'lib' under one roof.
Each of the repositories can be cloned and built with Gradle independently of all others.
Step 1. Create a Git Repository for 'lib'
Create a 'lib' repository using Atlassian Bitbucket Server/Data Center UI.
Clone this empty Git repository (I suppose the Bitbucket Server/Data Center is run on example.org domain):
Create `src/main/java/library/Library.java` file with the following content:
Create `build.gradle` with this content:
The project is quite minimalistic, its structure is:
Make sure the 'lib' project can be built separately:
Commit and push the changes:
Step 2. Create a Git Repository for 'app'
Create an 'app' repository using Atlassian Bitbucket Server/Data Center UI.
Clone this empty repository:
Create `src/main/java/app/App.java` file:
It invokes the library method. Also, create `build.gradle`:
Because of the dependency, its compilation will fail unless the artifact of the 'lib' project is uploaded to the Maven repository:
But once the artifact is uploaded to the Maven/Ivy repository, the compilation will succeed.
Commit and push the changes:
Step 3. Make Sure that the Git X-Modules App Is Installed
From Bitbucket Server/Data Center UI go to `Administration | Find new apps | Search the` Marketplace and type "X-Modules". Install the app if it's not installed.
Step 4. Create a Git Repository for 'parent'
I will create a 'parent' repository that would contain 'lib' and 'app' as X-Modules (from the Git perspective they will be regular Git directories). And then I will add additional Gradle configuration files to build the whole project from sources, skipping Maven/Ivy.
Create a 'parent' repository using Atlassian Bitbucket Server/Data Center UI.
When the Git X-Modules app is installed there's an "X-Modules" button on the 'parent' repository page. Click it.
As there're no branches in the repository, click "Create Default Branch" to create 'master'.
Now click "Add Module" to add 'lib' to the project.
Choose the 'lib' repository.
And the 'master' branch.
Make sure "This Repository Path" is "lib". It's the path where the 'lib' repository will be inserted.
Click "Add Module".
Without applying the changes click "Add Module" again.
Choose the 'app' repository and 'master' branch in it.
Now make sure "This Repository Path" is "app".
Click "Add Module".
Apply the changes.
Now the 'parent' repository contains 'lib' and 'app' subdirectories with the content of 'lib' and 'app' repositories correspondingly.
Clone the 'parent' repository:
Now create the `settings.gradle` file with the following content:
It will tell Gradle to build 'lib' from sources instead of getting the corresponding artefact from Maven. Finally, create `build.gradle`:
This will create a "run" task for the parent project and delegate it to the 'app' project.
Now the parent project can be built from sources bypassing Maven:
Add and commit the changes:
Now the parent repository contains both 'lib' and 'app' modules and can be built from sources independently. Any change to "lib/" and "app/" subdirectories of the parent will be automatically synchronized with 'lib' and 'app' repositories by the Git X-Modules app.
The 'lib' repository can be also used by another app. Just create another 'parent' repository containing the 'lib' repository and the other app's repository as X-Modules.
Published at DZone with permission of Dmitry Pavlenko. See the original article here.
Opinions expressed by DZone contributors are their own.