[ https://issues.apache.org/jira/browse/MNG-8096?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17837142#comment-17837142 ]
Joseph Leonard commented on MNG-8096: ------------------------------------- h1. Reproducer See [https://github.com/josple/mvn-multibuild-issue] (hopefully the README is clear enough – let me know if I can clarify anything). > 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 > Priority: Major > > h2. 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. > h2. Full details > h4. 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). > h4. 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. > h4. 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). -- This message was sent by Atlassian Jira (v8.20.10#820010)