Multi-project dependency resolution with gradle - gradle

Assumed we have two projects A and B. A provides artifacts that are consumed by B, i.e. A is some kind of base component where B builds upon.
All projects are git repositories, i.e. each has a main branch. For feature development there may be feature branches.
A feature development may affect
either B only
or A and B
A and B are built with gradle, i.e. there are gradle scripts. The script of B specifies A as a dependency.
The dependency of the B script of the main branch looks like
...
dependencies {
implementation("my.company:a-component:1.0.+")
...
Assume a feature development FEAT1 that affects both projects.
We create feature branches and name them like the features.
gradle builds artifacts of both projects, and we choose to give artifacts the feature name as version.
Then the dependency specification in the gradle script of B on branch FEAT1 looks like
...
dependencies {
implementation("my.company:a-component:FEAT1")
Admittedly this is an unusual version. But who cares, feature development is short-living, such versions (i.e. feature branches) come and go within few days. What finally counts is the main branch and the versions specified there.
Assume FEAT2 affects B only. So the dependency specification is again
...
dependencies {
implementation("my.company:a-component:1.0.+")
since there is no FEAT2 branch (i.e. no FEAT2 version) of A.
Now for the question: can I fetch the dependency from a feature branch if there is one, and otherwise default to main?
In pseudocode something like
...
dependencies {
implementation("my.company.a-component:${if (version FEAT2 of a-component exists) then FEAT2 else 1.0.+}")
Can I achieve that with gradle means?
If so any new feature branch of B would choose the version of its A dependency automatically correctly. That's what I would like to achieve.
I don't have more than basic gradle knowledge, read https://docs.gradle.org/current/userguide/single_versions.html and following, but didn't find an obvious solution.
We are currently using gradle 7.5.1.
The projects are Java/Kotlin projects, the artifacts are jar files.

Related

Multi module build in maven or separate builds

I have to make build architecture of my small project and I wonder how to proceed?
I have classes in one project for the so call core-part which can be used in other projects and api part in the first project which uses core-part.
So how to act?
To make multi-module Maven build and to produce 2 artifacts (which to upload to our company repo) and api-part have project dependency to core-part:
pom.xml
|
/core-part
|
/api-part(depends on core-part)
Create separate projects
Project1
/core-part
|
pom.xml
Project2
/api-part
|
pom.xml -> depends on project1's GAV artifact
Project3 ,...n depends on project1?
"Strong advice" : a project belongs in a multi-module build if release of that build requires release of that module, and vice versa. Multi-module projects should consist of things that must be released together.
An API should [practically] never depend on the release of its implementation, whereas the release of an API [nearly always] dictates the release of a new dependent implementation.
If you're not doing formal releases (why aren't you doing formal releases?) then this advice is still in force but less strong.
1. is the way if you'd like to build core-part and api-part within one build by building the aggregator/multi-module project (which has to have <packaging>pom).
There is actually a third artifact created (and installed and deployed to the repositories) then: The one for the aggregator project.
2. is the way if you'd like to handle (build, install, deploy) core-part and api-part individually. (An option you still have with 1. anyway.)
See:
POM Reference, Aggregation (or Multi-Module)
Maven: The Complete Reference, 3.6.2. Multi-module vs. Inheritance.

Maven multi-project depths

I was trying to build Maven pom in something similar to the following hierarchical form:
root
+-- A-POM
+-- B-POM
+-- C-POM
+---D-POM
I was hoping that this could take care of my changed module problem. That is, if C is changed, then A must be rebuilt, etc.
But I ran into the issue that it seems the packaging at root is "pom," and after that I can't have A as packaging "war" then continue to drill in to have A include B, C as its modules. It seems to me that any POM which does not have "pom" in the then it can't have child modules. Is my understanding correct? Is there a way to do what I wanted to do?
In addition, I don't seem about to chain the "changed" mechanism in Maven (must due to my lack of knowledge). I like to have Maven detect a dependent project has changed and rebuild all the affected projects.
Thanks so much!
the reactor project (the root of the multimodule project) must have pom packaging. So your nested structure is invalid since A is not of type pom and I'm pretty sure you won't get it to work this way.
Second point is that Maven is a modularized build system and uses repository mechanisms to locate pre-built artifacts instead of checking out all modules from version control and building them in a monolithic way like in the old days ;) This means that Maven cannot know what to rebuild when you change something at your module since it simple does not have all the other module there at this time.
I think this is more a CI task than that should be handled by the build system itself. I know that your can achieve such a behavior with an appropriate build/CI Server like Jenkins that supports upstream and downstream projects. This means it is able to detect dependencies between the projects and trigger other builds as soon as a dependency has been built. This comes close to the behavior you are trying to achieve.
Btw. rebuilding other projects is only required for SNAPSHOT dependencies. Jenkins with the maven plugin supports this behavior but, depending on the number of SNAPSHOT dependencies of your project, this can cause long chains of project builds on the server. Some folks are of the opinion that in general SNAPSHOT versions are hell for CI tasks since these artifacts can change over time and are not reproducible. You could think over completely omitting SNAPSHOT versions and building final versions each time. This would also obviate your requirement to rebuild other modules as soon as a module changes. There are simply no changes until you upgrade dependency versions.

MAVEN: Multi-module project, How to add different environments for build

There is a complex multi module project I am working on to move from ant.
Things we want to achieve are:
Ease of development, and packaging for developer.
Example: Parent Project
SON A
Parent Project
SON
GRANDSON A
GRANDSON B
DAUGTER (DEPENDS ON SON)
GRANDSON C
GRANDSON D
What I want is that when a developer is working on Daughter Project he just downloads the parent project and the SON dependency should be resolved from svn repository. I know it can be done by defining dependecy in pom.xml but that will conflict with my second requirement.
2) To download all daughters and sons of the parent project and compile them so that build consistently can be checked by automated build manager like Jenkins. And in addition I would also like to release the revisions on a flag to the maven repository if the build is successful. Please note that revision needs to be head of each module.
3) In both the procedure I want to create a tar file or my own style directories which will contain different jars in different directory as per my need. (I can achieve this by adding ant copy command; any better Idea on same)
What are the standard approaches in maven to achieve it.
In your Maven hierarchy, only the leaf projects (the grandchildren in your example) will be actual modules that contain code and produce an artifact (e.g. a jar). The internal nodes (Parent, Son, Daughter) will only be used to hold common configuration and dependencies that are shared by modules down the hierarchy. Therefore, Daughter cannot depend on Son, but Grandson C can depend on Grandson A, for instance.
If the parent project hierarchy have their own life cycle, i.e. the poms can be released independently, they should be on their on SVN module. Developers working on submodules do not even need to download these poms, as they would be automatically retrieved from your Archiva, if they are deployed there by Jenkins. See this answer for a similar situation.
However, you see to indicate you wish to build and release all modules are the same time. In this case, you can have the same pom hierarchy by keep all projects in the same SVN repository.

Maven dependency vs multimodule?

Very new to Maven, can someone please explain to me the difference between using maven modules vs just adding a dependency to your maven project to another maven project in your workspace? When would you use one over the other?
A dependency is a pre-built entity. You get the artifact for that dependency from Maven Central (or Nexus or the like.) It is common to use dependencies for code that belongs to other teams or projects. For example, suppose you need a CSV library in Android. You'd pull it as a dependency.
A Maven module gets built just like your project does. It is common to use Maven modules for components that the project owns. For example, maybe your project creates three jar files.
A dependency can be thought of as a lib/jar (aka Artifact in Maven parlance) that you need to use for building and/or running your code.
This artifact can either be built by your one of the modules of your multi module project or a third party pre-build library (for example log4j).
One of the concepts of maven is that each module is going to output a single artifact (say a jar). So in case of a complex project it is good idea to split your project to multiple modules. And these modules can be dependent on each other via declared dependencies.
See http://books.sonatype.com/mvnex-book/reference/multimodule-sect-intro.html for example of how a web app is split to parent and child modules and how they are linked.
One of the most confusing aspects of Maven is the fact that the parent pom can act as both a parent and as an aggregator.
99% of the functionality you think about in Maven is the parent pom aspect, where you inherit things like repositories, plugins, and most importantly, dependencies.
Dependencies are hard, tangible relationships between your libs that are evaluated during each build. If you think of your software as a meal, it's basically saying A requires ingredient B.
So let's say you're preparing lasagne. Then your dependency chain would look something like this:
lasagne
<- meatSauce
<- groundBeef
<- tomatoPaste
<- cheese
<- noodles
The key thing is, each of the above items (meatSause, groundBeef, cheese, etc) are individual builds that have their individual set of dependencies.
By contrast, the only section of your pom that pertains to aggregation is the modules section:
<modules>
<module>meatSauce</module>
<module>groundBeef</module>
<module>tomatoPaste</module>
<module>cheese</module>
<module>noodles</module>
</modules>
Aggregation simply tells your build engine that it should run these 5 builds in rapid succession:
groundBeef -> tomatoPaste -> cheese -> noodles -> meatSauce
The main benefit of aggregation is the convenience (just click build once) and ensuring the builds are in the correct order (e.g. you wouldn't want to build meatSauce before tomatoPaste).
Here's the thing though: even if you organize the libs as standalone projects without module aggregation, your build will still come out the same provided you build in the correct order.
Moreover, both Jenkins and Eclipse have mechanisms for triggering builds if a dependent project has changed (e.g. changing groundBeef will automatically trigger meatSauce).
Therefore if you're building out of Jenkins or Eclipse, there is no need for aggregation

Update dependencies in Project B when Project A is released

I have two multi module projects. One is the main project tree. The other project tree pulls in the artifacts (WARs, JARs, etc) from the first project. They each have their own separate parent pom.
I would like to keep their version numbers identical. Project 2 has a dependency management section that has the artifacts from project 1 in it. The problem with this is that I can't release the project due to snapshots.
For example. The version number (for both projects) is 3.4-SNAPSHOT. I can release a version 3.4 of project 1 and all the pom versions will be set from 3.4-SNAPSHOT to 3.5. When I go into project 2, I want to do the same exact thing. The hitch is that dependencies of project 1 are at 3.4-SNAPSHOT and I can't figure out how to automatically get them to be 3.5. It's like I want the dependencies to be updated to the release version, before actually releasing.
I understand that if I make both trees extend from the same parent pom, the maven release plugin would detect the dependencies as submodules and update them automatically.
I read a little bit about the versions plugin but I didn't like the use-latest-versions part of it (it seems way to dangerous). The Update-properties seemed promising but it upon a second cursory look, it looked like the use-latest-versions plugin packaged slightly different.
Also, I tried just using ${project.version} but that's basically the same as putting 3.4-SNAPSHOT in as the dependency - it'll error saying it can't release due to snapshots since the version actually hasn't been updated yet.
The way you have the projects configured they are totally independent. You might as well be asking for the version of your log4j dependency to be the same as your current project B version.
While it won't be automatic, you should replace the versions of all references to Project A within Project B to use a property that is defined in the parent pom for Project B. Then, when you release Project A, you only have to change one pom so the project.a.version property is now the released version instead of SNAPSHOT. After the release of B you could then change it back to ${project.version} to keep them in sync.
As you admit, the right answer here is to re-arrange your project to have a common parent. Anything else is just a workaround.
However, it looks to me like versions:use-releases might achieve your goal. You can specify an includes list to ensure only project 1 dependencies are updated.
If you wanted this to be automatic, you could consider binding this goal to one of the lifecycle phases prior to compile.
Edit: sadly this looks like its not possible. At least, that's how I understand by the "Executes by direct invocation only" mentioned at the top of the page.

Resources