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 68e4011336..152bd4b508 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -59,7 +59,6 @@ import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.project.ProjectBuildingResult; import org.apache.maven.project.ProjectSorter; -import org.apache.maven.repository.DelegatingLocalArtifactRepository; import org.apache.maven.repository.LocalRepositoryNotAccessibleException; import org.apache.maven.repository.internal.MavenRepositorySystemUtils; import org.apache.maven.settings.Mirror; @@ -162,8 +161,8 @@ public MavenExecutionResult execute( MavenExecutionRequest request ) catch ( RuntimeException e ) { result = - addExceptionToResult( new DefaultMavenExecutionResult(), - new InternalErrorException( "Internal error: " + e, e ) ); + addExceptionToResult( new DefaultMavenExecutionResult(), new InternalErrorException( "Internal error: " + + e, e ) ); } finally { @@ -173,17 +172,42 @@ public MavenExecutionResult execute( MavenExecutionRequest request ) return result; } + // + // 1) Setup initial properties. + // + // 2) Validate local repository directory is accessible. + // + // 3) Create RepositorySystemSession. + // + // 4) Create MavenSession. + // + // 5) Execute AbstractLifecycleParticipant.afterSessionStart(session) + // + // 6) Get reactor projects looking for read errors, and duplicate declarations + // + // 7) Create ProjectDependencyGraph using trimming which takes into account --projects and reactor mode. This ensures + // that the projects passed into the ReactorReader are only those specified. + // + // 8) Create ReactorReader with the project map created in 7) + // + // 9) Execute AbstractLifecycleParticipant.afterProjectsRead(session) + // + // 10) Create ProjectDependencyGraph without trimming (as trimming was done in 7). A new topological sort is required after + // the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject instances, which may change + // dependencies which can, in turn, affect the build order. + // + // 11) Execute LifecycleStarter.start() + // private MavenExecutionResult doExecute( MavenExecutionRequest request ) { - //TODO: Need a general way to inject standard properties if ( request.getStartTime() != null ) { request.getSystemProperties().put( "${build.timestamp}", new SimpleDateFormat( "yyyyMMdd-hhmm" ).format( request.getStartTime() ) ); - } - + } + request.setStartTime( new Date() ); - + MavenExecutionResult result = new DefaultMavenExecutionResult(); try @@ -195,11 +219,6 @@ private MavenExecutionResult doExecute( MavenExecutionRequest request ) return addExceptionToResult( result, e ); } - DelegatingLocalArtifactRepository delegatingLocalArtifactRepository = - new DelegatingLocalArtifactRepository( request.getLocalRepository() ); - - request.setLocalRepository( delegatingLocalArtifactRepository ); - DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request ); MavenSession session = new MavenSession( container, repoSession, request, result ); @@ -219,46 +238,50 @@ private MavenExecutionResult doExecute( MavenExecutionRequest request ) eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null ); - request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() ); - - //TODO: optimize for the single project or no project - List projects; try { - projects = getProjectsForMavenReactor( request ); + projects = getProjectsForMavenReactor( session ); } catch ( ProjectBuildingException e ) { return addExceptionToResult( result, e ); } - session.setProjects( projects ); + // + // This creates the graph and trims the projects down based on the user request using something like: + // + // -pl project0,project2 eclipse:eclipse + // + ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true ); - result.setTopologicallySortedProjects( session.getProjects() ); + session.setProjects( projectDependencyGraph.getSortedProjects() ); - result.setProject( session.getTopLevelProject() ); + if ( result.hasExceptions() ) + { + return result; + } + // + // Desired order of precedence for local artifact repositories + // + // Reactor + // Workspace + // User Local Repository + // + ReactorReader reactorRepository = null; try { - Map projectMap; - projectMap = getProjectMap( session.getProjects() ); - - // Desired order of precedence for local artifact repositories - // - // Reactor - // Workspace - // User Local Repository - ReactorReader reactorRepository = new ReactorReader( projectMap ); - - repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorRepository, - repoSession.getWorkspaceReader() ) ); + reactorRepository = new ReactorReader( session, getProjectMap( session.getProjects() ) ); } catch ( DuplicateProjectException e ) { return addExceptionToResult( result, e ); } + repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorRepository, + repoSession.getWorkspaceReader() ) ); + repoSession.setReadOnly(); ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); @@ -280,39 +303,28 @@ private MavenExecutionResult doExecute( MavenExecutionRequest request ) Thread.currentThread().setContextClassLoader( originalClassLoader ); } - try - { - ProjectSorter projectSorter = new ProjectSorter( session.getProjects() ); - - ProjectDependencyGraph projectDependencyGraph = createDependencyGraph( projectSorter, request ); - - session.setProjects( projectDependencyGraph.getSortedProjects() ); - - session.setProjectDependencyGraph( projectDependencyGraph ); - } - catch ( CycleDetectedException e ) - { - String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage(); - - ProjectCycleException error = new ProjectCycleException( message, e ); - - return addExceptionToResult( result, error ); - } - catch ( org.apache.maven.project.DuplicateProjectException e ) - { - return addExceptionToResult( result, e ); - } - catch ( MavenExecutionException e ) - { - return addExceptionToResult( result, e ); - } - - result.setTopologicallySortedProjects( session.getProjects() ); + // + // The projects need to be topologically after the participants have run their afterProjectsRead(session) + // because the participant is free to change the dependencies of a project which can potentially change the + // topological order of the projects, and therefore can potentially change the build order. + // + // Note that participants may affect the topological order of the projects but it is + // not expected that a participant will add or remove projects from the session. + // + projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false ); if ( result.hasExceptions() ) { return result; } + + session.setProjects( projectDependencyGraph.getSortedProjects() ); + + session.setProjectDependencyGraph( projectDependencyGraph ); + + result.setTopologicallySortedProjects( session.getProjects() ); + + result.setProject( session.getTopLevelProject() ); lifecycleStarter.execute( session ); @@ -465,8 +477,7 @@ else if ( request.isUpdateSnapshots() ) private String getUserAgent() { - return "Apache-Maven/" + getMavenVersion() - + " (Java " + System.getProperty( "java.version" ) + "; " + return "Apache-Maven/" + getMavenVersion() + " (Java " + System.getProperty( "java.version" ) + "; " + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")"; } @@ -563,11 +574,15 @@ private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, return result; } - - private List getProjectsForMavenReactor( MavenExecutionRequest request ) + + private List getProjectsForMavenReactor( MavenSession session ) throws ProjectBuildingException { - List projects = new ArrayList(); + MavenExecutionRequest request = session.getRequest(); + + request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() ); + + List projects = new ArrayList(); // We have no POM file. // @@ -582,12 +597,54 @@ private List getProjectsForMavenReactor( MavenExecutionRequest req return projects; } - List files = Arrays.asList( request.getPom().getAbsoluteFile() ); + List files = Arrays.asList( request.getPom().getAbsoluteFile() ); collectProjects( projects, files, request ); return projects; } - private Map getProjectMap( List projects ) + private void collectProjects( List projects, List files, MavenExecutionRequest request ) + throws ProjectBuildingException + { + ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest(); + + List results = + projectBuilder.build( files, request.isRecursive(), projectBuildingRequest ); + + boolean problems = false; + + for ( ProjectBuildingResult result : results ) + { + projects.add( result.getProject() ); + + if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() ) + { + logger.warn( "" ); + logger.warn( "Some problems were encountered while building the effective model for " + + result.getProject().getId() ); + + for ( ModelProblem problem : result.getProblems() ) + { + String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() ); + logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) ); + } + + problems = true; + } + } + + if ( problems ) + { + logger.warn( "" ); + logger.warn( "It is highly recommended to fix these problems" + + " because they threaten the stability of your build." ); + logger.warn( "" ); + logger.warn( "For this reason, future Maven versions might no" + + " longer support building such malformed projects." ); + logger.warn( "" ); + } + } + + private Map getProjectMap( Collection projects ) throws DuplicateProjectException { Map index = new LinkedHashMap(); @@ -629,47 +686,6 @@ private Map getProjectMap( List projects ) return index; } - private void collectProjects( List projects, List files, MavenExecutionRequest request ) - throws ProjectBuildingException - { - ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest(); - - List results = projectBuilder.build( files, request.isRecursive(), projectBuildingRequest ); - - boolean problems = false; - - for ( ProjectBuildingResult result : results ) - { - projects.add( result.getProject() ); - - if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() ) - { - logger.warn( "" ); - logger.warn( "Some problems were encountered while building the effective model for " - + result.getProject().getId() ); - - for ( ModelProblem problem : result.getProblems() ) - { - String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() ); - logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) ); - } - - problems = true; - } - } - - if ( problems ) - { - logger.warn( "" ); - logger.warn( "It is highly recommended to fix these problems" - + " because they threaten the stability of your build." ); - logger.warn( "" ); - logger.warn( "For this reason, future Maven versions might no" - + " longer support building such malformed projects." ); - logger.warn( "" ); - } - } - private void validateActivatedProfiles( List projects, List activeProfileIds ) { Collection notActivatedProfileIds = new LinkedHashSet( activeProfileIds ); @@ -689,27 +705,55 @@ private void validateActivatedProfiles( List projects, List projects, MavenExecutionRequest request, + MavenExecutionResult result, boolean trimming ) { - ProjectDependencyGraph graph = new DefaultProjectDependencyGraph( sorter ); + ProjectDependencyGraph projectDependencyGraph = null; - List activeProjects = sorter.getSortedProjects(); - - activeProjects = trimSelectedProjects( activeProjects, graph, request ); - activeProjects = trimResumedProjects( activeProjects, request ); - - if ( activeProjects.size() != sorter.getSortedProjects().size() ) + try { - graph = new FilteredProjectDependencyGraph( graph, activeProjects ); + ProjectSorter projectSorter = new ProjectSorter( projects ); + + projectDependencyGraph = new DefaultProjectDependencyGraph( projectSorter ); + + if ( trimming ) + { + List activeProjects = projectSorter.getSortedProjects(); + + activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request ); + activeProjects = trimResumedProjects( activeProjects, request ); + + if ( activeProjects.size() != projectSorter.getSortedProjects().size() ) + { + projectDependencyGraph = + new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects ); + } + } + } + catch ( CycleDetectedException e ) + { + String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage(); + + ProjectCycleException error = new ProjectCycleException( message, e ); + + addExceptionToResult( result, error ); + } + catch ( org.apache.maven.project.DuplicateProjectException e ) + { + addExceptionToResult( result, e ); + } + catch ( MavenExecutionException e ) + { + addExceptionToResult( result, e ); } - return graph; + return projectDependencyGraph; } private List trimSelectedProjects( List projects, ProjectDependencyGraph graph, diff --git a/maven-core/src/main/java/org/apache/maven/ReactorReader.java b/maven-core/src/main/java/org/apache/maven/ReactorReader.java index bc37eb71fb..b96bbed7aa 100644 --- a/maven-core/src/main/java/org/apache/maven/ReactorReader.java +++ b/maven-core/src/main/java/org/apache/maven/ReactorReader.java @@ -30,6 +30,7 @@ import java.util.Map; import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.execution.MavenSession; import org.apache.maven.project.MavenProject; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.repository.WorkspaceReader; @@ -52,7 +53,7 @@ class ReactorReader private WorkspaceRepository repository; - public ReactorReader( Map reactorProjects ) + public ReactorReader( MavenSession session, Map reactorProjects ) { projectsByGAV = reactorProjects; @@ -72,18 +73,18 @@ public ReactorReader( Map reactorProjects ) projects.add( project ); } - repository = new WorkspaceRepository( "reactor", new HashSet( projectsByGAV.keySet() ) ); + repository = new WorkspaceRepository( "reactor", new HashSet( projectsByGAV.keySet() ) ); } // // Public API // - + public WorkspaceRepository getRepository() { return repository; } - + public File findArtifact( Artifact artifact ) { String projectKey = ArtifactUtils.key( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() ); @@ -124,12 +125,12 @@ public List findVersions( Artifact artifact ) } return Collections.unmodifiableList( versions ); - } - + } + // // Implementation // - + private File find( MavenProject project, Artifact artifact ) { if ( "pom".equals( artifact.getExtension() ) ) @@ -143,7 +144,7 @@ private File find( MavenProject project, Artifact artifact ) { return projectArtifact.getFile(); } - else if ( !hasBeenPackaged( project ) ) + else if ( !hasBeenPackaged( project ) ) { // fallback to loose class files only if artifacts haven't been packaged yet // and only for plain old jars. Not war files, not ear files, not anything else. @@ -186,9 +187,7 @@ private boolean hasBeenPackaged( MavenProject project ) * * @param project The project to try to resolve the artifact from, must not be null. * @param requestedArtifact The artifact to resolve, must not be null. - * @return The matching artifact from the project or null if not found. - * - * Note that this + * @return The matching artifact from the project or null if not found. Note that this */ private Artifact findMatchingArtifact( MavenProject project, Artifact requestedArtifact ) { @@ -202,7 +201,7 @@ private Artifact findMatchingArtifact( MavenProject project, Artifact requestedA for ( Artifact attachedArtifact : RepositoryUtils.toArtifacts( project.getAttachedArtifacts() ) ) { - if ( attachedArtifactComparison ( requestedArtifact, attachedArtifact ) ) + if ( attachedArtifactComparison( requestedArtifact, attachedArtifact ) ) { return attachedArtifact; } @@ -222,7 +221,7 @@ private boolean attachedArtifactComparison( Artifact requested, Artifact attache && requested.getVersion().equals( attached.getVersion() ) && requested.getExtension().equals( attached.getExtension() ) && requested.getClassifier().equals( attached.getClassifier() ); - } + } /** * Determines whether the specified artifact refers to test classes. diff --git a/maven-core/src/main/java/org/apache/maven/project/ProjectSorter.java b/maven-core/src/main/java/org/apache/maven/project/ProjectSorter.java index d0ffa71a2a..16985e2a46 100644 --- a/maven-core/src/main/java/org/apache/maven/project/ProjectSorter.java +++ b/maven-core/src/main/java/org/apache/maven/project/ProjectSorter.java @@ -20,6 +20,7 @@ */ import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -70,7 +71,7 @@ public class ProjectSorter // In this case, both the verify and the report goals are called // in a different lifecycle. Though the compiler-plugin has a valid usecase, although // that seems to work fine. We need to take versions and lifecycle into account. - public ProjectSorter( List projects ) + public ProjectSorter( Collection projects ) throws CycleDetectedException, DuplicateProjectException { dag = new DAG();