From a9bcc8df9e2a31ef29d6d923913717e7e790bc88 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Thu, 5 Sep 2019 20:23:15 +1000 Subject: [PATCH] [MINVOKER-174] add a property to configure build order (#6) Signed-off-by: olivier lamy --- src/it/project-setup/pom.xml | 1 + .../src/it/project3/invoker.properties | 18 +++ .../src/it/project6/invoker.properties | 18 +++ src/it/project-setup/src/it/project6/pom.xml | 32 +++++ src/it/project-setup/verify.bsh | 8 ++ .../plugins/invoker/AbstractInvokerMojo.java | 126 ++++++++---------- .../plugins/invoker/InvokerProperties.java | 16 ++- .../maven/plugins/invoker/InvokerSession.java | 5 +- src/main/mdo/invocation.mdo | 9 +- .../plugins/invoker/InvokerMojoTest.java | 30 ++++- 10 files changed, 181 insertions(+), 82 deletions(-) create mode 100644 src/it/project-setup/src/it/project3/invoker.properties create mode 100644 src/it/project-setup/src/it/project6/invoker.properties create mode 100644 src/it/project-setup/src/it/project6/pom.xml diff --git a/src/it/project-setup/pom.xml b/src/it/project-setup/pom.xml index b68981b6..0922bba0 100644 --- a/src/it/project-setup/pom.xml +++ b/src/it/project-setup/pom.xml @@ -43,6 +43,7 @@ under the License. ${project.build.directory}/it project3 + project6 */pom.xml diff --git a/src/it/project-setup/src/it/project3/invoker.properties b/src/it/project-setup/src/it/project3/invoker.properties new file mode 100644 index 00000000..5cf17e62 --- /dev/null +++ b/src/it/project-setup/src/it/project3/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.ordinal=1 diff --git a/src/it/project-setup/src/it/project6/invoker.properties b/src/it/project-setup/src/it/project6/invoker.properties new file mode 100644 index 00000000..081215ba --- /dev/null +++ b/src/it/project-setup/src/it/project6/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.ordinal=2 diff --git a/src/it/project-setup/src/it/project6/pom.xml b/src/it/project-setup/src/it/project6/pom.xml new file mode 100644 index 00000000..4be58740 --- /dev/null +++ b/src/it/project-setup/src/it/project6/pom.xml @@ -0,0 +1,32 @@ + + + + + + 4.0.0 + test + project6 + 0.1-SNAPSHOT + pom + + + UTF-8 + + diff --git a/src/it/project-setup/verify.bsh b/src/it/project-setup/verify.bsh index ff4a0053..59a985d4 100644 --- a/src/it/project-setup/verify.bsh +++ b/src/it/project-setup/verify.bsh @@ -51,6 +51,14 @@ try return false; } + int indexProject3 = log.indexOf( "Building: project3" ); + int indexProject6 = log.indexOf( "Building: project6" ); + if(indexProject3 > indexProject6) + { + System.out.println( "FAILED! project3 should be build before project6" ); + return false; + } + File reportFile = new File( basedir, "target/invoker-reports/BUILD-project1.xml" ); if ( !reportFile.exists() ) { diff --git a/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java index 4197c86d..da8d3907 100644 --- a/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java +++ b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java @@ -93,6 +93,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -566,6 +567,12 @@ public abstract class AbstractInvokerMojo * # Path to an alternate settings.xml to use for Maven invocation with this IT. * # Since plugin version 3.0.1 * invoker.settingsFile = ../ + * + * # An integer value to control run order of projects. sorted in the ascending order of the ordinal. + * In other words, the BuildJobs with the slowest numbers will be executed first + * # Since plugin version 3.2.1 + * invoker.ordinal = 3 + * invoker.ordinal = 1 * * * @since 1.2 @@ -691,7 +698,7 @@ public void execute() setupReportsFolder(); } - BuildJob[] buildJobs; + List buildJobs; if ( pom == null ) { try @@ -716,10 +723,10 @@ public void execute() + "pom File parameter. Reason: " + e.getMessage(), e ); } - buildJobs = new BuildJob[] { new BuildJob( pom.getName(), BuildJob.Type.NORMAL ) }; + buildJobs = Collections.singletonList( new BuildJob( pom.getName(), BuildJob.Type.NORMAL ) ); } - if ( ( buildJobs == null ) || ( buildJobs.length < 1 ) ) + if ( buildJobs.isEmpty() ) { doFailIfNoProjects(); @@ -754,7 +761,7 @@ else if ( cloneProjectsTo == null && "maven-plugin".equals( project.getPackaging } // First run setup jobs. - BuildJob[] setupBuildJobs = null; + List setupBuildJobs = null; try { setupBuildJobs = getSetupBuildJobsFromFolders(); @@ -765,20 +772,20 @@ else if ( cloneProjectsTo == null && "maven-plugin".equals( project.getPackaging // TODO: Check shouldn't we fail in case of problems? } - if ( ( setupBuildJobs != null ) && ( setupBuildJobs.length > 0 ) ) + if ( !setupBuildJobs.isEmpty() ) { // Run setup jobs in single thread // mode. // // Some Idea about ordering? - getLog().info( "Running " + setupBuildJobs.length + " setup job" - + ( ( setupBuildJobs.length < 2 ) ? "" : "s" ) + ":" ); + getLog().info( "Running " + setupBuildJobs.size() + " setup job" + + ( ( setupBuildJobs.size() < 2 ) ? "" : "s" ) + ":" ); runBuilds( projectsDir, setupBuildJobs, 1 ); getLog().info( "Setup done." ); } // Afterwards run all other jobs. - BuildJob[] nonSetupBuildJobs = getNonSetupJobs( buildJobs ); + List nonSetupBuildJobs = getNonSetupJobs( buildJobs ); // We will run the non setup jobs with the configured // parallelThreads number. runBuilds( projectsDir, nonSetupBuildJobs, parallelThreads ); @@ -816,7 +823,7 @@ private void setupReportsFolder() } } - private BuildJob[] getNonSetupJobs( BuildJob[] buildJobs ) + private List getNonSetupJobs( List buildJobs ) { List result = new LinkedList<>(); for ( BuildJob buildJob : buildJobs ) @@ -826,8 +833,7 @@ private BuildJob[] getNonSetupJobs( BuildJob[] buildJobs ) result.add( buildJob ); } } - BuildJob[] buildNonSetupJobs = result.toArray( new BuildJob[result.size()] ); - return buildNonSetupJobs; + return result; } private void handleScriptRunnerWithScriptClassPath() @@ -858,7 +864,7 @@ private void handleScriptRunnerWithScriptClassPath() scriptRunner.setClassPath( scriptClassPath ); } - private void writeSummaryFile( BuildJob[] buildJobs ) + private void writeSummaryFile( List buildJobs ) throws MojoExecutionException { @@ -1216,7 +1222,7 @@ static boolean alreadyCloned( String subpath, List clonedSubpaths ) * @param buildJobs The build jobs to run must not be null nor contain null elements. * @throws org.apache.maven.plugin.MojoExecutionException If any build could not be launched. */ - private void runBuilds( final File projectsDir, BuildJob[] buildJobs, int runWithParallelThreads ) + private void runBuilds( final File projectsDir, List buildJobs, int runWithParallelThreads ) throws MojoExecutionException { if ( !localRepositoryPath.exists() ) @@ -2381,42 +2387,53 @@ private List calculateExcludes() * @throws IOException * @see {@link #setupIncludes} */ - private BuildJob[] getSetupBuildJobsFromFolders() - throws IOException + private List getSetupBuildJobsFromFolders() + throws IOException, MojoExecutionException { List excludes = calculateExcludes(); - BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); + List setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); if ( getLog().isDebugEnabled() ) { - getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) ); + getLog().debug( "Setup projects: " + setupPoms ); } return setupPoms; } + private static class OrdinalComparator implements Comparator + { + private static final OrdinalComparator INSTANCE = new OrdinalComparator(); + + @Override + public int compare( Object o1, Object o2 ) + { + return Integer.compare( ( ( BuildJob ) o1 ).getOrdinal(), ( ( BuildJob ) o2 ).getOrdinal() ); + } + } + /** * Gets the build jobs that should be processed. Note that the order of the returned build jobs is significant. * * @return The build jobs to process, may be empty but never null. * @throws java.io.IOException If the projects directory could not be scanned. */ - BuildJob[] getBuildJobs() - throws IOException + List getBuildJobs() + throws IOException, MojoExecutionException { - BuildJob[] buildJobs; + List buildJobs; if ( invokerTest == null ) { List excludes = calculateExcludes(); - BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); + List setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); if ( getLog().isDebugEnabled() ) { getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) ); } - BuildJob[] normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL ); + List normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL ); Map uniquePoms = new LinkedHashMap<>(); for ( BuildJob setupPom : setupPoms ) @@ -2431,7 +2448,7 @@ BuildJob[] getBuildJobs() } } - buildJobs = uniquePoms.values().toArray( new BuildJob[uniquePoms.size()] ); + buildJobs = new ArrayList<>( uniquePoms.values() ); } else { @@ -2474,12 +2491,12 @@ BuildJob[] getBuildJobs() * @return The build jobs matching the patterns, never null. * @throws java.io.IOException If the project directory could not be scanned. */ - private BuildJob[] scanProjectsDirectory( List includes, List excludes, String type ) - throws IOException + private List scanProjectsDirectory( List includes, List excludes, String type ) + throws IOException, MojoExecutionException { if ( !projectsDirectory.isDirectory() ) { - return new BuildJob[0]; + return Collections.emptyList(); } DirectoryScanner scanner = new DirectoryScanner(); @@ -2516,7 +2533,19 @@ private BuildJob[] scanProjectsDirectory( List includes, List ex } } - return matches.values().toArray( new BuildJob[matches.size()] ); + List projects = new ArrayList<>( matches.size() ); + + // setup ordinal values to have an order here + for ( BuildJob buildJob : matches.values() ) + { + InvokerProperties invokerProperties = + getInvokerProperties( new File( projectsDirectory, buildJob.getProject() ).getParentFile(), + null ); + buildJob.setOrdinal( invokerProperties.getOrdinal() ); + projects.add( buildJob ); + } + Collections.sort( projects, OrdinalComparator.INSTANCE ); + return projects; } /** @@ -2528,7 +2557,7 @@ private BuildJob[] scanProjectsDirectory( List includes, List ex * contain null elements. * @throws java.io.IOException If any path could not be relativized. */ - private void relativizeProjectPaths( BuildJob[] buildJobs ) + private void relativizeProjectPaths( List buildJobs ) throws IOException { String projectsDirPath = projectsDirectory.getCanonicalPath(); @@ -2778,47 +2807,6 @@ private InvokerProperties getInvokerProperties( final File projectDirectory, Pro props = new Properties(); } -// Path projectsSourceFolder = this.projectsDirectory.toPath(); -// Path projectsTargetFolder; -// if ( cloneProjectsTo != null ) -// { -// projectsTargetFolder = cloneProjectsTo.toPath(); -// } -// else -// { -// projectsTargetFolder = projectsSourceFolder; -// } -// -// Path projectDir = projectsTargetFolder.relativize( projectDirectory.toPath() ); -// -// for ( int i = 0; i < projectDir.getNameCount(); i++ ) -// { -// Path subInvokerProperties; -// if ( i == 0 ) -// { -// subInvokerProperties = projectsSourceFolder.resolve( invokerPropertiesFile ); -// } -// else -// { -// subInvokerProperties = -// projectsSourceFolder.resolve( projectDir.subpath( 0, i ) ).resolve( invokerPropertiesFile ); -// } -// -// getLog().debug( "Looking for " + subInvokerProperties ); -// -// if ( Files.isRegularFile( subInvokerProperties ) ) -// { -// try ( InputStream in = new FileInputStream( subInvokerProperties.toFile() ) ) -// { -// props.load( in ); -// } -// catch ( IOException e ) -// { -// throw new MojoExecutionException( "Failed to read invoker properties: " + subInvokerProperties ); -// } -// } -// } - File propertiesFile = new File( projectDirectory, invokerPropertiesFile ); if ( propertiesFile.isFile() ) { diff --git a/src/main/java/org/apache/maven/plugins/invoker/InvokerProperties.java b/src/main/java/org/apache/maven/plugins/invoker/InvokerProperties.java index e8ba20ee..a3b8f3c0 100644 --- a/src/main/java/org/apache/maven/plugins/invoker/InvokerProperties.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerProperties.java @@ -54,7 +54,8 @@ private enum InvocationProperty SYSTEM_PROPERTIES_FILE( "invoker.systemPropertiesFile" ), DEBUG( "invoker.debug" ), SETTINGS_FILE ( "invoker.settingsFile" ), - TIMEOUT_IN_SECONDS ( "invoker.timeoutInSeconds" ); + TIMEOUT_IN_SECONDS ( "invoker.timeoutInSeconds" ), + ORDINAL ( "invoker.ordinal" ); private final String key; @@ -136,6 +137,15 @@ public String getJobDescription() return this.properties.getProperty( "invoker.description", "" ); } + /** + * Get the corresponding ordinal value + * @return The ordinal value + */ + public int getOrdinal() + { + return Integer.parseInt( this.properties.getProperty( "invoker.ordinal", "0" ) ); + } + /** * Gets the specification of JRE versions on which this build job should be run. * @@ -302,14 +312,14 @@ public void configureInvocation( InvocationRequest request, int index ) String goals = get( InvocationProperty.GOALS, index ); if ( goals != null ) { - request.setGoals( new ArrayList( Arrays.asList( StringUtils.split( goals, ", \t\n\r\f" ) ) ) ); + request.setGoals( new ArrayList<>( Arrays.asList( StringUtils.split( goals, ", \t\n\r\f" ) ) ) ); } String profiles = get( InvocationProperty.PROFILES, index ); if ( profiles != null ) { // CHECKSTYLE_OFF: LineLength - request.setProfiles( new ArrayList( Arrays.asList( StringUtils.split( profiles, + request.setProfiles( new ArrayList<>( Arrays.asList( StringUtils.split( profiles, ", \t\n\r\f" ) ) ) ); // CHECKSTYLE_ON: LineLength } diff --git a/src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java b/src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java index 7c11479e..43ed9bbb 100644 --- a/src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java @@ -22,7 +22,6 @@ import static org.apache.maven.shared.utils.logging.MessageUtils.buffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.apache.maven.plugin.MojoFailureException; @@ -60,9 +59,9 @@ class InvokerSession * * @param buildJobs The build jobs to set, must not be null. */ - InvokerSession( BuildJob[] buildJobs ) + InvokerSession( List buildJobs ) { - this.buildJobs = new ArrayList<>( Arrays.asList( buildJobs ) ); + this.buildJobs = new ArrayList<>( buildJobs ); } /** diff --git a/src/main/mdo/invocation.mdo b/src/main/mdo/invocation.mdo index f4024234..a019b2d8 100644 --- a/src/main/mdo/invocation.mdo +++ b/src/main/mdo/invocation.mdo @@ -96,6 +96,13 @@ under the License. String The type of the build job. + + ordinal + 1.0.0 + false + int + BuildJobs will be sorted in the ascending order of the ordinal. In other words, the BuildJobs with the slowest numbers will be executed first + @@ -209,4 +216,4 @@ under the License. - \ No newline at end of file + diff --git a/src/test/java/org/apache/maven/plugins/invoker/InvokerMojoTest.java b/src/test/java/org/apache/maven/plugins/invoker/InvokerMojoTest.java index b58234ca..5d51c400 100644 --- a/src/test/java/org/apache/maven/plugins/invoker/InvokerMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/invoker/InvokerMojoTest.java @@ -25,6 +25,8 @@ import org.apache.maven.plugin.testing.AbstractMojoTestCase; import org.apache.maven.plugins.invoker.model.BuildJob; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; /** * @author Olivier Lamy @@ -34,6 +36,13 @@ public class InvokerMojoTest extends AbstractMojoTestCase { + private MavenProject getMavenProject() + { + MavenProject mavenProject = new MavenProject(); + mavenProject.setFile(new File("target/foo.txt")); + return mavenProject; + } + public void testSingleInvokerTest() throws Exception { @@ -42,9 +51,12 @@ public void testSingleInvokerTest() List goals = invokerMojo.getGoals( new File( dirPath ) ); assertEquals( 1, goals.size() ); setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); + setVariableValueToObject( invokerMojo, "invokerPropertiesFile", "invoker.properties"); + setVariableValueToObject( invokerMojo, "project", getMavenProject() ); setVariableValueToObject( invokerMojo, "invokerTest", "*dummy*" ); - BuildJob[] poms = invokerMojo.getBuildJobs(); - assertEquals( 1, poms.length ); + setVariableValueToObject( invokerMojo, "settings", new Settings() ); + List poms = invokerMojo.getBuildJobs(); + assertEquals( 1, poms.size() ); } public void testMultiInvokerTest() @@ -55,9 +67,12 @@ public void testMultiInvokerTest() List goals = invokerMojo.getGoals( new File( dirPath ) ); assertEquals( 1, goals.size() ); setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); + setVariableValueToObject( invokerMojo, "invokerPropertiesFile", "invoker.properties"); + setVariableValueToObject( invokerMojo, "project", getMavenProject() ); setVariableValueToObject( invokerMojo, "invokerTest", "*dummy*,*terpolatio*" ); - BuildJob[] poms = invokerMojo.getBuildJobs(); - assertEquals( 2, poms.length ); + setVariableValueToObject( invokerMojo, "settings", new Settings() ); + List poms = invokerMojo.getBuildJobs(); + assertEquals( 2, poms.size() ); } public void testFullPatternInvokerTest() @@ -68,9 +83,12 @@ public void testFullPatternInvokerTest() List goals = invokerMojo.getGoals( new File( dirPath ) ); assertEquals( 1, goals.size() ); setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); + setVariableValueToObject( invokerMojo, "invokerPropertiesFile", "invoker.properties"); + setVariableValueToObject( invokerMojo, "project", getMavenProject() ); setVariableValueToObject( invokerMojo, "invokerTest", "*" ); - BuildJob[] poms = invokerMojo.getBuildJobs(); - assertEquals( 4, poms.length ); + setVariableValueToObject( invokerMojo, "settings", new Settings() ); + List poms = invokerMojo.getBuildJobs(); + assertEquals( 4, poms.size() ); } public void testAlreadyCloned()