[
https://issues.apache.org/jira/browse/MNG-8096?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Tamas Cservenak reassigned MNG-8096:
------------------------------------
Assignee: Tamas Cservenak
> Inconsistent dependency resolution behaviour for concurrent multi-module
> build can cause failures
> -------------------------------------------------------------------------------------------------
>
> Key: MNG-8096
> URL: https://issues.apache.org/jira/browse/MNG-8096
> Project: Maven
> Issue Type: Bug
> Components: Core
> Affects Versions: 3.8.2, 3.9.6
> Reporter: Joseph Leonard
> Assignee: Tamas Cservenak
> Priority: Major
> Fix For: 3.9.10, 4.0.0-beta-5
>
>
> h1. tl;dr
> Maven can resolve dependencies either from:
> * an external repo
> * a class directory of a module being built within the reactor
> * a packaged jar of a module being built within the reactor
> If you run a concurrent multi-module build it is possible to get a race
> condition whereby the build of module _Foo_ may resolve module _Bar_ from
> either of the three resolution {_}channels{_}. This inconsistency can result
> in the Maven _war_ plugin sometimes failing to build a functional war file. I
> would expect a consistent resolution would always take place.
> h1. Full details
> h2. Scenario
> Consider you have a repo with the following structure:
> {noformat}
> App
> / \
> / \
> (compile scope) (test scope)
> / \
> \/_ _\/
> ModuleA TestSupportModule1
> /
> /
> (compile scope)
> /
> \/_
> ModuleB
> /
> /
> (test scope)
> /
> \/_
> TestSupportModule2
> {noformat}
> If you were to make a src code change to the following test support modules:
> * TestSupportModule1
> * TestSupportModule2
> Then the minimum number of modules we need to build to verify the change set
> is OK is:
> * TestSupportModule1
> * TestSupportModule2
> * ModuleB
> * App
> i.e. there is no requirement to build ModuleA because we know that none of
> the src code changes could impact the classpaths used in its maven build.
> We know that despite 'App' depending (transitively) on ModuleB there is no
> need for the 'App' build to wait for ModuleB to complete its build because
> the src code change to TestSupportModule2 will not impact any of the
> classpaths used in the App maven build. Therefore to get the most efficient
> build possible we ideally would invoke Maven to run with 2 threads and with
> instruction to build *two distinct* 'dependency graphs':
> * TestSupportModule1 followed by ModuleB
> * TestSupportModule1 followed by App
> The following Maven command achieves exactly what we want because the reactor
> build order is based only on the *direct* (i.e. non-transitive) dependencies
> of the modules provided to the reactor in the build command. Therefore the
> absence of ModuleA results in two distinct 'dependency graphs':
> {noformat}
> mvn clean verify -pl TestSupportModule1,TestSupportModule2,ModuleB,App -T 2
> {noformat}
> Note: In reality the code base I maintain has a very large monobuild with
> 100s of modules and this type of build optimisation makes a significant
> difference to the speed of our monobuild (we use
> [https://github.com/gitflow-incremental-builder/gitflow-incremental-builder]
> to automate the logic of determining which modules to include in the reactor
> based on our change set).
> h2. Issue
> We have encountered an issue in the above scenario because the 'App' build
> has a race condition with the ModuleB build which will result in one of the
> following three outcomes:
> * If the 'App' build starts before the ModuleB build has compiled its src
> classes then the 'App' build will resolve ModuleB from the external repo
> (i.e. equivalent to ModuleB not being in the reactor at all)
> * If the 'App' build starts after ModuleB has compiled its src classes but
> before it has packaged these classes into a jar then the 'App' build will
> resolve ModuleB's {{target/classes}} directory
> * If the 'App' build starts after ModuleB has packaged its jar file then the
> 'App' build will resolve ModuleB's {{target/ModuleB.jar}} file.
> In many scenarios this dependency resolution inconsistency doesn't represent
> a challenge. However, it does cause an issue in our case because the 'App'
> POM has its Maven {{packaging}} stanza configured to {{war}} and in the
> scenario where ModuleB's {{target/classes}} directory is resolved by the
> 'App' then this results in the resultant 'App' war file being packaged with a
> completely empty ModuleB.jar file.
> h2. Proposed solution
> Ideally we would like the Maven reactor to retain isolation between the *two
> distinct* 'dependency graphs' it constructs at _instantiation_ throughout the
> entire Maven build. This would mean, in the simple example above, that the
> 'App' would *always* resolves ModuleB from the external repo (regardless of
> whether the reactor has built ModuleB or not in a _separate_ 'dependency
> graph' in the reactor).
>
> h1. Reproducer
> See [https://github.com/josple/mvn-multibuild-issue] (hopefully the README is
> clear enough – let me know if I can clarify anything).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)