Index: src/main/java/hudson/maven/AbstractMavenBuild.java =================================================================== --- src/main/java/hudson/maven/AbstractMavenBuild.java (revision 0) +++ src/main/java/hudson/maven/AbstractMavenBuild.java (revision 0) @@ -0,0 +1,146 @@ +package hudson.maven; + +import hudson.Util; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.BuildListener; +import hudson.model.DependencyGraph; +import hudson.model.Hudson; +import hudson.model.Result; +import hudson.model.Run; +import hudson.model.Cause.UpstreamCause; + +import java.io.File; +import java.io.IOException; +import java.util.Calendar; +import java.util.Set; + +public abstract class AbstractMavenBuild

,R extends AbstractMavenBuild> extends AbstractBuild { + + /** + * Extra versbose debug switch. + */ + public static boolean debug = false; + + protected AbstractMavenBuild(P job) throws IOException { + super(job); + } + + public AbstractMavenBuild(P job, Calendar timestamp) { + super(job, timestamp); + } + + public AbstractMavenBuild(P project, File buildDir) throws IOException { + super(project, buildDir); + } + + /** + * Schedules all the downstream builds. + * + * @param downstreams + * List of downstream jobs that are already scheduled. + * The method will add jobs that it triggered here, + * and won't try to trigger jobs that are already in this list. + * @param listener + * Where the progress reports go. + */ + protected final void scheduleDownstreamBuilds(BuildListener listener) { + // trigger dependency builds + DependencyGraph graph = Hudson.getInstance().getDependencyGraph(); + for( AbstractProject down : getParent().getDownstreamProjects()) { +// if(downstreams.contains(down)) +// continue; // already triggered + + if(debug) + listener.getLogger().println("Considering whether to trigger "+down+" or not"); + + if(graph.hasIndirectDependencies(getProject(),down)) { + // if there's a longer dependency path to this project, + // then scheduling the build now is going to be a waste, + // so don't do that. + // let the longer path eventually trigger this build + if(debug) + listener.getLogger().println(" -> No, because there's a longer dependency path"); + continue; + } + + // if the downstream module depends on multiple modules, + // only trigger them when all the upstream dependencies are updated. + boolean trigger = true; + + if (down.isInQueue()) { + if(debug) + listener.getLogger().println(" -> No, because dependency is already in queue"); + trigger = false; + } else { + AbstractBuild dlb = down.getLastBuild(); // can be null. + for (AbstractMavenProject up : Util.filter(down.getUpstreamProjects(),AbstractMavenProject.class)) { + Run ulb; + if(up==getParent()) { + // the current build itself is not registered as lastSuccessfulBuild + // at this point, so we have to take that into account. ugly. + if(getResult()==null || !getResult().isWorseThan(Result.UNSTABLE)) + ulb = this; + else + ulb = up.getLastSuccessfulBuild(); + } else + ulb = up.getLastSuccessfulBuild(); + if(ulb==null) { + // if no usable build is available from the upstream, + // then we have to wait at least until this build is ready + if(debug) + listener.getLogger().println(" -> No, because another upstream "+up+" for "+down+" has no successful build"); + trigger = false; + break; + } + + // if no record of the relationship in the last build + // is available, we'll just have to assume that the condition + // for the new build is met, or else no build will be fired forever. + if(dlb==null) continue; + int n = dlb.getUpstreamRelationship(up); + if(n==-1) continue; + + assert ulb.getNumber()>=n; + + if(ulb.getNumber()==n) { + // there's no new build of this upstream since the last build + // of the downstream, and the upstream build is in progress. + // The new downstream build should wait until this build is started + AbstractProject bup = getBuildingUpstream(graph, up); + if(bup!=null) { + if(debug) + listener.getLogger().println(" -> No, because another upstream "+bup+" for "+down+" is building"); + trigger = false; + break; + } + } + } + } + + if(trigger) { + listener.getLogger().println(Messages.MavenBuild_Triggering(down.getName())); + down.scheduleBuild(new UpstreamCause(this)); + } + } + } + + /** + * Returns the project if any of the upstream project (or itself) is either + * building or is in the queue. + *

+ * This means eventually there will be an automatic triggering of + * the given project (provided that all builds went smoothly.) + */ + private AbstractProject getBuildingUpstream(DependencyGraph graph, AbstractProject project) { + Set tups = graph.getTransitiveUpstream(project); + tups.add(project); + for (AbstractProject tup : tups) { + if(tup!=getProject() && (tup.isBuilding() || tup.isInQueue())) + return tup; + } + return null; + } + + +} Index: src/main/java/hudson/maven/AbstractMavenProject.java =================================================================== --- src/main/java/hudson/maven/AbstractMavenProject.java (revision 15307) +++ src/main/java/hudson/maven/AbstractMavenProject.java (working copy) @@ -26,9 +26,11 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Action; +import hudson.model.DependencyGraph; +import hudson.model.Hudson; import hudson.model.ItemGroup; -import hudson.triggers.Trigger; import hudson.tasks.Maven.ProjectWithMaven; +import hudson.triggers.Trigger; import java.util.HashSet; import java.util.Set; @@ -65,4 +67,48 @@ } protected abstract void addTransientActionsFromBuild(R lastBuild, Set added); + + @Override + public boolean isBuildBlocked() { + boolean blocked = super.isBuildBlocked(); + if (!blocked) { + DependencyGraph graph = Hudson.getInstance().getDependencyGraph(); + AbstractProject bup = getBuildingUpstream(); + if(bup!=null) { + return true; + } + } + return blocked; + } + + public String getWhyBlocked() { + if (super.isBuildBlocked()) { + return super.getWhyBlocked(); + } else { + AbstractProject bup = getBuildingUpstream(); + String projectName = ""; + if(bup!=null) { + projectName = bup.getName(); + } + return "Upstream project is building: " + projectName; + } + } + + /** + * Returns the project if any of the upstream project (or itself) is either + * building or is in the queue. + *

+ * This means eventually there will be an automatic triggering of + * the given project (provided that all builds went smoothly.) + */ + private AbstractProject getBuildingUpstream() { + DependencyGraph graph = Hudson.getInstance().getDependencyGraph(); + Set tups = graph.getTransitiveUpstream(this); + tups.add(this); + for (AbstractProject tup : tups) { + if(tup!=this && (tup.isBuilding() || tup.isInQueue())) + return tup; + } + return null; + } } Index: src/main/java/hudson/maven/MavenBuild.java =================================================================== --- src/main/java/hudson/maven/MavenBuild.java (revision 15307) +++ src/main/java/hudson/maven/MavenBuild.java (working copy) @@ -68,7 +68,7 @@ * * @author Kohsuke Kawaguchi */ -public class MavenBuild extends AbstractBuild { +public class MavenBuild extends AbstractMavenBuild { /** * {@link MavenReporter}s that will contribute project actions. * Can be null if there's none. @@ -493,111 +493,8 @@ public void cleanUp(BuildListener listener) throws Exception { if(getResult().isBetterOrEqualTo(Result.SUCCESS)) - scheduleDownstreamBuilds(listener,new HashSet()); - } - } - - /** - * Schedules all the downstream builds. - * - * @param downstreams - * List of downstream jobs that are already scheduled. - * The method will add jobs that it triggered here, - * and won't try to trigger jobs that are already in this list. - * @param listener - * Where the progress reports go. - */ - /*package*/ final void scheduleDownstreamBuilds(BuildListener listener, Set downstreams) { - // trigger dependency builds - DependencyGraph graph = Hudson.getInstance().getDependencyGraph(); - for( AbstractProject down : getParent().getDownstreamProjects()) { - if(downstreams.contains(down)) - continue; // already triggered - - if(debug) - listener.getLogger().println("Considering whether to trigger "+down+" or not"); - - if(graph.hasIndirectDependencies(getProject(),down)) { - // if there's a longer dependency path to this project, - // then scheduling the build now is going to be a waste, - // so don't do that. - // let the longer path eventually trigger this build - if(debug) - listener.getLogger().println(" -> No, because there's a longer dependency path"); - continue; - } - - // if the downstream module depends on multiple modules, - // only trigger them when all the upstream dependencies are updated. - boolean trigger = true; - - AbstractBuild dlb = down.getLastBuild(); // can be null. - for (MavenModule up : Util.filter(down.getUpstreamProjects(),MavenModule.class)) { - MavenBuild ulb; - if(up==getProject()) { - // the current build itself is not registered as lastSuccessfulBuild - // at this point, so we have to take that into account. ugly. - if(getResult()==null || !getResult().isWorseThan(Result.UNSTABLE)) - ulb = MavenBuild.this; - else - ulb = up.getLastSuccessfulBuild(); - } else - ulb = up.getLastSuccessfulBuild(); - if(ulb==null) { - // if no usable build is available from the upstream, - // then we have to wait at least until this build is ready - if(debug) - listener.getLogger().println(" -> No, because another upstream "+up+" for "+down+" has no successful build"); - trigger = false; - break; - } - - // if no record of the relationship in the last build - // is available, we'll just have to assume that the condition - // for the new build is met, or else no build will be fired forever. - if(dlb==null) continue; - int n = dlb.getUpstreamRelationship(up); - if(n==-1) continue; - - assert ulb.getNumber()>=n; - - if(ulb.getNumber()==n) { - // there's no new build of this upstream since the last build - // of the downstream, and the upstream build is in progress. - // The new downstream build should wait until this build is started - AbstractProject bup = getBuildingUpstream(graph, up); - if(bup!=null) { - if(debug) - listener.getLogger().println(" -> No, because another upstream "+bup+" for "+down+" is building"); - trigger = false; - break; - } - } - } - - if(trigger) { - listener.getLogger().println(Messages.MavenBuild_Triggering(down.getName())); - downstreams.add(down); - down.scheduleBuild(new UpstreamCause(this)); - } - } - } - - /** - * Returns the project if any of the upstream project (or itself) is either - * building or is in the queue. - *

- * This means eventually there will be an automatic triggering of - * the given project (provided that all builds went smoothly.) - */ - private AbstractProject getBuildingUpstream(DependencyGraph graph, AbstractProject project) { - Set tups = graph.getTransitiveUpstream(project); - tups.add(project); - for (AbstractProject tup : tups) { - if(tup!=getProject() && (tup.isBuilding() || tup.isInQueue())) - return tup; + scheduleDownstreamBuilds(listener); } - return null; } private static final int MAX_PROCESS_CACHE = 5; Index: src/main/java/hudson/maven/MavenModuleSet.java =================================================================== --- src/main/java/hudson/maven/MavenModuleSet.java (revision 15307) +++ src/main/java/hudson/maven/MavenModuleSet.java (working copy) @@ -438,6 +438,10 @@ } protected void buildDependencyGraph(DependencyGraph graph) { + Collection modules = getModules(); + for (MavenModule m : modules) { + m.buildDependencyGraph(graph); + } publishers.buildDependencyGraph(this,graph); buildWrappers.buildDependencyGraph(this,graph); } Index: src/main/java/hudson/maven/MavenModuleSetBuild.java =================================================================== --- src/main/java/hudson/maven/MavenModuleSetBuild.java (revision 15307) +++ src/main/java/hudson/maven/MavenModuleSetBuild.java (working copy) @@ -25,11 +25,9 @@ import hudson.AbortException; import hudson.FilePath; -import hudson.FilePath.FileCallable; import hudson.Launcher; import hudson.Util; -import hudson.tasks.BuildWrapper; -import hudson.tasks.Maven.MavenInstallation; +import hudson.FilePath.FileCallable; import hudson.maven.MavenBuild.ProxyImpl2; import hudson.maven.reporters.MavenFingerprinter; import hudson.model.AbstractBuild; @@ -39,23 +37,15 @@ import hudson.model.BuildListener; import hudson.model.Fingerprint; import hudson.model.Hudson; -import hudson.model.Result; import hudson.model.ParametersAction; +import hudson.model.Result; import hudson.model.Cause.UpstreamCause; import hudson.remoting.Channel; import hudson.remoting.VirtualChannel; +import hudson.tasks.BuildWrapper; +import hudson.tasks.Maven.MavenInstallation; import hudson.util.ArgumentListBuilder; import hudson.util.StreamTaskListener; -import org.apache.maven.BuildFailureException; -import org.apache.maven.embedder.MavenEmbedderException; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.execution.ReactorManager; -import org.apache.maven.lifecycle.LifecycleExecutionException; -import org.apache.maven.monitor.event.EventDispatcher; -import org.apache.maven.project.MavenProject; -import org.apache.maven.project.ProjectBuildingException; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; import java.io.File; import java.io.IOException; @@ -68,12 +58,23 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import java.util.Properties; +import java.util.Set; +import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.maven.BuildFailureException; +import org.apache.maven.embedder.MavenEmbedderException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ReactorManager; +import org.apache.maven.lifecycle.LifecycleExecutionException; +import org.apache.maven.monitor.event.EventDispatcher; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuildingException; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + /** * {@link Build} for {@link MavenModuleSet}. * @@ -91,7 +92,7 @@ * * @author Kohsuke Kawaguchi */ -public final class MavenModuleSetBuild extends AbstractBuild { +public final class MavenModuleSetBuild extends AbstractMavenBuild { /** * {@link MavenReporter}s that will contribute project actions. * Can be null if there's none. @@ -492,10 +493,7 @@ // schedule downstream builds. for non aggregator style builds, // this is done by each module if(getResult().isBetterOrEqualTo(Result.SUCCESS)) { - for(AbstractProject down : getProject().getDownstreamProjects()) { - listener.getLogger().println(Messages.MavenBuild_Triggering(down.getName())); - down.scheduleBuild(new UpstreamCause(MavenModuleSetBuild.this)); - } + scheduleDownstreamBuilds(listener); } } @@ -503,7 +501,7 @@ performAllBuildStep(listener, project.getProperties(),false); } } - + /** * Runs Maven and builds the project. * @@ -752,8 +750,4 @@ private static final Logger LOGGER = Logger.getLogger(MavenModuleSetBuild.class.getName()); - /** - * Extra versbose debug switch. - */ - public static boolean debug = false; }