~launchpad-pqm/launchpad/devel

« back to all changes in this revision

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

  • Committer: Curtis Hovey
  • Date: 2011-08-21 14:21:06 UTC
  • mto: This revision was merged to the branch mainline in revision 13745.
  • Revision ID: curtis.hovey@canonical.com-20110821142106-x93hajd6iguma8gx
Update test that was enforcing bad grammar.

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)