1 # This file is part of Buildbot. Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
14 # Copyright Buildbot Team Members
17 from zope
.interface
import implements
18 from twisted
.persisted
import styles
19 from buildbot
import util
, interfaces
21 class SourceStamp(util
.ComparableMixin
, styles
.Versioned
):
22 """This is a tuple of (branch, revision, patchspec, changes, project, repository).
24 C{branch} is always valid, although it may be None to let the Source
25 step use its default branch. There are three possibilities for the
27 - (revision=REV, patchspec=None, changes=None): build REV. If REV is
28 None, build the HEAD revision from the given branch. Note that REV
29 must always be a string: SVN, Perforce, and other systems which use
30 integers should provide a string here, but the Source checkout step
31 will integerize it when making comparisons.
32 - (revision=REV, patchspec=(LEVEL, DIFF), changes=None): checkout REV,
33 then apply a patch to the source, with C{patch -pPATCHLEVEL <DIFF}.
34 If REV is None, checkout HEAD and patch it.
35 - (revision=None, patchspec=None, changes=[CHANGES]): let the Source
36 step check out the latest revision indicated by the given Changes.
37 CHANGES is a tuple of L{buildbot.changes.changes.Change} instances,
38 and all must be on the same branch.
41 persistenceVersion
= 2
42 persistenceForgets
= ( 'wasUpgraded', )
44 # all six of these are publically visible attributes
51 ssid
= None # filled in by db.get_sourcestampid()
53 compare_attrs
= ('branch', 'revision', 'patch', 'changes', 'project', 'repository')
55 implements(interfaces
.ISourceStamp
)
57 def __init__(self
, branch
=None, revision
=None, patch
=None,
58 changes
=None, project
='', repository
=''):
60 assert len(patch
) == 2
61 assert int(patch
[0]) != -1
64 self
.project
= project
65 self
.repository
= repository
67 self
.changes
= tuple(changes
)
68 # set branch and revision to most recent change
69 self
.branch
= changes
[-1].branch
70 revision
= changes
[-1].revision
71 if not self
.project
and hasattr(changes
[-1], 'project'):
72 self
.project
= changes
[-1].project
73 if not self
.repository
and hasattr(changes
[-1], 'repository'):
74 self
.repository
= changes
[-1].repository
76 if revision
is not None:
77 if isinstance(revision
, int):
78 revision
= str(revision
)
80 self
.revision
= revision
82 def canBeMergedWith(self
, other
):
83 if other
.repository
!= self
.repository
:
85 if other
.branch
!= self
.branch
:
86 return False # the builds are completely unrelated
87 if other
.project
!= self
.project
:
90 if self
.changes
and other
.changes
:
91 # TODO: consider not merging these. It's a tradeoff between
92 # minimizing the number of builds and obtaining finer-grained
95 elif self
.changes
and not other
.changes
:
96 return False # we're using changes, they aren't
97 elif not self
.changes
and other
.changes
:
98 return False # they're using changes, we aren't
100 if self
.patch
or other
.patch
:
101 return False # you can't merge patched builds with anything
102 if self
.revision
== other
.revision
:
103 # both builds are using the same specific revision, so they can
104 # be merged. It might be the case that revision==None, so they're
105 # both building HEAD.
110 def mergeWith(self
, others
):
111 """Generate a SourceStamp for the merger of me and all the other
112 SourceStamps. This is called by a Build when it starts, to figure
113 out what its sourceStamp should be."""
115 # either we're all building the same thing (changes==None), or we're
116 # all building changes (which can be merged)
118 changes
.extend(self
.changes
)
120 changes
.extend(ss
.changes
)
121 newsource
= SourceStamp(branch
=self
.branch
,
122 revision
=self
.revision
,
124 project
=self
.project
,
125 repository
=self
.repository
,
129 def getAbsoluteSourceStamp(self
, got_revision
):
130 return SourceStamp(branch
=self
.branch
, revision
=got_revision
,
131 patch
=self
.patch
, repository
=self
.repository
,
132 project
=self
.project
, changes
=self
.changes
)
135 # note: this won't work for VC systems with huge 'revision' strings
138 text
.append("for %s" % self
.project
)
140 text
.append("in %s" % self
.repository
)
141 if self
.revision
is None:
142 return text
+ [ "latest" ]
143 text
.append(str(self
.revision
))
145 text
.append("in '%s'" % self
.branch
)
147 text
.append("[patch]")
153 result
['revision'] = self
.revision
154 # TODO(maruel): Make the patch content a suburl.
155 result
['hasPatch'] = self
.patch
is not None
156 result
['branch'] = self
.branch
157 result
['changes'] = [c
.asDict() for c
in getattr(self
, 'changes', [])]
158 result
['project'] = self
.project
159 result
['repository'] = self
.repository
162 def upgradeToVersion1(self
):
163 # version 0 was untyped; in version 1 and later, types matter.
164 if self
.branch
is not None and not isinstance(self
.branch
, str):
165 self
.branch
= str(self
.branch
)
166 if self
.revision
is not None and not isinstance(self
.revision
, str):
167 self
.revision
= str(self
.revision
)
168 if self
.patch
is not None:
169 self
.patch
= ( int(self
.patch
[0]), str(self
.patch
[1]) )
170 self
.wasUpgraded
= True
172 def upgradeToVersion2(self
):
173 # version 1 did not have project or repository; just set them to a default ''
176 self
.wasUpgraded
= True
178 # vim: set ts=4 sts=4 sw=4 et: