~launchpad-pqm/launchpad/devel

« back to all changes in this revision

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

  • Committer: Julian Edwards
  • Date: 2011-07-28 20:46:18 UTC
  • mfrom: (13553 devel)
  • mto: This revision was merged to the branch mainline in revision 13555.
  • Revision ID: julian.edwards@canonical.com-20110728204618-tivj2wx2oa9s32bx
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2010 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
# pylint: disable-msg=E1002
 
4
 
 
5
"""The manager class for building packages from recipes."""
 
6
 
 
7
import os
 
8
import re
 
9
 
 
10
from canonical.buildd.debian import (
 
11
    DebianBuildManager,
 
12
    DebianBuildState,
 
13
    get_build_path,
 
14
)
 
15
RETCODE_SUCCESS = 0
 
16
RETCODE_FAILURE_INSTALL = 200
 
17
RETCODE_FAILURE_BUILD_TREE = 201
 
18
RETCODE_FAILURE_INSTALL_BUILD_DEPS = 202
 
19
RETCODE_FAILURE_BUILD_SOURCE_PACKAGE = 203
 
20
 
 
21
 
 
22
def splat_file(path, contents):
 
23
    """Write a string to the specified path.
 
24
 
 
25
    :param path: The path to store the string in.
 
26
    :param contents: The string to write to the file.
 
27
    """
 
28
    file_obj = open(path, 'w')
 
29
    try:
 
30
        file_obj.write(contents)
 
31
    finally:
 
32
        file_obj.close()
 
33
 
 
34
 
 
35
def get_chroot_path(build_id, *extra):
 
36
    """Return a path within the chroot.
 
37
 
 
38
    :param build_id: The build_id of the build.
 
39
    :param extra: Additional path elements.
 
40
    """
 
41
    return get_build_path(
 
42
        build_id, 'chroot-autobuild', os.environ['HOME'][1:], *extra)
 
43
 
 
44
 
 
45
class SourcePackageRecipeBuildState(DebianBuildState):
 
46
    """The set of states that a recipe build can be in."""
 
47
    BUILD_RECIPE = "BUILD_RECIPE"
 
48
 
 
49
 
 
50
class SourcePackageRecipeBuildManager(DebianBuildManager):
 
51
    """Build a source package from a bzr-builder recipe."""
 
52
 
 
53
    initial_build_state = SourcePackageRecipeBuildState.BUILD_RECIPE
 
54
 
 
55
    def __init__(self, slave, buildid):
 
56
        """Constructor.
 
57
 
 
58
        :param slave: A build slave device.
 
59
        :param buildid: The id of the build (a str).
 
60
        """
 
61
        DebianBuildManager.__init__(self, slave, buildid)
 
62
        self.build_recipe_path = slave._config.get(
 
63
            "sourcepackagerecipemanager", "buildrecipepath")
 
64
 
 
65
    def initiate(self, files, chroot, extra_args):
 
66
        """Initiate a build with a given set of files and chroot.
 
67
 
 
68
        :param files: The files sent by the manager with the request.
 
69
        :param chroot: The sha1sum of the chroot to use.
 
70
        :param extra_args: A dict of extra arguments.
 
71
        """
 
72
        self.recipe_text = extra_args['recipe_text']
 
73
        self.suite = extra_args['suite']
 
74
        self.component = extra_args['ogrecomponent']
 
75
        self.author_name = extra_args['author_name']
 
76
        self.author_email = extra_args['author_email']
 
77
        self.archive_purpose = extra_args['archive_purpose']
 
78
        self.distroseries_name = extra_args['distroseries_name']
 
79
 
 
80
        super(SourcePackageRecipeBuildManager, self).initiate(
 
81
            files, chroot, extra_args)
 
82
 
 
83
    def doRunBuild(self):
 
84
        """Run the build process to build the source package."""
 
85
        os.makedirs(get_chroot_path(self._buildid, 'work'))
 
86
        recipe_path = get_chroot_path(self._buildid, 'work/recipe')
 
87
        splat_file(recipe_path, self.recipe_text)
 
88
        args = [
 
89
            "buildrecipe.py", self._buildid, self.author_name.encode('utf-8'),
 
90
            self.author_email, self.suite, self.distroseries_name,
 
91
            self.component, self.archive_purpose]
 
92
        self.runSubProcess(self.build_recipe_path, args)
 
93
 
 
94
    def iterate_BUILD_RECIPE(self, retcode):
 
95
        """Move from BUILD_RECIPE to the next logical state."""
 
96
        if retcode == RETCODE_SUCCESS:
 
97
            self.gatherResults()
 
98
            print("Returning build status: OK")
 
99
        elif retcode == RETCODE_FAILURE_INSTALL_BUILD_DEPS:
 
100
            if not self.alreadyfailed:
 
101
                tmpLog = self.getTmpLogContents()
 
102
                rx = (
 
103
                    'The following packages have unmet dependencies:\n'
 
104
                    '.*: Depends: ([^ ]*( \([^)]*\))?)')
 
105
                mo = re.search(rx, tmpLog, re.M)
 
106
                if mo:
 
107
                    self._slave.depFail(mo.group(1))
 
108
                    print("Returning build status: DEPFAIL")
 
109
                    print("Dependencies: " + mo.group(1))
 
110
                else:
 
111
                    print("Returning build status: Build failed")
 
112
                    self._slave.buildFail()
 
113
            self.alreadyfailed = True
 
114
        elif (
 
115
            retcode >= RETCODE_FAILURE_INSTALL and
 
116
            retcode <= RETCODE_FAILURE_BUILD_SOURCE_PACKAGE):
 
117
            # XXX AaronBentley 2009-01-13: We should handle depwait separately
 
118
            if not self.alreadyfailed:
 
119
                self._slave.buildFail()
 
120
                print("Returning build status: Build failed.")
 
121
            self.alreadyfailed = True
 
122
        else:
 
123
            if not self.alreadyfailed:
 
124
                self._slave.builderFail()
 
125
                print("Returning build status: Builder failed.")
 
126
            self.alreadyfailed = True
 
127
        self._state = DebianBuildState.REAP
 
128
        self.doReapProcesses()
 
129
 
 
130
    def getChangesFilename(self):
 
131
        """Return the path to the changes file."""
 
132
        work_path = get_build_path(self._buildid)
 
133
        for name in os.listdir(work_path):
 
134
            if name.endswith('_source.changes'):
 
135
                return os.path.join(work_path, name)
 
136
 
 
137
    def gatherResults(self):
 
138
        """Gather the results of the build and add them to the file cache.
 
139
 
 
140
        The primary file we care about is the .changes file.
 
141
        The manifest is also a useful record.
 
142
        """
 
143
        DebianBuildManager.gatherResults(self)
 
144
        self._slave.addWaitingFile(get_build_path(self._buildid, 'manifest'))