59
votes

Colleagues have been touting the wonders of maven and its magical dependency stuff but I'm finding that it fails at what I would consider the obvious use.

Suppose I have a root folder with a master POM.

Then underneath I have some projects, call them A and B

B requires A and so the POM in the B folder has the appropriate dependency entry in it

Now, back in the root folder, in a profile, I specify that I want to build B.

When I perform the usual mvn clean install, I get a failure because A was not built.

My friends tell me I have to specify both A and B in that main profile in the root.

But isn't the whole point of dependency management that maven sees B, goes to the B POM file where it sees the dependency on A and so it should go build A automatically.

6
Did you specify A and B as modules of the master?millimoose
Also, can you post how you're specifying to only build a single module through a profile in the parent POM?millimoose
No, I didn't specify both --- that's the point --- I'm arguing that it should have been sufficient to specify B and then maven, by looking at the POM for B, should figure out all by itself that it has to first build A.David
I wasn't asking if you specified to build both A and B, just if A is a module of the parent project, using the <module> element. If you didn't, Maven can't possibly know that A exists. It works using logical artefact IDs, not module layout conventions, and the <module> elements are a way to tell it where to search for ones not in the repository.millimoose
If you have a master artifact master:master:1.0-SNAPSHOT in C:\work\master, there is absolutely nothing mandating in Maven that its child module master:A:1.0-SNAPSHOT be located in C:\work\master\A. Dependencies are resolved using artifact IDs, not using directory names. In master:B:1.0-SNAPSHOT, you declare a dependency on the artifact master:A:1.0-SNAPSHOT. If you don't put in the <module> declaration for the directory where A is located in, Maven can't know it should search that directory for a pom.xml.millimoose

6 Answers

45
votes

With the master POM:

~/scratch/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>scratch</groupId>
    <artifactId>scratch</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>nipple</module>
        <module>cabbage</module>
    </modules>
</project>

And the module POMs:

~/scratch/nipple/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <artifactId>scratch</artifactId>
        <groupId>scratch</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <groupId>scratch</groupId>
    <artifactId>nipple</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

~/scratch/cabbage/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <artifactId>scratch</artifactId>
        <groupId>scratch</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <groupId>scratch</groupId>
    <artifactId>cabbage</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>scratch</groupId>
            <artifactId>nipple</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

I can issue mvn package in the root directory after clearing out my local repository and end up with all the modules built. (Into empty-ish JARs, but built.)

Maven seems to look for dependencies either in the repository, or in the build in progress. It will not automatically traverse your project structure when you're only building a single module, because it's not required that you even have the parent project on your computer, much less one directory above the current module. (The parent-child relationship isn't even bijective.)

A reason why this is so might be because a directory layout where the location of modules would be predictable is in no way mandatory. It's even somewhat common and acceptable that the layout for the above example be like this:

projects
|
+--scratch
|  |
|  +--scratch-parent
|  |  |
|  |  +--pom.xml [The POM of scratch:scratch:1.0-SNAPSHOT]
|  |
|  +--nipple
|  |  |
|  |  +--pom.xml [The POM of scratch:mod1:1.0-SNAPSHOT]
|  |
|  +--cabbage
|  |  |
|  |  +--pom.xml [The POM of scratch:mod2:1.0-SNAPSHOT]

In this case, the <modules> section of the parent POM would be:

<modules>
    <module>../nipple</module>
    <module>../cabbage</module>
</modules>

Notice that there is nothing saying which artifact ID is in which module. It just serves to tell Maven that these are filesystem locations where to search for other artifacts related to this build.

26
votes

A reason I can think of that your desired behaviour hasn't been implemented is as follows:

Suppose I'm working on both projects A and B. Currently A is broken. If dependency resolution happened as you would like, I would never be able to build B until A was fixed. So I either have to roll back my changes to A, or focus on fixing A first. Either way possibly not what I want to focus on right now.

Generally B wants to work with the "last good" version of A, rather than the latest. Using the dependencies from the repository means they at least compiled ok (and hopefully the unit tests were run too).

13
votes

Take a look at the Maven reactor plugin, in particular reactor:make, which builds a module and all of the modules which it depends on.

5
votes

Rich is totally right. What you describe is generally not the expected behaviour. Although, as stated by deterb, the Maven reactor supports partial builds if the modules are known by the parent POM.

Building with mvn install -pl B -am should also make (-am) B's dependencies (that is, A).

Anyway, module A must be a module of the parent POM.

(see Maven Modules + Building a Single Specific Module)

5
votes

If you're working with IntelliJ, they have a little magic checkbox: "Resolve Workspace Artifacts" in their Maven run configuration. So no need to install nor build from the parent.

2
votes

The answer is just that its not the way Maven works. The idea behind Maven is to give the developer a logical, simple system for controlling dependencies. Getting dependencies from the repository is the key to this. Every exception weakens this control and simplicity. Adding A as a dependency in the parent POM fully addresses your scenario without adding further exceptions. Using batch files or ant scripts is another way to address your scenario.