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:
If you are wondering what was the schedule of @JCreteUnconf 2018! Really ๐ my first #JCrete & #Unconference experience ๐คฉ brilliant people with top notch content. Thanks a lot for all the disorganizers @heinzkabutz @javaperftuning @rgransberger @aalmiray @IXSC & others ๐๐ช๐ pic.twitter.com/iHk0dxSqle
โ Badr Elhouari (@badrelhouari) July 26, 2018
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
Inspired by a session during @JCreteUnconf I've created a URL for up-for-grabs issues on the @ASFMavenProject . Go to https://t.co/qXt6HZK9Ek and let us know if you want to help us on one of these (or other) issues. #opensourcecontribution
โ Robert Scholte (@rfscholte) August 5, 2018
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!
Trackbacks/Pingbacks