You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by be...@apache.org on 2009/07/29 15:48:20 UTC

svn commit: r798906 - in /maven/components/trunk/maven-core/src: main/java/org/apache/maven/execution/ProjectSorter.java test/java/org/apache/maven/execution/ProjectSorterTest.java

Author: bentmann
Date: Wed Jul 29 13:48:19 2009
New Revision: 798906

URL: http://svn.apache.org/viewvc?rev=798906&view=rev
Log:
[MNG-3814] Reactor builds fail due to erroneous cycle in project sorting which does not consider versions

Modified:
    maven/components/trunk/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java
    maven/components/trunk/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java

Modified: maven/components/trunk/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java
URL: http://svn.apache.org/viewvc/maven/components/trunk/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java?rev=798906&r1=798905&r2=798906&view=diff
==============================================================================
--- maven/components/trunk/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java (original)
+++ maven/components/trunk/maven-core/src/main/java/org/apache/maven/execution/ProjectSorter.java Wed Jul 29 13:48:19 2009
@@ -29,10 +29,15 @@
 
 import org.apache.maven.artifact.ArtifactUtils;
 import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Extension;
+import org.apache.maven.model.Parent;
+import org.apache.maven.model.Plugin;
 import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.dag.CycleDetectedException;
 import org.codehaus.plexus.util.dag.DAG;
 import org.codehaus.plexus.util.dag.TopologicalSorter;
+import org.codehaus.plexus.util.dag.Vertex;
 
 public class ProjectSorter
 {
@@ -70,81 +75,88 @@
     {
         dag = new DAG();
 
-        Map<String,MavenProject> projectMap = new HashMap<String,MavenProject>();
+        // groupId:artifactId:version -> project
+        Map<String, MavenProject> projectMap = new HashMap<String, MavenProject>( projects.size() * 2 );
+
+        // groupId:artifactId -> (version -> vertex)
+        Map<String, Map<String, Vertex>> vertexMap = new HashMap<String, Map<String, Vertex>>( projects.size() * 2 );
 
         for ( MavenProject project : projects )
         {
-            String id = getId( project );
+            String projectId = getId( project );
 
-            if ( dag.getVertex( id ) != null )
-            {
-                MavenProject conflictingProject = projectMap.get( id );
+            MavenProject conflictingProject = projectMap.put( projectId, project );
 
-                throw new DuplicateProjectException( id, conflictingProject.getFile(), project.getFile(), "Project '" + id + "' is duplicated in the reactor" );
+            if ( conflictingProject != null )
+            {
+                throw new DuplicateProjectException( projectId, conflictingProject.getFile(), project.getFile(),
+                                                     "Project '" + projectId + "' is duplicated in the reactor" );
             }
 
-            dag.addVertex( id );
+            String projectKey = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
 
-            projectMap.put( id, project );
+            Map<String, Vertex> vertices = vertexMap.get( projectKey );
+            if ( vertices == null )
+            {
+                vertices = new HashMap<String, Vertex>( 2, 1 );
+                vertexMap.put( projectKey, vertices );
+            }
+            vertices.put( project.getVersion(), dag.addVertex( projectId ) );
         }
 
-        for ( MavenProject project : projects )
+        for ( Vertex projectVertex : (List<Vertex>) dag.getVerticies() )
         {
-            String id = getId( project );
+            String projectId = projectVertex.getLabel();
 
-            for( Dependency dependency : project.getDependencies() )
+            MavenProject project = projectMap.get( projectId );
+
+            for ( Dependency dependency : project.getDependencies() )
             {
-                String dependencyId = ArtifactUtils.versionlessKey( dependency.getGroupId(), dependency.getArtifactId() );
+                addEdge( projectMap, vertexMap, project, projectVertex, dependency.getGroupId(),
+                         dependency.getArtifactId(), dependency.getVersion(), false, false );
+            }
 
-                if ( dag.getVertex( dependencyId ) != null )
-                {
-                    project.addProjectReference( projectMap.get( dependencyId ) );
+            Parent parent = project.getModel().getParent();
 
-                    dag.addEdge( id, dependencyId );
-                }
+            if ( parent != null )
+            {
+                // Parent is added as an edge, but must not cause a cycle - so we remove any other edges it has in conflict
+                addEdge( projectMap, vertexMap, null, projectVertex, parent.getGroupId(), parent.getArtifactId(),
+                         parent.getVersion(), true, false );
             }
 
-            MavenProject parent = project.getParent();
-            
-            if ( parent != null )
+            List<Plugin> buildPlugins = project.getBuildPlugins();
+            if ( buildPlugins != null )
             {
-                String parentId = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() );
-                if ( dag.getVertex( parentId ) != null )
+                for ( Plugin plugin : buildPlugins )
                 {
-                    // Parent is added as an edge, but must not cause a cycle - so we remove any other edges it has in conflict
-                    if ( dag.hasEdge( parentId, id ) )
+                    addEdge( projectMap, vertexMap, project, projectVertex, plugin.getGroupId(),
+                             plugin.getArtifactId(), plugin.getVersion(), false, true );
+
+                    for ( Dependency dependency : plugin.getDependencies() )
                     {
-                        dag.removeEdge( parentId, id );
+                        addEdge( projectMap, vertexMap, project, projectVertex, dependency.getGroupId(),
+                                 dependency.getArtifactId(), dependency.getVersion(), false, true );
                     }
-                    
-                    dag.addEdge( id, parentId );
                 }
             }
-            
-            /*
-            
-            TODO: Now that the build plan is fully fleshed out we have cycles
-            
-            if ( project.getBuildPlugins() != null )
+
+            List<Extension> buildExtensions = project.getBuildExtensions();
+            if ( buildExtensions != null )
             {
-                for( Plugin plugin : project.getBuildPlugins() )
+                for ( Extension extension : buildExtensions )
                 {
-                    String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
-                    
-                    if ( ( dag.getVertex( pluginId ) != null ) && !pluginId.equals( id ) )
-                    {
-                        addEdgeWithParentCheck( projectMap, pluginId, project, id );
-                    }
+                    addEdge( projectMap, vertexMap, project, projectVertex, extension.getGroupId(),
+                             extension.getArtifactId(), extension.getVersion(), false, true );
                 }
             }
-            */
         }
 
-        List<MavenProject> sortedProjects = new ArrayList<MavenProject>();
+        List<MavenProject> sortedProjects = new ArrayList<MavenProject>( projects.size() );
 
         List<String> sortedProjectLabels = TopologicalSorter.sort( dag );
-         
-        for( String id : sortedProjectLabels )
+
+        for ( String id : sortedProjectLabels )
         {
             sortedProjects.add( projectMap.get( id ) );
         }
@@ -152,30 +164,73 @@
         this.sortedProjects = Collections.unmodifiableList( sortedProjects );
     }
 
-    private void addEdgeWithParentCheck( Map<String,MavenProject> projectMap, String projectRefId, MavenProject project, String id )
+    private void addEdge( Map<String, MavenProject> projectMap, Map<String, Map<String, Vertex>> vertexMap,
+                          MavenProject project, Vertex projectVertex, String groupId, String artifactId,
+                          String version, boolean force, boolean safe )
         throws CycleDetectedException
     {
-        MavenProject extProject = projectMap.get( projectRefId );
+        String projectKey = ArtifactUtils.versionlessKey( groupId, artifactId );
+
+        Map<String, Vertex> vertices = vertexMap.get( projectKey );
 
-        if ( extProject == null )
+        if ( vertices != null )
+        {
+            if ( isSpecificVersion( version ) )
+            {
+                Vertex vertex = vertices.get( version );
+                if ( vertex != null )
+                {
+                    addEdge( projectVertex, vertex, project, projectMap, force, safe );
+                }
+            }
+            else
+            {
+                for ( Vertex vertex : vertices.values() )
+                {
+                    addEdge( projectVertex, vertex, project, projectMap, force, safe );
+                }
+            }
+        }
+    }
+
+    private void addEdge( Vertex fromVertex, Vertex toVertex, MavenProject fromProject,
+                          Map<String, MavenProject> projectMap, boolean force, boolean safe )
+        throws CycleDetectedException
+    {
+        if ( fromVertex.equals( toVertex ) )
         {
             return;
         }
 
-        project.addProjectReference( extProject );
+        if ( fromProject != null )
+        {
+            MavenProject toProject = projectMap.get( toVertex.getLabel() );
+            fromProject.addProjectReference( toProject );
+        }
+
+        if ( force && toVertex.getChildren().contains( fromVertex ) )
+        {
+            dag.removeEdge( toVertex, fromVertex );
+        }
 
-        MavenProject extParent = extProject.getParent();
-        if ( extParent != null )
+        try
         {
-            String parentId = ArtifactUtils.versionlessKey( extParent.getGroupId(), extParent.getArtifactId() );
-            // Don't add edge from parent to extension if a reverse edge already exists
-            if ( !dag.hasEdge( projectRefId, id ) || !parentId.equals( id ) )
+            dag.addEdge( fromVertex, toVertex );
+        }
+        catch ( CycleDetectedException e )
+        {
+            if ( !safe )
             {
-                dag.addEdge( id, projectRefId );
+                throw e;
             }
         }
     }
 
+    private boolean isSpecificVersion( String version )
+    {
+        return !( StringUtils.isEmpty( version ) || version.startsWith( "[" ) || version.startsWith( "(" ) );
+    }
+
     // TODO: !![jc; 28-jul-2005] check this; if we're using '-r' and there are aggregator tasks, this will result in weirdness.
     public MavenProject getTopLevelProject()
     {
@@ -216,7 +271,7 @@
 
     public static String getId( MavenProject project )
     {
-        return ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
+        return ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
     }
 
 }

Modified: maven/components/trunk/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java
URL: http://svn.apache.org/viewvc/maven/components/trunk/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java?rev=798906&r1=798905&r2=798906&view=diff
==============================================================================
--- maven/components/trunk/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java (original)
+++ maven/components/trunk/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java Wed Jul 29 13:48:19 2009
@@ -29,6 +29,9 @@
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Extension;
 import org.apache.maven.model.Model;
+import org.apache.maven.model.Parent;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.PluginManagement;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.plexus.util.dag.CycleDetectedException;
 
@@ -42,25 +45,115 @@
     extends TestCase
 {
 
-    public void testShouldNotFailWhenProjectReferencesNonExistentProject()
+    private Parent createParent( MavenProject project )
+    {
+        return createParent( project.getGroupId(), project.getArtifactId(), project.getVersion() );
+    }
+
+    private Parent createParent( String groupId, String artifactId, String version )
+    {
+        Parent plugin = new Parent();
+        plugin.setGroupId( groupId );
+        plugin.setArtifactId( artifactId );
+        plugin.setVersion( version );
+        return plugin;
+    }
+
+    private Dependency createDependency( MavenProject project )
+    {
+        return createDependency( project.getGroupId(), project.getArtifactId(), project.getVersion() );
+    }
+
+    private Dependency createDependency( String groupId, String artifactId, String version )
+    {
+        Dependency depdendency = new Dependency();
+        depdendency.setGroupId( groupId );
+        depdendency.setArtifactId( artifactId );
+        depdendency.setVersion( version );
+        return depdendency;
+    }
+
+    private Plugin createPlugin( MavenProject project )
+    {
+        return createPlugin( project.getGroupId(), project.getArtifactId(), project.getVersion() );
+    }
+
+    private Plugin createPlugin( String groupId, String artifactId, String version )
+    {
+        Plugin plugin = new Plugin();
+        plugin.setGroupId( groupId );
+        plugin.setArtifactId( artifactId );
+        plugin.setVersion( version );
+        return plugin;
+    }
+
+    private Extension createExtension( String groupId, String artifactId, String version )
+    {
+        Extension extension = new Extension();
+        extension.setGroupId( groupId );
+        extension.setArtifactId( artifactId );
+        extension.setVersion( version );
+        return extension;
+    }
+
+    private static MavenProject createProject( String groupId, String artifactId, String version )
+    {
+        Model model = new Model();
+        model.setGroupId( groupId );
+        model.setArtifactId( artifactId );
+        model.setVersion( version );
+        model.setBuild( new Build() );
+        return new MavenProject( model );
+    }
+
+    public void testShouldNotFailWhenPluginDepReferencesCurrentProject()
         throws CycleDetectedException, DuplicateProjectException
     {
         MavenProject project = createProject( "group", "artifact", "1.0" );
-        Model model = project.getModel();
 
-        Build build = model.getBuild();
+        Build build = project.getModel().getBuild();
 
-        if ( build == null )
-        {
-            build = new Build();
-            model.setBuild( build );
-        }
+        Plugin plugin = createPlugin( "other.group", "other-artifact", "1.0" );
 
-        Extension extension = new Extension();
+        Dependency dep = createDependency( "group", "artifact", "1.0" );
+
+        plugin.addDependency( dep );
+
+        build.addPlugin( plugin );
+
+        new ProjectSorter( Collections.singletonList( project ) );
+    }
+
+    public void testShouldNotFailWhenManagedPluginDepReferencesCurrentProject()
+        throws CycleDetectedException, DuplicateProjectException
+    {
+        MavenProject project = createProject( "group", "artifact", "1.0" );
+
+        Build build = project.getModel().getBuild();
+
+        PluginManagement pMgmt = new PluginManagement();
+
+        Plugin plugin = createPlugin( "other.group", "other-artifact", "1.0" );
+
+        Dependency dep = createDependency( "group", "artifact", "1.0" );
 
-        extension.setArtifactId( "other-artifact" );
-        extension.setGroupId( "other.group" );
-        extension.setVersion( "1.0" );
+        plugin.addDependency( dep );
+
+        pMgmt.addPlugin( plugin );
+
+        build.setPluginManagement( pMgmt );
+
+        new ProjectSorter( Collections.singletonList( project ) );
+    }
+
+    public void testShouldNotFailWhenProjectReferencesNonExistentProject()
+        throws CycleDetectedException, DuplicateProjectException
+    {
+        MavenProject project = createProject( "group", "artifact", "1.0" );
+
+        Build build = project.getModel().getBuild();
+
+        Extension extension = createExtension( "other.group", "other-artifact", "1.0" );
 
         build.addExtension( extension );
 
@@ -70,7 +163,7 @@
     public void testMatchingArtifactIdsDifferentGroupIds()
         throws CycleDetectedException, DuplicateProjectException
     {
-        List projects = new ArrayList();
+        List<MavenProject> projects = new ArrayList<MavenProject>();
         MavenProject project1 = createProject( "groupId1", "artifactId", "1.0" );
         projects.add( project1 );
         MavenProject project2 = createProject( "groupId2", "artifactId", "1.0" );
@@ -86,7 +179,7 @@
     public void testMatchingGroupIdsDifferentArtifactIds()
         throws CycleDetectedException, DuplicateProjectException
     {
-        List projects = new ArrayList();
+        List<MavenProject> projects = new ArrayList<MavenProject>();
         MavenProject project1 = createProject( "groupId", "artifactId1", "1.0" );
         projects.add( project1 );
         MavenProject project2 = createProject( "groupId", "artifactId2", "1.0" );
@@ -102,7 +195,7 @@
     public void testMatchingIdsAndVersions()
         throws CycleDetectedException
     {
-        List projects = new ArrayList();
+        List<MavenProject> projects = new ArrayList<MavenProject>();
         MavenProject project1 = createProject( "groupId", "artifactId", "1.0" );
         projects.add( project1 );
         MavenProject project2 = createProject( "groupId", "artifactId", "1.0" );
@@ -121,41 +214,152 @@
     }
 
     public void testMatchingIdsAndDifferentVersions()
-        throws CycleDetectedException
+        throws CycleDetectedException, DuplicateProjectException
     {
-        List projects = new ArrayList();
+        List<MavenProject> projects = new ArrayList<MavenProject>();
         MavenProject project1 = createProject( "groupId", "artifactId", "1.0" );
         projects.add( project1 );
         MavenProject project2 = createProject( "groupId", "artifactId", "2.0" );
         projects.add( project2 );
 
-        try
-        {
-            projects = new ProjectSorter( projects ).getSortedProjects();
-            fail( "Duplicate projects should fail" );
-        }
-        catch ( DuplicateProjectException e )
-        {
-            // expected
-            assertTrue( true );
-        }
+        projects = new ProjectSorter( projects ).getSortedProjects();
+        assertEquals( project1, projects.get( 0 ) );
+        assertEquals( project2, projects.get( 1 ) );
     }
 
-    private Dependency createDependency( MavenProject project )
+    public void testPluginDependenciesInfluenceSorting()
+        throws Exception
     {
-        Dependency depdendency = new Dependency();
-        depdendency.setArtifactId( project.getArtifactId() );
-        depdendency.setGroupId( project.getGroupId() );
-        depdendency.setVersion( project.getVersion() );
-        return depdendency;
+        List<MavenProject> projects = new ArrayList<MavenProject>();
+
+        MavenProject parentProject = createProject( "groupId", "parent", "1.0" );
+        projects.add( parentProject );
+
+        MavenProject declaringProject = createProject( "groupId", "declarer", "1.0" );
+        declaringProject.setParent( parentProject );
+        declaringProject.getModel().setParent( createParent( parentProject ) );
+        projects.add( declaringProject );
+
+        MavenProject pluginLevelDepProject = createProject( "groupId", "plugin-level-dep", "1.0" );
+        pluginLevelDepProject.setParent( parentProject );
+        pluginLevelDepProject.getModel().setParent( createParent( parentProject ) );
+        projects.add( pluginLevelDepProject );
+
+        MavenProject pluginProject = createProject( "groupId", "plugin", "1.0" );
+        pluginProject.setParent( parentProject );
+        pluginProject.getModel().setParent( createParent( parentProject ) );
+        projects.add( pluginProject );
+
+        Plugin plugin = createPlugin( pluginProject );
+
+        plugin.addDependency( createDependency( pluginLevelDepProject ) );
+
+        Build build = declaringProject.getModel().getBuild();
+
+        build.addPlugin( plugin );
+
+        projects = new ProjectSorter( projects ).getSortedProjects();
+
+        assertEquals( parentProject, projects.get( 0 ) );
+
+        // the order of these two is non-deterministic, based on when they're added to the reactor.
+        assertTrue( projects.contains( pluginProject ) );
+        assertTrue( projects.contains( pluginLevelDepProject ) );
+
+        // the declaring project MUST be listed after the plugin and its plugin-level dep, though.
+        assertEquals( declaringProject, projects.get( 3 ) );
     }
 
-    private static MavenProject createProject( String groupId, String artifactId, String version )
+    public void testPluginDependenciesInfluenceSorting_DeclarationInParent()
+        throws Exception
     {
-        Model model = new Model();
-        model.setGroupId( groupId );
-        model.setArtifactId( artifactId );
-        model.setVersion( version );
-        return new MavenProject( model );
+        List<MavenProject> projects = new ArrayList<MavenProject>();
+
+        MavenProject parentProject = createProject( "groupId", "parent-declarer", "1.0" );
+        projects.add( parentProject );
+
+        MavenProject pluginProject = createProject( "groupId", "plugin", "1.0" );
+        pluginProject.setParent( parentProject );
+        pluginProject.getModel().setParent( createParent( parentProject ) );
+        projects.add( pluginProject );
+
+        MavenProject pluginLevelDepProject = createProject( "groupId", "plugin-level-dep", "1.0" );
+        pluginLevelDepProject.setParent( parentProject );
+        pluginLevelDepProject.getModel().setParent( createParent( parentProject ) );
+        projects.add( pluginLevelDepProject );
+
+        Plugin plugin = createPlugin( pluginProject );
+
+        plugin.addDependency( createDependency( pluginLevelDepProject ) );
+
+        Build build = parentProject.getModel().getBuild();
+
+        build.addPlugin( plugin );
+
+        projects = new ProjectSorter( projects ).getSortedProjects();
+
+        System.out.println( projects );
+
+        assertEquals( parentProject, projects.get( 0 ) );
+
+        // the order of these two is non-deterministic, based on when they're added to the reactor.
+        assertTrue( projects.contains( pluginProject ) );
+        assertTrue( projects.contains( pluginLevelDepProject ) );
     }
+
+    public void testPluginVersionsAreConsidered()
+        throws Exception
+    {
+        List<MavenProject> projects = new ArrayList<MavenProject>();
+
+        MavenProject pluginProjectA = createProject( "group", "plugin-a", "2.0-SNAPSHOT" );
+        projects.add( pluginProjectA );
+        pluginProjectA.getModel().getBuild().addPlugin( createPlugin( "group", "plugin-b", "1.0" ) );
+
+        MavenProject pluginProjectB = createProject( "group", "plugin-b", "2.0-SNAPSHOT" );
+        projects.add( pluginProjectB );
+        pluginProjectB.getModel().getBuild().addPlugin( createPlugin( "group", "plugin-a", "1.0" ) );
+
+        projects = new ProjectSorter( projects ).getSortedProjects();
+
+        assertTrue( projects.contains( pluginProjectA ) );
+        assertTrue( projects.contains( pluginProjectB ) );
+    }
+
+    public void testDependencyPrecedesProjectThatUsesSpecificDependencyVersion()
+        throws Exception
+    {
+        List<MavenProject> projects = new ArrayList<MavenProject>();
+
+        MavenProject usingProject = createProject( "group", "project", "1.0" );
+        projects.add( usingProject );
+        usingProject.getModel().addDependency( createDependency( "group", "dependency", "1.0" ) );
+
+        MavenProject pluginProject = createProject( "group", "dependency", "1.0" );
+        projects.add( pluginProject );
+
+        projects = new ProjectSorter( projects ).getSortedProjects();
+
+        assertEquals( pluginProject, projects.get( 0 ) );
+        assertEquals( usingProject, projects.get( 1 ) );
+    }
+
+    public void testDependencyPrecedesProjectThatUsesUnresolvedDependencyVersion()
+        throws Exception
+    {
+        List<MavenProject> projects = new ArrayList<MavenProject>();
+
+        MavenProject usingProject = createProject( "group", "project", "1.0" );
+        projects.add( usingProject );
+        usingProject.getModel().addDependency( createDependency( "group", "dependency", "[1.0,)" ) );
+
+        MavenProject pluginProject = createProject( "group", "dependency", "1.0" );
+        projects.add( pluginProject );
+
+        projects = new ProjectSorter( projects ).getSortedProjects();
+
+        assertEquals( pluginProject, projects.get( 0 ) );
+        assertEquals( usingProject, projects.get( 1 ) );
+    }
+
 }