waterfall: fix DST calculation. Closes #137.
[buildbot.git] / buildbot / sourcestamp.py
blob69d3c308c679a77c942891bb07f13e7455a33931
2 from zope.interface import implements
3 from buildbot import util, interfaces
5 class SourceStamp(util.ComparableMixin):
6 """This is a tuple of (branch, revision, patchspec, changes).
8 C{branch} is always valid, although it may be None to let the Source
9 step use its default branch. There are three possibilities for the
10 remaining elements:
11 - (revision=REV, patchspec=None, changes=None): build REV. If REV is
12 None, build the HEAD revision from the given branch.
13 - (revision=REV, patchspec=(LEVEL, DIFF), changes=None): checkout REV,
14 then apply a patch to the source, with C{patch -pPATCHLEVEL <DIFF}.
15 If REV is None, checkout HEAD and patch it.
16 - (revision=None, patchspec=None, changes=[CHANGES]): let the Source
17 step check out the latest revision indicated by the given Changes.
18 CHANGES is a tuple of L{buildbot.changes.changes.Change} instances,
19 and all must be on the same branch.
20 """
22 # all four of these are publically visible attributes
23 branch = None
24 revision = None
25 patch = None
26 changes = ()
28 compare_attrs = ('branch', 'revision', 'patch', 'changes')
30 implements(interfaces.ISourceStamp)
32 def __init__(self, branch=None, revision=None, patch=None,
33 changes=None):
34 self.branch = branch
35 self.revision = revision
36 self.patch = patch
37 if changes:
38 self.changes = tuple(changes)
39 self.branch = changes[0].branch
41 def canBeMergedWith(self, other):
42 if other.branch != self.branch:
43 return False # the builds are completely unrelated
45 if self.changes and other.changes:
46 # TODO: consider not merging these. It's a tradeoff between
47 # minimizing the number of builds and obtaining finer-grained
48 # results.
49 return True
50 elif self.changes and not other.changes:
51 return False # we're using changes, they aren't
52 elif not self.changes and other.changes:
53 return False # they're using changes, we aren't
55 if self.patch or other.patch:
56 return False # you can't merge patched builds with anything
57 if self.revision == other.revision:
58 # both builds are using the same specific revision, so they can
59 # be merged. It might be the case that revision==None, so they're
60 # both building HEAD.
61 return True
63 return False
65 def mergeWith(self, others):
66 """Generate a SourceStamp for the merger of me and all the other
67 BuildRequests. This is called by a Build when it starts, to figure
68 out what its sourceStamp should be."""
70 # either we're all building the same thing (changes==None), or we're
71 # all building changes (which can be merged)
72 changes = []
73 changes.extend(self.changes)
74 for req in others:
75 assert self.canBeMergedWith(req) # should have been checked already
76 changes.extend(req.changes)
77 newsource = SourceStamp(branch=self.branch,
78 revision=self.revision,
79 patch=self.patch,
80 changes=changes)
81 return newsource
83 def getText(self):
84 # TODO: this won't work for VC's with huge 'revision' strings
85 if self.revision is None:
86 return [ "latest" ]
87 text = [ str(self.revision) ]
88 if self.branch:
89 text.append("in '%s'" % self.branch)
90 if self.patch:
91 text.append("[patch]")
92 return text