Merge lp:~james-w/linaro-image-tools/architecture-support into lp:linaro-image-tools/11.11

Proposed by James Westby
Status: Merged
Approved by: Michael Hudson-Doyle
Approved revision: 105
Merged at revision: 66
Proposed branch: lp:~james-w/linaro-image-tools/architecture-support
Merge into: lp:linaro-image-tools/11.11
Prerequisite: lp:~james-w/linaro-image-tools/add-packages-to-hwpack
Diff against target: 413 lines (+115/-56)
5 files modified
hwpack/hardwarepack.py (+4/-2)
hwpack/packages.py (+19/-5)
hwpack/testing.py (+2/-8)
hwpack/tests/test_hardwarepack.py (+30/-19)
hwpack/tests/test_packages.py (+60/-22)
To merge this branch: bzr merge lp:~james-w/linaro-image-tools/architecture-support
Reviewer Review Type Date Requested Status
Michael Hudson-Doyle (community) Approve
Review via email: mp+34484@code.launchpad.net

This proposal supersedes a proposal from 2010-09-02.

Description of the change

Hi,

Here's a rather simple branch to add architecture support through the code
we have now.

I just updated the spec to state that ARCHITECTURE is a required field in
the metadata, so that's now a required argument to the constructor of the
corresponding class, and it is output in the __str__ method.

Also the Package classes get architecture as an ivar, such that we can
track which architecture they are for, which leads to the change in the
get_packages_file function, which can now stop hardcoding "all".

The dummy test package object defaults to "all" as that way the tests
will work on multiple architectures without updating.

However, that leaves a gap, so I added a couple of new tests to ensure
that we fetch packages from the right architecture.

In order to support cross-build of hardware packs I stole a trick from
the chdist tool, which allows us to tell apt to pretend it is on a
different architecture. I added a test to peek inside the implementation
and check we were writing the config correctly, but the two added tests
check that it has the desired effect.

There's still a gap, as noted in the spec, about how we specify the
architectures to build for, as I'm not sure whether it should be in
the configuration file or on the command line, or some combination
of the two. This will mainly depend on how we want to hook it in to
lexbuilder I think. At least now all the infrastructure is in place
to build for any arch that we like.

Thanks,

James

To post a comment you must log in.
105. By James Westby

Merge add-packages-to-hwpack in to architecture-support.

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Looks fine to me!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hwpack/hardwarepack.py'
2--- hwpack/hardwarepack.py 2010-09-02 23:07:45 +0000
3+++ hwpack/hardwarepack.py 2010-09-02 23:07:46 +0000
4@@ -26,8 +26,8 @@
5 :type support: str or None
6 """
7
8- def __init__(self, name, version, origin=None, maintainer=None,
9- support=None):
10+ def __init__(self, name, version, architecture, origin=None,
11+ maintainer=None, support=None):
12 """Create the Metadata for a hardware pack.
13
14 See the instance variables for a description of the arguments.
15@@ -37,11 +37,13 @@
16 self.origin = origin
17 self.maintainer = maintainer
18 self.support = support
19+ self.architecture = architecture
20
21 def __str__(self):
22 """Get the contents of the metadata file."""
23 metadata = "NAME=%s\n" % self.name
24 metadata += "VERSION=%s\n" % self.version
25+ metadata += "ARCHITECTURE=%s\n" % self.architecture
26 if self.origin is not None:
27 metadata += "ORIGIN=%s\n" % self.origin
28 if self.maintainer is not None:
29
30=== modified file 'hwpack/packages.py'
31--- hwpack/packages.py 2010-09-02 23:07:45 +0000
32+++ hwpack/packages.py 2010-09-02 23:07:46 +0000
33@@ -23,7 +23,7 @@
34 parts.append('Filename: %s' % package.filename)
35 parts.append('Size: %d' % package.size)
36 # TODO: architecture support
37- parts.append('Architecture: all')
38+ parts.append('Architecture: %s' % package.architecture)
39 parts.append('MD5sum: %s' % package.md5)
40 content += "\n".join(parts)
41 content += "\n\n"
42@@ -75,9 +75,13 @@
43 :ivar md5: the hex representation of the md5sum of the contents of
44 the package.
45 :type md5: str
46+ :ivar architecture: the architecture that the package is for, may be
47+ 'all'.
48+ :type architecture: str
49 """
50
51- def __init__(self, name, version, filename, content, size, md5):
52+ def __init__(self, name, version, filename, content, size, md5,
53+ architecture):
54 """Create a FetchedPackage.
55
56 See the instance variables for the arguments.
57@@ -88,6 +92,7 @@
58 self.content = content
59 self.size = size
60 self.md5 = md5
61+ self.architecture = architecture
62
63 def __eq__(self, other):
64 return (self.name == other.name
65@@ -95,7 +100,8 @@
66 and self.filename == other.filename
67 and self.content.read() == other.content.read()
68 and self.size == other.size
69- and self.md5 == other.md5)
70+ and self.md5 == other.md5
71+ and self.architecture == other.architecture)
72
73 def __hash__(self):
74 return hash(
75@@ -105,7 +111,7 @@
76 class PackageFetcher(object):
77 """A class to fetch packages from a defined list of sources."""
78
79- def __init__(self, sources):
80+ def __init__(self, sources, architecture=None):
81 """Create a PackageFetcher.
82
83 Once created a PackageFetcher should have its `prepare` method
84@@ -114,8 +120,11 @@
85 :param sources: a list of sources such that they can be prefixed
86 with "deb " and fed to apt.
87 :type sources: an iterable of str
88+ :param architecture: the architecture to fetch packages for.
89+ :type architecture: str
90 """
91 self.sources = sources
92+ self.architecture = architecture
93 self.tempdir = None
94
95 def prepare(self):
96@@ -143,6 +152,10 @@
97 with open(sources_list, 'w') as f:
98 for source in self.sources:
99 f.write("deb %s\n" % source)
100+ if self.architecture is not None:
101+ apt_conf = os.path.join(self.tempdir, "etc", "apt", "apt.conf")
102+ with open(apt_conf, 'w') as f:
103+ f.write('Apt {\nArchitecture "%s";\n}\n' % self.architecture)
104 self.cache = Cache(rootdir=self.tempdir, memonly=True)
105 self.cache.update()
106 self.cache.open()
107@@ -183,6 +196,7 @@
108 (acqfile.destfile, acqfile.error_text))
109 result_package = FetchedPackage(
110 candidate.package.name, candidate.version, base,
111- open(destfile), candidate.size, candidate.md5)
112+ open(destfile), candidate.size, candidate.md5,
113+ candidate.architecture)
114 results.append(result_package)
115 return results
116
117=== modified file 'hwpack/testing.py'
118--- hwpack/testing.py 2010-09-02 23:07:45 +0000
119+++ hwpack/testing.py 2010-09-02 23:07:46 +0000
120@@ -52,16 +52,10 @@
121 See FetchedPackage for the instance variables.
122 """
123
124- def __init__(self, name, version):
125- """Create a DummyFetchedPackage.
126-
127- :param name: the name of the package.
128- :type name: str
129- :param version: the version of the package.
130- :type version: str
131- """
132+ def __init__(self, name, version, architecture="all"):
133 self.name = name
134 self.version = version
135+ self.architecture = architecture
136
137 @property
138 def filename(self):
139
140=== modified file 'hwpack/tests/test_hardwarepack.py'
141--- hwpack/tests/test_hardwarepack.py 2010-09-02 23:07:45 +0000
142+++ hwpack/tests/test_hardwarepack.py 2010-09-02 23:07:46 +0000
143@@ -12,56 +12,67 @@
144 class MetadataTests(TestCase):
145
146 def test_name(self):
147- metadata = Metadata("ahwpack", "3")
148+ metadata = Metadata("ahwpack", "3", "armel")
149 self.assertEqual("ahwpack", metadata.name)
150
151 def test_version(self):
152- metadata = Metadata("ahwpack", "3")
153+ metadata = Metadata("ahwpack", "3", "armel")
154 self.assertEqual("3", metadata.version)
155
156+ def test_architecture(self):
157+ metadata = Metadata("ahwpack", "3", "armel")
158+ self.assertEqual("armel", metadata.architecture)
159+
160 def test_default_origin_is_None(self):
161- metadata = Metadata("ahwpack", "4")
162+ metadata = Metadata("ahwpack", "4", "armel")
163 self.assertEqual(None, metadata.origin)
164
165 def test_origin(self):
166- metadata = Metadata("ahwpack", "4", origin="linaro")
167+ metadata = Metadata("ahwpack", "4", "armel", origin="linaro")
168 self.assertEqual("linaro", metadata.origin)
169
170 def test_default_maintainer_is_None(self):
171- metadata = Metadata("ahwpack", "4")
172+ metadata = Metadata("ahwpack", "4", "armel")
173 self.assertEqual(None, metadata.maintainer)
174
175 def test_maintainer(self):
176- metadata = Metadata("ahwpack", "4", maintainer="Some maintainer")
177+ metadata = Metadata(
178+ "ahwpack", "4", "armel", maintainer="Some maintainer")
179 self.assertEqual("Some maintainer", metadata.maintainer)
180
181 def test_default_support_is_None(self):
182- metadata = Metadata("ahwpack", "4")
183+ metadata = Metadata("ahwpack", "4", "armel")
184 self.assertEqual(None, metadata.support)
185
186 def test_support(self):
187- metadata = Metadata("ahwpack", "4", support="supported")
188+ metadata = Metadata("ahwpack", "4", "armel", support="supported")
189 self.assertEqual("supported", metadata.support)
190
191 def test_str(self):
192- metadata = Metadata("ahwpack", "4")
193- self.assertEqual("NAME=ahwpack\nVERSION=4\n", str(metadata))
194+ metadata = Metadata("ahwpack", "4", "armel")
195+ self.assertEqual(
196+ "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n", str(metadata))
197
198 def test_str_with_origin(self):
199- metadata = Metadata("ahwpack", "4", origin="linaro")
200+ metadata = Metadata("ahwpack", "4", "armel", origin="linaro")
201 self.assertEqual(
202- "NAME=ahwpack\nVERSION=4\nORIGIN=linaro\n", str(metadata))
203+ "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\nORIGIN=linaro\n",
204+ str(metadata))
205
206 def test_str_with_maintainer(self):
207- metadata = Metadata("ahwpack", "4", maintainer="Some Maintainer")
208+ metadata = Metadata(
209+ "ahwpack", "4", "armel", maintainer="Some Maintainer")
210 self.assertEqual(
211- "NAME=ahwpack\nVERSION=4\nMAINTAINER=Some Maintainer\n",
212+ "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
213+ "MAINTAINER=Some Maintainer\n",
214 str(metadata))
215
216 def test_str_with_support(self):
217- metadata = Metadata("ahwpack", "4", support="unsupported")
218+ metadata = Metadata("ahwpack", "4", "armel", support="unsupported")
219 self.assertEqual(
220- "NAME=ahwpack\nVERSION=4\nSUPPORT=unsupported\n", str(metadata))
221+ "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
222+ "SUPPORT=unsupported\n",
223+ str(metadata))
224
225
226 class HardwarePackHasFile(TarfileHasFile):
227@@ -111,7 +122,7 @@
228
229 def setUp(self):
230 super(HardwarePackTests, self).setUp()
231- self.metadata = Metadata("ahwpack", 4)
232+ self.metadata = Metadata("ahwpack", 4, "armel")
233
234 def test_format_is_1_0(self):
235 hwpack = HardwarePack(self.metadata)
236@@ -122,7 +133,7 @@
237 self.assertEqual("hwpack_ahwpack_4.tar.gz", hwpack.filename())
238
239 def test_filename_with_support(self):
240- metadata = Metadata("ahwpack", "4", support="supported")
241+ metadata = Metadata("ahwpack", "4", "armel", support="supported")
242 hwpack = HardwarePack(metadata)
243 self.assertEqual(
244 "hwpack_ahwpack_4_supported.tar.gz", hwpack.filename())
245@@ -144,7 +155,7 @@
246
247 def test_creates_metadata_file(self):
248 metadata = Metadata(
249- "ahwpack", "4", origin="linaro",
250+ "ahwpack", "4", "armel", origin="linaro",
251 maintainer="Some Maintainer", support="unsupported")
252 hwpack = HardwarePack(metadata)
253 tf = self.get_tarfile(hwpack)
254
255=== modified file 'hwpack/tests/test_packages.py'
256--- hwpack/tests/test_packages.py 2010-09-02 23:07:45 +0000
257+++ hwpack/tests/test_packages.py 2010-09-02 23:07:46 +0000
258@@ -19,13 +19,13 @@
259 class GetPackagesFileTests(TestCase):
260
261 def test_single_stanza(self):
262- package = DummyFetchedPackage("foo", "1.1")
263+ package = DummyFetchedPackage("foo", "1.1", architecture="armel")
264 self.assertEqual(textwrap.dedent("""\
265 Package: foo
266 Version: 1.1
267 Filename: %(filename)s
268 Size: %(size)d
269- Architecture: all
270+ Architecture: armel
271 MD5sum: %(md5)s
272 \n""" % {
273 'filename': package.filename,
274@@ -45,68 +45,77 @@
275
276 def test_attributes(self):
277 package = FetchedPackage(
278- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
279+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa",
280+ "armel")
281 self.assertEqual("foo", package.name)
282 self.assertEqual("1.1", package.version)
283 self.assertEqual("foo_1.1.deb", package.filename)
284 self.assertEqual("xxxx", package.content.read())
285 self.assertEqual(4, package.size)
286 self.assertEqual("aaaa", package.md5)
287+ self.assertEqual("armel", package.architecture)
288
289 def test_equal(self):
290 package1 = FetchedPackage(
291- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
292+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
293 package2 = FetchedPackage(
294- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
295+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
296 self.assertEqual(package1, package2)
297
298 def test_not_equal_different_name(self):
299 package1 = FetchedPackage(
300- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
301+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
302 package2 = FetchedPackage(
303- "bar", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
304+ "bar", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
305 self.assertNotEqual(package1, package2)
306
307 def test_not_equal_different_version(self):
308 package1 = FetchedPackage(
309- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
310+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
311 package2 = FetchedPackage(
312- "foo", "1.2", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
313+ "foo", "1.2", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
314 self.assertNotEqual(package1, package2)
315
316 def test_not_equal_different_filename(self):
317 package1 = FetchedPackage(
318- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
319+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
320 package2 = FetchedPackage(
321- "foo", "1.1", "afoo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
322+ "foo", "1.1", "afoo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
323 self.assertNotEqual(package1, package2)
324
325 def test_not_equal_different_content(self):
326 package1 = FetchedPackage(
327- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
328+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
329 package2 = FetchedPackage(
330- "foo", "1.1", "foo_1.1.deb", StringIO("yyyy"), 4, "aaaa")
331+ "foo", "1.1", "foo_1.1.deb", StringIO("yyyy"), 4, "aaaa", "armel")
332 self.assertNotEqual(package1, package2)
333
334 def test_not_equal_different_size(self):
335 package1 = FetchedPackage(
336- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
337+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
338 package2 = FetchedPackage(
339- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 5, "aaaa")
340+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 5, "aaaa", "armel")
341 self.assertNotEqual(package1, package2)
342
343 def test_not_equal_different_md5(self):
344 package1 = FetchedPackage(
345- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
346- package2 = FetchedPackage(
347- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "bbbb")
348+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
349+ package2 = FetchedPackage(
350+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "bbbb", "armel")
351+ self.assertNotEqual(package1, package2)
352+
353+ def test_not_equal_different_architecture(self):
354+ package1 = FetchedPackage(
355+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
356+ package2 = FetchedPackage(
357+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "i386")
358 self.assertNotEqual(package1, package2)
359
360 def test_equal_hash_equal(self):
361 package1 = FetchedPackage(
362- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
363+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
364 package2 = FetchedPackage(
365- "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa")
366+ "foo", "1.1", "foo_1.1.deb", StringIO("xxxx"), 4, "aaaa", "armel")
367 self.assertEqual(hash(package1), hash(package2))
368
369
370@@ -177,8 +186,18 @@
371 open(os.path.join(
372 fetcher.tempdir, "etc", "apt", "sources.list")).read())
373
374- def get_fetcher(self, sources):
375- fetcher = PackageFetcher([s.sources_entry for s in sources])
376+ def test_prepare_with_arch_creates_etc_apt_apt_conf(self):
377+ fetcher = PackageFetcher([], architecture="arch")
378+ self.addCleanup(fetcher.cleanup)
379+ fetcher.prepare()
380+ self.assertEqual(
381+ 'Apt {\nArchitecture "arch";\n}\n',
382+ open(os.path.join(
383+ fetcher.tempdir, "etc", "apt", "apt.conf")).read())
384+
385+ def get_fetcher(self, sources, architecture=None):
386+ fetcher = PackageFetcher(
387+ [s.sources_entry for s in sources], architecture=architecture)
388 self.addCleanup(fetcher.cleanup)
389 fetcher.prepare()
390 return fetcher
391@@ -257,3 +276,22 @@
392 fetcher = self.get_fetcher([old_source, new_source])
393 fetched = fetcher.fetch_packages(["bar"])
394 self.assertEqual(new_source_packages[0], fetched[0])
395+
396+ def test_fetch_package_records_correct_architecture(self):
397+ available_package = DummyFetchedPackage(
398+ "foo", "1.0", architecture="nonexistant")
399+ source = self.useFixture(AptSourceFixture([available_package]))
400+ fetcher = self.get_fetcher([source], architecture="nonexistant")
401+ self.assertEqual(
402+ "nonexistant", fetcher.fetch_packages(["foo"])[0].architecture)
403+
404+ def test_fetch_package_fetches_from_correct_architecture(self):
405+ wanted_package = DummyFetchedPackage(
406+ "foo", "1.0", architecture="arch1")
407+ unwanted_package = DummyFetchedPackage(
408+ "foo", "1.1", architecture="arch2")
409+ source = self.useFixture(
410+ AptSourceFixture([wanted_package, unwanted_package]))
411+ fetcher = self.get_fetcher([source], architecture="arch1")
412+ self.assertEqual(
413+ wanted_package, fetcher.fetch_packages(["foo"])[0])

Subscribers

People subscribed via source and target branches