[MNG-4660] --resume-from flag resolves inter-module dependencies

Co-authored-by: Martin Kanters <Martin.Kanters@infosupport.com>
This commit is contained in:
Maarten Mulders 2020-04-14 22:04:55 +02:00 committed by rfscholte
parent 58b67bb08d
commit 26b35a8ca9
4 changed files with 111 additions and 12 deletions

View File

@ -20,14 +20,20 @@ package org.apache.maven;
*/
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Named;
@ -37,6 +43,7 @@ import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Model;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.internal.MavenWorkspaceReader;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.repository.WorkspaceRepository;
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
@ -57,6 +64,10 @@ class ReactorReader
private static final Collection<String> COMPILE_PHASE_TYPES =
Arrays.asList( "jar", "ejb-client", "war", "rar", "ejb3", "par", "sar", "wsr", "har", "app-client" );
private Logger logger;
private MavenSession session;
private Map<String, MavenProject> projectsByGAV;
private Map<String, List<MavenProject>> projectsByGA;
@ -64,9 +75,16 @@ class ReactorReader
private WorkspaceRepository repository;
@Inject
ReactorReader( MavenSession session )
ReactorReader( MavenSession session, Logger logger )
{
projectsByGAV = session.getProjectMap();
this.logger = logger;
this.session = session;
this.projectsByGAV = new HashMap<>( session.getAllProjects().size() * 2 );
session.getAllProjects().forEach( project ->
{
String projectId = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
this.projectsByGAV.put( projectId, project );
} );
projectsByGA = new HashMap<>( projectsByGAV.size() * 2 );
for ( MavenProject project : projectsByGAV.values() )
@ -152,12 +170,19 @@ class ReactorReader
}
Artifact projectArtifact = findMatchingArtifact( project, artifact );
File packagedArtifactFile = determinePreviouslyPackagedArtifactFile( project, projectArtifact );
if ( hasArtifactFileFromPackagePhase( projectArtifact ) )
{
return projectArtifact.getFile();
}
else if ( !hasBeenPackaged( project ) )
// Check whether an earlier Maven run might have produced an artifact that is still on disk.
else if ( packagedArtifactFile != null && packagedArtifactFile.exists()
&& isPackagedArtifactUpToDate( project, packagedArtifactFile ) )
{
return packagedArtifactFile;
}
else if ( !hasBeenPackagedDuringThisSession( 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.
@ -172,9 +197,21 @@ class ReactorReader
else
{
String type = artifact.getProperty( "type", "" );
if ( project.hasLifecyclePhase( "compile" ) && COMPILE_PHASE_TYPES.contains( type ) )
File outputDirectory = new File( project.getBuild().getOutputDirectory() );
// Check if the project is being built during this session, and if we can expect any output.
// There is no need to check if the build has created any outputs, see MNG-2222.
boolean projectCompiledDuringThisSession
= project.hasLifecyclePhase( "compile" ) && COMPILE_PHASE_TYPES.contains( type );
// Check if the project is part of the session (not filtered by -pl, -rf, etc). If so, we check
// if a possible earlier Maven invocation produced some output for that project which we can use.
boolean projectHasOutputFromPreviousSession
= !session.getProjects().contains( project ) && outputDirectory.exists();
if ( projectHasOutputFromPreviousSession || projectCompiledDuringThisSession )
{
return new File( project.getBuild().getOutputDirectory() );
return outputDirectory;
}
}
}
@ -184,12 +221,68 @@ class ReactorReader
return null;
}
private File determinePreviouslyPackagedArtifactFile( MavenProject project, Artifact artifact )
{
if ( artifact == null )
{
return null;
}
String fileName = String.format( "%s.%s", project.getBuild().getFinalName(), artifact.getExtension() );
return new File( project.getBuild().getDirectory(), fileName );
}
private boolean hasArtifactFileFromPackagePhase( Artifact projectArtifact )
{
return projectArtifact != null && projectArtifact.getFile() != null && projectArtifact.getFile().exists();
}
private boolean hasBeenPackaged( MavenProject project )
private boolean isPackagedArtifactUpToDate( MavenProject project, File packagedArtifactFile )
{
Path outputDirectory = Paths.get( project.getBuild().getOutputDirectory() );
if ( !outputDirectory.toFile().exists() )
{
return true;
}
try ( Stream<Path> outputFiles = Files.walk( outputDirectory ) )
{
// Not using File#lastModified() to avoid a Linux JDK8 milliseconds precision bug: JDK-8177809.
long artifactLastModified = Files.getLastModifiedTime( packagedArtifactFile.toPath() ).toMillis();
if ( session.getProjectBuildingRequest().getBuildStartTime() != null )
{
long buildStartTime = session.getProjectBuildingRequest().getBuildStartTime().getTime();
if ( artifactLastModified > buildStartTime )
{
return true;
}
}
Iterator<Path> iterator = outputFiles.iterator();
while ( iterator.hasNext() )
{
Path outputFile = iterator.next();
long outputFileLastModified = Files.getLastModifiedTime( outputFile ).toMillis();
if ( outputFileLastModified > artifactLastModified )
{
logger.warn( "Packaged artifact is not up-to-date compared to the build output directory" );
return false;
}
}
return true;
}
catch ( IOException e )
{
logger.warn( "An I/O error occurred while checking if the packaged artifact is up-to-date "
+ "against the build output directory. "
+ "Continuing with the assumption that it is up-to-date.", e );
return true;
}
}
private boolean hasBeenPackagedDuringThisSession( MavenProject project )
{
return project.hasLifecyclePhase( "package" ) || project.hasLifecyclePhase( "install" )
|| project.hasLifecyclePhase( "deploy" );

View File

@ -297,7 +297,7 @@ public class MavenSession
private final Settings settings;
@Deprecated
/** @deprecated This appears to only be used in the ReactorReader and we can do any processing required there */
/** @deprecated This appears not to be used anywhere within Maven itself. */
public Map<String, MavenProject> getProjectMap()
{
return projectMap;

View File

@ -38,7 +38,6 @@ import javax.inject.Singleton;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
@ -197,9 +196,11 @@ public class MavenMetadataSource
DependencyManagement dependencyManagement = model.getDependencyManagement();
managedDependencies = dependencyManagement == null ? null : dependencyManagement.getDependencies();
MavenSession session = legacySupport.getSession();
MavenProject project = session.getProjectMap().get(
ArtifactUtils.key( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() ) );
pomRepositories = project.getRemoteArtifactRepositories();
pomRepositories = session.getProjects().stream()
.filter( p -> artifact.equals( p.getArtifact() ) )
.map( MavenProject::getRemoteArtifactRepositories )
.findFirst()
.orElseGet( ArrayList::new );
}
else if ( artifact instanceof ArtifactWithDependencies )
{

View File

@ -158,7 +158,12 @@ under the License.
<name>Mike Mol (MNG-6665)</name>
</contributor>
<contributor>
<name>Martin Kanters (MNG-6665, MNG-6065)</name>
<name>Martin Kanters</name>
<organization>Info Support</organization>
</contributor>
<contributor>
<name>Maarten Mulders</name>
<organization>Info Support</organization>
</contributor>
<contributor>
<name>Luc Klaassen (MNG-6065)</name>