~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/canonical/buildd/debian.py

  • Committer: mbp at canonical
  • Date: 2011-11-20 23:37:23 UTC
  • mto: This revision was merged to the branch mainline in revision 14344.
  • Revision ID: mbp@canonical.com-20111120233723-370p96db2crru5tm
Delete canonical.buildd again

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009, 2010 Canonical Ltd.  This software is licensed under the
2
 
# GNU Affero General Public License version 3 (see the file LICENSE).
3
 
 
4
 
# Authors: Daniel Silverstone <daniel.silverstone@canonical.com>
5
 
#      and Adam Conrad <adam.conrad@canonical.com>
6
 
 
7
 
# Buildd Slave sbuild manager implementation
8
 
 
9
 
__metaclass__ = type
10
 
 
11
 
import os
12
 
 
13
 
from canonical.buildd.slave import (
14
 
    BuildManager,
15
 
    )
16
 
 
17
 
 
18
 
class DebianBuildState:
19
 
    """States for the DebianBuildManager."""
20
 
    INIT = "INIT"
21
 
    UNPACK = "UNPACK"
22
 
    MOUNT = "MOUNT"
23
 
    SOURCES = "SOURCES"
24
 
    UPDATE = "UPDATE"
25
 
    REAP = "REAP"
26
 
    UMOUNT = "UMOUNT"
27
 
    CLEANUP = "CLEANUP"
28
 
 
29
 
 
30
 
class DebianBuildManager(BuildManager):
31
 
    """Base behaviour for Debian chrooted builds."""
32
 
 
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
40
 
        slave.emptyLog()
41
 
        self.alreadyfailed = False
42
 
 
43
 
    @property
44
 
    def initial_build_state(self):
45
 
        raise NotImplementedError()
46
 
 
47
 
    def initiate(self, files, chroot, extra_args):
48
 
        """Initiate a build with a given set of files and chroot."""
49
 
 
50
 
        self.arch_tag = extra_args.get('arch_tag', self._slave.getArch())
51
 
        self.sources_list = extra_args.get('archives')
52
 
 
53
 
        BuildManager.initiate(self, files, chroot, extra_args)
54
 
 
55
 
    def doSourcesList(self):
56
 
        """Override apt/sources.list.
57
 
 
58
 
        Mainly used for PPA builds.
59
 
        """
60
 
        args = ["override-sources-list", self._buildid]
61
 
        args.extend(self.sources_list)
62
 
        self.runSubProcess(self._sourcespath, args)
63
 
 
64
 
    def doUpdateChroot(self):
65
 
        """Perform the chroot upgrade."""
66
 
        self.runSubProcess(
67
 
            self._updatepath,
68
 
            ["update-debian-chroot", self._buildid, self.arch_tag])
69
 
 
70
 
    def doRunBuild(self):
71
 
        """Run the main build process.
72
 
 
73
 
        Subclasses must override this.
74
 
        """
75
 
        raise NotImplementedError()
76
 
 
77
 
    def doReapProcesses(self):
78
 
        """Reap any processes left lying around in the chroot."""
79
 
        self.runSubProcess( self._scanpath, [self._scanpath, self._buildid] )
80
 
 
81
 
    @staticmethod
82
 
    def _parseChangesFile(linesIter):
83
 
        """A generator that iterates over files listed in a changes file.
84
 
 
85
 
        :param linesIter: an iterable of lines in a changes file.
86
 
        """
87
 
        seenfiles = False
88
 
        for line in linesIter:
89
 
            if line.endswith("\n"):
90
 
                line = line[:-1]
91
 
            if not seenfiles and line.startswith("Files:"):
92
 
                seenfiles = True
93
 
            elif seenfiles:
94
 
                if not line.startswith(' '):
95
 
                    break
96
 
                filename = line.split(' ')[-1]
97
 
                yield filename
98
 
 
99
 
    def getChangesFilename(self):
100
 
        changes = self._dscfile[:-4] + "_" + self.arch_tag + ".changes"
101
 
        return get_build_path(self._buildid, changes)
102
 
 
103
 
    def gatherResults(self):
104
 
        """Gather the results of the build and add them to the file cache.
105
 
 
106
 
        The primary file we care about is the .changes file. We key from there.
107
 
        """
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())
112
 
        chfile.seek(0)
113
 
        seenfiles = False
114
 
 
115
 
        for fn in self._parseChangesFile(chfile):
116
 
            self._slave.addWaitingFile(get_build_path(self._buildid, fn))
117
 
 
118
 
        chfile.close()
119
 
 
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
123
 
        # exit_code.
124
 
        print ("Iterating with success flag %s against stage %s"
125
 
               % (success, self._state))
126
 
        func = getattr(self, "iterate_" + self._state, None)
127
 
        if func is None:
128
 
            raise ValueError, "Unknown internal state " + self._state
129
 
        func(success)
130
 
 
131
 
    def iterate_INIT(self, success):
132
 
        """Just finished initializing the build."""
133
 
        if success != 0:
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
139
 
            self.doCleanup()
140
 
        else:
141
 
            self._state = DebianBuildState.UNPACK
142
 
            self.doUnpack()
143
 
 
144
 
    def iterate_UNPACK(self, success):
145
 
        """Just finished unpacking the tarball."""
146
 
        if success != 0:
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
152
 
            self.doCleanup()
153
 
        else:
154
 
            self._state = DebianBuildState.MOUNT
155
 
            self.doMounting()
156
 
 
157
 
    def iterate_MOUNT(self, success):
158
 
        """Just finished doing the mounts."""
159
 
        if success != 0:
160
 
            if not self.alreadyfailed:
161
 
                self._slave.chrootFail()
162
 
                self.alreadyfailed = True
163
 
            self._state = DebianBuildState.UMOUNT
164
 
            self.doUnmounting()
165
 
        else:
166
 
            if self.sources_list is not None:
167
 
                self._state = DebianBuildState.SOURCES
168
 
                self.doSourcesList()
169
 
            else:
170
 
                self._state = DebianBuildState.UPDATE
171
 
                self.doUpdateChroot()
172
 
 
173
 
    def getTmpLogContents(self):
174
 
        try:
175
 
            tmpLogHandle = open(os.path.join(self._cachepath, "buildlog"))
176
 
            return tmpLogHandle.read()
177
 
        finally:
178
 
            tmpLogHandle.close()
179
 
 
180
 
    def iterate_SOURCES(self, success):
181
 
        """Just finished overwriting sources.list."""
182
 
        if success != 0:
183
 
            if not self.alreadyfailed:
184
 
                self._slave.chrootFail()
185
 
                self.alreadyfailed = True
186
 
            self._state = DebianBuildState.REAP
187
 
            self.doReapProcesses()
188
 
        else:
189
 
            self._state = DebianBuildState.UPDATE
190
 
            self.doUpdateChroot()
191
 
 
192
 
    def iterate_UPDATE(self, success):
193
 
        """Just finished updating the chroot."""
194
 
        if success != 0:
195
 
            if not self.alreadyfailed:
196
 
                self._slave.chrootFail()
197
 
                self.alreadyfailed = True
198
 
            self._state = DebianBuildState.REAP
199
 
            self.doReapProcesses()
200
 
        else:
201
 
            self._state = self.initial_build_state
202
 
            self.doRunBuild()
203
 
 
204
 
    def iterate_REAP(self, success):
205
 
        """Finished reaping processes; ignore error returns."""
206
 
        self._state = DebianBuildState.UMOUNT
207
 
        self.doUnmounting()
208
 
 
209
 
    def iterate_UMOUNT(self, success):
210
 
        """Just finished doing the unmounting."""
211
 
        if success != 0:
212
 
            if not self.alreadyfailed:
213
 
                self._slave.builderFail()
214
 
                self.alreadyfailed = True
215
 
        self._state = DebianBuildState.CLEANUP
216
 
        self.doCleanup()
217
 
 
218
 
    def iterate_CLEANUP(self, success):
219
 
        """Just finished the cleanup."""
220
 
        if success != 0:
221
 
            if not self.alreadyfailed:
222
 
                self._slave.builderFail()
223
 
                self.alreadyfailed = True
224
 
        else:
225
 
            # Successful clean
226
 
            if not self.alreadyfailed:
227
 
                self._slave.buildOK()
228
 
        self._slave.buildComplete()
229
 
 
230
 
 
231
 
def get_build_path(build_id, *extra):
232
 
    """Generate a path within the build directory.
233
 
 
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.
237
 
    """
238
 
    return os.path.join(
239
 
        os.environ["HOME"], "build-" + build_id, *extra)