1
# Copyright 2009, 2010 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
# Authors: Daniel Silverstone <daniel.silverstone@canonical.com>
5
# and Adam Conrad <adam.conrad@canonical.com>
7
# Buildd Slave sbuild manager implementation
13
from canonical.buildd.slave import (
18
class DebianBuildState:
19
"""States for the DebianBuildManager."""
30
class DebianBuildManager(BuildManager):
31
"""Base behaviour for Debian chrooted builds."""
33
def __init__(self, slave, buildid):
34
BuildManager.__init__(self, slave, buildid)
35
self._updatepath = slave._config.get("debianmanager", "updatepath")
36
self._scanpath = slave._config.get("debianmanager", "processscanpath")
37
self._sourcespath = slave._config.get("debianmanager", "sourcespath")
38
self._cachepath = slave._config.get("slave","filecache")
39
self._state = DebianBuildState.INIT
41
self.alreadyfailed = False
44
def initial_build_state(self):
45
raise NotImplementedError()
47
def initiate(self, files, chroot, extra_args):
48
"""Initiate a build with a given set of files and chroot."""
50
self.arch_tag = extra_args.get('arch_tag', self._slave.getArch())
51
self.sources_list = extra_args.get('archives')
53
BuildManager.initiate(self, files, chroot, extra_args)
55
def doSourcesList(self):
56
"""Override apt/sources.list.
58
Mainly used for PPA builds.
60
args = ["override-sources-list", self._buildid]
61
args.extend(self.sources_list)
62
self.runSubProcess(self._sourcespath, args)
64
def doUpdateChroot(self):
65
"""Perform the chroot upgrade."""
68
["update-debian-chroot", self._buildid, self.arch_tag])
71
"""Run the main build process.
73
Subclasses must override this.
75
raise NotImplementedError()
77
def doReapProcesses(self):
78
"""Reap any processes left lying around in the chroot."""
79
self.runSubProcess( self._scanpath, [self._scanpath, self._buildid] )
82
def _parseChangesFile(linesIter):
83
"""A generator that iterates over files listed in a changes file.
85
:param linesIter: an iterable of lines in a changes file.
88
for line in linesIter:
89
if line.endswith("\n"):
91
if not seenfiles and line.startswith("Files:"):
94
if not line.startswith(' '):
96
filename = line.split(' ')[-1]
99
def getChangesFilename(self):
100
changes = self._dscfile[:-4] + "_" + self.arch_tag + ".changes"
101
return get_build_path(self._buildid, changes)
103
def gatherResults(self):
104
"""Gather the results of the build and add them to the file cache.
106
The primary file we care about is the .changes file. We key from there.
108
path = self.getChangesFilename()
109
name = os.path.basename(path)
110
chfile = open(path, "r")
111
self._slave.waitingfiles[name] = self._slave.storeFile(chfile.read())
115
for fn in self._parseChangesFile(chfile):
116
self._slave.addWaitingFile(get_build_path(self._buildid, fn))
120
def iterate(self, success):
121
# When a Twisted ProcessControl class is killed by SIGTERM,
122
# which we call 'build process aborted', 'None' is returned as
124
print ("Iterating with success flag %s against stage %s"
125
% (success, self._state))
126
func = getattr(self, "iterate_" + self._state, None)
128
raise ValueError, "Unknown internal state " + self._state
131
def iterate_INIT(self, success):
132
"""Just finished initializing the build."""
134
if not self.alreadyfailed:
135
# The init failed, can't fathom why that would be...
136
self._slave.builderFail()
137
self.alreadyfailed = True
138
self._state = DebianBuildState.CLEANUP
141
self._state = DebianBuildState.UNPACK
144
def iterate_UNPACK(self, success):
145
"""Just finished unpacking the tarball."""
147
if not self.alreadyfailed:
148
# The unpack failed for some reason...
149
self._slave.chrootFail()
150
self.alreadyfailed = True
151
self._state = DebianBuildState.CLEANUP
154
self._state = DebianBuildState.MOUNT
157
def iterate_MOUNT(self, success):
158
"""Just finished doing the mounts."""
160
if not self.alreadyfailed:
161
self._slave.chrootFail()
162
self.alreadyfailed = True
163
self._state = DebianBuildState.UMOUNT
166
if self.sources_list is not None:
167
self._state = DebianBuildState.SOURCES
170
self._state = DebianBuildState.UPDATE
171
self.doUpdateChroot()
173
def getTmpLogContents(self):
175
tmpLogHandle = open(os.path.join(self._cachepath, "buildlog"))
176
return tmpLogHandle.read()
180
def iterate_SOURCES(self, success):
181
"""Just finished overwriting sources.list."""
183
if not self.alreadyfailed:
184
self._slave.chrootFail()
185
self.alreadyfailed = True
186
self._state = DebianBuildState.REAP
187
self.doReapProcesses()
189
self._state = DebianBuildState.UPDATE
190
self.doUpdateChroot()
192
def iterate_UPDATE(self, success):
193
"""Just finished updating the chroot."""
195
if not self.alreadyfailed:
196
self._slave.chrootFail()
197
self.alreadyfailed = True
198
self._state = DebianBuildState.REAP
199
self.doReapProcesses()
201
self._state = self.initial_build_state
204
def iterate_REAP(self, success):
205
"""Finished reaping processes; ignore error returns."""
206
self._state = DebianBuildState.UMOUNT
209
def iterate_UMOUNT(self, success):
210
"""Just finished doing the unmounting."""
212
if not self.alreadyfailed:
213
self._slave.builderFail()
214
self.alreadyfailed = True
215
self._state = DebianBuildState.CLEANUP
218
def iterate_CLEANUP(self, success):
219
"""Just finished the cleanup."""
221
if not self.alreadyfailed:
222
self._slave.builderFail()
223
self.alreadyfailed = True
226
if not self.alreadyfailed:
227
self._slave.buildOK()
228
self._slave.buildComplete()
231
def get_build_path(build_id, *extra):
232
"""Generate a path within the build directory.
234
:param build_id: the build id to use.
235
:param extra: the extra path segments within the build directory.
236
:return: the generated path.
239
os.environ["HOME"], "build-" + build_id, *extra)