Multi-module project builds with Maven and Gradle

Just came back from wonderful JCreteยฎ 2018. This year didn't disappoint, we had close to 110 people openly sharing ideas and knowledge on both technical and soft skills matters. Here's a picture of the final schedule, captured by fellow Java Champion Badr Elhouari:

Every single one of those notes represents a session where JCrete participants poured their passion to share and exchange ideas. On Tuesday morning Robert Scholte (@rfscholte) held a Maven Q&A session and I felt this was the right opportunity to ask some questions regarding an specific setup for a multi-project build. We had briefly discussed the matter after lunch on the day prior so we had a head start getting into the session. What I didn't expect was that the session turned into a big pair programming experience as everyone in attendance had an opinion on how to solve the problem, yay for mob programming!

What was the problem I faced? Here's the main idea: suppose you have a 2 module multi-project build with module 1 named project1 and module 2 named project2; project1 is set as a dependency on project2, and project2 defines an executable entry point. The source code for this setup is available at aalmiray/maven-gradle-playground, and comes with Gradle and Maven variants. The file structure looks like this

.
โ”œโ”€โ”€ build.gradle
โ”œโ”€โ”€ gradle
โ”‚   โ””โ”€โ”€ wrapper
โ”‚       โ”œโ”€โ”€ gradle-wrapper.jar
โ”‚       โ””โ”€โ”€ gradle-wrapper.properties
โ”œโ”€โ”€ gradle.properties
โ”œโ”€โ”€ gradlew
โ”œโ”€โ”€ gradlew.bat
โ”œโ”€โ”€ pom.xml
โ”œโ”€โ”€ project1
โ”‚   โ”œโ”€โ”€ pom.xml
โ”‚   โ””โ”€โ”€ src
โ”‚       โ””โ”€โ”€ main
โ”‚           โ””โ”€โ”€ java
โ”‚               โ””โ”€โ”€ sample
โ”‚                   โ””โ”€โ”€ Version.java
โ”œโ”€โ”€ project2
โ”‚   โ”œโ”€โ”€ build.gradle
โ”‚   โ”œโ”€โ”€ pom.xml
โ”‚   โ””โ”€โ”€ src
โ”‚       โ””โ”€โ”€ main
โ”‚           โ””โ”€โ”€ java
โ”‚               โ””โ”€โ”€ sample
โ”‚                   โ””โ”€โ”€ Env.java
โ””โ”€โ”€ settings.gradle

I'll explain the Gradle build first as it shorter and it happens to be the one where I have more experience. The root build.gradle file defines common configuration to all modules, gradle.properties defines project properties that have static values (these properties could also be defined inside build.gradle), and settings.gradle defines the modules that belong to this multi-project build.

gradle.properties

version = 0.0.0
group   = com.acme

settings.gradle

include 'project1'
include 'project2'

build.gradle

apply plugin: 'base'

subprojects {
    apply plugin: 'java'

    repositories {
        mavenCentral()
    }
}

These 3 files provide the equivalent behavior of a parent POM in a Maven multi-project build, which right now is limited to setting publishing coordinates (group and version) as well as marking every module as a Java project. You may notice that the build.gradle file for project1 is missing, that's because it's not needed at all given that its parent has defined all required behavior. It this missing file is bugging you then you can create an empty build.gradle. Now, the build file for project2 is where the final piece of the puzzle is found:

project2/build.gradle

apply plugin: 'application'

mainClassName = 'sample.Env'

dependencies {
    compile project(':project1')
}

This build file establishes the relationship between modules and defines the executable coordinates for project2. Alright, with this setup it's possible to compile and execute project2 in one go. We can do this at the root level (the directory where the root build.gradle file is located) or inside the directory that contains project2 (if you're having trouble figuring out how to invoke Gradle or its wrapper inside a multi project build do yourself a favor and install gdub).

$ gw :project2:run
Using gradle at '/tmp/maven-gradle-playground/gradlew' to run buildfile '/tmp/maven-gradle-playground/build.gradle':

> Task :project1:compileJava
> Task :project1:processResources NO-SOURCE
> Task :project1:classes
> Task :project1:jar
> Task :project2:compileJava
> Task :project2:processResources NO-SOURCE
> Task :project2:classes

> Task :project2:run
hello - 1

Notice that project1 is compiled before project2, project2 is executed right after that. This is possible because the application plugin is aware of project dependencies and it knows it requires all binaries and dependencies before proceeding, in this case it requires the compilation output of project1. This is an important aspect that we'll look again with the equivalent Maven configuration. If the same command line is executed again without making any changes to the sources we get

$ gw :project2:run
Using gradle at '/tmp/maven-gradle-playground/gradlew' to run buildfile '/tmp/maven-gradle-playground/build.gradle':

> Task :project1:compileJava UP-TO-DATE
> Task :project1:processResources NO-SOURCE
> Task :project1:classes UP-TO-DATE
> Task :project1:jar UP-TO-DATE
> Task :project2:compileJava UP-TO-DATE
> Task :project2:processResources NO-SOURCE
> Task :project2:classes UP-TO-DATE

> Task :project2:run
hello - 1

If a change is made to project1, say the constant value is changed from 1 to 2, we get

$ gw :project2:run
Using gradle at '/tmp/maven-gradle-playground/gradlew' to run buildfile '/tmp/maven-gradle-playground/build.gradle':

> Task :project1:compileJava
> Task :project1:processResources NO-SOURCE
> Task :project1:classes
> Task :project1:jar
> Task :project2:compileJava
> Task :project2:processResources NO-SOURCE
> Task :project2:classes

> Task :project2:run
hello - 2

Very well, this sums up my expectations for this multi-project build when using Gradle. Now the case for Maven. We'll need a parent POM that defines common settings among modules as well as the modules that belong to this build. We'll also need POM files for both modules, making sure that project1 is defined as a dependency of project2 and that project2 can be executed. This is how these files looked after a few refinements were made during the mob programming session:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.acme</groupId>
    <artifactId>parentpom</artifactId>
    <version>0.0.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>project1</module>
        <module>project2</module>
    </modules>
</project>

project1/pom.xml

<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>
    <artifactId>project1</artifactId>
    <packaging>jar</packaging>

    <parent>
        <groupId>com.acme</groupId>
        <artifactId>parentpom</artifactId>
        <version>0.0.0</version>
    </parent>
</project>

project2/prom.xml

<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>
    <artifactId>project2</artifactId>
    <packaging>jar</packaging>

    <parent>
        <groupId>com.acme</groupId>
        <artifactId>parentpom</artifactId>
        <version>0.0.0</version>
    </parent>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>sample.Env</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>project1</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
</project>

My first problem was caused by a confusion between the usage of the -am vs -amd flags. The mvn -h command line yields the following descriptions for each flag

-am,--also-make                        If project list is specified, also
                                       build projects required by the
                                       list
-amd,--also-make-dependents            If project list is specified, also
                                       build projects that depend on
                                       projects on the list

It turns out I was invoking -amd where actually I needed -am. If you haven't used the -am flag in a multi-module build before let me tell you, it's the bee's knees! This is part of the behavior that I was missing in Maven after spending so much time with Gradle. This flag lets you build dependent projects and use their compilation outputs as part of the dependency graph, no need to push intermediate artifacts to the local repository by mindlessly invoking (and forgetting to invoke from time to time) mvn install as we often do. With this new information in mind I was able to compile project2 and all of its required dependencies using the following command line

$ mvn -am -pl :project2 compile
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] parentpom                                                          [pom]
[INFO] project1                                                           [jar]
[INFO] project2                                                           [jar]
[INFO] 
[INFO] -------------------------< com.acme:parentpom >-------------------------
[INFO] Building parentpom 0.0.0                                           [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] -------------------------< com.acme:project1 >--------------------------
[INFO] Building project1 0.0.0                                            [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ project1 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /tmp/maven-gradle-playground/project1/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ project1 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /tmp/maven-gradle-playground/project1/target/classes
[INFO] 
[INFO] -------------------------< com.acme:project2 >--------------------------
[INFO] Building project2 0.0.0                                            [3/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ project2 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /tmp/maven-gradle-playground/project2/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ project2 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /tmp/maven-gradle-playground/project2/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parentpom 0.0.0 .................................... SUCCESS [  0.006 s]
[INFO] project1 ........................................... SUCCESS [  0.823 s]
[INFO] project2 0.0.0 ..................................... SUCCESS [  0.036 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.147 s
[INFO] ------------------------------------------------------------------------

Alright, the next step was to attempt running project2 by specifying a different goal

$ mvn -am -pl :project2 compile exec:java
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] parentpom                                                          [pom]
[INFO] project1                                                           [jar]
[INFO] project2                                                           [jar]
[INFO] 
[INFO] -------------------------< com.acme:parentpom >-------------------------
[INFO] Building parentpom 0.0.0                                           [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] --- exec-maven-plugin:1.3.1:java (default-cli) @ parentpom ---
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parentpom 0.0.0 .................................... FAILURE [  0.467 s]
[INFO] project1 ........................................... SKIPPED
[INFO] project2 0.0.0 ..................................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.918 s
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.3.1:java (default-cli) on project parentpom: The parameters 'mainClass' for goal org.codehaus.mojo:exec-maven-plugin:1.3.1:java are missing or invalid -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginParameterException

Oops, definitely not what I was expecting to see. As it turns out, every goal defined on the command line will be applied to every single project in the reactor, this means exec:java will be invoked on all three projects, hmmm not good. The mob tried a couple of different combinations but we couldn't get it to work, but not all was lost as Robert pointed out this is a feature that may be available in a future version of Maven, tracked by MNG-6118. He also mentioned there's a workaround to this problem which is applying the Exec plugin at the root and marking it as skipped, making sure the skip flag is disabled in project2. This changes the configuration to the following

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.acme</groupId>
    <artifactId>parentpom</artifactId>
    <version>0.0.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>project1</module>
        <module>project2</module>
    </modules>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <version>1.2.1</version>
                    <configuration>
                        <skip>true</skip>
                        <mainClass>**undefined**</mainClass>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

project2/pom.xml

<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>
    <artifactId>project2</artifactId>
    <packaging>jar</packaging>

    <parent>
        <groupId>com.acme</groupId>
        <artifactId>parentpom</artifactId>
        <version>0.0.0</version>
    </parent>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <configuration>
                    <skip>false</skip>
                    <mainClass>sample.Env</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>project1</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
</project>

Notice that the root POM file defines a bogus value for the mainClass property of the Exec plugin, as this property is required even if the plugin is configured as skipped. Alright, time to try out the previous command line again

$ mvn -am -pl :project2 compile exec:java
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] parentpom                                                          [pom]
[INFO] project1                                                           [jar]
[INFO] project2                                                           [jar]
[INFO] 
[INFO] -------------------------< com.acme:parentpom >-------------------------
[INFO] Building parentpom 0.0.0                                           [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ parentpom >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ parentpom <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ parentpom ---
[INFO] skipping execute as per configuraion
[INFO] 
[INFO] -------------------------< com.acme:project1 >--------------------------
[INFO] Building project1 0.0.0                                            [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ project1 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /tmp/maven-gradle-playground/project1/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ project1 ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ project1 >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ project1 <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ project1 ---
[INFO] skipping execute as per configuraion
[INFO] 
[INFO] -------------------------< com.acme:project2 >--------------------------
[INFO] Building project2 0.0.0                                            [3/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ project2 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /tmp/maven-gradle-playground/project2/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ project2 ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ project2 >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ project2 <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ project2 ---
hello - 1
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parentpom 0.0.0 .................................... SUCCESS [  0.208 s]
[INFO] project1 ........................................... SUCCESS [  0.312 s]
[INFO] project2 0.0.0 ..................................... SUCCESS [  0.011 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.878 s
[INFO] ------------------------------------------------------------------------

Success! Notice that the exec:java goal is configured on all projects but skipped when invoked on the parent and project1 projects. This is so cool despite the verbose configuration when compared to Gradle. By the way did you catch that the command line goals are compile exec:java? It turns out that the Exec plugin does not have an explicit dependency on the compilation output, you must make it explicit! This is the key difference with the Gradle equivalent setup. If only the exec:java goal is configured we get

$ mvn -am -pl :project2  exec:java
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] parentpom                                                          [pom]
[INFO] project1                                                           [jar]
[INFO] project2                                                           [jar]
[INFO] 
[INFO] -------------------------< com.acme:parentpom >-------------------------
[INFO] Building parentpom 0.0.0                                           [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ parentpom >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ parentpom <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ parentpom ---
[INFO] skipping execute as per configuraion
[INFO] 
[INFO] -------------------------< com.acme:project1 >--------------------------
[INFO] Building project1 0.0.0                                            [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ project1 >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ project1 <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ project1 ---
[INFO] skipping execute as per configuraion
[INFO] 
[INFO] -------------------------< com.acme:project2 >--------------------------
[INFO] Building project2 0.0.0                                            [3/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ project2 >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ project2 <<<
[INFO] 
Downloading from central: https://repo.maven.apache.org/maven2/com/acme/project1/0.0.0/project1-0.0.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parentpom 0.0.0 .................................... SUCCESS [  0.196 s]
[INFO] project1 ........................................... SUCCESS [  0.003 s]
[INFO] project2 0.0.0 ..................................... FAILURE [  0.472 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.007 s
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project project2: Could not resolve dependencies for project com.acme:project2:jar:0.0.0: Could not find artifact com.acme:project1:jar:0.0.0 in central (https://repo.maven.apache.org/maven2) -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException
[ERROR] 
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR]   mvn <goals> -rf :project2

The final test was to make a change in project1 and see if project2 would pick it, and so I modified the constant version again from 1 to 2 and fired up the command line once more

$ mvn -am -pl :project2 compile exec:java
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] parentpom                                                          [pom]
[INFO] project1                                                           [jar]
[INFO] project2                                                           [jar]
[INFO] 
[INFO] -------------------------< com.acme:parentpom >-------------------------
[INFO] Building parentpom 0.0.0                                           [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ parentpom >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ parentpom <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ parentpom ---
[INFO] skipping execute as per configuraion
[INFO] 
[INFO] -------------------------< com.acme:project1 >--------------------------
[INFO] Building project1 0.0.0                                            [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ project1 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /tmp/maven-gradle-playground/project1/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ project1 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /tmp/maven-gradle-playground/project1/target/classes
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ project1 >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ project1 <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ project1 ---
[INFO] skipping execute as per configuraion
[INFO] 
[INFO] -------------------------< com.acme:project2 >--------------------------
[INFO] Building project2 0.0.0                                            [3/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ project2 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /tmp/maven-gradle-playground/project2/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ project2 ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ project2 >>>
[INFO] 
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ project2 <<<
[INFO] 
[INFO] 
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ project2 ---
hello - 1
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parentpom 0.0.0 .................................... SUCCESS [  0.198 s]
[INFO] project1 ........................................... SUCCESS [  0.628 s]
[INFO] project2 0.0.0 ..................................... SUCCESS [  0.012 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.184 s
[INFO] ------------------------------------------------------------------------

Aaaand no luck. Which is strange given that the compile step for project1 clearly states that there were changes and recompiled the sources. I must be missing something here and will follow up with the Maven team. Adding a clean goal does produce the expected result but it may not be a good idea in a big multi-project build as it clears the previous results resulting in slower build times.

UPDATE: Robert has pointed out there may be a bug in the compiler when incremental compilation is in use, see MCOMPILER-349.

The learning experience during the mob programming session was great, as we were able to share different configuration options from the point of view of two different build systems: Gradle and Maven. Robert also shared some of the possible features coming in Maven 4.0 by discussing the roadmap (some can be seen at this link). Inspired by what happened in this session Robert decided to run a Maven Bug Fixing session on Friday, where different teams rolled their sleeves and got busy solving a couple of Maven bugs. This was a great opportunity for newcomers that haven't had the chance to experience the thrill of contributing to an Open Source project. Later Robert decided to continue the idea with this

Maven is not a perfect tool (nor is Gradle for that matter), we all have heard the Maven/Gradle bashing from colleagues (or ourselves) at some point in our developer career. Instead of spending time raging about the state of the tools, what if we used that energy to create something positive? If you use Maven at regular intervals and have the passion to help others, please consider joining Robert and the rest of the Maven team to pick up some of these issues, we all win!

Keep on coding!

Liked it? Take a second to support aalmiray on Patreon!
Become a patron at Patreon!

Trackbacks/Pingbacks

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

ˆ Back To Top