Jump to: navigation, search
Icon-cleanup.png This article is in need of attention because it does not follow our wiki guidelines. Check namespace.
If you want to contribute, please read the rules for this wiki and if you have any questions, don't hesitate to contact the wiki team, we are more then willing to help you! :-)

Maven is a Java Build tool that is gaining more and more popularity in the Java Developper community. It allows developpers to describe their project as combinaison of java sources and libraries needed for linking. Maven will automatically fetch missing jar file on and download it if available at the right version.

Why is Maven a nightmare to package ?

First, Maven is a Java application and suffers from the habitual flaws from the packaging point of view. A good summary of such flaws can be found here : [1]

On the other hand, Maven is a tool that heavily relies on the Java ecosystem : it has a very high number of build dependencies (~hundreds, according to the structure of my ~/.m2, see below)...And those dependencies have a very high number of build dependencies too, including maven itself !

Building a tool A that requires the existence of A as a dependency is a well know situation in packaging universe, and can be solved with a technic called "bootstraping" (that is : you provide A as binary inside source to build a minimal A from source, then you use this minimal A to build the whole A tool). Cyclic Dependencies is however something far more common with maven than with, for instance, gcc.

Maven dependencies tree is composed of mixed version of same package, which makes the packaging even more difficult. It is just unrealistic to packaque all of them, as it would waste a lot of disk space on OBS for duplicated functionnalities ( has 6gb of jar file !)


There are several ways of packaging Maven.

One of them is to patch the sources in order to add support for an "improved" offline mode, taken from JPP maven package. This adds a new executable, jpp-mvn, that is a "offline" version of mvn (maven executable). However the patches are not trivial ones so this makes package maintenance difficult and can probably tie us to JPP release cycle to get up to date patches.

Another way to do is to rely exclusively on the maven provided facilities to handle package build. Maven is used to fetch, download dependencies from a repository, build a package from source, and then deploy it. Package deployment can occur on a repository, to make it available for others users that may fetch and download it using maven. The idea is that repository can be remote or local. We can use maven to fetch dependencies from a local repository, build a package and deploy it to another local repository ; the later can then become a local repository to fetch from dependencies. This approach requires no patch and is quite elegant, but it is not immediatly compatible with JPP approach. However both approach requires a lot of time because of the number of dependencies involved in Maven build process.

I've chosen the second approach. To summarize, we embed a maven repository containing all needed dependencies, we build package, then we deploy it to %buildroot/usr/share/maven2/repository. Later package can then use /usr/share/maven2/repository as a local mirror to get all their dependencies. (This can be in with /usr/share/maven2/conf/settings.xml)

Maven behavior

- Every jar files is stored in a cache located in M2_HOME, which is generally ~/.m2/

- In the cache the jar file is stored under the following path : groupID/artifactID/version/ , where groupID is translated into a path by substitution of '.' with '/' ; groupID is the java namespace where is "located" the java package (for instance : org.apache.ant, org.apache.maven, ...).

- To disable test, use -Dmaven.test.skip=true flag

- To forbid maven to fetch for a package on a remote repository, use -o flag. Maven will exit with an error if a jar file is missing from the cache.

- To deploy external file (that is file already provided in opensuse) the command is : mvn -Dmaven.repo.local=REPO deploy:deploy-file -Durl=file://%buildroot/%{_datadir}/maven2/repository -DrepositoryId=local -DrepositoryLayout=default -DgroupId=GROUPID -Dpackaging=jar -DartifactId=ARTIFACTID -Dversion=VERSION -Dfile=FILE

Where REPO, GROUPID,ARTIFACTID,VERSION and FILE are variables. Due to the length of this command, it may be wise to define a macro for it.

Writter work so far

(in home/vlj/maven2)

- In order to bootstrap Maven, I've installed Maven locally using official binaries. Then I've built it from source, in order to retrieve the needed dependencies. I included the ~/.m2 directory with the source.

- The hard step (and unfinished one) is to create a package for every jar in this ~/.m2 directory not in opensuse build service. To do that, I download the source of the jar file, run locally maven to retrieve all the dependencies, and include the corresponding .m2 directory. (This is taking a lot of time)

- In order to make maven "aware" of the already built package, I use it to "deploy" built package : maven -DaltDeploymentRepository=local::default::file:///usr/share/maven2/repository deploy

- Versions must be handled by hand. A common pattern seen in maven is that package A v1.1 will require A v1.0 to be built, for instance. Generally, A v1.1 is backward compatible, so you can change pom file to make package A v1.1 require A v1.1 to be built. However, you may need to manually provide such v1.1 package for the purpose of the bootstrap, so you need to build and deploy it on your computer, outside of OBS.

Useful Tools

I made a little script /usr/bin/mvn-util, that get installed with maven2-bootstrap package. It aliases some shell command to ease maven packaging :

mvn-util prepare : clean your maven2 cache repository (in ~/.m2/repository)
mvn-util build : launch a deployment of the maven project in current directory. This command makes maven download every artifact needed for building AND deployment.
mvn-util pack : create a m2repo.tar.bz2 archive of your ~/.m2 repository

I also made a little java tool mvn-helper. This tool takes a pom file as argument, and replaces every artifact version with the one in /usr/share/maven2/tools/version.xml ; obviously, this file must describe as much as possible what you have in /usr/share/maven2/repository. This tool is intended as a workaround for maven strict version policy.