1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
|
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
import operator
import os
from sqlobject import SQLObjectNotFound
class BuildDaemonPackagesArchSpecific:
"""Parse and implement "PackagesArchSpecific"."""
def __init__(self, pas_dir, distroseries):
self.pas_file = os.path.join(pas_dir, "Packages-arch-specific")
self.distroseries = distroseries
self.permit = {}
self._parsePAS()
def _parsePAS(self):
"""Parse self.pas_file and construct the permissible arch lists.
A PAS source line looks like this:
%openoffice.org2: i386 sparc powerpc amd64
A PAS binary line looks like this:
cmucl: i386 sparc amd64
"""
try:
fd = open(self.pas_file, "r")
except IOError:
return
all_arch_tags = set([a.architecturetag for a in
self.distroseries.architectures])
for line in fd:
line = line.split("#")[0]
line = line.strip()
if not line:
continue
is_source = False
if line.startswith("%"):
is_source = True
line = line[1:]
pkgname, arch_tags = line.split(":", 1)
is_exclude = False
if "!" in arch_tags:
is_exclude = True
arch_tags = arch_tags.replace("!", "")
line_arch_tags = arch_tags.strip().split()
arch_tags = set(line_arch_tags).intersection(all_arch_tags)
if is_exclude:
arch_tags = all_arch_tags - arch_tags
if not is_source:
ret = self._handleBinaryPAS(pkgname, arch_tags)
if ret is None:
continue
pkgname, arch_tags = ret
self.permit[pkgname] = arch_tags
fd.close()
def _handleBinaryPAS(self, binary_name, arch_tags):
# We need to find a sourcepackagename, so search for it against
# the nominated distroarchseries (it could be any one, but
# using this one simplifies testing). If the sourcepackagename
# changes across arches then we'll have problems. We hope
# this'll never happen!
default_architecture = self.distroseries.nominatedarchindep
try:
binary_publications = default_architecture.getReleasedPackages(
binary_name)
except SQLObjectNotFound:
# Can't find it at all...
return None
if len(binary_publications) == 0:
# Can't find it, so give up
return None
# Use the first binary, they will all point to the same build and
# consequently to the same source.
test_binary = binary_publications[0]
build = test_binary.binarypackagerelease.build
# If the source produces more than one binary it can't be restricted,
# The binary PAS line is completely ignored.
if build.binarypackages.count() > 1:
return None
# The source produces a single binary, so it can be restricted.
source_name = build.source_package_release.name
# The arch-independent builder /must/ be included in the
# arch_tags, regardless of whether the binary PAS line allows
# for it. If it is omitted and the package includes an arch-all
# binary, that binary will not be built! See thread on Launchpad
# list during Aug/2006 for more details on discussion. -- kiko
default_architecture_tag = default_architecture.architecturetag
if default_architecture_tag not in arch_tags:
arch_tags.add(default_architecture_tag)
return source_name, arch_tags
def determineArchitecturesToBuild(pubrec, legal_archseries,
distroseries, pas_verify=None):
"""Return a list of architectures for which this publication should build.
This function answers the question: given a publication, what
architectures should we build it for? It takes a set of legal
distroarchseries and the distribution series for which we are
building, and optionally a BuildDaemonPackagesArchSpecific
(informally known as 'P-a-s') instance.
The P-a-s component contains a list of forbidden architectures for
each source package, which should be respected regardless of which
architectures have been requested in the source package metadata,
for instance:
* 'aboot' should only build on powerpc
* 'mozilla-firefox' should not build on sparc
This black/white list is an optimization to suppress temporarily
known-failures build attempts and thus saving build-farm time.
For PPA publications we only consider architectures supported by PPA
subsystem (`DistroArchSeries`.supports_virtualized flag) and P-a-s is
turned off to give the users the chance to test their fixes for upstream
problems.
:param: pubrec: `ISourcePackagePublishingHistory` representing the
source publication.
:param: legal_archseries: a list of all initialized `DistroArchSeries`
to be considered.
:param: distroseries: the context `DistroSeries`.
:param: pas_verify: optional P-a-s verifier object/component.
:return: a list of `DistroArchSeries` for which the source publication in
question should be built.
"""
hint_string = pubrec.sourcepackagerelease.architecturehintlist
assert hint_string, 'Missing arch_hint_list'
# Ignore P-a-s for PPA publications.
if pubrec.archive.is_ppa:
pas_verify = None
# The 'PPA supported' flag only applies to virtualized archives
if pubrec.archive.require_virtualized:
legal_archseries = [
arch for arch in legal_archseries if arch.supports_virtualized]
# Cope with no virtualization support at all. It usually happens when
# a distroseries is created and initialized, by default no
# architecture supports its. Distro-team might take some time to
# decide which architecture will be allowed for PPAs and queue-builder
# will continue to work meanwhile.
if not legal_archseries:
return []
legal_arch_tags = set(
arch.architecturetag for arch in legal_archseries if arch.enabled)
hint_archs = set(hint_string.split())
# If a *-any architecture wildcard is present, build for everything
# we can. We only support Linux-based architectures at the moment,
# and any-any isn't a valid wildcard. See bug #605002.
if hint_archs.intersection(('any', 'linux-any')):
package_tags = legal_arch_tags
else:
# We need to support arch tags like any-foo and linux-foo, so remove
# supported kernel prefixes. See bug #73761.
stripped_archs = hint_archs
for kernel in ('linux', 'any'):
stripped_archs = set(
arch.replace("%s-" % kernel, "") for arch in stripped_archs)
package_tags = stripped_archs.intersection(legal_arch_tags)
# 'all' is only used as a last resort, to create an arch-indep
# build where no builds would otherwise exist.
if len(package_tags) == 0 and 'all' in hint_archs:
nominated_arch = distroseries.nominatedarchindep
if nominated_arch in legal_archseries:
package_tags = set([nominated_arch.architecturetag])
else:
package_tags = set()
if pas_verify:
build_tags = set()
for tag in package_tags:
sourcepackage_name = pubrec.sourcepackagerelease.name
if sourcepackage_name in pas_verify.permit:
permitted = pas_verify.permit[sourcepackage_name]
if tag not in permitted:
continue
build_tags.add(tag)
else:
build_tags = package_tags
sorted_archseries = sorted(legal_archseries,
key=operator.attrgetter('architecturetag'))
return [arch for arch in sorted_archseries
if arch.architecturetag in build_tags]
|