MavenProblems

This page describes some of the general problems we've run into with Maven (Not specific bugs that can be easily addressed).  The goal of this page is to help clarify these problems and figure out if/how they can be resolved.  These issues are not blockers, they are more just ideas about what an ideal build system might look like, and what areas Maven might improve in the future.

 

Note: Many responses to these issues can be found on the Sonatype Blog.

 

  • No easy way to test multiple dependency configurations (JBBUILD-550, SUREFIRE-762).  Some projects, such as hibernate would benefit from an easy way to configure multiple dependency configurations.  This is especially useful for setting up multiple test configurations.  For projects like Microcontainer and EJB3 it might be useful to be able to test against the set of thirdparty dependencies used by AS version 5 and then test against dependencies of AS version 6.

 

  • Speed of Builds.  Maven builds are significantly slower than Ant builds.  A full build of the App server would take about 5-6 minutes and an incremental build about 1-2 minutes.  With Maven, the build takes about 10-12 minutes for a full build and 3-5 minutes for an incremental build.  Having fast incremental builds is very important to facilitate development.

 

  • Building a subset of modules (JBBUILD-548).  (This is resolved in Maven 2.1 and above with the Reactor Mode) Maven has no support for building only a subset of a multi-module project's modules.  This means that a developer only has a choice between building all the modules, or changing directories and building a single module.  Maven should allow modules to be selected from the command line.  Maven also has no way to build all local dependent modules.  For example  moduleA depends on moduleB, if I build moduleA and moduleB is found locally, it should also be built in some cases.

 

  • Maven is limited for of sharing configuration information among modules of a multi-module build.  For example, in a multi-module build I might want to have a set of properties that are generated before any module build.  This could be something like generate a timestamp, get the current svn revision, etc.  In Maven these properties must be set separately for each module.  If there was better support for shared module tasks, the properties configuration could be run before any module and the properties would easily be available to all the modules.

 

  • One artifact per project rule.  Maven has a general rule that there should be only a single artifact per project.  This is important to maven dependency resolution, because there is only one set of dependencies in the POM.  If multiple artifacts are generated in by a single POM (this commonly done by the assembly plugin) then there is no way to list a separate set of dependencies for the non-primary artifact.  Maven projects also often include a -sources jar and a -javadocs jar.  There is no intuitive way to generate a source jar for a secondary project jar.  For example if my project includes a "-client" jar, then I might also want something like a "-client-sources" jar.

 

  • The release process is not reliable.  Maven projects use the maven release plugin to help automate the release.  Unfortunately we have experienced numerous problems with this plugin.  The problem is not so much the bugs in the plugin.  The major problem comes from the fact that the bugs with the release plugin are not discovered until release time.  There is not an easy way to test a full release locally.  I'm not sure what the best solution to this problem is.  I think a lot of the problems with the release plugin stem from two things. 

 

One is the svn tag and checkout.  Because these are not done prior to the start of the release process, if there are any problems with these steps they will block a release.  They can be difficult to debug because the release plugin forces the whole process to restart in order to solve the issue.  Manually doing a tag an checkout is much easier because issues with svn can be resolved by themselves outside of the release plugin automation process.

 

The second major issue comes from changing the version numbers in the pom at release time.  The POM versions are changed automatically by the release plugin.  This seems like a good thing at first, but it can cause unforseen problems in multimodule projects that need to resolve inter-module dependencies.  Sometimes dependency resolution errors are found only at release time because the new versions do not yet exist in the maven repository.  This is another area where a more manual process would be preferred in many cases because the multiple step nature of the release plugin can make debugging difficult and time consuming.

 

  • No separation of build instructions vs. general project information.  In Maven the POM supplies both the set of build instructions (lifecycle) and general information about the project and dependency information.  IMO this is a flawed model.  Instead of having a single file, there should be some separation between the build instructions and the project information needed in the repository.

 

1. When a POM is used as a dependency (not a parent) from the repository only certain information is useful to the calling project.  Information like the project identifiers (groupId, artifactId), genearal project information (name, description, scm, etc).  Other information is ignored: everything in the build and reporting sections, the distributionManagement, and the repositories should be ignored but they aren't.  This means that each POM has a lot of information that is never used once it's in the repository and is more complicated than necessary.

 

2. If a POM is used in a parent/child relationship, then the this information will be used.  The problem with the parent/child model that Maven uses, is that build information can only be picked up from a single source.  This means that I can't get some build configuration from one POM and other information from another.

 

3. The project configuration (POM) used locally is not necessarily the same as what should be released/deployed.  For example, in a local build configuration we might want to avoid using a specific version of a dependency and instead just let the dependency resolver get the latest released version.  In the repository however, the released and/or deployed POM should always specify the version of the dependency that was used during the build.  Maven recently has some workarounds for this problem such as the versions-maven-plugin which helps to keep dependency versions up to date, but the problem still exists that I have to manually use this plugin to update the dependencies.  In an ideal build tool, this fuctionality would be automatic.

 

  • No easy way to add plugin configurations to a lifecycle.  This is perhaps a problem in Plexus.  There is a lot of duplicated code in Maven because there is not always a good separation between the plugin code and the plugin configuration.  An example of this is the maven source plugin.  The source plugin is basically a different configuration of the jar plugin, but currently there needs to be two separate plugins because there is no way to add a different configuration of the jar plugin to the lifecycle.  Another example is the compile vs. test-compile goals, these two goals are the same logic with a slightly different configuration.  Ant has a single compile task and compile vs. test-compile is just a different configuration of the same task.

 

  • Limited Access to the Build Lifecycle.  There is no simple way in Maven to add a new phase to the build lifecycle.  The only way to change the lifecycle is to define a new packaging type and create a completely new lifecycle.  There is only limited control over the order of plugin execution in a phase.  For example, the core maven plugins always come first in the lifecycle phase to which they are assigned.  There is no way to add a plugin execution to the beginning of a phase, or to easily hook in (without a plugin) to the build lifecycle.

 

  • Repository structure does not facilitate multi-platform builds. The Maven repository format does not have a good way to host multiple builds of a project that target different platforms (for example different JVM versions).  The recommended way to do this is by using a classifier for each JDK version (-jdk14), however this has significant problems.

 

1. It requires that all dependencies in the tree use the same classifier (so you can't have some with -jdk14 and some with -java14)

 

2. All artifacts must have the same list of dependencies.  Multiple dependency lists could be created using profiles, but this causes     other problems.

 

3. There is no way to run separate tests for each target platform because of the limited access to the build lifecycle and the project    classpaths.

 

4. It is not practical to use separate artifactIds for the separate target platforms because Maven does not support this.  The only way to do it would be to create a completely separate project structure for each platform.

 

  • No local separation of plugin dependencies vs. project dependencies.  Maven uses a local repository (typically ~/.m2/repository) to store all dependencies used during a build.  This includes dependencies of the project in addition to dependencies of the plugins.  In some cases I may want to build against a clean repository for project dependencies, but use a shared repository for plugin dependencies.  Also, I may want to track all project dependencies to find potential duplicates or unused artifacts.  Currently Maven might download several different versions of the same jar used in various plugins in the lifecycle.

 

  • Deployed pom is the same as a local pom.  The maven install and deploy plugins do not modify the pom, and there is no easy way to resolve some of the differences between what should go into a local development pom vs. what goes into a pom deployed in the repository. 
    • For example, in a multi-module build it would be convenient if the project version could be stored in a single place instead of holding the version in each pom.  This is a problem in maven because you don't want to deploy a pom with no version to the repository.  A solution would be to add the version upon deployment. 
    • Another example is dependency ranges and SNAPSHOT versions.  A local pom used for development should be able to contain version ranges, but a deployed pom should resolve this range to the actual dependency version used. 
    • When using dependency management in a parent it would be better if the dependency versions used during the build were added to the POM instead of relying on the info from the parent in the repository.

 

  • No way to handle renamed artifacts and/or link two dependency resolution IDs.  (http://jira.codehaus.org/browse/MNG-4530)There are many situations where it would be useful to link two dependency IDs to tell Maven that the two IDs refer to the same artifact.  For example when a dependency is moved to a new groupId, the old groupId/artifactId is treated as a completely separate artifact.  The old groupId/artifactId must then be excluded from each location where it appears in the dependency tree.  Maven should have a way to link these together and ignore any occurrences of the old groupId/artifactId.

 

  • No global excludes.  (http://jira.codehaus.org/browse/MNG-1977)  Maven only allows excludes which are tied to a specific dependency.  There is no way to exlude a dependency across an entire project.  For large projects with large dependency trees which contain components that have been renamed/moved multiple times, this is very difficult to maintain.  It also adds the requirement of using the enforcer plugin to ban certain dependencies which adds time and complexity to the build.

 

  • Maven only allows a single source directory per project.  In some cases it's useful to separate source code into multiple directories that have different compilation parameters.  Unfortunately this is not allowed in Maven.

 

  • No field for file extension in POM or in dependency config.  Maven uses the field "packaging" to set the file extension for the resulting project artifact.  This works fine for defining a lifecycle but works badly when mapping a simple packaging to a non-default
    file extension.  Or a common file extenstion to a non-default packaging.  For example the JBoss application server supports several different file extensions for jar files (sar, par, etc).  These are simple jar files with a non-standard extension, but it requires complex plugin configuration to allow the non-standard extensions.  Further complication is introduced when trying to use the generated artifacts in the repository.  The "type" field of the dependency configuration must be set to correctly find the file.  And the plugin that understands this type must be added as a build extension.  A much simpler solution would be to allow an extension field in the POM and dependency declaration that would allow a non-standard file extension to be used.

 

  • No way to distinguish between a build order dependency and a classpath dependency.  In most multi-module builds, it's necessary to set contstraints around the build order of the modules.  For example, module A must be built before module B.  The only way to control this in Maven is to add a dependency on module A to the pom of module B.  However, it's not always necessary for the jar to be added to the classpath.

 

Addional Information

 

Comparison of Maven vs. Ant

http://www.iternum.com/knowhow/guidelines/maven-vs-ant/

Some uses cases where Maven is not a good fit

http://java.dzone.com/articles/experiences-gradle-or-what-i

Some Maven ranting

http://kent.spillner.org/blog/work/2009/11/14/java-build-tools.html

 

.