Trees | Indices | Help |
|
---|
|
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. 4 # 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 8 # details. 9 # 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. 13 # 14 # Copyright Buildbot Team Members 15 16 17 from zope.interface import implements 18 from twisted.persisted import styles 19 from twisted.internet import defer 20 from buildbot.changes.changes import Change 21 from buildbot import util, interfaces22 # TODO: kill this class, or at least make it less significant 23 -class SourceStamp(util.ComparableMixin, styles.Versioned):24 """This is a tuple of (branch, revision, patchspec, changes, project, repository). 25 26 C{branch} is always valid, although it may be None to let the Source 27 step use its default branch. There are three possibilities for the 28 remaining elements: 29 - (revision=REV, patchspec=None, changes=None): build REV. If REV is 30 None, build the HEAD revision from the given branch. Note that REV 31 must always be a string: SVN, Perforce, and other systems which use 32 integers should provide a string here, but the Source checkout step 33 will integerize it when making comparisons. 34 - (revision=REV, patchspec=(LEVEL, DIFF), changes=None): checkout REV, 35 then apply a patch to the source, with C{patch -pPATCHLEVEL <DIFF}. 36 If REV is None, checkout HEAD and patch it. 37 - (revision=None, patchspec=None, changes=[CHANGES]): let the Source 38 step check out the latest revision indicated by the given Changes. 39 CHANGES is a tuple of L{buildbot.changes.changes.Change} instances, 40 and all must be on the same branch. 41 42 @ivar ssid: sourcestamp ID, or None if this sourcestamp has not yet been 43 added to the database 44 45 @ivar branch: branch name or None 46 47 @ivar revision: revision string or None 48 49 @ivar patch: tuple (patch level, patch body) or None 50 51 @ivar patch_info: tuple (patch author, patch comment) or None 52 53 @ivar changes: tuple of changes that went into this source stamp, sorted by 54 number 55 56 @ivar project: project name 57 58 @ivar repository: repository URL 59 """ 60 61 persistenceVersion = 3 62 persistenceForgets = ( 'wasUpgraded', ) 63 64 # all seven of these are publicly visible attributes 65 branch = None 66 revision = None 67 patch = None 68 patch_info = None 69 changes = () 70 project = '' 71 repository = '' 72 codebase = '' 73 sourcestampsetid = None 74 ssid = None 75 76 compare_attrs = ('branch', 'revision', 'patch', 'patch_info', 'changes', 'project', 'repository', 'codebase') 77 78 implements(interfaces.ISourceStamp) 79 80 @classmethod129 d.addCallback(got_changes) 130 return d 13182 """ 83 Class method to create a L{SourceStamp} from a dictionary as returned 84 by L{SourceStampConnectorComponent.getSourceStamp}. 85 86 @param master: build master instance 87 @param ssdict: source stamp dictionary 88 89 @returns: L{SourceStamp} via Deferred 90 """ 91 # try to fetch from the cache, falling back to _make_ss if not 92 # found 93 cache = master.caches.get_cache("SourceStamps", cls._make_ss) 94 return cache.get(ssdict['ssid'], ssdict=ssdict, master=master)95 96 @classmethod98 sourcestamp = cls(_fromSsdict=True) 99 sourcestamp.ssid = ssid 100 sourcestamp.branch = ssdict['branch'] 101 sourcestamp.revision = ssdict['revision'] 102 sourcestamp.project = ssdict['project'] 103 sourcestamp.repository = ssdict['repository'] 104 sourcestamp.codebase = ssdict['codebase'] 105 sourcestamp.sourcestampsetid = ssdict['sourcestampsetid'] 106 107 sourcestamp.patch = None 108 if ssdict['patch_body']: 109 sourcestamp.patch = (ssdict['patch_level'], ssdict['patch_body'], 110 ssdict.get('patch_subdir')) 111 sourcestamp.patch_info = (ssdict['patch_author'], 112 ssdict['patch_comment']) 113 114 if ssdict['changeids']: 115 # sort the changeids in order, oldest to newest 116 sorted_changeids = sorted(ssdict['changeids']) 117 def gci(id): 118 d = master.db.changes.getChange(id) 119 d.addCallback(lambda chdict : 120 Change.fromChdict(master, chdict)) 121 return d122 d = defer.gatherResults([ gci(id) 123 for id in sorted_changeids ]) 124 else: 125 d = defer.succeed([]) 126 def got_changes(changes): 127 sourcestamp.changes = tuple(changes) 128 return sourcestamp132 - def __init__(self, branch=None, revision=None, patch=None, sourcestampsetid=None, 133 patch_info=None, changes=None, project='', repository='', 134 codebase = '', _fromSsdict=False, _ignoreChanges=False):135 self._addSourceStampToDatabase_lock = defer.DeferredLock(); 136 137 # skip all this madness if we're being built from the database 138 if _fromSsdict: 139 return 140 141 if patch is not None: 142 assert 2 <= len(patch) <= 3 143 assert int(patch[0]) != -1 144 self.sourcestampsetid = sourcestampsetid 145 self.branch = branch 146 self.patch = patch 147 self.patch_info = patch_info 148 self.project = project or '' 149 self.repository = repository or '' 150 self.codebase = codebase or '' 151 if changes: 152 self.changes = changes = list(changes) 153 changes.sort() 154 if not _ignoreChanges: 155 # set branch and revision to most recent change 156 self.branch = changes[-1].branch 157 revision = changes[-1].revision 158 if not self.project and hasattr(changes[-1], 'project'): 159 self.project = changes[-1].project 160 if not self.repository and hasattr(changes[-1], 'repository'): 161 self.repository = changes[-1].repository 162 163 if revision is not None: 164 if isinstance(revision, int): 165 revision = str(revision) 166 167 self.revision = revision168170 # this algorithm implements the "compatible" mergeRequests defined in 171 # detail in cfg-buidlers.texinfo; change that documentation if the 172 # algorithm changes! 173 if other.codebase != self.codebase: 174 return False 175 if other.repository != self.repository: 176 return False 177 if other.branch != self.branch: 178 return False # the builds are completely unrelated 179 if other.project != self.project: 180 return False 181 if self.patch or other.patch: 182 return False # you can't merge patched builds with anything 183 184 if self.changes and other.changes: 185 return True 186 elif self.changes and not other.changes: 187 return False # we're using changes, they aren't 188 elif not self.changes and other.changes: 189 return False # they're using changes, we aren't 190 191 if self.revision == other.revision: 192 # both builds are using the same specific revision, so they can 193 # be merged. It might be the case that revision==None, so they're 194 # both building HEAD. 195 return True 196 197 return False198200 """Generate a SourceStamp for the merger of me and all the other 201 SourceStamps. This is called by a Build when it starts, to figure 202 out what its sourceStamp should be.""" 203 204 # either we're all building the same thing (changes==None), or we're 205 # all building changes (which can be merged) 206 changes = [] 207 changes.extend(self.changes) 208 for ss in others: 209 changes.extend(ss.changes) 210 newsource = SourceStamp(sourcestampsetid=self.sourcestampsetid, 211 branch=self.branch, 212 revision=self.revision, 213 patch=self.patch, 214 patch_info=self.patch_info, 215 project=self.project, 216 repository=self.repository, 217 codebase=self.codebase, 218 changes=changes) 219 return newsource220222 # Create an exact but identityless copy 223 return SourceStamp(branch=self.branch, revision=self.revision, 224 patch=self.patch, repository=self.repository, 225 codebase=self.codebase, patch_info=self.patch_info, 226 project=self.project, changes=self.changes, 227 _ignoreChanges=True)228 233235 # note: this won't work for VC systems with huge 'revision' strings 236 text = [] 237 if self.project: 238 text.append("for %s" % self.project) 239 if self.repository: 240 text.append("in %s" % self.repository) 241 if self.codebase: 242 text.append("(%s)" % self.codebase) 243 if self.revision is None: 244 return text + [ "latest" ] 245 text.append(str(self.revision)) 246 if self.branch: 247 text.append("in '%s'" % self.branch) 248 if self.patch: 249 text.append("[patch]") 250 return text251253 result = {} 254 # Constant 255 result['revision'] = self.revision 256 257 # TODO(maruel): Make the patch content a suburl. 258 result['hasPatch'] = self.patch is not None 259 if self.patch: 260 result['patch_level'] = self.patch[0] 261 result['patch_body'] = self.patch[1] 262 if len(self.patch) > 2: 263 result['patch_subdir'] = self.patch[2] 264 if self.patch_info: 265 result['patch_author'] = self.patch_info[0] 266 result['patch_comment'] = self.patch_info[1] 267 268 result['branch'] = self.branch 269 result['changes'] = [c.asDict() for c in getattr(self, 'changes', [])] 270 result['project'] = self.project 271 result['repository'] = self.repository 272 result['codebase'] = self.codebase 273 return result274276 styles.Versioned.__setstate__(self, d) 277 self._addSourceStampToDatabase_lock = defer.DeferredLock();278280 # version 0 was untyped; in version 1 and later, types matter. 281 if self.branch is not None and not isinstance(self.branch, str): 282 self.branch = str(self.branch) 283 if self.revision is not None and not isinstance(self.revision, str): 284 self.revision = str(self.revision) 285 if self.patch is not None: 286 self.patch = ( int(self.patch[0]), str(self.patch[1]) ) 287 self.wasUpgraded = True288290 # version 1 did not have project or repository; just set them to a default '' 291 self.project = '' 292 self.repository = '' 293 self.wasUpgraded = True294296 #The database has been upgraded where all existing sourcestamps got an 297 #setid equal to its ssid 298 self.sourcestampsetid = self.ssid 299 #version 2 did not have codebase; set to '' 300 self.codebase = '' 301 self.wasUpgraded = True302304 "temporary; do not use widely!" 305 if self.sourcestampsetid: 306 return defer.succeed(self.sourcestampsetid) 307 else: 308 return self.addSourceStampToDatabase(master)309 310 311 @util.deferredLocked('_addSourceStampToDatabase_lock')313 # add it to the DB 314 patch_body = None 315 patch_level = None 316 patch_subdir = None 317 if self.patch: 318 patch_level = self.patch[0] 319 patch_body = self.patch[1] 320 if len(self.patch) > 2: 321 patch_subdir = self.patch[2] 322 323 patch_author = None 324 patch_comment = None 325 if self.patch_info: 326 patch_author, patch_comment = self.patch_info 327 328 def get_setid(): 329 if sourcestampsetid is not None: 330 return defer.succeed( sourcestampsetid ) 331 else: 332 return master.db.sourcestampsets.addSourceStampSet()333 334 def set_setid(setid): 335 self.sourcestampsetid = setid 336 return setid 337 338 def add_sourcestamp(setid): 339 return master.db.sourcestamps.addSourceStamp( 340 sourcestampsetid=setid, 341 branch=self.branch, revision=self.revision, 342 repository=self.repository, codebase=self.codebase, 343 project=self.project, 344 patch_body=patch_body, patch_level=patch_level, 345 patch_author=patch_author, patch_comment=patch_comment, 346 patch_subdir=patch_subdir, 347 changeids=[c.number for c in self.changes]) 348 349 def set_ssid(ssid): 350 self.ssid = ssid 351 return ssid 352 353 d = get_setid() 354 d.addCallback(set_setid) 355 d.addCallback(add_sourcestamp) 356 d.addCallback(set_ssid) 357 d.addCallback(lambda _ : self.sourcestampsetid) 358 return d 359
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Nov 21 16:23:04 2012 | http://epydoc.sourceforge.net |