diff --git a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java index cb2511d252..421a00a564 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Date; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -36,6 +37,7 @@ import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ProjectDependencyGraph; import org.apache.maven.execution.ProjectSorter; import org.apache.maven.lifecycle.LifecycleExecutor; import org.apache.maven.project.MavenProject; @@ -140,14 +142,14 @@ public MavenExecutionResult execute( MavenExecutionRequest request ) } try - { + { ProjectSorter projectSorter = new ProjectSorter( session.getProjects() ); - projects = projectSorter.getSortedProjects(); + ProjectDependencyGraph projectDependencyGraph = createDependencyGraph( projectSorter, request ); - session.setProjects( projects ); + session.setProjects( projectDependencyGraph.getSortedProjects() ); - session.setProjectDependencyGraph( new DefaultProjectDependencyGraph( projectSorter ) ); + session.setProjectDependencyGraph( projectDependencyGraph ); } catch ( CycleDetectedException e ) { @@ -161,6 +163,10 @@ public MavenExecutionResult execute( MavenExecutionRequest request ) { return processResult( result, e ); } + catch ( MavenExecutionException e ) + { + return processResult( result, e ); + } // Desired order of precedence for local artifact repositories // @@ -293,6 +299,8 @@ private void collectProjects( List projects, List files, Mav for ( File file : files ) { MavenProject project = projectBuilder.build( file, request.getProjectBuildingRequest() ); + + projects.add( project ); if ( ( project.getModules() != null ) && !project.getModules().isEmpty() && request.isRecursive() ) { @@ -340,8 +348,6 @@ else if ( moduleFile.isDirectory() ) collectProjects( projects, moduleFiles, request ); } - - projects.add( project ); } } @@ -368,4 +374,90 @@ protected Logger getLogger() return logger; } + private ProjectDependencyGraph createDependencyGraph( ProjectSorter sorter, MavenExecutionRequest request ) + throws MavenExecutionException + { + ProjectDependencyGraph graph = new DefaultProjectDependencyGraph( sorter ); + + if ( !request.getSelectedProjects().isEmpty() ) + { + File reactorDirectory = request.getPom().getParentFile().getAbsoluteFile(); + + Map projectsByFile = new HashMap(); + + for ( MavenProject project : sorter.getSortedProjects() ) + { + projectsByFile.put( project.getFile(), project ); + } + + List selectedProjects = new ArrayList( request.getSelectedProjects().size() ); + + for ( String selectedProject : request.getSelectedProjects() ) + { + File pomFile = new File( reactorDirectory, selectedProject ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, Maven.POMv4 ); + } + + MavenProject project = projectsByFile.get( pomFile ); + + if ( project != null ) + { + selectedProjects.add( project ); + } + else + { + throw new MavenExecutionException( "Could not find project in reactor: " + selectedProject, + request.getPom() ); + } + } + + boolean makeUpstream = false; + boolean makeDownstream = false; + if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) ) + { + makeUpstream = true; + } + else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) ) + { + makeDownstream = true; + } + else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) ) + { + makeUpstream = true; + makeDownstream = true; + } + else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) ) + { + throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(), + request.getPom() ); + } + + Collection makeProjects = new LinkedHashSet( selectedProjects ); + + if ( makeUpstream || makeDownstream ) + { + for ( MavenProject selectedProject : selectedProjects ) + { + if ( makeUpstream ) + { + makeProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) ); + } + if ( makeDownstream ) + { + makeProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) ); + } + } + } + + // TODO: process resume from + + graph = new FilteredProjectDependencyGraph( graph, makeProjects ); + } + + return graph; + } + } diff --git a/maven-core/src/main/java/org/apache/maven/DefaultProjectDependencyGraph.java b/maven-core/src/main/java/org/apache/maven/DefaultProjectDependencyGraph.java index bd902c35a6..9a5ee64c47 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultProjectDependencyGraph.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultProjectDependencyGraph.java @@ -70,17 +70,7 @@ public List getDownstreamProjects( MavenProject project, boolean t getDownstreamProjects( ProjectSorter.getId( project ), projectIds, transitive ); - List projects = new ArrayList(); - - for ( MavenProject p : sorter.getSortedProjects() ) - { - if ( projectIds.contains( ProjectSorter.getId( p ) ) ) - { - projects.add( p ); - } - } - - return projects; + return getProjects( projectIds ); } private void getDownstreamProjects( String projectId, Collection projectIds, boolean transitive ) @@ -96,6 +86,48 @@ private void getDownstreamProjects( String projectId, Collection project } } + public List getUpstreamProjects( MavenProject project, boolean transitive ) + { + if ( project == null ) + { + throw new IllegalArgumentException( "project missing" ); + } + + Collection projectIds = new HashSet(); + + getUpstreamProjects( ProjectSorter.getId( project ), projectIds, transitive ); + + return getProjects( projectIds ); + } + + private void getUpstreamProjects( String projectId, Collection projectIds, boolean transitive ) + { + for ( String id : sorter.getDependencies( projectId ) ) + { + projectIds.add( id ); + + if ( transitive ) + { + getUpstreamProjects( id, projectIds, transitive ); + } + } + } + + private List getProjects( Collection projectIds ) + { + List projects = new ArrayList(); + + for ( MavenProject p : sorter.getSortedProjects() ) + { + if ( projectIds.contains( ProjectSorter.getId( p ) ) ) + { + projects.add( p ); + } + } + + return projects; + } + @Override public String toString() { diff --git a/maven-core/src/main/java/org/apache/maven/FilteredProjectDependencyGraph.java b/maven-core/src/main/java/org/apache/maven/FilteredProjectDependencyGraph.java new file mode 100644 index 0000000000..8d748c9664 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/FilteredProjectDependencyGraph.java @@ -0,0 +1,111 @@ +package org.apache.maven; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.execution.ProjectDependencyGraph; +import org.apache.maven.project.MavenProject; + +/** + * Provides a sub view of another dependency graph. + * + * @author Benjamin Bentmann + */ +class FilteredProjectDependencyGraph + implements ProjectDependencyGraph +{ + + private ProjectDependencyGraph projectDependencyGraph; + + private Map whiteList; + + private List sortedProjects; + + /** + * Creates a new project dependency graph from the specified graph. + * + * @param projectDependencyGraph The project dependency graph to create a sub view from, must not be {@code null}. + * @param whiteList The projects on which the dependency view should focus, must not be {@code null}. + */ + public FilteredProjectDependencyGraph( ProjectDependencyGraph projectDependencyGraph, + Collection whiteList ) + { + if ( projectDependencyGraph == null ) + { + throw new IllegalArgumentException( "project dependency graph missing" ); + } + + this.projectDependencyGraph = projectDependencyGraph; + + this.whiteList = new IdentityHashMap(); + + for ( MavenProject project : whiteList ) + { + this.whiteList.put( project, null ); + } + } + + public List getSortedProjects() + { + if ( sortedProjects == null ) + { + sortedProjects = applyFilter( projectDependencyGraph.getSortedProjects() ); + } + + return new ArrayList( sortedProjects ); + } + + public List getDownstreamProjects( MavenProject project, boolean transitive ) + { + return applyFilter( projectDependencyGraph.getDownstreamProjects( project, transitive ) ); + } + + public List getUpstreamProjects( MavenProject project, boolean transitive ) + { + return applyFilter( projectDependencyGraph.getUpstreamProjects( project, transitive ) ); + } + + private List applyFilter( Collection projects ) + { + List filtered = new ArrayList( projects.size() ); + + for ( MavenProject project : projects ) + { + if ( whiteList.containsKey( project ) ) + { + filtered.add( project ); + } + } + + return filtered; + } + + @Override + public String toString() + { + return getSortedProjects().toString(); + } + +} diff --git a/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java b/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java index 6a01f43713..a84ed2ba19 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java +++ b/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java @@ -85,6 +85,12 @@ public class DefaultMavenExecutionRequest private String reactorFailureBehavior = REACTOR_FAIL_FAST; + private List selectedProjects; + + private String resumeFrom; + + private String makeBehavior; + private Properties properties; private Date startTime; @@ -200,6 +206,26 @@ public String getReactorFailureBehavior() return reactorFailureBehavior; } + public List getSelectedProjects() + { + if ( selectedProjects == null ) + { + selectedProjects = new ArrayList(); + } + + return selectedProjects; + } + + public String getResumeFrom() + { + return resumeFrom; + } + + public String getMakeBehavior() + { + return makeBehavior; + } + public Date getStartTime() { return startTime; @@ -414,6 +440,34 @@ public MavenExecutionRequest setReactorFailureBehavior( String failureBehavior ) return this; } + public MavenExecutionRequest setSelectedProjects( List selectedProjects ) + { + if ( selectedProjects != null ) + { + this.selectedProjects = new ArrayList( selectedProjects ); + } + else + { + this.selectedProjects = null; + } + + return this; + } + + public MavenExecutionRequest setResumeFrom( String project ) + { + this.resumeFrom = project; + + return this; + } + + public MavenExecutionRequest setMakeBehavior( String makeBehavior ) + { + this.makeBehavior = makeBehavior; + + return this; + } + public MavenExecutionRequest addActiveProfile( String profile ) { if ( !getActiveProfiles().contains( profile ) ) diff --git a/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java b/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java index d782f31479..1478b95a62 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java +++ b/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java @@ -64,6 +64,16 @@ public interface MavenExecutionRequest static final String REACTOR_FAIL_NEVER = "FAIL_NEVER"; + // ---------------------------------------------------------------------- + // Reactor Make Mode + // ---------------------------------------------------------------------- + + static final String REACTOR_MAKE_UPSTREAM = "make-upstream"; + + static final String REACTOR_MAKE_DOWNSTREAM = "make-downstream"; + + static final String REACTOR_MAKE_BOTH = "make-both"; + // ---------------------------------------------------------------------- // Artifactr repository policies // ---------------------------------------------------------------------- @@ -97,6 +107,15 @@ public interface MavenExecutionRequest MavenExecutionRequest setReactorFailureBehavior( String failureBehavior ); String getReactorFailureBehavior(); + MavenExecutionRequest setSelectedProjects( List projects ); + List getSelectedProjects(); + + MavenExecutionRequest setResumeFrom( String project ); + String getResumeFrom(); + + MavenExecutionRequest setMakeBehavior( String makeBehavior ); + String getMakeBehavior(); + // Recursive (really to just process the top-level POM) MavenExecutionRequest setRecursive( boolean recursive ); boolean isRecursive(); diff --git a/maven-core/src/main/java/org/apache/maven/execution/ProjectDependencyGraph.java b/maven-core/src/main/java/org/apache/maven/execution/ProjectDependencyGraph.java index f053063585..bbc1091f3a 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/ProjectDependencyGraph.java +++ b/maven-core/src/main/java/org/apache/maven/execution/ProjectDependencyGraph.java @@ -40,7 +40,7 @@ public interface ProjectDependencyGraph List getSortedProjects(); /** - * Gets the downstream projects of the specified projects. A downstream project is a project that directly or + * Gets the downstream projects of the specified project. A downstream project is a project that directly or * indirectly depends on the given project. * * @param project The project whose downstream projects should be retrieved, must not be {@code null}. @@ -50,4 +50,15 @@ public interface ProjectDependencyGraph */ List getDownstreamProjects( MavenProject project, boolean transitive ); + /** + * Gets the upstream projects of the specified project. An upstream project is a project that directly or indirectly + * is a prerequisite of the given project. + * + * @param project The project whose upstream projects should be retrieved, must not be {@code null}. + * @param transitive A flag whether to retrieve all direct and indirect upstream projects or just the immediate + * upstream projects. + * @return The upstream projects in the build order, never {@code null}. + */ + List getUpstreamProjects( MavenProject project, boolean transitive ); + } diff --git a/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java b/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java index 6e4a7fa552..c2834cc5a1 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java +++ b/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java @@ -193,6 +193,11 @@ public List getDependents( String id ) return dag.getParentLabels( id ); } + public List getDependencies( String id ) + { + return dag.getChildLabels( id ); + } + public static String getId( MavenProject project ) { return ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ); diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java index d4595d6bf5..81c337829b 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java @@ -84,6 +84,14 @@ public class CLIManager public static final String FAIL_NEVER = "fn"; + public static final String RESUME_FROM = "rf"; + + public static final String PROJECT_LIST = "pl"; + + public static final String ALSO_MAKE = "am"; + + public static final String ALSO_MAKE_DEPENDENTS = "amd"; + public static final String LOG_FILE = "l"; private Options options; @@ -117,6 +125,10 @@ public CLIManager() options.addOption( OptionBuilder.withLongOpt( "fail-fast" ).withDescription( "Stop at first failure in reactorized builds" ).create( FAIL_FAST ) ); options.addOption( OptionBuilder.withLongOpt( "fail-at-end" ).withDescription( "Only fail the build afterwards; allow all non-impacted builds to continue" ).create( FAIL_AT_END ) ); options.addOption( OptionBuilder.withLongOpt( "fail-never" ).withDescription( "NEVER fail the build, regardless of project result" ).create( FAIL_NEVER ) ); + options.addOption( OptionBuilder.withLongOpt( "resume-from" ).hasArg().withDescription( "Resume reactor from specified project" ).create( RESUME_FROM ) ); + options.addOption( OptionBuilder.withLongOpt( "projects" ).withDescription( "Build specified reactor projects instead of all projects" ).hasArg().create( PROJECT_LIST ) ); + options.addOption( OptionBuilder.withLongOpt( "also-make" ).withDescription( "If project list is specified, also build projects required by the list" ).create( ALSO_MAKE ) ); + options.addOption( OptionBuilder.withLongOpt( "also-make-dependents" ).withDescription( "If project list is specified, also build projects that depend on projects on the list" ).create( ALSO_MAKE_DEPENDENTS ) ); options.addOption( OptionBuilder.withLongOpt( "log-file" ).hasArg().withDescription( "Log file to where all build output will go." ).create( LOG_FILE ) ); options.addOption( OptionBuilder.withLongOpt( "show-version" ).withDescription( "Display version information WITHOUT stopping build" ).create( SHOW_VERSION ) ); @@ -138,7 +150,7 @@ public CommandLine parse( String[] args ) private String[] cleanArgs( String[] args ) { - List cleaned = new ArrayList(); + List cleaned = new ArrayList(); StringBuffer currentArg = null; @@ -226,7 +238,7 @@ private String[] cleanArgs( String[] args ) } else { - cleanArgs = (String[]) cleaned.toArray( new String[cleanedSz] ); + cleanArgs = cleaned.toArray( new String[cleanedSz] ); } return cleanArgs; diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIRequestUtils.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIRequestUtils.java index c9edfdbfab..06866984e2 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIRequestUtils.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIRequestUtils.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; @@ -33,6 +34,7 @@ import org.apache.maven.embedder.MavenEmbedder; import org.apache.maven.execution.DefaultMavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequest; +import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.cli.CommandLineUtils; public final class CLIRequestUtils @@ -267,7 +269,34 @@ else if ( quiet ) { request.setPom( pom ); } - + + if ( commandLine.hasOption( CLIManager.RESUME_FROM ) ) + { + request.setResumeFrom( commandLine.getOptionValue( CLIManager.RESUME_FROM ) ); + } + + if ( commandLine.hasOption( CLIManager.PROJECT_LIST ) ) + { + String projectList = commandLine.getOptionValue( CLIManager.PROJECT_LIST ); + String[] projects = StringUtils.split( projectList, "," ); + request.setSelectedProjects( Arrays.asList( projects ) ); + } + + if ( commandLine.hasOption( CLIManager.ALSO_MAKE ) && !commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) ) + { + request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM ); + } + else if ( !commandLine.hasOption( CLIManager.ALSO_MAKE ) + && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) ) + { + request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM ); + } + else if ( commandLine.hasOption( CLIManager.ALSO_MAKE ) + && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) ) + { + request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_BOTH ); + } + return request; }