You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by si...@apache.org on 2008/09/04 01:36:04 UTC

svn commit: r691831 - in /maven/components/branches/sisbell-maven-2.1-profile: maven-core/src/main/java/org/apache/maven/ maven-core/src/main/java/org/apache/maven/execution/ maven-core/src/test/java/org/apache/maven/execution/ maven-project/src/main/a...

Author: sisbell
Date: Wed Sep  3 16:36:03 2008
New Revision: 691831

URL: http://svn.apache.org/viewvc?rev=691831&view=rev
Log:
Moved Project sorter out of maven-project. Only one class from a different package used it. Now its an inner class of ReactorManager.

Added:
    maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java
Removed:
    maven/components/branches/sisbell-maven-2.1-profile/maven-project/src/main/aspect/org/apache/maven/project/aspect/ProjectCollisionReporterAspect.aj
    maven/components/branches/sisbell-maven-2.1-profile/maven-project/src/main/java/org/apache/maven/project/ProjectSorter.java
    maven/components/branches/sisbell-maven-2.1-profile/maven-project/src/test/java/org/apache/maven/project/ProjectSorterTest.java
Modified:
    maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/ProjectCycleException.java
    maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java

Modified: maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/ProjectCycleException.java
URL: http://svn.apache.org/viewvc/maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/ProjectCycleException.java?rev=691831&r1=691830&r2=691831&view=diff
==============================================================================
--- maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/ProjectCycleException.java (original)
+++ maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/ProjectCycleException.java Wed Sep  3 16:36:03 2008
@@ -1,7 +1,6 @@
 package org.apache.maven;
 
 import org.apache.maven.execution.ReactorManager;
-import org.apache.maven.project.ProjectSorter;
 import org.codehaus.plexus.util.dag.CycleDetectedException;
 
 import java.util.List;
@@ -9,7 +8,7 @@
 /**
  * Exception which occurs when creating a new {@link ReactorManager} instance,
  * due to failure to sort the current projects. The embedded {@link CycleDetectedException}
- * is thrown by the {@link ProjectSorter}, and context of this wrapped exception
+ * is thrown by the ProjectSorter and context of this wrapped exception
  * includes the list of projects that contain the cycle, along with a friendly
  * rendering of the cycle message indicating that it comes from the current projects list.
  *

Modified: maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java
URL: http://svn.apache.org/viewvc/maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java?rev=691831&r1=691830&r2=691831&view=diff
==============================================================================
--- maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java (original)
+++ maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java Wed Sep  3 16:36:03 2008
@@ -24,14 +24,15 @@
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.project.DuplicateProjectException;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.project.ProjectSorter;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.ReportPlugin;
+import org.apache.maven.model.Extension;
 import org.codehaus.plexus.util.dag.CycleDetectedException;
+import org.codehaus.plexus.util.dag.DAG;
+import org.codehaus.plexus.util.dag.TopologicalSorter;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 public class ReactorManager
 {
@@ -188,4 +189,210 @@
     {
         return buildFailuresByProject.size() + buildSuccessesByProject.size() > 1;
     }
+
+    protected static class ProjectSorter
+    {
+        private final DAG dag;
+
+        private final List sortedProjects;
+
+        private MavenProject topLevelProject;
+
+        /**
+         * Sort a list of projects.
+         * <ul>
+         * <li>collect all the vertices for the projects that we want to build.</li>
+         * <li>iterate through the deps of each project and if that dep is within
+         * the set of projects we want to build then add an edge, otherwise throw
+         * the edge away because that dependency is not within the set of projects
+         * we are trying to build. we assume a closed set.</li>
+         * <li>do a topo sort on the graph that remains.</li>
+         * </ul>
+         * @throws DuplicateProjectException if any projects are duplicated by id
+         */
+        // MAVENAPI FIXME: the DAG used is NOT only used to represent the dependency relation,
+        // but also for <parent>, <build><plugin>, <reports>. We need multiple DAG's
+        // since a DAG can only handle 1 type of relationship properly.
+        // Usecase:  This is detected as a cycle:
+        // org.apache.maven:maven-plugin-api                -(PARENT)->
+        // org.apache.maven:maven                           -(inherited REPORTING)->
+        // org.apache.maven.plugins:maven-checkstyle-plugin -(DEPENDENCY)->
+        // org.apache.maven:maven-plugin-api
+        // 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 )
+            throws CycleDetectedException, DuplicateProjectException
+        {
+            dag = new DAG();
+
+            Map projectMap = new HashMap();
+
+            for ( Iterator i = projects.iterator(); i.hasNext(); )
+            {
+                MavenProject project = (MavenProject) i.next();
+
+                String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
+
+                if ( dag.getVertex( id ) != null )
+                {
+                    MavenProject conflictingProject = (MavenProject) projectMap.get( id );
+
+                    throw new DuplicateProjectException( id,
+                                                         conflictingProject.getFile(),
+                                                         project.getFile(),
+                                                         "Project '"
+                                                         + id
+                                                         + "' is duplicated in the reactor" );
+                }
+
+                dag.addVertex( id );
+
+                projectMap.put( id, project );
+            }
+
+            for ( Iterator i = projects.iterator(); i.hasNext(); )
+            {
+                MavenProject project = (MavenProject) i.next();
+
+                String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
+
+                for ( Iterator j = project.getDependencies().iterator(); j.hasNext(); )
+                {
+                    Dependency dependency = (Dependency) j.next();
+
+                    String dependencyId = ArtifactUtils
+                        .versionlessKey( dependency.getGroupId(), dependency.getArtifactId() );
+
+                    if ( dag.getVertex( dependencyId ) != null )
+                    {
+                        project.addProjectReference( (MavenProject) projectMap.get( dependencyId ) );
+
+                        dag.addEdge( id, dependencyId );
+                    }
+                }
+
+                MavenProject parent = project.getParent();
+                if ( parent != null )
+                {
+                    String parentId = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() );
+                    if ( dag.getVertex( parentId ) != null )
+                    {
+                        // 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 ) )
+                        {
+                            dag.removeEdge( parentId, id );
+                        }
+                        dag.addEdge( id, parentId );
+                    }
+                }
+
+                List buildPlugins = project.getBuildPlugins();
+                if ( buildPlugins != null )
+                {
+                    for ( Iterator j = buildPlugins.iterator(); j.hasNext(); )
+                    {
+                        Plugin plugin = (Plugin) j.next();
+                        String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
+                        if ( ( dag.getVertex( pluginId ) != null ) && !pluginId.equals( id ) )
+                        {
+                            addEdgeWithParentCheck( projectMap, pluginId, project, id );
+                        }
+                    }
+                }
+
+                List reportPlugins = project.getReportPlugins();
+                if ( reportPlugins != null )
+                {
+                    for ( Iterator j = reportPlugins.iterator(); j.hasNext(); )
+                    {
+                        ReportPlugin plugin = (ReportPlugin) j.next();
+                        String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
+                        if ( ( dag.getVertex( pluginId ) != null ) && !pluginId.equals( id ) )
+                        {
+                            addEdgeWithParentCheck( projectMap, pluginId, project, id );
+                        }
+                    }
+                }
+
+                for ( Iterator j = project.getBuildExtensions().iterator(); j.hasNext(); )
+                {
+                    Extension extension = (Extension) j.next();
+                    String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() );
+                    if ( dag.getVertex( extensionId ) != null )
+                    {
+                        addEdgeWithParentCheck( projectMap, extensionId, project, id );
+                    }
+                }
+            }
+
+            List sortedProjects = new ArrayList();
+
+            for ( Iterator i = TopologicalSorter.sort( dag ).iterator(); i.hasNext(); )
+            {
+                String id = (String) i.next();
+
+                sortedProjects.add( projectMap.get( id ) );
+            }
+
+            this.sortedProjects = Collections.unmodifiableList( sortedProjects );
+        }
+
+        private void addEdgeWithParentCheck( Map projectMap, String projectRefId, MavenProject project, String id )
+            throws CycleDetectedException
+        {
+            MavenProject extProject = (MavenProject) projectMap.get( projectRefId );
+
+            if ( extProject == null )
+            {
+                return;
+            }
+
+            project.addProjectReference( extProject );
+
+            MavenProject extParent = extProject.getParent();
+            if ( extParent != null )
+            {
+                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( id, projectRefId );
+                }
+            }
+        }
+
+        // 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()
+        {
+            if ( topLevelProject == null )
+            {
+                for ( Iterator i = sortedProjects.iterator(); i.hasNext() && ( topLevelProject == null ); )
+                {
+                    MavenProject project = (MavenProject) i.next();
+                    if ( project.isExecutionRoot() )
+                    {
+                        topLevelProject = project;
+                    }
+                }
+            }
+
+            return topLevelProject;
+        }
+
+        public List getSortedProjects()
+        {
+            return sortedProjects;
+        }
+
+        public boolean hasMultipleProjects()
+        {
+            return sortedProjects.size() > 1;
+        }
+
+        private List getDependents( String id )
+        {
+            return dag.getParentLabels( id );
+        }
+    }
 }

Added: maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java
URL: http://svn.apache.org/viewvc/maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java?rev=691831&view=auto
==============================================================================
--- maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java (added)
+++ maven/components/branches/sisbell-maven-2.1-profile/maven-core/src/test/java/org/apache/maven/execution/ProjectSorterTest.java Wed Sep  3 16:36:03 2008
@@ -0,0 +1,161 @@
+package org.apache.maven.execution;
+
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Extension;
+import org.apache.maven.model.Model;
+import org.apache.maven.project.DuplicateProjectException;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.dag.CycleDetectedException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Test sorting projects by dependencies.
+ *
+ * @author <a href="mailto:brett@apache.org">Brett Porter</a>
+ * @version $Id: ProjectSorterTest.java 513038 2007-02-28 22:54:19Z jvanzyl $
+ */
+public class ProjectSorterTest
+    extends TestCase
+{
+
+    public void testShouldNotFailWhenProjectReferencesNonExistentProject()
+        throws CycleDetectedException, DuplicateProjectException
+    {
+        MavenProject project = createProject( "group", "artifact", "1.0" );
+        Model model = project.getModel();
+
+        Build build = model.getBuild();
+
+        if ( build == null )
+        {
+            build = new Build();
+            model.setBuild( build );
+        }
+
+        Extension extension = new Extension();
+
+        extension.setArtifactId( "other-artifact" );
+        extension.setGroupId( "other.group" );
+        extension.setVersion( "1.0" );
+
+        build.addExtension( extension );
+
+        new ReactorManager.ProjectSorter( Collections.singletonList( project ) );
+    }
+
+    public void testMatchingArtifactIdsDifferentGroupIds()
+        throws CycleDetectedException, DuplicateProjectException
+    {
+        List projects = new ArrayList();
+        MavenProject project1 = createProject( "groupId1", "artifactId", "1.0" );
+        projects.add( project1 );
+        MavenProject project2 = createProject( "groupId2", "artifactId", "1.0" );
+        projects.add( project2 );
+        project1.getDependencies().add( createDependency( project2 ) );
+
+        projects = new ReactorManager.ProjectSorter( projects ).getSortedProjects();
+
+        assertEquals( project2, projects.get( 0 ) );
+        assertEquals( project1, projects.get( 1 ) );
+    }
+
+    public void testMatchingGroupIdsDifferentArtifactIds()
+        throws CycleDetectedException, DuplicateProjectException
+    {
+        List projects = new ArrayList();
+        MavenProject project1 = createProject( "groupId", "artifactId1", "1.0" );
+        projects.add( project1 );
+        MavenProject project2 = createProject( "groupId", "artifactId2", "1.0" );
+        projects.add( project2 );
+        project1.getDependencies().add( createDependency( project2 ) );
+
+        projects = new ReactorManager.ProjectSorter( projects ).getSortedProjects();
+
+        assertEquals( project2, projects.get( 0 ) );
+        assertEquals( project1, projects.get( 1 ) );
+    }
+
+    public void testMatchingIdsAndVersions()
+        throws CycleDetectedException
+    {
+        List projects = new ArrayList();
+        MavenProject project1 = createProject( "groupId", "artifactId", "1.0" );
+        projects.add( project1 );
+        MavenProject project2 = createProject( "groupId", "artifactId", "1.0" );
+        projects.add( project2 );
+
+        try
+        {
+            projects = new ReactorManager.ProjectSorter( projects ).getSortedProjects();
+            fail( "Duplicate projects should fail" );
+        }
+        catch ( DuplicateProjectException e )
+        {
+            // expected
+            assertTrue( true );
+        }
+    }
+
+    public void testMatchingIdsAndDifferentVersions()
+        throws CycleDetectedException
+    {
+        List projects = new ArrayList();
+        MavenProject project1 = createProject( "groupId", "artifactId", "1.0" );
+        projects.add( project1 );
+        MavenProject project2 = createProject( "groupId", "artifactId", "2.0" );
+        projects.add( project2 );
+
+        try
+        {
+            projects = new ReactorManager.ProjectSorter( projects ).getSortedProjects();
+            fail( "Duplicate projects should fail" );
+        }
+        catch ( DuplicateProjectException e )
+        {
+            // expected
+            assertTrue( true );
+        }
+    }
+
+    private Dependency createDependency( MavenProject project )
+    {
+        Dependency depdendency = new Dependency();
+        depdendency.setArtifactId( project.getArtifactId() );
+        depdendency.setGroupId( project.getGroupId() );
+        depdendency.setVersion( project.getVersion() );
+        return depdendency;
+    }
+
+    private static MavenProject createProject( String groupId, String artifactId, String version )
+    {
+        Model model = new Model();
+        model.setGroupId( groupId );
+        model.setArtifactId( artifactId );
+        model.setVersion( version );
+        return new MavenProject( model );
+    }
+}