Add modules to a maven multi-module project with different relative paths - java

In the project I am currently working we have different maven projects in different SVN directories, something like this:
(simplified)
...service/rest-api/trunk/project1
...service/common/trunk/project2
...service/common/trunk/parent-aggregator
The last one (parent-aggregator) is a maven pom project that contains the shared dependencies and the multi-module configuration.
So as I am using Eclipse svn (subclipse) client, it allows me to import all those projects into my eclipse workspace, having all the projects in the same directory, therefore the configuration I created can use the relative paths:
parent-aggregator pom.xml:
<modules>
<module>../project1</module>
<module>../project2</module>
</modules>
The issue came when one of my colleagues got the projects using tortoise svn client and then imported the projects to eclipse, tortoise is replicating the svn directory structure in his local file system.
So whenever he tries to do a mvn clean install to the parent-aggregator, it fails due to project1 not being reachable. Which makes sense as in his machine the projects are not in the same directory.
Is there a way to reference modules so both structures can work?
I tried so far to use the artifactId:
<module>artifactId</module>
it doesn't work.
Also tried adding project name, by first defining a:
<name>project1</name>
inside project1 pom.xml
And then referencing it in module:
<module>project1</module>
But it keeps telling me that it cannot find the child module.
The temporary solution that we are using is having different relative path in his local machine:
<modules>
<module>../../../rest-api/trunk/project1</module>
<module>../project2</module>
</modules>
Which I don't like at all as we should have a single approach that we can keep in SVN.

The first thing you should do is to change your structure as well as in SVN..cause if you have multi-module build you express that those modules belong togehter so you should represent in your structure.
+--- root (pom.xml)
+--- mod-rest-api (pom.xml)
+--- mod-war (pom.xml)
+--- mod-p1 (pom.xml)
If you change your project according to the above you only have entries like this:
<modules>
<module>mod-rest-api</module>
<module>mod-war</module>
<module>mod-p1</module>
</modules>
This will simplify your entries and you don't need to have relativePath entries for your parents.
Furthermore you can have the structure in SVN as well:
URL/project/trunk
+--- root (pom.xml)
+--- mod-rest-api (pom.xml)
+--- mod-war (pom.xml)
+--- mod-p1 (pom.xml)
So you have URL/project/tags and URL/project/branches
Having entries in your modules like .. is from my point of view a build smell which indicates something is wrong with your folder structure in relationship to the project architecture.

Related

How to include the source set output of one subproject into another?

I have the following project structure where all three subprojects use the java-library plugin and the root project only has an allprojects block that sets the group and version information.
Root project 'project'
+--- Project ':project-a'
+--- Project ':project-b'
+--- Project ':project-c'
project-a has no dependencies
project-c depends on project-a via api(project(":project-a"))
project-b depends on project-c (and transitively on project-a) via api(project(":project-c"))
What I want to achieve is the following:
When project-c is built via the build task, the jar file that is procduced should include all classes from
project(":project-a").sourceSets["main"].output
Similarly, when project-b is built, the jar should include all classes from both
project(":project-a").sourceSets["main"].output and
project(":project-b").sourceSets["main"].output
What I've tried is writing
project(":project-c").evaluationDependsOn(":project-a")
project(":project-b").evaluationDependsOn(":project-c")
in the root project build.gradle.kts and then using
tasks.jar { from(project(":project-a").sourceSets["main"].output) }
and
tasks.jar {
from(project(":project-a").sourceSets["main"].output)
from(project(":project-c").sourceSets["main"].output)
}
in project-c's and project-b's build.gradle.kts, respectively.
While this solution works, it feels pretty hacky. From my current understandig this should somehow be possible using Gradle configurations. So, my question is if there is any better way to achieve what I want to do?
EDIT:
After some reading, I've realised that the calls to evaluationDependsOn are not necessary if configuring the jar task is done lazily as was done in this example (and described here).

Skip maven build for unmodified modules?

I am using maven multi module build like below
<modules>
<module>module1</module>
<module>module2</module>// dependant on module 1
<module>module3</module>// dependant on module 2
<module>warApplication</module>// its a war file dependant on above modules
</modules>
Present build behaviour :-
If i make a change only in module 1, maven will build even module2, module3 source files and then put module1,module2,module3 jar files under warApplication/target/lib.
Is there a configuration/way when i trigger the build, maven verifies if any file was modified under the module, if no skip that module. For war file it will put specific
lib file that got modified. This will save a lot of time.
To check if any file got modified under module or not, maven can store last build time . Before starting build it will check if any of the file got modified after last build timestamp, if yes build it otherwise skip it.
Is there any maven plugin available for this ?
Update :-
My project structure is
TopFolder
.mvn directory -> extensions.xml
build directory->build-Projects-> pom.xml
build directory-> parent -> pom.xml
modules - > module1 -> pom.xml
modules - > module2 -> pom.xml
modules - > module3 -> pom.xml
Here is i am including modules in parent pom
<modules>
<module>../../modules/module1</module>
<module>../../modules/module2</module>
</modules>
. I have included scm tag <developerConnection>scm:svn:https://comp.com/svn/trunk</developerConnection> in both below projects
build directory->build-Projects-> pom.xml
build directory-> parent -> pom.xml
Running build from build directory->build-Projects-> pom.xml.
But the issue is when I change any file any module and run build from build directory-> pom.xml, its not detecting module changes ?
Porbably the best option is using incremental module builder. The first time after making changes in your extension file you need to mvn install and so on you can use mvn -b incremental package which just build only the modules which are changed from the previous build. The github address is https://github.com/khmarbaise/incremental-module-builder
You can use profiles to build what you know has change, as timestamp isn't supported within the pom
See here for more details : Maven Modules optional in pom.xml

get maven clean install to work like maven clean + maven install

I have the following project hierarchy:
app
|-module1
| |-pom.xml
|-module2
| |-pom.xml
|-pom.xml
Module1 and module2 both copies files to the same target directory, so im using the app's pom.xml to clear that directory. My problem is, the execution order right now is module1[clean], module1[install], module2[clean], module2[install], app[clean], app[install], so everything module1 and module2 puts into that directory will be deleted.
I would like to get it to execute all clean first, then all install, even when i run mvn clean install. Or if there is another way to execute app[clean] before module1[install] and module2[install], that would work too.
EDIT
I ended up making a separate module (Netbeans POM projekt) for cleaning alone. Not the sollution i was hoping for, but it works for now.
The root of the problem here is that you're trying to make Maven do something that sort-of contradicts Maven's multi-module "conventions", as well as conflicting with Maven's "understanding" of a "target directory". There is a reason why Maven's reactor is operating the way that it does, and it is to preserve the Maven "spirit" (or "convention") of how modules are structured in a multi-module build.
In Maven, the target directory is supposed to belong only to one project: each project has its own target directory. In your scenario, there should really be a different target directory for app, module1 and module2.
I suppose your best bet, in order to both achieve your objective and keep your build process flexible, is to:
Have module1 output its own JAR into its own target directory (module1/target).
Have module2 output its own JAR into its own target directory (module2/target).
Add a plugin to app (the parent module) that will collect whatever it needs from module1/target and module2/target into app/target, and do whatever processing on those artifacts.

How to remove dependency: maven sub-modules creates implicit dependency on parent module

I have a maven project
Parent Project (with the following modules) (package type pom)
API Project (package type jar)
Packaging Project (has a dependency on API Project) (package type custom)
I want to be able to deploy the API project to Nexus repositories so others can leverage that code. The Packaging Project is more of a supporting utility project for a smaller set of deployment use-cases. The Parent Project wraps it all together for me.
When I deploy the API project to the nexus repo, it deploys fine. If i try to make a brand new project that has a depdency on API, it finds the API dependency in nexus but then also wants the Parent project as well. Is there any way to get around publishing the parent project as its really isnt necessary for use of the API lib when used via the nexus repo?
Any tips on how to organize my maven proj to support this?
When you add a <parent> reference to a Maven project what you are doing is saying: "Take all the configuration from that parent and inject it into my model, then override with the following"
Therefore, in order for Maven to build the model of your project, it is necessary for Maven to retrieve the parent itself. In other words, adding a <parent> tag creates an explicit hard dependency between the parent and the child.
The good news is that Inheritance does not have to follow Aggregation. What does that exactly mean?
Aggregation is when you list <modules> in your pom. It tells Maven that the reactor (i.e. the set of projects that Maven builds) should also include the following (sub)projects.
Inheritance is when you set a project's <parent>.
Nowhere does Maven enforce that a project's <parent> has to list its children as <modules> and nowhere does Maven enforce that a projects <modules> must list the project as a <parent>.
Some people will set up their project like so
ROOT/
+- pom.xml
+- parent/
| \- pom.xml
+- api/
| \- pom.xml
\- packaging/
\- pom.xml
where the parent of ROOT, api and packaging is actually a child of ROOT. Or sometimes ROOT will be a standalone project with no parent [In fact this is a pattern I use a lot myself. When I am working on several related project I will throw together an aggregating pom.xml on my local disk and open that with my IDE and that way all the related code is available as one single "project", even though the actual modules may come from different sources]
So in your case the solution would be to remove the <parent> tag from your "API" module.
Now! There is a downside. When you remove the <parent> tag from your "API" module you have removed all the defaults that your parent project is providing, so you will need to copy those defaults that are relevant to the "API" project or else you may find subtle changes in behaviour. For example, you should definately copy over the pinning of plugin versions, and any <dependencyManagement> that is relevant to the "API" dependencies. There are other bits you may have to copy, but you should use the Maven command mvn help:effective-pom before and after removing the <parent> tag as an aid to seeing the effective differences
for my developments I use uber-pom maven plugin which merges information in maven project hierarchy and provides independent pom as result and I publish artifacts in maven central without any extra links to parents

Maven parent pom vs modules pom

There seem to be several ways to structure parent poms in a multiproject build and I wondering if anyone had any thoughts on what the advantages / drawbacks are in each way.
The simplest method of having a parent pom would be putting it in the root of a project i.e.
myproject/
myproject-core/
myproject-api/
myproject-app/
pom.xml
where the pom.xml is both the parent project as well as describes the -core -api and -app modules
The next method is to separate out the parent into its own subdirectory as in
myproject/
mypoject-parent/
pom.xml
myproject-core/
myproject-api/
myproject-app/
Where the parent pom still contains the modules but they're relative, e.g. ../myproject-core
Finally, there's the option where the module definition and the parent are separated as in
myproject/
mypoject-parent/
pom.xml
myproject-core/
myproject-api/
myproject-app/
pom.xml
Where the parent pom contains any "shared" configuration (dependencyManagement, properties etc.) and the myproject/pom.xml contains the list of modules.
The intention is to be scalable to a large scale build so should be scalable to a large number of projects and artifacts.
A few bonus questions:
Where is the best place to define the various shared configuration as in source control, deployment directories, common plugins etc. (I'm assuming the parent but I've often been bitten by this and they've ended up in each project rather than a common one).
How do the maven-release plugin, hudson and nexus deal with how you set up your multi-projects (possibly a giant question, it's more if anyone has been caught out when by how a multi-project build has been set up)?
Edit: Each of the sub projects have their own pom.xml, I've left it out to keep it terse.
In my opinion, to answer this question, you need to think in terms of project life cycle and version control. In other words, does the parent pom have its own life cycle i.e. can it be released separately of the other modules or not?
If the answer is yes (and this is the case of most projects that have been mentioned in the question or in comments), then the parent pom needs his own module from a VCS and from a Maven point of view and you'll end up with something like this at the VCS level:
root
|-- parent-pom
| |-- branches
| |-- tags
| `-- trunk
| `-- pom.xml
`-- projectA
|-- branches
|-- tags
`-- trunk
|-- module1
| `-- pom.xml
|-- moduleN
| `-- pom.xml
`-- pom.xml
This makes the checkout a bit painful and a common way to deal with that is to use svn:externals. For example, add a trunks directory:
root
|-- parent-pom
| |-- branches
| |-- tags
| `-- trunk
| `-- pom.xml
|-- projectA
| |-- branches
| |-- tags
| `-- trunk
| |-- module1
| | `-- pom.xml
| |-- moduleN
| | `-- pom.xml
| `-- pom.xml
`-- trunks
With the following externals definition:
parent-pom http://host/svn/parent-pom/trunk
projectA http://host/svn/projectA/trunk
A checkout of trunks would then result in the following local structure (pattern #2):
root/
parent-pom/
pom.xml
projectA/
Optionally, you can even add a pom.xml in the trunks directory:
root
|-- parent-pom
| |-- branches
| |-- tags
| `-- trunk
| `-- pom.xml
|-- projectA
| |-- branches
| |-- tags
| `-- trunk
| |-- module1
| | `-- pom.xml
| |-- moduleN
| | `-- pom.xml
| `-- pom.xml
`-- trunks
`-- pom.xml
This pom.xml is a kind of "fake" pom: it is never released, it doesn't contain a real version since this file is never released, it only contains a list of modules. With this file, a checkout would result in this structure (pattern #3):
root/
parent-pom/
pom.xml
projectA/
pom.xml
This "hack" allows to launch of a reactor build from the root after a checkout and make things even more handy. Actually, this is how I like to setup maven projects and a VCS repository for large builds: it just works, it scales well, it gives all the flexibility you may need.
If the answer is no (back to the initial question), then I think you can live with pattern #1 (do the simplest thing that could possibly work).
Now, about the bonus questions:
Where is the best place to define the various shared configuration as in source control, deployment directories, common plugins etc. (I'm assuming the parent but I've often been bitten by this and they've ended up in each project rather than a common one).
Honestly, I don't know how to not give a general answer here (like "use the level at which you think it makes sense to mutualize things"). And anyway, child poms can always override inherited settings.
How do the maven-release plugin, hudson and nexus deal with how you set up your multi-projects (possibly a giant question, it's more if anyone has been caught out when by how a multi-project build has been set up)?
The setup I use works well, nothing particular to mention.
Actually, I wonder how the maven-release-plugin deals with pattern #1 (especially with the <parent> section since you can't have SNAPSHOT dependencies at release time). This sounds like a chicken or egg problem but I just can't remember if it works and was too lazy to test it.
From my experience and Maven best practices there are two kinds of "parent poms"
"company" parent pom - this pom contains your company specific information and configuration that inherit every pom and doesn't need to be copied. These informations are:
repositories
distribution managment sections
common plugins configurations (like maven-compiler-plugin source and target versions)
organization, developers, etc
Preparing this parent pom need to be done with caution, because all your company poms will inherit from it, so this pom have to be mature and stable (releasing a version of parent pom should not affect to release all your company projects!)
second kind of parent pom is a multimodule parent. I prefer your first solution - this is a default maven convention for multi module projects, very often represents VCS code structure
The intention is to be scalable to a large scale build so should be scalable to a large number of projects and artifacts.
Mutliprojects have structure of trees - so you aren't arrown down to one level of parent pom. Try to find a suitable project struture for your needs - a classic exmample is how to disrtibute mutimodule projects
distibution/
documentation/
myproject/
myproject-core/
myproject-api/
myproject-app/
pom.xml
pom.xml
A few bonus questions:
Where is the best place to define the various shared configuration as in source control, deployment directories, common plugins etc. (I'm assuming the parent but I've often been bitten by this and they've ended up in each project rather than a common one).
This configuration has to be wisely splitted into a "company" parent pom and project parent pom(s). Things related to all you project go to "company" parent and this related to current project go to project one's.
How do the maven-release plugin, hudson and nexus deal with how you set up your multi-projects (possibly a giant question, it's more if anyone has been caught out when by how a multi-project build has been set up)?
Company parent pom have to be released first. For multiprojects standard rules applies. CI server need to know all to build the project correctly.
An independent parent is the best practice for sharing configuration and options across otherwise uncoupled components. Apache has a parent pom project to share legal notices and some common packaging options.
If your top-level project has real work in it, such as aggregating javadoc or packaging a release, then you will have conflicts between the settings needed to do that work and the settings you want to share out via parent. A parent-only project avoids that.
A common pattern (ignoring #1 for the moment) is have the projects-with-code use a parent project as their parent, and have it use the top-level as a parent. This allows core things to be shared by all, but avoids the problem described in #2.
The site plugin will get very confused if the parent structure is not the same as the directory structure. If you want to build an aggregate site, you'll need to do some fiddling to get around this.
Apache CXF is an example the pattern in #2.
There is one little catch with the third approach. Since aggregate POMs (myproject/pom.xml) usually don't have parent at all, they do not share configuration. That means all those aggregate POMs will have only default repositories.
That is not a problem if you only use plugins from Central, however, this will fail if you run plugin using the plugin:goal format from your internal repository. For example, you can have foo-maven-plugin with the groupId of org.example providing goal generate-foo. If you try to run it from the project root using command like mvn org.example:foo-maven-plugin:generate-foo, it will fail to run on the aggregate modules (see compatibility note).
Several solutions are possible:
Deploy plugin to the Maven Central (not always possible).
Specify repository section in all of your aggregate POMs (breaks DRY principle).
Have this internal repository configured in the settings.xml (either in local settings at ~/.m2/settings.xml or in the global settings at /conf/settings.xml). Will make build fail without those settings.xml (could be OK for large in-house projects that are never supposed to be built outside of the company).
Use the parent with repositories settings in your aggregate POMs (could be too many parent POMs?).

Categories