What is the difference between using maven -pl option and running maven from module level?

karokir picture karokir · Feb 24, 2016 · Viewed 12k times · Source

Is there any difference between

C:/dev/path/to/Project> mvn package -pl MyModule -am -s settings.xml

and

C:/dev/path/to/Project/MyModule> mvn package -am -s ../settings.xml

As far as I'm concerned, the result of these two actions should be he same.

However, the behaviour seems to be different in each case: the former one seems to be more extensive; the latter ends more quickly - and I'm trying to understand why is that.

Answer

A_Di-Matteo picture A_Di-Matteo · Mar 2, 2016

Let's first clarify something about multi-module (aggregator) build (that is, invoking the build from the aggregator/parent project) and building a single module.

Let's use the following example:

-+ modules-project
 |- module-a
 |- module-b (depends on module-a)

Hence modules-project will have as part of its pom the following:

<modules>
    <module>module-a</module>
    <module>module-b</module>
</modules>

When building from the module-project folder:

module-project> mvn clean install

The Maven Reactor will create the dependency graph of the declared modules and build them accordingly, hence module-a will be built before module-b because module-b depends on module-a

module-b> mvn clean install

Would work correctly, only building module-b, resolving module-a as a dependency (which we installed previously) and using it as part of the build classpath where required.

If instead of invoking a clean install on modules-project we would have invoked a clean package, Maven would not have installed anything in our local Maven cache, hence the dependency to module-a in the module-b project could not be resolved. To clarify:

module-project> mvn clean package

Will still build the whole project (with all of its submodules) and create the packages (that is, the jar files).

module-b> mvn clean package

Would now fail, because the dependency to module-a is not present in the local Maven cache nor in any Maven repository, that is, it doesn't exist as a dependency and Maven doesn't know anything about other modules, it is building a simple project (a module) without any knowledge about other modules (as provided by modules-project). When building modules-project instead, Maven is building each module with much more information, knowing that a module has a dependency to another module it will not look on the local cache or on any Maven repository for inter-modules dependencies. Again, it's a reactor build.

That's why you can build a sub-module as a separate build only if you already built the whole multi-module project and installed its artifacts at least in the local Maven cache. And that's why the best practice is always to work from the multi-module project, so that Maven has all the required information and you could:

  • Build the whole multi-module project (mvn clean install)
  • Build a single or a set of modules via the -pl option (but still starting from the multi-module project)
  • Build a single or a set modules and their dependencies via the -pl and -am options (still from the multi-module project)

Let's clarify now the last point, which will also answer your question.

modules-project> mvn clean package -pl module-a

Would run fine, building only module-a as part of the reactor build. However

modules-project> mvn clean package -pl module-b

Would fail, because Maven will try as requested to build module-b, but, again, would look for module-a as a dependency and not a module, hence in the local Maven cache or in the configured Maven repository, not finding it.

modules-project> mvn clean package -pl module-b -am

Would eventually work fine, because now Maven is also building the dependent modules (thanks to -am) and knows exactly where to find them (as part of the multi-module information, that is, the modules section).

Let's look at a last case:

module-b> mvn clean package -am

Would fail if we never installed the whole multi-module project (i.e. we always run clean package), the -am option would be ignored if not part of a reactor build (because it is an option of the reactor, not of the normal build) and Maven would still try to find module-a in local cache or in repositories.

That's why you should always build a sub-module from its aggregator/parent and via the -pl and -am options, to make sure that:

  • You build correctly, using the right dependencies and modules information
  • You build against the latest version of your code/dependency and not against something you installed previously in your maven cache which may not be aligned with its module's code

That's also why your observation is right, the first invocation you mentioned (building a submodule from the aggregator project) takes slightly longer because it is not simply the build of a submodule but something more (because there are more information to process and it is a different type of build, a reactor one, potentially building dependent modules also), while building directly a submodule (from its directory) is a simpler Maven build and as such also quicker (but more error prone because of the lack of some information).

More information on these options for a reactor build could be found here.