提交 e4b84a2f 编写于 作者: A Andrew Bayer

Revert "Initial work on DependencyGraph"

This reverts commit 7fe3a4a3.
上级 4f3172c5
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package hudson; package hudson;
import hudson.model.AbstractProject; import hudson.model.AbstractProject;
import hudson.model.Job;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import hudson.security.ACL; import hudson.security.ACL;
...@@ -77,23 +76,20 @@ public class DependencyRunner implements Runnable { ...@@ -77,23 +76,20 @@ public class DependencyRunner implements Runnable {
} }
} }
private void populate(Collection<? extends Job> projectList) { private void populate(Collection<? extends AbstractProject> projectList) {
for (Job<?,?> j : projectList) { for (AbstractProject<?,?> p : projectList) {
if (j instanceof AbstractProject) { if (polledProjects.contains(p)) {
AbstractProject<?,?> p = (AbstractProject<?,?>)j; // Project will be readded at the queue, so that we always use
if (polledProjects.contains(p)) { // the longest path
// Project will be readded at the queue, so that we always use LOGGER.fine("removing project " + p.getName() + " for re-add");
// the longest path polledProjects.remove(p);
LOGGER.fine("removing project " + p.getName() + " for re-add"); }
polledProjects.remove(p);
}
LOGGER.fine("adding project " + p.getName()); LOGGER.fine("adding project " + p.getName());
polledProjects.add(p); polledProjects.add(p);
// Add all downstream dependencies // Add all downstream dependencies
populate(p.getDownstreamProjects()); populate(p.getDownstreamProjects());
}
} }
} }
......
...@@ -102,6 +102,11 @@ import jenkins.model.lazy.LazyBuildMixIn; ...@@ -102,6 +102,11 @@ import jenkins.model.lazy.LazyBuildMixIn;
*/ */
public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends AbstractBuild<P,R>> extends Run<P,R> implements Queue.Executable, LazyBuildMixIn.LazyLoadingRun<P,R>, RunWithSCMMixIn.RunWithSCM<P,R> { public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends AbstractBuild<P,R>> extends Run<P,R> implements Queue.Executable, LazyBuildMixIn.LazyLoadingRun<P,R>, RunWithSCMMixIn.RunWithSCM<P,R> {
/**
* Set if we want the blame information to flow from upstream to downstream build.
*/
private static final boolean upstreamCulprits = SystemProperties.getBoolean("hudson.upstreamCulprits");
/** /**
* Name of the agent this project was built on. * Name of the agent this project was built on.
* Null or "" if built by the master. (null happens when we read old record that didn't have this information.) * Null or "" if built by the master. (null happens when we read old record that didn't have this information.)
...@@ -244,6 +249,24 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs ...@@ -244,6 +249,24 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
this.builtOn = builtOn; this.builtOn = builtOn;
} }
/**
* Gets the nearest ancestor {@link AbstractBuild} that belongs to
* {@linkplain AbstractProject#getRootProject() the root project of getProject()} that
* dominates/governs/encompasses this build.
*
* <p>
* Some projects (such as matrix projects, Maven projects, or promotion processes) form a tree of jobs,
* and still in some of them, builds of child projects are related/tied to that of the parent project.
* In such a case, this method returns the governing build.
*
* @return never null. In the worst case the build dominates itself.
* @since 1.421
* @see AbstractProject#getRootProject()
*/
public AbstractBuild<?,?> getRootBuild() {
return this;
}
/** /**
* Used to render the side panel "Back to project" link. * Used to render the side panel "Back to project" link.
* *
...@@ -1038,10 +1061,10 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs ...@@ -1038,10 +1061,10 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
// if any of the downstream project is configured with 'keep dependency component', // if any of the downstream project is configured with 'keep dependency component',
// we need to keep this log // we need to keep this log
OUTER: OUTER:
for (Job<?,?> p : getParent().getDownstreamProjects()) { for (AbstractProject<?,?> p : getParent().getDownstreamProjects()) {
if (!p.isKeepDependencies()) continue; if (!p.isKeepDependencies()) continue;
Run<?,?> fb = p.getFirstBuild(); AbstractBuild<?,?> fb = p.getFirstBuild();
if (fb==null) continue; // no active record if (fb==null) continue; // no active record
// is there any active build that depends on us? // is there any active build that depends on us?
...@@ -1052,7 +1075,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs ...@@ -1052,7 +1075,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
if (i<fb.getNumber()) if (i<fb.getNumber())
continue OUTER; // all the other records are younger than the first record, so pointless to search. continue OUTER; // all the other records are younger than the first record, so pointless to search.
Run<?,?> b = p.getBuildByNumber(i); AbstractBuild<?,?> b = p.getBuildByNumber(i);
if (b!=null) if (b!=null)
return Messages.AbstractBuild_KeptBecause(b); return Messages.AbstractBuild_KeptBecause(b);
} }
...@@ -1062,11 +1085,225 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs ...@@ -1062,11 +1085,225 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
} }
/** /**
* Retained for compatibility. * Gets the dependency relationship from this build (as the source)
* and that project (as the sink.)
*
* @return
* range of build numbers that represent which downstream builds are using this build.
* The range will be empty if no build of that project matches this (or there is no {@link FingerprintAction}), but it'll never be null.
*/
public RangeSet getDownstreamRelationship(AbstractProject that) {
RangeSet rs = new RangeSet();
FingerprintAction f = getAction(FingerprintAction.class);
if (f==null) return rs;
// look for fingerprints that point to this build as the source, and merge them all
for (Fingerprint e : f.getFingerprints().values()) {
if (upstreamCulprits) {
// With upstreamCulprits, we allow downstream relationships
// from intermediate jobs
rs.add(e.getRangeSet(that));
} else {
BuildPtr o = e.getOriginal();
if (o!=null && o.is(this))
rs.add(e.getRangeSet(that));
}
}
return rs;
}
/**
* Works like {@link #getDownstreamRelationship(AbstractProject)} but returns
* the actual build objects, in ascending order.
* @since 1.150
*/
public Iterable<AbstractBuild<?,?>> getDownstreamBuilds(final AbstractProject<?,?> that) {
final Iterable<Integer> nums = getDownstreamRelationship(that).listNumbers();
return new Iterable<AbstractBuild<?, ?>>() {
public Iterator<AbstractBuild<?, ?>> iterator() {
return Iterators.removeNull(
new AdaptedIterator<Integer,AbstractBuild<?,?>>(nums) {
protected AbstractBuild<?, ?> adapt(Integer item) {
return that.getBuildByNumber(item);
}
});
}
};
}
/**
* Gets the dependency relationship from this build (as the sink)
* and that project (as the source.)
*
* @return
* Build number of the upstream build that feed into this build,
* or -1 if no record is available (for example if there is no {@link FingerprintAction}, even if there is an {@link Cause.UpstreamCause}).
*/
public int getUpstreamRelationship(AbstractProject that) {
FingerprintAction f = getAction(FingerprintAction.class);
if (f==null) return -1;
int n = -1;
// look for fingerprints that point to the given project as the source, and merge them all
for (Fingerprint e : f.getFingerprints().values()) {
if (upstreamCulprits) {
// With upstreamCulprits, we allow upstream relationships
// from intermediate jobs
Fingerprint.RangeSet rangeset = e.getRangeSet(that);
if (!rangeset.isEmpty()) {
n = Math.max(n, rangeset.listNumbersReverse().iterator().next());
}
} else {
BuildPtr o = e.getOriginal();
if (o!=null && o.belongsTo(that))
n = Math.max(n,o.getNumber());
}
}
return n;
}
/**
* Works like {@link #getUpstreamRelationship(AbstractProject)} but returns the
* actual build object.
*
* @return
* null if no such upstream build was found, or it was found but the
* build record is already lost.
*/
public AbstractBuild<?,?> getUpstreamRelationshipBuild(AbstractProject<?,?> that) {
int n = getUpstreamRelationship(that);
if (n==-1) return null;
return that.getBuildByNumber(n);
}
/**
* Gets the downstream builds of this build, which are the builds of the
* downstream projects that use artifacts of this build.
*
* @return
* For each project with fingerprinting enabled, returns the range
* of builds (which can be empty if no build uses the artifact from this build or downstream is not {@link AbstractProject#isFingerprintConfigured}.)
*/
public Map<AbstractProject,RangeSet> getDownstreamBuilds() {
Map<AbstractProject,RangeSet> r = new HashMap<AbstractProject,RangeSet>();
for (AbstractProject p : getParent().getDownstreamProjects()) {
if (p.isFingerprintConfigured())
r.put(p,getDownstreamRelationship(p));
}
return r;
}
/**
* Gets the upstream builds of this build, which are the builds of the
* upstream projects whose artifacts feed into this build.
* @return empty if there is no {@link FingerprintAction} (even if there is an {@link Cause.UpstreamCause})
* @see #getTransitiveUpstreamBuilds()
*/
public Map<AbstractProject,Integer> getUpstreamBuilds() {
return _getUpstreamBuilds(getParent().getUpstreamProjects());
}
/**
* Works like {@link #getUpstreamBuilds()} but also includes all the transitive
* dependencies as well.
*/ */
public static final class DependencyChange extends Run.DependencyChange { public Map<AbstractProject,Integer> getTransitiveUpstreamBuilds() {
public DependencyChange(Job<?, ?> project, int fromId, int toId) { return _getUpstreamBuilds(getParent().getTransitiveUpstreamProjects());
super(project, fromId, toId); }
private Map<AbstractProject, Integer> _getUpstreamBuilds(Collection<AbstractProject> projects) {
Map<AbstractProject,Integer> r = new HashMap<AbstractProject,Integer>();
for (AbstractProject p : projects) {
int n = getUpstreamRelationship(p);
if (n>=0)
r.put(p,n);
}
return r;
}
/**
* Gets the changes in the dependency between the given build and this build.
* @return empty if there is no {@link FingerprintAction}
*/
public Map<AbstractProject,DependencyChange> getDependencyChanges(AbstractBuild from) {
if (from==null) return Collections.emptyMap(); // make it easy to call this from views
FingerprintAction n = this.getAction(FingerprintAction.class);
FingerprintAction o = from.getAction(FingerprintAction.class);
if (n==null || o==null) return Collections.emptyMap();
Map<AbstractProject,Integer> ndep = n.getDependencies(true);
Map<AbstractProject,Integer> odep = o.getDependencies(true);
Map<AbstractProject,DependencyChange> r = new HashMap<AbstractProject,DependencyChange>();
for (Map.Entry<AbstractProject,Integer> entry : odep.entrySet()) {
AbstractProject p = entry.getKey();
Integer oldNumber = entry.getValue();
Integer newNumber = ndep.get(p);
if (newNumber!=null && oldNumber.compareTo(newNumber)<0) {
r.put(p,new DependencyChange(p,oldNumber,newNumber));
}
}
return r;
}
/**
* Represents a change in the dependency.
*/
public static final class DependencyChange {
/**
* The dependency project.
*/
public final AbstractProject project;
/**
* Version of the dependency project used in the previous build.
*/
public final int fromId;
/**
* {@link Build} object for {@link #fromId}. Can be null if the log is gone.
*/
public final AbstractBuild from;
/**
* Version of the dependency project used in this build.
*/
public final int toId;
public final AbstractBuild to;
public DependencyChange(AbstractProject<?,?> project, int fromId, int toId) {
this.project = project;
this.fromId = fromId;
this.toId = toId;
this.from = project.getBuildByNumber(fromId);
this.to = project.getBuildByNumber(toId);
}
/**
* Gets the {@link AbstractBuild} objects (fromId,toId].
* <p>
* This method returns all such available builds in the ascending order
* of IDs, but due to log rotations, some builds may be already unavailable.
*/
public List<AbstractBuild> getBuilds() {
List<AbstractBuild> r = new ArrayList<AbstractBuild>();
AbstractBuild<?,?> b = project.getNearestBuild(fromId);
if (b!=null && b.getNumber()==fromId)
b = b.getNextBuild(); // fromId exclusive
while (b!=null && b.getNumber()<=toId) {
r.add(b);
b = b.getNextBuild();
}
return r;
} }
} }
......
...@@ -471,6 +471,28 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A ...@@ -471,6 +471,28 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
return AlternativeUiTextProvider.get(BUILD_NOW_TEXT, this, getParameterizedJobMixIn().getBuildNowText()); return AlternativeUiTextProvider.get(BUILD_NOW_TEXT, this, getParameterizedJobMixIn().getBuildNowText());
} }
/**
* Gets the nearest ancestor {@link TopLevelItem} that's also an {@link AbstractProject}.
*
* <p>
* Some projects (such as matrix projects, Maven projects, or promotion processes) form a tree of jobs
* that acts as a single unit. This method can be used to find the top most dominating job that
* covers such a tree.
*
* @return never null.
* @see AbstractBuild#getRootBuild()
*/
public AbstractProject<?,?> getRootProject() {
if (this instanceof TopLevelItem) {
return this;
} else {
ItemGroup p = this.getParent();
if (p instanceof AbstractProject)
return ((AbstractProject) p).getRootProject();
return this;
}
}
/** /**
* Gets the directory where the module is checked out. * Gets the directory where the module is checked out.
* *
...@@ -1090,9 +1112,9 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A ...@@ -1090,9 +1112,9 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
* Because the downstream build is in progress, and we are configured to wait for that. * Because the downstream build is in progress, and we are configured to wait for that.
*/ */
public static class BecauseOfDownstreamBuildInProgress extends CauseOfBlockage { public static class BecauseOfDownstreamBuildInProgress extends CauseOfBlockage {
public final Job<?,?> up; public final AbstractProject<?,?> up;
public BecauseOfDownstreamBuildInProgress(Job<?,?> up) { public BecauseOfDownstreamBuildInProgress(AbstractProject<?,?> up) {
this.up = up; this.up = up;
} }
...@@ -1106,9 +1128,9 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A ...@@ -1106,9 +1128,9 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
* Because the upstream build is in progress, and we are configured to wait for that. * Because the upstream build is in progress, and we are configured to wait for that.
*/ */
public static class BecauseOfUpstreamBuildInProgress extends CauseOfBlockage { public static class BecauseOfUpstreamBuildInProgress extends CauseOfBlockage {
public final Job<?,?> up; public final AbstractProject<?,?> up;
public BecauseOfUpstreamBuildInProgress(Job<?,?> up) { public BecauseOfUpstreamBuildInProgress(AbstractProject<?,?> up) {
this.up = up; this.up = up;
} }
...@@ -1133,18 +1155,52 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A ...@@ -1133,18 +1155,52 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
} }
} }
if (blockBuildWhenDownstreamBuilding()) { if (blockBuildWhenDownstreamBuilding()) {
Job<?,?> bup = getBuildingDownstream(); AbstractProject<?,?> bup = getBuildingDownstream();
if (bup!=null) if (bup!=null)
return new BecauseOfDownstreamBuildInProgress(bup); return new BecauseOfDownstreamBuildInProgress(bup);
} }
if (blockBuildWhenUpstreamBuilding()) { if (blockBuildWhenUpstreamBuilding()) {
Job<?,?> bup = getBuildingUpstream(); AbstractProject<?,?> bup = getBuildingUpstream();
if (bup!=null) if (bup!=null)
return new BecauseOfUpstreamBuildInProgress(bup); return new BecauseOfUpstreamBuildInProgress(bup);
} }
return null; return null;
} }
/**
* Returns the project if any of the downstream project is either
* building, waiting, pending or buildable.
* <p>
* This means eventually there will be an automatic triggering of
* the given project (provided that all builds went smoothly.)
*/
public AbstractProject getBuildingDownstream() {
Set<Task> unblockedTasks = Jenkins.getInstance().getQueue().getUnblockedTasks();
for (AbstractProject tup : getTransitiveDownstreamProjects()) {
if (tup!=this && (tup.isBuilding() || unblockedTasks.contains(tup)))
return tup;
}
return null;
}
/**
* Returns the project if any of the upstream project is either
* building or is in the queue.
* <p>
* This means eventually there will be an automatic triggering of
* the given project (provided that all builds went smoothly.)
*/
public AbstractProject getBuildingUpstream() {
Set<Task> unblockedTasks = Jenkins.getInstance().getQueue().getUnblockedTasks();
for (AbstractProject tup : getTransitiveUpstreamProjects()) {
if (tup!=this && (tup.isBuilding() || unblockedTasks.contains(tup)))
return tup;
}
return null;
}
public List<SubTask> getSubTasks() { public List<SubTask> getSubTasks() {
List<SubTask> r = new ArrayList<SubTask>(); List<SubTask> r = new ArrayList<SubTask>();
r.add(this); r.add(this);
...@@ -1580,31 +1636,97 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A ...@@ -1580,31 +1636,97 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
*/ */
public abstract boolean isFingerprintConfigured(); public abstract boolean isFingerprintConfigured();
/**
* Gets the other {@link AbstractProject}s that should be built
* when a build of this project is completed.
*/
@Exported
public final List<AbstractProject> getDownstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getDownstream(this);
}
@Exported
public final List<AbstractProject> getUpstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getUpstream(this);
}
/** /**
* Returns only those upstream projects that defines {@link BuildTrigger} to this project. * Returns only those upstream projects that defines {@link BuildTrigger} to this project.
* This is a subset of {@link #getUpstreamProjects()}, only including {@link AbstractProject}s. * This is a subset of {@link #getUpstreamProjects()}
* <p>No longer used in the UI. * <p>No longer used in the UI.
* @return A List of upstream projects that has a {@link BuildTrigger} to this project. * @return A List of upstream projects that has a {@link BuildTrigger} to this project.
*/ */
public final List<AbstractProject> getBuildTriggerUpstreamProjects() { public final List<AbstractProject> getBuildTriggerUpstreamProjects() {
ArrayList<AbstractProject> result = new ArrayList<AbstractProject>(); ArrayList<AbstractProject> result = new ArrayList<AbstractProject>();
for (Job<?,?> j : getUpstreamProjects()) { for (AbstractProject<?,?> ap : getUpstreamProjects()) {
if (j instanceof AbstractProject) { BuildTrigger buildTrigger = ap.getPublishersList().get(BuildTrigger.class);
AbstractProject<?,?> ap = (AbstractProject) j; if (buildTrigger != null)
BuildTrigger buildTrigger = ap.getPublishersList().get(BuildTrigger.class); if (buildTrigger.getChildProjects(ap).contains(this))
if (buildTrigger != null) result.add(ap);
if (buildTrigger.getChildProjects(ap).contains(this))
result.add(ap);
}
} }
return result; return result;
} }
/**
* Gets all the upstream projects including transitive upstream projects.
*
* @since 1.138
*/
public final Set<AbstractProject> getTransitiveUpstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getTransitiveUpstream(this);
}
/**
* Gets all the downstream projects including transitive downstream projects.
*
* @since 1.138
*/
public final Set<AbstractProject> getTransitiveDownstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getTransitiveDownstream(this);
}
/**
* Gets the dependency relationship map between this project (as the source)
* and that project (as the sink.)
*
* @return
* can be empty but not null. build number of this project to the build
* numbers of that project.
*/
public SortedMap<Integer, RangeSet> getRelationship(AbstractProject that) {
TreeMap<Integer,RangeSet> r = new TreeMap<Integer,RangeSet>(REVERSE_INTEGER_COMPARATOR);
checkAndRecord(that, r, this.getBuilds());
// checkAndRecord(that, r, that.getBuilds());
return r;
}
/**
* Helper method for getDownstreamRelationship.
*
* For each given build, find the build number range of the given project and put that into the map.
*/
private void checkAndRecord(AbstractProject that, TreeMap<Integer, RangeSet> r, Collection<R> builds) {
for (R build : builds) {
RangeSet rs = build.getDownstreamRelationship(that);
if(rs==null || rs.isEmpty())
continue;
int n = build.getNumber();
RangeSet value = r.get(n);
if(value==null)
r.put(n,rs);
else
value.add(rs);
}
}
/** /**
* Builds the dependency graph. * Builds the dependency graph.
* Since 1.558, not abstract and by default includes dependencies contributed by {@link #triggers()}. * Since 1.558, not abstract and by default includes dependencies contributed by {@link #triggers()}.
*/ */
@Override
protected void buildDependencyGraph(DependencyGraph graph) { protected void buildDependencyGraph(DependencyGraph graph) {
triggers().buildDependencyGraph(this, graph); triggers().buildDependencyGraph(this, graph);
} }
...@@ -2039,6 +2161,12 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A ...@@ -2039,6 +2161,12 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
return Items.findNearest(AbstractProject.class, name, context); return Items.findNearest(AbstractProject.class, name, context);
} }
private static final Comparator<Integer> REVERSE_INTEGER_COMPARATOR = new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
};
private static final Logger LOGGER = Logger.getLogger(AbstractProject.class.getName()); private static final Logger LOGGER = Logger.getLogger(AbstractProject.class.getName());
/** /**
......
...@@ -68,17 +68,17 @@ import java.util.Stack; ...@@ -68,17 +68,17 @@ import java.util.Stack;
* @see Jenkins#getDependencyGraph() * @see Jenkins#getDependencyGraph()
* @author Kohsuke Kawaguchi * @author Kohsuke Kawaguchi
*/ */
public class DependencyGraph implements Comparator<Job> { public class DependencyGraph implements Comparator<AbstractProject> {
private Map<Job, List<DependencyGroup>> forward = new HashMap<Job, List<DependencyGroup>>(); private Map<AbstractProject, List<DependencyGroup>> forward = new HashMap<AbstractProject, List<DependencyGroup>>();
private Map<Job, List<DependencyGroup>> backward = new HashMap<Job, List<DependencyGroup>>(); private Map<AbstractProject, List<DependencyGroup>> backward = new HashMap<AbstractProject, List<DependencyGroup>>();
private transient Map<Class<?>, Object> computationalData; private transient Map<Class<?>, Object> computationalData;
private boolean built; private boolean built;
private Comparator<Job<?,?>> topologicalOrder; private Comparator<AbstractProject<?,?>> topologicalOrder;
private List<Job<?,?>> topologicallySorted; private List<AbstractProject<?,?>> topologicallySorted;
/** /**
* Builds the dependency graph. * Builds the dependency graph.
...@@ -91,7 +91,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -91,7 +91,7 @@ public class DependencyGraph implements Comparator<Job> {
SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM);
try { try {
this.computationalData = new HashMap<Class<?>, Object>(); this.computationalData = new HashMap<Class<?>, Object>();
for( Job p : Jenkins.getInstance().allItems(Job.class) ) for( AbstractProject p : Jenkins.getInstance().allItems(AbstractProject.class) )
p.buildDependencyGraph(this); p.buildDependencyGraph(this);
forward = finalize(forward); forward = finalize(forward);
...@@ -110,36 +110,36 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -110,36 +110,36 @@ public class DependencyGraph implements Comparator<Job> {
* See http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm * See http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
*/ */
private void topologicalDagSort() { private void topologicalDagSort() {
DirectedGraph<Job> g = new DirectedGraph<Job>() { DirectedGraph<AbstractProject> g = new DirectedGraph<AbstractProject>() {
@Override @Override
protected Collection<Job> nodes() { protected Collection<AbstractProject> nodes() {
final Set<Job> nodes = new HashSet<Job>(); final Set<AbstractProject> nodes = new HashSet<AbstractProject>();
nodes.addAll(forward.keySet()); nodes.addAll(forward.keySet());
nodes.addAll(backward.keySet()); nodes.addAll(backward.keySet());
return nodes; return nodes;
} }
@Override @Override
protected Collection<Job> forward(Job node) { protected Collection<AbstractProject> forward(AbstractProject node) {
return getDownstream(node); return getDownstream(node);
} }
}; };
List<SCC<Job>> sccs = g.getStronglyConnectedComponents(); List<SCC<AbstractProject>> sccs = g.getStronglyConnectedComponents();
final Map<Job,Integer> topoOrder = new HashMap<Job,Integer>(); final Map<AbstractProject,Integer> topoOrder = new HashMap<AbstractProject,Integer>();
topologicallySorted = new ArrayList<Job<?,?>>(); topologicallySorted = new ArrayList<AbstractProject<?,?>>();
int idx=0; int idx=0;
for (SCC<Job> scc : sccs) { for (SCC<AbstractProject> scc : sccs) {
for (Job n : scc) { for (AbstractProject n : scc) {
topoOrder.put(n,idx++); topoOrder.put(n,idx++);
topologicallySorted.add(n); topologicallySorted.add(n);
} }
} }
topologicalOrder = new Comparator<Job<?, ?>>() { topologicalOrder = new Comparator<AbstractProject<?, ?>>() {
@Override @Override
public int compare(Job<?,?> o1, Job<?,?> o2) { public int compare(AbstractProject<?,?> o1, AbstractProject<?,?> o2) {
return topoOrder.get(o1)-topoOrder.get(o2); return topoOrder.get(o1)-topoOrder.get(o2);
} }
}; };
...@@ -179,7 +179,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -179,7 +179,7 @@ public class DependencyGraph implements Comparator<Job> {
* @return * @return
* can be empty but never null. * can be empty but never null.
*/ */
public List<Job> getDownstream(Job p) { public List<AbstractProject> getDownstream(AbstractProject p) {
return get(forward,p,false); return get(forward,p,false);
} }
...@@ -189,34 +189,33 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -189,34 +189,33 @@ public class DependencyGraph implements Comparator<Job> {
* @return * @return
* can be empty but never null. * can be empty but never null.
*/ */
public List<Job> getUpstream(Job p) { public List<AbstractProject> getUpstream(AbstractProject p) {
return get(backward,p,true); return get(backward,p,true);
} }
private List<Job> get(Map<Job, List<DependencyGroup>> map, Job src, boolean up) { private List<AbstractProject> get(Map<AbstractProject, List<DependencyGroup>> map, AbstractProject src, boolean up) {
List<DependencyGroup> v = map.get(src); List<DependencyGroup> v = map.get(src);
if(v==null) return Collections.emptyList(); if(v==null) return Collections.emptyList();
List<Job> result = new ArrayList<Job>(v.size()); List<AbstractProject> result = new ArrayList<AbstractProject>(v.size());
for (DependencyGroup d : v) result.add(up ? d.getUpstreamProject() : d.getDownstreamProject()); for (DependencyGroup d : v) result.add(up ? d.getUpstreamProject() : d.getDownstreamProject());
return result; return result;
} }
/** /**
* @since 1.341 * @since 1.341
*/ */
public List<Dependency> getDownstreamDependencies(Job p) { public List<Dependency> getDownstreamDependencies(AbstractProject p) {
return get(forward,p); return get(forward,p);
} }
/** /**
* @since 1.341 * @since 1.341
*/ */
public List<Dependency> getUpstreamDependencies(Job p) { public List<Dependency> getUpstreamDependencies(AbstractProject p) {
return get(backward,p); return get(backward,p);
} }
private List<Dependency> get(Map<Job, List<DependencyGroup>> map, Job src) { private List<Dependency> get(Map<AbstractProject, List<DependencyGroup>> map, AbstractProject src) {
List<DependencyGroup> v = map.get(src); List<DependencyGroup> v = map.get(src);
if(v==null) { if(v==null) {
return ImmutableList.of(); return ImmutableList.of();
...@@ -234,7 +233,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -234,7 +233,7 @@ public class DependencyGraph implements Comparator<Job> {
* @deprecated since 1.341; use {@link #addDependency(Dependency)} * @deprecated since 1.341; use {@link #addDependency(Dependency)}
*/ */
@Deprecated @Deprecated
public void addDependency(Job upstream, Job downstream) { public void addDependency(AbstractProject upstream, AbstractProject downstream) {
addDependency(new Dependency(upstream,downstream)); addDependency(new Dependency(upstream,downstream));
} }
...@@ -252,8 +251,8 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -252,8 +251,8 @@ public class DependencyGraph implements Comparator<Job> {
* @deprecated since 1.341 * @deprecated since 1.341
*/ */
@Deprecated @Deprecated
public void addDependency(Job upstream, Collection<? extends Job> downstream) { public void addDependency(AbstractProject upstream, Collection<? extends AbstractProject> downstream) {
for (Job p : downstream) for (AbstractProject p : downstream)
addDependency(upstream,p); addDependency(upstream,p);
} }
...@@ -261,15 +260,15 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -261,15 +260,15 @@ public class DependencyGraph implements Comparator<Job> {
* @deprecated since 1.341 * @deprecated since 1.341
*/ */
@Deprecated @Deprecated
public void addDependency(Collection<? extends Job> upstream, Job downstream) { public void addDependency(Collection<? extends AbstractProject> upstream, AbstractProject downstream) {
for (Job p : upstream) for (AbstractProject p : upstream)
addDependency(p,downstream); addDependency(p,downstream);
} }
/** /**
* Lists up {@link DependencyDeclarer} from the collection and let them builds dependencies. * Lists up {@link DependencyDeclarer} from the collection and let them builds dependencies.
*/ */
public void addDependencyDeclarers(Job upstream, Collection<?> possibleDependecyDeclarers) { public void addDependencyDeclarers(AbstractProject upstream, Collection<?> possibleDependecyDeclarers) {
for (Object o : possibleDependecyDeclarers) { for (Object o : possibleDependecyDeclarers) {
if (o instanceof DependencyDeclarer) { if (o instanceof DependencyDeclarer) {
DependencyDeclarer dd = (DependencyDeclarer) o; DependencyDeclarer dd = (DependencyDeclarer) o;
...@@ -284,15 +283,15 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -284,15 +283,15 @@ public class DependencyGraph implements Comparator<Job> {
* A non-direct dependency is a path of dependency "edge"s from the source to the destination, * A non-direct dependency is a path of dependency "edge"s from the source to the destination,
* where the length is greater than 1. * where the length is greater than 1.
*/ */
public boolean hasIndirectDependencies(Job src, Job dst) { public boolean hasIndirectDependencies(AbstractProject src, AbstractProject dst) {
Set<Job> visited = new HashSet<Job>(); Set<AbstractProject> visited = new HashSet<AbstractProject>();
Stack<Job> queue = new Stack<Job>(); Stack<AbstractProject> queue = new Stack<AbstractProject>();
queue.addAll(getDownstream(src)); queue.addAll(getDownstream(src));
queue.remove(dst); queue.remove(dst);
while(!queue.isEmpty()) { while(!queue.isEmpty()) {
Job p = queue.pop(); AbstractProject p = queue.pop();
if(p==dst) if(p==dst)
return true; return true;
if(visited.add(p)) if(visited.add(p))
...@@ -305,27 +304,27 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -305,27 +304,27 @@ public class DependencyGraph implements Comparator<Job> {
/** /**
* Gets all the direct and indirect upstream dependencies of the given project. * Gets all the direct and indirect upstream dependencies of the given project.
*/ */
public Set<Job> getTransitiveUpstream(Job src) { public Set<AbstractProject> getTransitiveUpstream(AbstractProject src) {
return getTransitive(backward,src,true); return getTransitive(backward,src,true);
} }
/** /**
* Gets all the direct and indirect downstream dependencies of the given project. * Gets all the direct and indirect downstream dependencies of the given project.
*/ */
public Set<Job> getTransitiveDownstream(Job src) { public Set<AbstractProject> getTransitiveDownstream(AbstractProject src) {
return getTransitive(forward,src,false); return getTransitive(forward,src,false);
} }
private Set<Job> getTransitive(Map<Job, List<DependencyGroup>> direction, Job src, boolean up) { private Set<AbstractProject> getTransitive(Map<AbstractProject, List<DependencyGroup>> direction, AbstractProject src, boolean up) {
Set<Job> visited = new HashSet<Job>(); Set<AbstractProject> visited = new HashSet<AbstractProject>();
Stack<Job> queue = new Stack<Job>(); Stack<AbstractProject> queue = new Stack<AbstractProject>();
queue.add(src); queue.add(src);
while(!queue.isEmpty()) { while(!queue.isEmpty()) {
Job p = queue.pop(); AbstractProject p = queue.pop();
for (Job child : get(direction,p,up)) { for (AbstractProject child : get(direction,p,up)) {
if(visited.add(child)) if(visited.add(child))
queue.add(child); queue.add(child);
} }
...@@ -334,7 +333,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -334,7 +333,7 @@ public class DependencyGraph implements Comparator<Job> {
return visited; return visited;
} }
private void add(Map<Job, List<DependencyGroup>> map, Job key, Dependency dep) { private void add(Map<AbstractProject, List<DependencyGroup>> map, AbstractProject key, Dependency dep) {
List<DependencyGroup> set = map.get(key); List<DependencyGroup> set = map.get(key);
if(set==null) { if(set==null) {
set = new ArrayList<DependencyGroup>(); set = new ArrayList<DependencyGroup>();
...@@ -352,8 +351,8 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -352,8 +351,8 @@ public class DependencyGraph implements Comparator<Job> {
set.add(new DependencyGroup(dep)); set.add(new DependencyGroup(dep));
} }
private Map<Job, List<DependencyGroup>> finalize(Map<Job, List<DependencyGroup>> m) { private Map<AbstractProject, List<DependencyGroup>> finalize(Map<AbstractProject, List<DependencyGroup>> m) {
for (Entry<Job, List<DependencyGroup>> e : m.entrySet()) { for (Entry<AbstractProject, List<DependencyGroup>> e : m.entrySet()) {
Collections.sort( e.getValue(), NAME_COMPARATOR ); Collections.sort( e.getValue(), NAME_COMPARATOR );
e.setValue( Collections.unmodifiableList(e.getValue()) ); e.setValue( Collections.unmodifiableList(e.getValue()) );
} }
...@@ -372,7 +371,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -372,7 +371,7 @@ public class DependencyGraph implements Comparator<Job> {
/** /**
* Compare two Projects based on the topological order defined by this Dependency Graph * Compare two Projects based on the topological order defined by this Dependency Graph
*/ */
public int compare(Job o1, Job o2) { public int compare(AbstractProject o1, AbstractProject o2) {
return topologicalOrder.compare(o1,o2); return topologicalOrder.compare(o1,o2);
} }
...@@ -384,7 +383,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -384,7 +383,7 @@ public class DependencyGraph implements Comparator<Job> {
* *
* @since 1.521 * @since 1.521
*/ */
public List<Job<?,?>> getTopologicallySorted() { public List<AbstractProject<?,?>> getTopologicallySorted() {
return topologicallySorted; return topologicallySorted;
} }
...@@ -393,18 +392,18 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -393,18 +392,18 @@ public class DependencyGraph implements Comparator<Job> {
* @since 1.341 * @since 1.341
*/ */
public static class Dependency { public static class Dependency {
private Job upstream, downstream; private AbstractProject upstream, downstream;
public Dependency(Job upstream, Job downstream) { public Dependency(AbstractProject upstream, AbstractProject downstream) {
this.upstream = upstream; this.upstream = upstream;
this.downstream = downstream; this.downstream = downstream;
} }
public Job getUpstreamProject() { public AbstractProject getUpstreamProject() {
return upstream; return upstream;
} }
public Job getDownstreamProject() { public AbstractProject getDownstreamProject() {
return downstream; return downstream;
} }
...@@ -421,7 +420,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -421,7 +420,7 @@ public class DependencyGraph implements Comparator<Job> {
* @param actions Add Actions for the triggered build to this list; never null * @param actions Add Actions for the triggered build to this list; never null
* @return True to trigger a build of the downstream project * @return True to trigger a build of the downstream project
*/ */
public boolean shouldTriggerBuild(Run build, TaskListener listener, public boolean shouldTriggerBuild(AbstractBuild build, TaskListener listener,
List<Action> actions) { List<Action> actions) {
return true; return true;
} }
...@@ -463,7 +462,7 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -463,7 +462,7 @@ public class DependencyGraph implements Comparator<Job> {
DependencyGroup(Dependency first) { DependencyGroup(Dependency first) {
this.upstream = first.getUpstreamProject(); this.upstream = first.getUpstreamProject();
this.downstream = first.getDownstreamProject(); this.downstream= first.getDownstreamProject();
group.add(first); group.add(first);
} }
...@@ -475,13 +474,13 @@ public class DependencyGraph implements Comparator<Job> { ...@@ -475,13 +474,13 @@ public class DependencyGraph implements Comparator<Job> {
return group; return group;
} }
private Job upstream, downstream; private AbstractProject upstream, downstream;
public Job getUpstreamProject() { public AbstractProject getUpstreamProject() {
return upstream; return upstream;
} }
public Job getDownstreamProject() { public AbstractProject getDownstreamProject() {
return downstream; return downstream;
} }
} }
......
...@@ -72,14 +72,11 @@ import java.util.ArrayList; ...@@ -72,14 +72,11 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
...@@ -182,13 +179,6 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R ...@@ -182,13 +179,6 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
// this should have been DescribableList but now it's too late // this should have been DescribableList but now it's too late
protected CopyOnWriteList<JobProperty<? super JobT>> properties = new CopyOnWriteList<JobProperty<? super JobT>>(); protected CopyOnWriteList<JobProperty<? super JobT>> properties = new CopyOnWriteList<JobProperty<? super JobT>>();
private static final Comparator<Integer> REVERSE_INTEGER_COMPARATOR = new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
};
@Restricted(NoExternalUse.class) @Restricted(NoExternalUse.class)
public transient RunIdMigrator runIdMigrator; public transient RunIdMigrator runIdMigrator;
...@@ -342,63 +332,6 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R ...@@ -342,63 +332,6 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
return keepDependencies; return keepDependencies;
} }
/**
* Returns the project if any of the downstream project is either
* building, waiting, pending or buildable.
* <p>
* This means eventually there will be an automatic triggering of
* the given project (provided that all builds went smoothly.)
*/
public Job getBuildingDownstream() {
Set<Queue.Task> unblockedTasks = Jenkins.getInstance().getQueue().getUnblockedTasks();
for (Job tup : getTransitiveDownstreamProjects()) {
if (tup!=this && (tup.isBuilding() || unblockedTasks.contains(tup)))
return tup;
}
return null;
}
/**
* Returns the project if any of the upstream project is either
* building or is in the queue.
* <p>
* This means eventually there will be an automatic triggering of
* the given project (provided that all builds went smoothly.)
*/
public Job getBuildingUpstream() {
Set<Queue.Task> unblockedTasks = Jenkins.getInstance().getQueue().getUnblockedTasks();
for (Job tup : getTransitiveUpstreamProjects()) {
if (tup!=this && (tup.isBuilding() || unblockedTasks.contains(tup)))
return tup;
}
return null;
}
/**
* Gets the nearest ancestor {@link TopLevelItem} that's also an {@link Job}.
*
* <p>
* Some projects (such as matrix projects, Maven projects, or promotion processes) form a tree of jobs
* that acts as a single unit. This method can be used to find the top most dominating job that
* covers such a tree.
*
* @return never null.
* @see Run#getRootBuild()
*/
public Job<?,?> getRootProject() {
if (this instanceof TopLevelItem) {
return this;
} else {
ItemGroup p = this.getParent();
if (p instanceof Job)
return ((Job) p).getRootProject();
return this;
}
}
/** /**
* Allocates a new buildCommand number. * Allocates a new buildCommand number.
*/ */
...@@ -566,86 +499,9 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R ...@@ -566,86 +499,9 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
return Collections.<Job> singleton(this); return Collections.<Job> singleton(this);
} }
/**
* Builds the dependency graph. No-op by default.
* @param graph
*/
protected void buildDependencyGraph(DependencyGraph graph) {
}
/**
* Gets the other {@link Job}s that should be built
* when a build of this project is completed.
*/
@Exported
public final List<Job> getDownstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getDownstream(this);
}
@Exported
public final List<Job> getUpstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getUpstream(this);
}
/**
* Gets all the upstream projects including transitive upstream projects.
*
* @since 1.138
*/
public final Set<Job> getTransitiveUpstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getTransitiveUpstream(this);
}
/**
* Gets all the downstream projects including transitive downstream projects.
*
* @since 1.138
*/
public final Set<Job> getTransitiveDownstreamProjects() {
return Jenkins.getInstance().getDependencyGraph().getTransitiveDownstream(this);
}
/**
* Gets the dependency relationship map between this project (as the source)
* and that project (as the sink.)
*
* @return
* can be empty but not null. build number of this project to the build
* numbers of that project.
*/
public SortedMap<Integer, RangeSet> getRelationship(AbstractProject that) {
TreeMap<Integer,RangeSet> r = new TreeMap<Integer,RangeSet>(REVERSE_INTEGER_COMPARATOR);
checkAndRecord(that, r, this.getBuilds());
// checkAndRecord(that, r, that.getBuilds());
return r;
}
/**
* Helper method for getDownstreamRelationship.
*
* For each given build, find the build number range of the given project and put that into the map.
*/
private void checkAndRecord(AbstractProject that, TreeMap<Integer, RangeSet> r, Collection<RunT> builds) {
for (RunT build : builds) {
RangeSet rs = build.getDownstreamRelationship(that);
if(rs==null || rs.isEmpty())
continue;
int n = build.getNumber();
RangeSet value = r.get(n);
if(value==null)
r.put(n,rs);
else
value.add(rs);
}
}
/** /**
* Adds {@link JobProperty}. * Adds {@link JobProperty}.
* *
* @since 1.188 * @since 1.188
*/ */
public void addProperty(JobProperty<? super JobT> jobProp) throws IOException { public void addProperty(JobProperty<? super JobT> jobProp) throws IOException {
......
...@@ -44,10 +44,6 @@ import hudson.console.PlainTextConsoleOutputStream; ...@@ -44,10 +44,6 @@ import hudson.console.PlainTextConsoleOutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import hudson.tasks.Fingerprinter;
import hudson.util.AdaptedIterator;
import hudson.util.Iterators;
import jenkins.util.SystemProperties; import jenkins.util.SystemProperties;
import hudson.Util; import hudson.Util;
import hudson.XmlFile; import hudson.XmlFile;
...@@ -84,14 +80,12 @@ import java.text.SimpleDateFormat; ...@@ -84,14 +80,12 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
...@@ -149,11 +143,6 @@ import org.kohsuke.stapler.interceptor.RequirePOST; ...@@ -149,11 +143,6 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,RunT>> public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,RunT>>
extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster { extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster {
/**
* Set if we want the blame information to flow from upstream to downstream build.
*/
private static final boolean upstreamCulprits = SystemProperties.getBoolean("hudson.upstreamCulprits");
/** /**
* The original {@link Queue.Item#getId()} has not yet been mapped onto the {@link Run} instance. * The original {@link Queue.Item#getId()} has not yet been mapped onto the {@link Run} instance.
* @since 1.601 * @since 1.601
...@@ -972,246 +961,6 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run ...@@ -972,246 +961,6 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
return nextBuild; return nextBuild;
} }
/**
* Gets the dependency relationship from this build (as the source)
* and that project (as the sink.)
*
* @return
* range of build numbers that represent which downstream builds are using this build.
* The range will be empty if no build of that project matches this (or there is no {@link Fingerprinter.FingerprintAction}), but it'll never be null.
*/
public Fingerprint.RangeSet getDownstreamRelationship(Job that) {
Fingerprint.RangeSet rs = new Fingerprint.RangeSet();
Fingerprinter.FingerprintAction f = getAction(Fingerprinter.FingerprintAction.class);
if (f==null) return rs;
// look for fingerprints that point to this build as the source, and merge them all
for (Fingerprint e : f.getFingerprints().values()) {
if (upstreamCulprits) {
// With upstreamCulprits, we allow downstream relationships
// from intermediate jobs
rs.add(e.getRangeSet(that));
} else {
Fingerprint.BuildPtr o = e.getOriginal();
if (o!=null && o.is(this))
rs.add(e.getRangeSet(that));
}
}
return rs;
}
/**
* Works like {@link #getDownstreamRelationship(Job)} but returns
* the actual build objects, in ascending order.
* @since 1.150
*/
public Iterable<Run<?,?>> getDownstreamBuilds(final Job<?,?> that) {
final Iterable<Integer> nums = getDownstreamRelationship(that).listNumbers();
return new Iterable<Run<?, ?>>() {
public Iterator<Run<?, ?>> iterator() {
return Iterators.removeNull(
new AdaptedIterator<Integer,Run<?,?>>(nums) {
protected Run<?, ?> adapt(Integer item) {
return that.getBuildByNumber(item);
}
});
}
};
}
/**
* Gets the dependency relationship from this build (as the sink)
* and that project (as the source.)
*
* @return
* Build number of the upstream build that feed into this build,
* or -1 if no record is available (for example if there is no {@link Fingerprinter.FingerprintAction}, even if there is an {@link Cause.UpstreamCause}).
*/
public int getUpstreamRelationship(Job that) {
Fingerprinter.FingerprintAction f = getAction(Fingerprinter.FingerprintAction.class);
if (f==null) return -1;
int n = -1;
// look for fingerprints that point to the given project as the source, and merge them all
for (Fingerprint e : f.getFingerprints().values()) {
if (upstreamCulprits) {
// With upstreamCulprits, we allow upstream relationships
// from intermediate jobs
Fingerprint.RangeSet rangeset = e.getRangeSet(that);
if (!rangeset.isEmpty()) {
n = Math.max(n, rangeset.listNumbersReverse().iterator().next());
}
} else {
Fingerprint.BuildPtr o = e.getOriginal();
if (o!=null && o.belongsTo(that))
n = Math.max(n,o.getNumber());
}
}
return n;
}
/**
* Works like {@link #getUpstreamRelationship(Job)} but returns the
* actual build object.
*
* @return
* null if no such upstream build was found, or it was found but the
* build record is already lost.
*/
public Run<?,?> getUpstreamRelationshipBuild(Job<?,?> that) {
int n = getUpstreamRelationship(that);
if (n==-1) return null;
return that.getBuildByNumber(n);
}
/**
* Gets the downstream builds of this build, which are the builds of the
* downstream projects that use artifacts of this build.
*
* @return
* For each project with fingerprinting enabled, returns the range
* of builds (which can be empty if no build uses the artifact from this build.
*/
public Map<Job,Fingerprint.RangeSet> getDownstreamBuilds() {
Map<Job,Fingerprint.RangeSet> r = new HashMap<Job,Fingerprint.RangeSet>();
for (Job p : getParent().getDownstreamProjects()) {
r.put(p,getDownstreamRelationship(p));
}
return r;
}
/**
* Gets the upstream builds of this build, which are the builds of the
* upstream projects whose artifacts feed into this build.
* @return empty if there is no {@link Fingerprinter.FingerprintAction} (even if there is an {@link Cause.UpstreamCause})
* @see #getTransitiveUpstreamBuilds()
*/
public Map<Job,Integer> getUpstreamBuilds() {
return _getUpstreamBuilds(getParent().getUpstreamProjects());
}
/**
* Works like {@link #getUpstreamBuilds()} but also includes all the transitive
* dependencies as well.
*/
public Map<Job,Integer> getTransitiveUpstreamBuilds() {
return _getUpstreamBuilds(getParent().getTransitiveUpstreamProjects());
}
private Map<Job, Integer> _getUpstreamBuilds(Collection<Job> projects) {
Map<Job,Integer> r = new HashMap<Job,Integer>();
for (Job p : projects) {
int n = getUpstreamRelationship(p);
if (n>=0)
r.put(p,n);
}
return r;
}
/**
* Gets the changes in the dependency between the given build and this build.
* @return empty if there is no {@link Fingerprinter.FingerprintAction}
*/
public Map<Job,Run.DependencyChange> getDependencyChanges(Run from) {
if (from==null) return Collections.emptyMap(); // make it easy to call this from views
Fingerprinter.FingerprintAction n = this.getAction(Fingerprinter.FingerprintAction.class);
Fingerprinter.FingerprintAction o = from.getAction(Fingerprinter.FingerprintAction.class);
if (n==null || o==null) return Collections.emptyMap();
Map<Job,Integer> ndep = n.getDependencies(true);
Map<Job,Integer> odep = o.getDependencies(true);
Map<Job,Run.DependencyChange> r = new HashMap<Job,Run.DependencyChange>();
for (Map.Entry<Job,Integer> entry : odep.entrySet()) {
Job p = entry.getKey();
Integer oldNumber = entry.getValue();
Integer newNumber = ndep.get(p);
if (newNumber!=null && oldNumber.compareTo(newNumber)<0) {
r.put(p,new Run.DependencyChange(p,oldNumber,newNumber));
}
}
return r;
}
/**
* Gets the nearest ancestor {@link Run} that belongs to
* {@linkplain Job#getRootProject() the root project of getParent()} that
* dominates/governs/encompasses this build.
*
* <p>
* Some projects (such as matrix projects, Maven projects, or promotion processes) form a tree of jobs,
* and still in some of them, builds of child projects are related/tied to that of the parent project.
* In such a case, this method returns the governing build.
*
* @return never null. In the worst case the build dominates itself.
* @since 1.421
* @see Job#getRootProject()
*/
public Run<?,?> getRootBuild() {
return this;
}
/**
* Represents a change in the dependency.
*/
public static class DependencyChange {
/**
* The dependency project.
*/
public final Job project;
/**
* Version of the dependency project used in the previous build.
*/
public final int fromId;
/**
* {@link Run} object for {@link #fromId}. Can be null if the log is gone.
*/
public final Run from;
/**
* Version of the dependency project used in this build.
*/
public final int toId;
public final Run to;
public DependencyChange(Job<?,?> project, int fromId, int toId) {
this.project = project;
this.fromId = fromId;
this.toId = toId;
this.from = project.getBuildByNumber(fromId);
this.to = project.getBuildByNumber(toId);
}
/**
* Gets the {@link Run} objects (fromId,toId].
* <p>
* This method returns all such available builds in the ascending order
* of IDs, but due to log rotations, some builds may be already unavailable.
*/
public List<Run> getBuilds() {
List<Run> r = new ArrayList<Run>();
Run<?,?> b = project.getNearestBuild(fromId);
if (b!=null && b.getNumber()==fromId)
b = b.getNextBuild(); // fromId exclusive
while (b!=null && b.getNumber()<=toId) {
r.add(b);
b = b.getNextBuild();
}
return r;
}
}
/** /**
* Returns the URL of this {@link Run}, relative to the context root of Hudson. * Returns the URL of this {@link Run}, relative to the context root of Hudson.
* *
......
...@@ -245,24 +245,21 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer { ...@@ -245,24 +245,21 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer {
SecurityContext orig = ACL.impersonate(auth); SecurityContext orig = ACL.impersonate(auth);
try { try {
if (dep.shouldTriggerBuild(build, listener, buildActions)) { if (dep.shouldTriggerBuild(build, listener, buildActions)) {
Job j = dep.getDownstreamProject(); AbstractProject p = dep.getDownstreamProject();
if (j instanceof AbstractProject) { // Allow shouldTriggerBuild to return false first, in case it is skipping because of a lack of Item.READ/DISCOVER permission:
AbstractProject p = (AbstractProject) j; if (p.isDisabled()) {
// Allow shouldTriggerBuild to return false first, in case it is skipping because of a lack of Item.READ/DISCOVER permission: logger.println(Messages.BuildTrigger_Disabled(ModelHyperlinkNote.encodeTo(p)));
if (p.isDisabled()) { continue;
logger.println(Messages.BuildTrigger_Disabled(ModelHyperlinkNote.encodeTo(p)));
continue;
}
boolean scheduled = p.scheduleBuild(p.getQuietPeriod(), new UpstreamCause((Run) build), buildActions.toArray(new Action[buildActions.size()]));
if (Jenkins.getInstance().getItemByFullName(p.getFullName()) == p) {
String name = ModelHyperlinkNote.encodeTo(p);
if (scheduled) {
logger.println(Messages.BuildTrigger_Triggering(name));
} else {
logger.println(Messages.BuildTrigger_InQueue(name));
}
} // otherwise upstream users should not know that it happened
} }
boolean scheduled = p.scheduleBuild(p.getQuietPeriod(), new UpstreamCause((Run)build), buildActions.toArray(new Action[buildActions.size()]));
if (Jenkins.getInstance().getItemByFullName(p.getFullName()) == p) {
String name = ModelHyperlinkNote.encodeTo(p);
if (scheduled) {
logger.println(Messages.BuildTrigger_Triggering(name));
} else {
logger.println(Messages.BuildTrigger_InQueue(name));
}
} // otherwise upstream users should not know that it happened
} }
} finally { } finally {
SecurityContextHolder.setContext(orig); SecurityContextHolder.setContext(orig);
...@@ -272,28 +269,24 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer { ...@@ -272,28 +269,24 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer {
return true; return true;
} }
public void buildDependencyGraph(Job j, DependencyGraph graph) { public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
if (j instanceof AbstractProject) { for (AbstractProject p : getChildProjects(owner))
AbstractProject owner = (AbstractProject) j; graph.addDependency(new Dependency(owner, p) {
@Override
for (AbstractProject p : getChildProjects(owner)) public boolean shouldTriggerBuild(AbstractBuild build, TaskListener listener,
graph.addDependency(new Dependency(owner, p) { List<Action> actions) {
@Override AbstractProject downstream = getDownstreamProject();
public boolean shouldTriggerBuild(Run build, TaskListener listener, if (Jenkins.getInstance().getItemByFullName(downstream.getFullName()) != downstream) { // this checks Item.READ also on parent folders
List<Action> actions) { LOGGER.log(Level.WARNING, "Running as {0} cannot even see {1} for trigger from {2}", new Object[] {Jenkins.getAuthentication().getName(), downstream, getUpstreamProject()});
Job downstream = getDownstreamProject(); return false; // do not even issue a warning to build log
if (Jenkins.getInstance().getItemByFullName(downstream.getFullName()) != downstream) { // this checks Item.READ also on parent folders
LOGGER.log(Level.WARNING, "Running as {0} cannot even see {1} for trigger from {2}", new Object[]{Jenkins.getAuthentication().getName(), downstream, getUpstreamProject()});
return false; // do not even issue a warning to build log
}
if (!downstream.hasPermission(Item.BUILD)) {
listener.getLogger().println(Messages.BuildTrigger_you_have_no_permission_to_build_(ModelHyperlinkNote.encodeTo(downstream)));
return false;
}
return build.getResult().isBetterOrEqualTo(threshold);
} }
}); if (!downstream.hasPermission(Item.BUILD)) {
} listener.getLogger().println(Messages.BuildTrigger_you_have_no_permission_to_build_(ModelHyperlinkNote.encodeTo(downstream)));
return false;
}
return build.getResult().isBetterOrEqualTo(threshold);
}
});
} }
@Override @Override
......
...@@ -149,7 +149,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD ...@@ -149,7 +149,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
return BuildStepMonitor.NONE; return BuildStepMonitor.NONE;
} }
public void buildDependencyGraph(Job owner, DependencyGraph graph) { public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
if (enableFingerprintsInDependencyGraph) { if (enableFingerprintsInDependencyGraph) {
RunList builds = owner.getBuilds(); RunList builds = owner.getBuilds();
Set<String> seenUpstreamProjects = new HashSet<String>(); Set<String> seenUpstreamProjects = new HashSet<String>();
...@@ -157,12 +157,12 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD ...@@ -157,12 +157,12 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
for ( ListIterator iter = builds.listIterator(); iter.hasNext(); ) { for ( ListIterator iter = builds.listIterator(); iter.hasNext(); ) {
Run build = (Run) iter.next(); Run build = (Run) iter.next();
for (FingerprintAction action : build.getActions(FingerprintAction.class)) { for (FingerprintAction action : build.getActions(FingerprintAction.class)) {
for (Job key : action.getDependencies().keySet()) { for (AbstractProject key : action.getDependencies().keySet()) {
if (key == owner) { if (key == owner) {
continue; // Avoid self references continue; // Avoid self references
} }
Job p = key; AbstractProject p = key;
// TODO is this harmful to call unconditionally, so it would apply also to MavenModule for example? // TODO is this harmful to call unconditionally, so it would apply also to MavenModule for example?
if (key.getClass().getName().equals("hudson.matrix.MatrixConfiguration")) { if (key.getClass().getName().equals("hudson.matrix.MatrixConfiguration")) {
p = key.getRootProject(); p = key.getRootProject();
...@@ -175,7 +175,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD ...@@ -175,7 +175,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
seenUpstreamProjects.add(p.getName()); seenUpstreamProjects.add(p.getName());
graph.addDependency(new Dependency(p, owner) { graph.addDependency(new Dependency(p, owner) {
@Override @Override
public boolean shouldTriggerBuild(Run build, public boolean shouldTriggerBuild(AbstractBuild build,
TaskListener listener, TaskListener listener,
List<Action> actions) { List<Action> actions) {
// Fingerprints should not trigger builds. // Fingerprints should not trigger builds.
...@@ -389,7 +389,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD ...@@ -389,7 +389,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
/** /**
* Gets the dependency to other existing builds in a map. * Gets the dependency to other existing builds in a map.
*/ */
public Map<Job,Integer> getDependencies() { public Map<AbstractProject,Integer> getDependencies() {
return getDependencies(false); return getDependencies(false);
} }
...@@ -400,8 +400,8 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD ...@@ -400,8 +400,8 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
* the result, even if it doesn't exist * the result, even if it doesn't exist
* @since 1.430 * @since 1.430
*/ */
public Map<Job,Integer> getDependencies(boolean includeMissing) { public Map<AbstractProject,Integer> getDependencies(boolean includeMissing) {
Map<Job,Integer> r = new HashMap<Job,Integer>(); Map<AbstractProject,Integer> r = new HashMap<AbstractProject,Integer>();
for (Fingerprint fp : getFingerprints().values()) { for (Fingerprint fp : getFingerprints().values()) {
BuildPtr bp = fp.getOriginal(); BuildPtr bp = fp.getOriginal();
...@@ -411,6 +411,11 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD ...@@ -411,6 +411,11 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
try { try {
Job job = bp.getJob(); Job job = bp.getJob();
if (job==null) continue; // project no longer exists if (job==null) continue; // project no longer exists
if (!(job instanceof AbstractProject)) {
// Ignoring this for now. In the future we may want a dependency map function not limited to AbstractProject.
// (Could be used by getDependencyChanges if pulled up from AbstractBuild into Run, for example.)
continue;
}
if (job.getParent()==build.getParent()) if (job.getParent()==build.getParent())
continue; // we are the parent of the build owner, that is almost like we are the owner continue; // we are the parent of the build owner, that is almost like we are the owner
if(!includeMissing && job.getBuildByNumber(bp.getNumber())==null) if(!includeMissing && job.getBuildByNumber(bp.getNumber())==null)
...@@ -419,7 +424,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD ...@@ -419,7 +424,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
Integer existing = r.get(job); Integer existing = r.get(job);
if(existing!=null && existing>bp.getNumber()) if(existing!=null && existing>bp.getNumber())
continue; // the record in the map is already up to date continue; // the record in the map is already up to date
r.put(job, bp.getNumber()); r.put((AbstractProject) job, bp.getNumber());
} catch (AccessDeniedException e) { } catch (AccessDeniedException e) {
// Need to log in to access this job, so ignore // Need to log in to access this job, so ignore
continue; continue;
......
...@@ -25,7 +25,6 @@ package jenkins.model; ...@@ -25,7 +25,6 @@ package jenkins.model;
import hudson.model.AbstractProject; import hudson.model.AbstractProject;
import hudson.model.DependencyGraph; import hudson.model.DependencyGraph;
import hudson.model.Job;
import hudson.tasks.BuildWrapper; import hudson.tasks.BuildWrapper;
import hudson.tasks.Builder; import hudson.tasks.Builder;
import hudson.tasks.Publisher; import hudson.tasks.Publisher;
...@@ -51,7 +50,7 @@ public interface DependencyDeclarer { ...@@ -51,7 +50,7 @@ public interface DependencyDeclarer {
// so that this concept can be extended elsewhere, like maven projects and so on. // so that this concept can be extended elsewhere, like maven projects and so on.
/** /**
* Invoked from {@link Job#buildDependencyGraph(DependencyGraph)}. * Invoked from {@link AbstractProject#buildDependencyGraph(DependencyGraph)}.
* *
* @param owner * @param owner
* The project that owns the publishers, builders, etc. * The project that owns the publishers, builders, etc.
...@@ -62,5 +61,5 @@ public interface DependencyDeclarer { ...@@ -62,5 +61,5 @@ public interface DependencyDeclarer {
* @param graph * @param graph
* The dependency graph being built. Never null. * The dependency graph being built. Never null.
*/ */
void buildDependencyGraph(Job owner, DependencyGraph graph); void buildDependencyGraph(AbstractProject owner, DependencyGraph graph);
} }
...@@ -95,17 +95,12 @@ public abstract class RunWithSCMMixIn<JobT extends Job<JobT, RunT> & Queue.Task, ...@@ -95,17 +95,12 @@ public abstract class RunWithSCMMixIn<JobT extends Job<JobT, RunT> & Queue.Task,
if (p instanceof AbstractBuild && upstreamCulprits) { if (p instanceof AbstractBuild && upstreamCulprits) {
// If we have dependencies since the last successful build, add their authors to our list // If we have dependencies since the last successful build, add their authors to our list
if (p.getPreviousNotFailedBuild() != null) { if (p.getPreviousNotFailedBuild() != null) {
Map<Job, Run.DependencyChange> depmap = Map<AbstractProject, AbstractBuild.DependencyChange> depmap =
p.getDependencyChanges(p.getPreviousSuccessfulBuild()); ((AbstractBuild<?,?>) p).getDependencyChanges((AbstractBuild<?,?>)p.getPreviousSuccessfulBuild());
for (Run.DependencyChange dep : depmap.values()) { for (AbstractBuild.DependencyChange dep : depmap.values()) {
for (Run<?, ?> rawRun : dep.getBuilds()) { for (AbstractBuild<?, ?> b : dep.getBuilds()) {
if (rawRun instanceof RunWithSCM) { for (ChangeLogSet.Entry entry : b.getChangeSet()) {
RunWithSCM<?, ?> b = (RunWithSCM<?,?>) rawRun; r.add(entry.getAuthor());
for (ChangeLogSet<? extends ChangeLogSet.Entry> c : b.getChangeSets()) {
for (ChangeLogSet.Entry e : c) {
r.add(e.getAuthor());
}
}
} }
} }
} }
......
...@@ -139,10 +139,10 @@ public final class ReverseBuildTrigger extends Trigger<Job> implements Dependenc ...@@ -139,10 +139,10 @@ public final class ReverseBuildTrigger extends Trigger<Job> implements Dependenc
return result != null && result.isBetterOrEqualTo(threshold); return result != null && result.isBetterOrEqualTo(threshold);
} }
@Override public void buildDependencyGraph(final Job downstream, DependencyGraph graph) { @Override public void buildDependencyGraph(final AbstractProject downstream, DependencyGraph graph) {
for (Job upstream : Items.fromNameList(downstream.getParent(), upstreamProjects, Job.class)) { for (AbstractProject upstream : Items.fromNameList(downstream.getParent(), upstreamProjects, AbstractProject.class)) {
graph.addDependency(new DependencyGraph.Dependency(upstream, downstream) { graph.addDependency(new DependencyGraph.Dependency(upstream, downstream) {
@Override public boolean shouldTriggerBuild(Run upstreamBuild, TaskListener listener, List<Action> actions) { @Override public boolean shouldTriggerBuild(AbstractBuild upstreamBuild, TaskListener listener, List<Action> actions) {
return shouldTrigger(upstreamBuild, listener); return shouldTrigger(upstreamBuild, listener);
} }
}); });
......
...@@ -93,10 +93,10 @@ public class DependencyGraphTest extends HudsonTestCase { ...@@ -93,10 +93,10 @@ public class DependencyGraphTest extends HudsonTestCase {
super(buildResult); super(buildResult);
this.down = down; this.down = down;
} }
public void buildDependencyGraph(Job owner, DependencyGraph graph) { public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
graph.addDependency(new DependencyGraph.Dependency(owner, down) { graph.addDependency(new DependencyGraph.Dependency(owner, down) {
@Override @Override
public boolean shouldTriggerBuild(Run build, TaskListener listener, public boolean shouldTriggerBuild(AbstractBuild build, TaskListener listener,
List<Action> actions) { List<Action> actions) {
// Trigger for ODD build number // Trigger for ODD build number
if (build.getNumber() % 2 == 1) { if (build.getNumber() % 2 == 1) {
...@@ -122,7 +122,7 @@ public class DependencyGraphTest extends HudsonTestCase { ...@@ -122,7 +122,7 @@ public class DependencyGraphTest extends HudsonTestCase {
// @LocalData for this test has jobs w/o anonymous Item.READ // @LocalData for this test has jobs w/o anonymous Item.READ
AbstractProject up = (AbstractProject) jenkins.getItem("hiddenUpstream"); AbstractProject up = (AbstractProject) jenkins.getItem("hiddenUpstream");
assertNotNull("hiddenUpstream project not found", up); assertNotNull("hiddenUpstream project not found", up);
List<Job> down = jenkins.getDependencyGraph().getDownstream(up); List<AbstractProject> down = jenkins.getDependencyGraph().getDownstream(up);
assertEquals("Should have one downstream project", 1, down.size()); assertEquals("Should have one downstream project", 1, down.size());
} finally { } finally {
SecurityContextHolder.clearContext(); SecurityContextHolder.clearContext();
...@@ -150,9 +150,9 @@ public class DependencyGraphTest extends HudsonTestCase { ...@@ -150,9 +150,9 @@ public class DependencyGraphTest extends HudsonTestCase {
jenkins.rebuildDependencyGraph(); jenkins.rebuildDependencyGraph();
DependencyGraph g = jenkins.getDependencyGraph(); DependencyGraph g = jenkins.getDependencyGraph();
List<Job<?, ?>> sorted = g.getTopologicallySorted(); List<AbstractProject<?, ?>> sorted = g.getTopologicallySorted();
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
for (Job<?, ?> p : sorted) { for (AbstractProject<?, ?> p : sorted) {
buf.append(p.getName()); buf.append(p.getName());
} }
String r = buf.toString(); String r = buf.toString();
......
...@@ -38,7 +38,6 @@ import hudson.model.FreeStyleProject; ...@@ -38,7 +38,6 @@ import hudson.model.FreeStyleProject;
import hudson.model.DependencyGraph; import hudson.model.DependencyGraph;
import hudson.model.DependencyGraph.Dependency; import hudson.model.DependencyGraph.Dependency;
import hudson.model.Item; import hudson.model.Item;
import hudson.model.Job;
import hudson.model.Result; import hudson.model.Result;
import hudson.model.Run; import hudson.model.Run;
import hudson.model.TaskListener; import hudson.model.TaskListener;
...@@ -368,7 +367,7 @@ public class BuildTriggerTest { ...@@ -368,7 +367,7 @@ public class BuildTriggerTest {
} }
@Override @Override
public boolean shouldTriggerBuild(Run build, TaskListener listener, List<Action> actions) { public boolean shouldTriggerBuild(AbstractBuild build, TaskListener listener, List<Action> actions) {
if (block) { if (block) {
try { try {
Thread.sleep(5000); Thread.sleep(5000);
...@@ -387,12 +386,9 @@ public class BuildTriggerTest { ...@@ -387,12 +386,9 @@ public class BuildTriggerTest {
} }
@Override @SuppressWarnings("rawtypes") @Override @SuppressWarnings("rawtypes")
public void buildDependencyGraph(Job j, DependencyGraph graph) { public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
if (j instanceof AbstractProject) { for (AbstractProject ch: getChildProjects(owner)) {
AbstractProject owner = (AbstractProject) j; graph.addDependency(new Dep(owner, ch));
for (AbstractProject ch : getChildProjects(owner)) {
graph.addDependency(new Dep(owner, ch));
}
} }
} }
} }
......
...@@ -102,8 +102,8 @@ public class FingerprinterTest { ...@@ -102,8 +102,8 @@ public class FingerprinterTest {
j.jenkins.rebuildDependencyGraph(); j.jenkins.rebuildDependencyGraph();
List<Job> downstreamProjects = upstream.getDownstreamProjects(); List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
List<Job> upstreamProjects = downstream.getUpstreamProjects(); List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
assertEquals(1, downstreamProjects.size()); assertEquals(1, downstreamProjects.size());
assertEquals(1, upstreamProjects.size()); assertEquals(1, upstreamProjects.size());
...@@ -142,9 +142,9 @@ public class FingerprinterTest { ...@@ -142,9 +142,9 @@ public class FingerprinterTest {
j.jenkins.rebuildDependencyGraph(); j.jenkins.rebuildDependencyGraph();
List<Job> downstreamProjects = upstream.getDownstreamProjects(); List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
List<Job> downstreamProjects2 = upstream2.getDownstreamProjects(); List<AbstractProject> downstreamProjects2 = upstream2.getDownstreamProjects();
List<Job> upstreamProjects = downstream.getUpstreamProjects(); List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
assertEquals(1, downstreamProjects.size()); assertEquals(1, downstreamProjects.size());
assertEquals(1, downstreamProjects2.size()); assertEquals(1, downstreamProjects2.size());
...@@ -165,9 +165,9 @@ public class FingerprinterTest { ...@@ -165,9 +165,9 @@ public class FingerprinterTest {
j.jenkins.rebuildDependencyGraph(); j.jenkins.rebuildDependencyGraph();
List<Job> downstreamProjects = upstream.getDownstreamProjects(); List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
List<Job> upstreamProjects = downstream.getUpstreamProjects(); List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
List<Job> upstreamProjects2 = downstream2.getUpstreamProjects(); List<AbstractProject> upstreamProjects2 = downstream2.getUpstreamProjects();
assertEquals(2, downstreamProjects.size()); assertEquals(2, downstreamProjects.size());
assertEquals(1, upstreamProjects.size()); assertEquals(1, upstreamProjects.size());
...@@ -189,8 +189,8 @@ public class FingerprinterTest { ...@@ -189,8 +189,8 @@ public class FingerprinterTest {
Jenkins.getInstance().rebuildDependencyGraph(); Jenkins.getInstance().rebuildDependencyGraph();
List<Job> upstreamProjects = downstream.getUpstreamProjects(); List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
List<Job> downstreamProjects = upstream.getDownstreamProjects(); List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
assertEquals(0, upstreamProjects.size()); assertEquals(0, upstreamProjects.size());
assertEquals(0, downstreamProjects.size()); assertEquals(0, downstreamProjects.size());
...@@ -204,8 +204,8 @@ public class FingerprinterTest { ...@@ -204,8 +204,8 @@ public class FingerprinterTest {
Jenkins.getInstance().rebuildDependencyGraph(); Jenkins.getInstance().rebuildDependencyGraph();
List<Job> upstreamProjects = p.getUpstreamProjects(); List<AbstractProject> upstreamProjects = p.getUpstreamProjects();
List<Job> downstreamProjects = p.getDownstreamProjects(); List<AbstractProject> downstreamProjects = p.getDownstreamProjects();
assertEquals(0, upstreamProjects.size()); assertEquals(0, upstreamProjects.size());
assertEquals(0, downstreamProjects.size()); assertEquals(0, downstreamProjects.size());
...@@ -229,9 +229,9 @@ public class FingerprinterTest { ...@@ -229,9 +229,9 @@ public class FingerprinterTest {
assertEquals("There should only be one FreestyleBuild", 1, builds.size()); assertEquals("There should only be one FreestyleBuild", 1, builds.size());
FreeStyleBuild build = builds.iterator().next(); FreeStyleBuild build = builds.iterator().next();
assertEquals(Result.SUCCESS, build.getResult()); assertEquals(Result.SUCCESS, build.getResult());
List<Job> downstream = j.jenkins.getDependencyGraph().getDownstream(matrixProject); List<AbstractProject> downstream = j.jenkins.getDependencyGraph().getDownstream(matrixProject);
assertTrue(downstream.contains(freestyleProject)); assertTrue(downstream.contains(freestyleProject));
List<Job> upstream = j.jenkins.getDependencyGraph().getUpstream(freestyleProject); List<AbstractProject> upstream = j.jenkins.getDependencyGraph().getUpstream(freestyleProject);
assertTrue(upstream.contains(matrixProject)); assertTrue(upstream.contains(matrixProject));
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册