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
5
"""The manager class for building packages from recipes."""
10
from canonical.buildd.debian import (
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
22
def splat_file(path, contents):
23
"""Write a string to the specified path.
25
:param path: The path to store the string in.
26
:param contents: The string to write to the file.
28
file_obj = open(path, 'w')
30
file_obj.write(contents)
35
def get_chroot_path(build_id, *extra):
36
"""Return a path within the chroot.
38
:param build_id: The build_id of the build.
39
:param extra: Additional path elements.
41
return get_build_path(
42
build_id, 'chroot-autobuild', os.environ['HOME'][1:], *extra)
45
class SourcePackageRecipeBuildState(DebianBuildState):
46
"""The set of states that a recipe build can be in."""
47
BUILD_RECIPE = "BUILD_RECIPE"
50
class SourcePackageRecipeBuildManager(DebianBuildManager):
51
"""Build a source package from a bzr-builder recipe."""
53
initial_build_state = SourcePackageRecipeBuildState.BUILD_RECIPE
55
def __init__(self, slave, buildid):
58
:param slave: A build slave device.
59
:param buildid: The id of the build (a str).
61
DebianBuildManager.__init__(self, slave, buildid)
62
self.build_recipe_path = slave._config.get(
63
"sourcepackagerecipemanager", "buildrecipepath")
65
def initiate(self, files, chroot, extra_args):
66
"""Initiate a build with a given set of files and chroot.
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.
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']
80
super(SourcePackageRecipeBuildManager, self).initiate(
81
files, chroot, extra_args)
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)
89
"buildrecipe", 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)
94
def iterate_BUILD_RECIPE(self, retcode):
95
"""Move from BUILD_RECIPE to the next logical state."""
96
if retcode == RETCODE_SUCCESS:
98
print("Returning build status: OK")
99
elif retcode == RETCODE_FAILURE_INSTALL_BUILD_DEPS:
100
if not self.alreadyfailed:
101
tmpLog = self.getTmpLogContents()
103
'The following packages have unmet dependencies:\n'
104
'.*: Depends: ([^ ]*( \([^)]*\))?)')
105
mo = re.search(rx, tmpLog, re.M)
107
self._slave.depFail(mo.group(1))
108
print("Returning build status: DEPFAIL")
109
print("Dependencies: " + mo.group(1))
111
print("Returning build status: Build failed")
112
self._slave.buildFail()
113
self.alreadyfailed = True
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
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()
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)
137
def gatherResults(self):
138
"""Gather the results of the build and add them to the file cache.
140
The primary file we care about is the .changes file.
141
The manifest is also a useful record.
143
DebianBuildManager.gatherResults(self)
144
self._slave.addWaitingFile(get_build_path(self._buildid, 'manifest'))