2

I am currently trying to migrate a multi-app project from Ant to Maven.
At the moment the project consists of multiple packages, creating some kind of dependency tree, without circular dependencies. The leaves of this tree are "application" packages, containing a Main. Intermediate nodes are "library" packages, used by other library "packages" or "application" packages.
The nodes are allowed to "grow together" to a single node or leaf. I figured out, that those packages should probably be grouped into maven modules and I now have a structure similar to this:

root
    - lib1
    - lib1A (depends on lib1)
    - lib1B (depends on lib1)
    - app1A (depends on lib1A)
    - lib2  (depends on lib1B)
    - lib2A (depends on lib2)
    - lib2B (depends on lib2)
    - app2  (depends on lib2A and lib2B)
    - lib3  (depends on lib2A and lib2B)
    - app3A (depends on lib3)
    - app3B (depends on lib3)

Basically a library and an application can depend on one or more other libraries.
Now I would like to be able to build each app on it's own and create an executable jar for it.
The way I am trying to do it now is the following:

  • configure the pom.xml of every app to use maven-assembly-plugin to create an executable jar.
  • Build each needed module for a specific app.
  • Build the app-module, which results in a executable jar.

So the build for app2 would build lib1, lib1A and lib1B, lib2, lib2A and lib2B and finally app2.
However, to automate the build, I would need to create a build-script for every app, which takes care of building all needed dependecies, which maven should already do by itself.
Also, if I want to build multiple apps at once, I would need to build all libraries multiple times, or track the already built modules by myself.
As I am new to maven, I am not sure if that's the correct way to manage such a multi-app project.
So I am asking for some advice on how to correctly manage this use case.

EDIT:
To clarify what I would like to be able to do:

  • build a single app with it's dependencies, without building all apps (running maven on the parent pom).
  • build multiple apps (not all) with their dependencies, without building the dependencies multiple times.
Robert P
  • 9,398
  • 10
  • 58
  • 100
  • What is the harm in just building all the apps (and forgetting about the ones you don't need)? – J Fabian Meier Oct 10 '19 at 11:24
  • @JFMeier the project is pretty big, building all apps takes very long. If I only made changes to `app1A`, there is no reason to build anything other then `lib1`, `lib1A` and `app1A`. The build-time would be significantly smaller. – Robert P Oct 10 '19 at 11:29
  • You _can_ use `-am` and `-pl` options to build parts of your projects with all the libraries you need, but you easily get into version hell because your modules start to have different version numbers parts of which are increased sometimes. – J Fabian Meier Oct 10 '19 at 11:36
  • @JFMeier what do you mean by "version hell"? In my case, all modules always need to use the latest version of all libraries. – Robert P Oct 10 '19 at 12:15
  • Say you have a simple structure like app1, app2 and lib. app1 and app2 both depend on lib. At the beginning, all have the version `1.0.0-SNAPSHOT` in their POM. Now you build a release from app1, together with its dependency lib. You build them as version `1.0.0`. After that, you (or the maven release plugin) change the version in both modules POM to `1.0.1-SNAPSHOT`. Now you also need to update the POM of app2 to reference `1.0.1-SNAPSHOT` of lib. Still, the version in the POM of app2 is `1.0.0-SNAPSHOT`. So the versions of your modules start to diverge. – J Fabian Meier Oct 10 '19 at 12:20
  • @JFMeier as I am new to maven, I am currently playing around in a demo project. There, I created some properties for the version in the parent `pom` like `app1.version` and `lib1.version` and in the modules pom I use them as `${lib1.version}`. That way, the versions can be managed in one file and all others use the same version automatically. So versioning shouldn't be a problem in this case. The idea comes form [here](https://stackoverflow.com/questions/19123013/maven-version-with-a-property). – Robert P Oct 10 '19 at 12:26
  • I just noticed, that this results in a warning and might not work in newer maven versions. But I guess the `version` in my library modules is irrelevant and can be inherited from the parent, so I can use `${parent.project.version}` when defining the dependencies. – Robert P Oct 10 '19 at 12:34
  • Which then means that after you build version `1.1.0` of app1, this version number is "gone", and you cannot build version `1.1.0` of app2 any more. – J Fabian Meier Oct 10 '19 at 13:54
  • It seems to work pretty good in my demo project now. All libraries use the parent version, the apps use their own version, since they are not used as dependencies in other modules. Using `-pl app1,app2 -am` I can build multiple apps and the needed libs at once. We used to manage the version-numbers by our self until now, so I don't think we need a plugin to manage that for us. Also since we have full control of all this modules, the versioning of them is not as relevant as it is in other maven projects, every build should use the latest version of every part of it. – Robert P Oct 10 '19 at 14:07
  • Ok, I see. I haven't seen that construction before, but it seems reasonable. – J Fabian Meier Oct 10 '19 at 14:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/200656/discussion-between-springrbua-and-jf-meier). – Robert P Oct 10 '19 at 14:16

3 Answers3

0

If you define the dependencies in the respective POM and build the whole project (at the root level), then Maven automatically orders the modules topologically, which means that builds every module once and everything is done in the right order.

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • True, but I think it is worth it to specifically state that "root" needs to be a Maven parent pom defining the children as modules. I am not sure that this is already the case in the proposed structure in the question. – Gimby Oct 10 '19 at 08:30
  • @JFMeier that's correct, but I would like to be able to build a single application only. Building all applications takes a lot longer and is not always necessary. So I am looking for a way to build individual applications (including their modules) without building all applications. I edited the question to clarify that. – Robert P Oct 10 '19 at 09:26
  • 1
    If that's the requirement than you have to extract each app into separate repository so you can build separately and extract the libs into separate repos also...Apart from that why do you need to build a single application? – khmarbaise Oct 10 '19 at 10:14
  • @khmarbaise as I said, it is a multi-app project, where each app is independent form the others. They only share some common code. I don't know if you have some experience with Android Development, but there you have so called `app-modules` and `lib-modules`, where `app-modules` are used to create APKs, while `lib-modules` are used for shared code. I would like to achieve something similar in maven. – Robert P Oct 10 '19 at 11:21
0

I am now using a parent project, which defines the maven version, the common dependencies and the common plugin configurations.
The parent project also defines it's child modules in module-tags.
Every child module references this parent project and uses the same version.
To build the applications, I am running maven inside the parent project, using the -pl and -am flag, as mentioned in this comment.
The -pl flag tells maven to only build the listed modules, instead of building the whole project.
The -am flag tells maven to also build the needed dependencies.
Example:
Given the following structure:

parent
---- lib1
---- lib1A (depends on lib1)
---- lib1B (depends on lib1)
---- lib2 (depends on lib1B)
---- lib2A (depends on lib2)
---- lib2B (depends on lib2)
---- app1A (depends on lib1A)
---- app2A (depends on lib2A)
---- app2B (depends on lib2B)

Executing mvn clean install -pl app1A,app2A -am would build all modules except app2B and lib2B.
Also, the module lib1, which is used by app1A and app2A would only be built once.

Usually, you want to version each module independently, but in our case this would introduce a huge effort, since we have a lot of modules building on top of each other. Small changes in the "lowest" module (lib1 in the example) would cause changes in almost every module.
So in this case we would need to increase every version number and update all referenced dependencies in all modules.
We instead decided to always rebuild all dependencies, resulting in a always up-to-date Jar. That's why we only manage the maven version in the parent project.

Robert P
  • 9,398
  • 10
  • 58
  • 100
  • what if app2 needs its parent to be specific (ex:"spring-boot-starter-parent" ) and app1 doesn't ? – user1767316 Nov 02 '22 at 08:38
  • I am not sure about that, but in that case the parent of app1 would be a different one then in app2. We don't have this case, all projects have the same parent. Of course we use normal versions for third party libraries. – Robert P Nov 02 '22 at 10:50
-1

I think with maven multi-module, you just need to navigate to the module that you want to build, then run maven command from there. Maven will build that module with associated dependencies automatically without build unnecessary modules.

Edited: The original answer was incorrect, after did some test I found the following should be helpful: Maven Modules + Building a Single Specific Module You just need to add -pl and -am flags when building sepecific module from parent level.

Hưng Chu
  • 1,027
  • 5
  • 11
  • This does not seem to be true. If I run `mvn clean package` from `app1`, without building `lib1` first, I get the error `Could not find artifact my.test:lib1:jar:0.0.1-SNAPSHOT` – Robert P Oct 10 '19 at 11:19
  • I have double-checked again, seem like you're correct (I was manually using mvn clean install before, then I can run 'mvn clean package' in individual module then it would always succeed). I have done some research again it seems like this fit your question: https://stackoverflow.com/questions/1114026/maven-modules-building-a-single-specific-module – Hưng Chu Oct 15 '19 at 09:06
  • This seems to be the correct solution for my case. I am currently validating this approach for our case and I will at it as a solution. – Robert P Oct 15 '19 at 11:02
  • Then i think now the only problem is about your build automation script. I have the same problem with this long time ago and did not have enough time to investigate on a standard solution for Jenkins pipeline script though. – Hưng Chu Oct 16 '19 at 04:06
  • What kind of problem are you facing with your build automation script? – Robert P Oct 16 '19 at 06:06
  • It just about to determine the changes in specific modules when new commits go in then build on those specific modules instead of all modules. Anyway, it not a priority for me at the moment so I will investigate later. I was just thinking about the standard solution widely used for Jenkins pipeline script in that scenario because I think i am not the only one facing this lol (I am not a DevOps engineer btw ). – Hưng Chu Oct 16 '19 at 06:39
  • In our case, we don't build modules on every change but instead we make daily builds, where we simply build all applications. So the build automation does not seem to cause problems in our case. – Robert P Oct 16 '19 at 13:38
  • Yes because of the different styles of ci/cd I think. We are following approach build and test on every pull request / deploy every time we merge to develop branch. I already come up with some solutions for our case but do not have time to implement yet. – Hưng Chu Oct 16 '19 at 13:48