From 483ec3556cd43273939d397a26da5793fa741411 Mon Sep 17 00:00:00 2001 From: Martin Kanters Date: Tue, 11 Jan 2022 21:47:19 +0100 Subject: [PATCH] NOJIRA: Made the intention of the reactor code more clear and changed the order of steps to minimize sorting calls. --- .../maven/graph/DefaultGraphBuilder.java | 227 +++++++++--------- 1 file changed, 108 insertions(+), 119 deletions(-) diff --git a/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java b/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java index ff863df4a4e4..5563cb762d80 100644 --- a/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java @@ -30,6 +30,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Named; @@ -136,77 +138,83 @@ private Result sessionDependencyGraph( final MavenSessio private Result reactorDependencyGraph( MavenSession session, List projects ) throws CycleDetectedException, DuplicateProjectException, MavenExecutionException { - ProjectDependencyGraph projectDependencyGraph = new DefaultProjectDependencyGraph( projects ); - List activeProjects = projectDependencyGraph.getSortedProjects(); - List allSortedProjects = projectDependencyGraph.getSortedProjects(); - activeProjects = trimProjectsToRequest( activeProjects, projectDependencyGraph, session.getRequest() ); - activeProjects = trimSelectedProjects( - activeProjects, allSortedProjects, projectDependencyGraph, session.getRequest() ); - activeProjects = trimResumedProjects( activeProjects, projectDependencyGraph, session.getRequest() ); - activeProjects = trimExcludedProjects( activeProjects, projectDependencyGraph, session.getRequest() ); - - if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() ) + final ProjectDependencyGraph projectDependencyGraph = new DefaultProjectDependencyGraph( projects ); + final List allSortedProjects = projectDependencyGraph.getSortedProjects(); + final MavenExecutionRequest request = session.getRequest(); + + // Select projects into the reactor + final Set projectsToActivate = new HashSet<>( allSortedProjects.size() ); + final Set projectsBySelection = getProjectsBySelection( allSortedProjects, request ); + projectsToActivate.addAll( projectsBySelection ); + + // If no projects were selected deliberately by the user, fall back to all projects in this dir and below. + if ( projectsToActivate.isEmpty() ) { - projectDependencyGraph = new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects ); + final Set projectsByDirectory = getProjectsByDirectory( allSortedProjects, request ); + projectsToActivate.addAll( projectsByDirectory ); + } + + // Add all transitive dependencies + final Set alsoMakeDependencies = getAlsoMakeDependencies( projectsToActivate, request, + projectDependencyGraph ); + projectsToActivate.addAll( alsoMakeDependencies ); + + // Trim until resumed project + final Set resumedProjects = getResumedProjects( projectsToActivate, allSortedProjects, request ); + projectsToActivate.retainAll( resumedProjects ); + + // Add back all transitive dependencies after projects were trimmed + projectsToActivate.addAll( alsoMakeDependencies ); + + // Remove unwanted projects from the reactor + getExcludedProjects( projectsToActivate, allSortedProjects, request ) + .forEach( projectsToActivate::remove ); + + if ( projectsToActivate.size() != allSortedProjects.size() ) + { + // Sort all projects in build order + final List activeProjects = new ArrayList<>( projectsToActivate ); + activeProjects.sort( comparing( allSortedProjects::indexOf ) ); + + return Result.success( new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects ) ); } return Result.success( projectDependencyGraph ); } - private List trimProjectsToRequest( List activeProjects, - ProjectDependencyGraph graph, + private Set getProjectsByDirectory( List allSortedProjects, MavenExecutionRequest request ) throws MavenExecutionException { - List result = activeProjects; - - if ( request.getPom() != null ) + if ( request.getPom() == null ) { - result = getProjectsInRequestScope( request, activeProjects ); - - List sortedProjects = graph.getSortedProjects(); - result.sort( comparing( sortedProjects::indexOf ) ); - - result = includeAlsoMakeTransitively( result, request, graph ); + return new HashSet<>( allSortedProjects ); } - return result; + return getProjectsInRequestScope( request, allSortedProjects ); } - private List trimSelectedProjects( List projects, List allSortedProjects, - ProjectDependencyGraph graph, MavenExecutionRequest request ) + private Set getProjectsBySelection( List allSortedProjects, + MavenExecutionRequest request ) throws MavenExecutionException { - List result = projects; - ProjectActivation projectActivation = request.getProjectActivation(); Set requiredSelectors = projectActivation.getRequiredActiveProjectSelectors(); Set optionalSelectors = projectActivation.getOptionalActiveProjectSelectors(); - if ( !requiredSelectors.isEmpty() || !optionalSelectors.isEmpty() ) + if ( requiredSelectors.isEmpty() && optionalSelectors.isEmpty() ) { - Set selectedProjects = new HashSet<>( requiredSelectors.size() + optionalSelectors.size() ); - selectedProjects.addAll( - getProjectsBySelectors( request, allSortedProjects, requiredSelectors, true ) ); - selectedProjects.addAll( - getProjectsBySelectors( request, allSortedProjects, optionalSelectors, false ) ); - - // it can be empty when an optional project is missing from the reactor, fallback to returning all projects - if ( !selectedProjects.isEmpty() ) - { - result = new ArrayList<>( selectedProjects ); - - result = includeAlsoMakeTransitively( result, request, graph ); - - // Order the new list in the original order - List sortedProjects = graph.getSortedProjects(); - result.sort( comparing( sortedProjects::indexOf ) ); - } + return Collections.emptySet(); } - return result; + Set selectedProjects = new HashSet<>( requiredSelectors.size() + optionalSelectors.size() ); + selectedProjects.addAll( getProjectsBySelectors( request, allSortedProjects, requiredSelectors, true ) ); + selectedProjects.addAll( getProjectsBySelectors( request, allSortedProjects, optionalSelectors, false ) ); + + return selectedProjects; } - private Set getProjectsBySelectors( MavenExecutionRequest request, List projects, + private Set getProjectsBySelectors( MavenExecutionRequest request, + List allSortedProjects, Set projectSelectors, boolean required ) throws MavenExecutionException { @@ -215,7 +223,7 @@ private Set getProjectsBySelectors( MavenExecutionRequest request, for ( String selector : projectSelectors ) { - Optional optSelectedProject = projects.stream() + Optional optSelectedProject = allSortedProjects.stream() .filter( project -> isMatchingProject( project, selector, reactorDirectory ) ) .findFirst(); if ( !optSelectedProject.isPresent() ) @@ -246,106 +254,86 @@ private Set getProjectsBySelectors( MavenExecutionRequest request, return selectedProjects; } - private List trimResumedProjects( List projects, ProjectDependencyGraph graph, - MavenExecutionRequest request ) + private Set getResumedProjects( Set projectsToActivate, + List allSortedProjects, MavenExecutionRequest request ) throws MavenExecutionException { - List result = projects; - - if ( StringUtils.isNotEmpty( request.getResumeFrom() ) ) + if ( StringUtils.isEmpty( request.getResumeFrom() ) ) { - File reactorDirectory = getReactorDirectory( request ); - - String selector = request.getResumeFrom(); + return projectsToActivate; + } - MavenProject resumingFromProject = projects.stream() - .filter( project -> isMatchingProject( project, selector, reactorDirectory ) ) - .findFirst() - .orElseThrow( () -> new MavenExecutionException( - "Could not find project to resume reactor build from: " + selector + " vs " - + formatProjects( projects ), request.getPom() ) ); - int resumeFromProjectIndex = projects.indexOf( resumingFromProject ); - List retainingProjects = result.subList( resumeFromProjectIndex, projects.size() ); + File reactorDirectory = getReactorDirectory( request ); + List projectSelection = new ArrayList<>( projectsToActivate ); + projectSelection.sort( comparing( allSortedProjects::indexOf ) ); - result = includeAlsoMakeTransitively( retainingProjects, request, graph ); - } + String selector = request.getResumeFrom(); + MavenProject resumingFromProject = projectsToActivate.stream() + .filter( project -> isMatchingProject( project, selector, reactorDirectory ) ) + .findFirst() + .orElseThrow( () -> new MavenExecutionException( + "Could not find project to resume reactor build from: " + selector + " vs " + + formatProjects( projectSelection ), request.getPom() ) ); - return result; + int resumeFromProjectIndex = projectSelection.indexOf( resumingFromProject ); + return new HashSet<>( projectSelection.subList( resumeFromProjectIndex, projectSelection.size() ) ); } - private List trimExcludedProjects( List projects, ProjectDependencyGraph graph, - MavenExecutionRequest request ) + private Set getExcludedProjects( Set projectsToActivate, + List allSortedProjects, + MavenExecutionRequest request ) throws MavenExecutionException { - List result = projects; - ProjectActivation projectActivation = request.getProjectActivation(); Set requiredSelectors = projectActivation.getRequiredInactiveProjectSelectors(); Set optionalSelectors = projectActivation.getOptionalInactiveProjectSelectors(); - if ( !requiredSelectors.isEmpty() || !optionalSelectors.isEmpty() ) + if ( requiredSelectors.isEmpty() && optionalSelectors.isEmpty() ) { - Set excludedProjects = new HashSet<>( requiredSelectors.size() + optionalSelectors.size() ); - List allProjects = graph.getAllProjects(); - excludedProjects.addAll( getProjectsBySelectors( request, allProjects, requiredSelectors, true ) ); - excludedProjects.addAll( getProjectsBySelectors( request, allProjects, optionalSelectors, false ) ); + return Collections.emptySet(); + } - result = new ArrayList<>( projects ); - result.removeAll( excludedProjects ); + Set excludedProjects = new HashSet<>( requiredSelectors.size() + optionalSelectors.size() ); + excludedProjects.addAll( getProjectsBySelectors( request, allSortedProjects, requiredSelectors, true ) ); + excludedProjects.addAll( getProjectsBySelectors( request, allSortedProjects, optionalSelectors, false ) ); - if ( result.isEmpty() ) - { - boolean isPlural = excludedProjects.size() > 1; - String message = String.format( "The project exclusion%s in --projects/-pl resulted in an " - + "empty reactor, please correct %s.", isPlural ? "s" : "", isPlural ? "them" : "it" ); - throw new MavenExecutionException( message, request.getPom() ); - } + if ( excludedProjects.size() >= projectsToActivate.size() ) + { + boolean isPlural = excludedProjects.size() > 1; + String message = String.format( "The project exclusion%s in --projects/-pl resulted in an " + + "empty reactor, please correct %s.", isPlural ? "s" : "", isPlural ? "them" : "it" ); + throw new MavenExecutionException( message, request.getPom() ); } - return result; + return excludedProjects; } - private List includeAlsoMakeTransitively( List projects, MavenExecutionRequest request, - ProjectDependencyGraph graph ) + private Set getAlsoMakeDependencies( Set projectsToActivate, + MavenExecutionRequest request, ProjectDependencyGraph graph ) throws MavenExecutionException { - List result = projects; - String makeBehavior = request.getMakeBehavior(); boolean makeBoth = MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( makeBehavior ); boolean makeUpstream = makeBoth || MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( makeBehavior ); boolean makeDownstream = makeBoth || MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( makeBehavior ); - if ( StringUtils.isNotEmpty( makeBehavior ) && !makeUpstream && !makeDownstream ) + if ( !makeUpstream && !makeDownstream ) { - throw new MavenExecutionException( "Invalid reactor make behavior: " + makeBehavior, - request.getPom() ); - } - - if ( makeUpstream || makeDownstream ) - { - Set projectsSet = new HashSet<>( projects ); - - for ( MavenProject project : projects ) + if ( StringUtils.isNotEmpty( makeBehavior ) ) { - if ( makeUpstream ) - { - projectsSet.addAll( graph.getUpstreamProjects( project, true ) ); - } - if ( makeDownstream ) - { - projectsSet.addAll( graph.getDownstreamProjects( project, true ) ); - } + throw new MavenExecutionException( "Invalid reactor make behavior: " + makeBehavior, request.getPom() ); } - result = new ArrayList<>( projectsSet ); - - // Order the new list in the original order - List sortedProjects = graph.getSortedProjects(); - result.sort( comparing( sortedProjects::indexOf ) ); + return Collections.emptySet(); } - return result; + List emptyList = Collections.emptyList(); + return projectsToActivate.stream() + .flatMap( project -> Stream.concat( + ( makeUpstream ? graph.getUpstreamProjects( project, true ) : emptyList ).stream(), + ( makeDownstream ? graph.getDownstreamProjects( project, true ) : emptyList ).stream() + ) ) + .collect( Collectors.toSet() ); } private void enrichRequestFromResumptionData( List projects, MavenExecutionRequest request ) @@ -360,15 +348,16 @@ private void enrichRequestFromResumptionData( List projects, Maven } } - private List getProjectsInRequestScope( MavenExecutionRequest request, List projects ) + private Set getProjectsInRequestScope( MavenExecutionRequest request, + List allSortedProjects ) throws MavenExecutionException { if ( request.getPom() == null ) { - return projects; + return new HashSet<>( allSortedProjects ); } - MavenProject requestPomProject = projects.stream() + MavenProject requestPomProject = allSortedProjects.stream() .filter( project -> request.getPom().equals( project.getFile() ) ) .findFirst() .orElseThrow( () -> new MavenExecutionException( @@ -377,7 +366,7 @@ private List getProjectsInRequestScope( MavenExecutionRequest requ List modules = requestPomProject.getCollectedProjects() != null ? requestPomProject.getCollectedProjects() : Collections.emptyList(); - List result = new ArrayList<>( modules ); + Set result = new HashSet<>( modules ); result.add( requestPomProject ); return result; } @@ -477,7 +466,7 @@ private void validateProjects( List projects, MavenExecutionReques { Map projectsMap = new HashMap<>(); - List projectsInRequestScope = getProjectsInRequestScope( request, projects ); + Set projectsInRequestScope = getProjectsInRequestScope( request, projects ); for ( MavenProject p : projectsInRequestScope ) { String projectKey = ArtifactUtils.key( p.getGroupId(), p.getArtifactId(), p.getVersion() );