Merge branch 'master' of git://github.com/bbangert/buildbot
[buildbot.git] / buildbot / sourcestamp.py
blob28ef4bf9c70660bee5c842b52217699f695d3c2f
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 # set branch and revision to most recent change
40 self.branch = changes[-1].branch
41 self.revision = changes[-1].revision
43 def canBeMergedWith(self, other):
44 if other.branch != self.branch:
45 return False # the builds are completely unrelated
47 if self.changes and other.changes:
48 # TODO: consider not merging these. It's a tradeoff between
49 # minimizing the number of builds and obtaining finer-grained
50 # results.
51 return True
52 elif self.changes and not other.changes:
53 return False # we're using changes, they aren't
54 elif not self.changes and other.changes:
55 return False # they're using changes, we aren't
57 if self.patch or other.patch:
58 return False # you can't merge patched builds with anything
59 if self.revision == other.revision:
60 # both builds are using the same specific revision, so they can
61 # be merged. It might be the case that revision==None, so they're
62 # both building HEAD.
63 return True
65 return False
67 def mergeWith(self, others):
68 """Generate a SourceStamp for the merger of me and all the other
69 BuildRequests. This is called by a Build when it starts, to figure
70 out what its sourceStamp should be."""
72 # either we're all building the same thing (changes==None), or we're
73 # all building changes (which can be merged)
74 changes = []
75 changes.extend(self.changes)
76 for req in others:
77 assert self.canBeMergedWith(req) # should have been checked already
78 changes.extend(req.changes)
79 newsource = SourceStamp(branch=self.branch,
80 revision=self.revision,
81 patch=self.patch,
82 changes=changes)
83 return newsource
85 def getAbsoluteSourceStamp(self, got_revision):
86 return SourceStamp(branch=self.branch, revision=got_revision, patch=self.patch)
88 def getText(self):
89 # TODO: this won't work for VC's with huge 'revision' strings
90 if self.revision is None:
91 return [ "latest" ]
92 text = [ str(self.revision) ]
93 if self.branch:
94 text.append("in '%s'" % self.branch)
95 if self.patch:
96 text.append("[patch]")
97 return text