Merge lp:~wgrant/launchpad/bug-1350208 into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 17231
Proposed branch: lp:~wgrant/launchpad/bug-1350208
Merge into: lp:launchpad
Diff against target: 308 lines (+181/-50)
4 files modified
lib/lp/soyuz/adapters/buildarch.py (+2/-2)
lib/lp/soyuz/adapters/tests/test_buildarch.py (+1/-1)
lib/lp/soyuz/model/binarypackagebuild.py (+75/-47)
lib/lp/soyuz/tests/test_build_set.py (+103/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/bug-1350208
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+240808@code.launchpad.net

Commit message

Fix createForSource to correctly handle unusual arch-indep cases (eg. changes in nominatedarchindep, or "Architecture: powerpc all").

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/soyuz/adapters/buildarch.py'
--- lib/lp/soyuz/adapters/buildarch.py 2013-05-31 04:27:26 +0000
+++ lib/lp/soyuz/adapters/buildarch.py 2014-11-06 05:30:00 +0000
@@ -37,7 +37,7 @@
3737
3838
39def determine_architectures_to_build(hintlist, archive, distroseries,39def determine_architectures_to_build(hintlist, archive, distroseries,
40 legal_archseries):40 legal_archseries, need_arch_indep):
41 """Return a list of architectures for which this publication should build.41 """Return a list of architectures for which this publication should build.
4242
43 This function answers the question: given a list of architectures and43 This function answers the question: given a list of architectures and
@@ -78,7 +78,7 @@
7878
79 # 'all' is only used as a last resort, to create an arch-indep build79 # 'all' is only used as a last resort, to create an arch-indep build
80 # where no builds would otherwise exist.80 # where no builds would otherwise exist.
81 if len(build_tags) == 0 and 'all' in hint_archs:81 if need_arch_indep and len(build_tags) == 0 and 'all' in hint_archs:
82 nominated_arch = distroseries.nominatedarchindep82 nominated_arch = distroseries.nominatedarchindep
83 if nominated_arch in legal_archseries:83 if nominated_arch in legal_archseries:
84 build_tags = set([nominated_arch.architecturetag])84 build_tags = set([nominated_arch.architecturetag])
8585
=== modified file 'lib/lp/soyuz/adapters/tests/test_buildarch.py'
--- lib/lp/soyuz/adapters/tests/test_buildarch.py 2013-09-11 06:05:44 +0000
+++ lib/lp/soyuz/adapters/tests/test_buildarch.py 2014-11-06 05:30:00 +0000
@@ -34,7 +34,7 @@
34 if arch.architecturetag in allowed_arch_tags]34 if arch.architecturetag in allowed_arch_tags]
35 architectures = determine_architectures_to_build(35 architectures = determine_architectures_to_build(
36 pub.sourcepackagerelease.architecturehintlist, pub.archive,36 pub.sourcepackagerelease.architecturehintlist, pub.archive,
37 self.publisher.breezy_autotest, allowed_archs)37 self.publisher.breezy_autotest, allowed_archs, True)
38 self.assertContentEqual(38 self.assertContentEqual(
39 expected_arch_tags, [a.architecturetag for a in architectures])39 expected_arch_tags, [a.architecturetag for a in architectures])
4040
4141
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2014-11-06 00:33:31 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2014-11-06 05:30:00 +0000
@@ -13,7 +13,10 @@
13 ]13 ]
1414
15import datetime15import datetime
16from operator import itemgetter16from operator import (
17 attrgetter,
18 itemgetter,
19 )
1720
18import apt_pkg21import apt_pkg
19import pytz22import pytz
@@ -1383,58 +1386,83 @@
1383 if not distroarch.processor.restricted or1386 if not distroarch.processor.restricted or
1384 distroarch.processor in archive.enabled_restricted_processors]1387 distroarch.processor in archive.enabled_restricted_processors]
13851388
1386 def _createMissingBuildForArchitecture(self, sourcepackagerelease,
1387 archive, arch, pocket,
1388 logger=None):
1389 """Create a build for a given architecture if it doesn't exist yet.
1390
1391 Return the just-created `IBinaryPackageBuild` record already
1392 scored or None if a suitable build is already present.
1393 """
1394 exact_build = self.getBySourceAndLocation(
1395 sourcepackagerelease, archive, arch)
1396 if exact_build is not None:
1397 return None
1398
1399 build_candidate = self.findBuiltOrPublishedBySourceAndArchive(
1400 sourcepackagerelease, archive).get(arch.architecturetag)
1401 if build_candidate is not None:
1402 return None
1403
1404 build = self.new(
1405 source_package_release=sourcepackagerelease,
1406 distro_arch_series=arch, archive=archive, pocket=pocket)
1407 # Create the builds in suspended mode for disabled archives.
1408 build_queue = build.queueBuild(suspended=not archive.enabled)
1409 Store.of(build).flush()
1410
1411 if logger is not None:
1412 logger.debug(
1413 "Created %s [%d] in %s (%d)"
1414 % (build.title, build.id, build.archive.displayname,
1415 build_queue.lastscore))
1416
1417 return build
1418
1419 def createForSource(self, sourcepackagerelease, archive, distroseries,1389 def createForSource(self, sourcepackagerelease, archive, distroseries,
1420 pocket, architectures_available=None, logger=None):1390 pocket, architectures_available=None, logger=None):
1421 """See `ISourcePackagePublishingHistory`."""1391 """See `ISourcePackagePublishingHistory`."""
1392
1393 # Exclude any architectures which already have built or copied
1394 # binaries. A new build with the same archtag could never
1395 # succeed; its files would conflict during upload.
1396 relevant_builds = self.findBuiltOrPublishedBySourceAndArchive(
1397 sourcepackagerelease, archive).values()
1398
1399 # Find any architectures that already have a build that exactly
1400 # matches, regardless of status. We can't create a second build
1401 # with the same (SPR, Archive, DAS).
1402 # XXX wgrant 2014-11-06: Should use a bulk query.
1403 for das in distroseries.architectures:
1404 build = self.getBySourceAndLocation(
1405 sourcepackagerelease, archive, das)
1406 if build is not None:
1407 relevant_builds.append(build)
1408
1409 skip_archtags = set(
1410 bpb.distro_arch_series.architecturetag for bpb in relevant_builds)
1411 # We need to assign the arch-indep role to a build unless an
1412 # arch-indep build has already succeeded, or another build in
1413 # this series already has it set.
1414 need_arch_indep = not any(bpb.arch_indep for bpb in relevant_builds)
1415
1416 # Find the architectures for which the source should end up with
1417 # binaries, parsing architecturehintlist as you'd expect.
1418 # For an architecturehintlist of just 'all', this will
1419 # be the current nominatedarchindep if need_arch_indep,
1420 # otherwise nothing.
1422 if architectures_available is None:1421 if architectures_available is None:
1423 architectures_available = list(1422 architectures_available = list(
1424 distroseries.buildable_architectures)1423 distroseries.buildable_architectures)
1425
1426 architectures_available = self._getAllowedArchitectures(1424 architectures_available = self._getAllowedArchitectures(
1427 archive, architectures_available)1425 archive, architectures_available)
14281426 candidate_architectures = determine_architectures_to_build(
1429 build_architectures = determine_architectures_to_build(
1430 sourcepackagerelease.architecturehintlist, archive, distroseries,1427 sourcepackagerelease.architecturehintlist, archive, distroseries,
1431 architectures_available)1428 architectures_available, need_arch_indep)
14321429
1433 builds = []1430 # Filter out any architectures for which we earlier found sufficient
1434 for arch in build_architectures:1431 # builds.
1435 build_candidate = self._createMissingBuildForArchitecture(1432 needed_architectures = [
1436 sourcepackagerelease, archive, arch, pocket, logger=logger)1433 das for das in candidate_architectures
1437 if build_candidate is not None:1434 if das.architecturetag not in skip_archtags]
1438 builds.append(build_candidate)1435 if not needed_architectures:
14391436 return []
1440 return builds1437
1438 arch_indep_das = None
1439 if need_arch_indep:
1440 # The ideal arch_indep build is nominatedarchindep. But if
1441 # that isn't a build we would otherwise create, use the DAS
1442 # with the lowest Processor.id.
1443 # XXX wgrant 2014-11-06: The fact that production's
1444 # Processor 1 is i386, a good arch-indep candidate, is a
1445 # total coincidence and this isn't a hack. I promise.
1446 if distroseries.nominatedarchindep in needed_architectures:
1447 arch_indep_das = distroseries.nominatedarchindep
1448 else:
1449 arch_indep_das = sorted(
1450 needed_architectures, key=attrgetter('processor.id'))[0]
1451
1452 # Create builds for the remaining architectures.
1453 new_builds = []
1454 for das in needed_architectures:
1455 build = self.new(
1456 source_package_release=sourcepackagerelease,
1457 distro_arch_series=das, archive=archive, pocket=pocket,
1458 arch_indep=das == arch_indep_das)
1459 new_builds.append(build)
1460 # Create the builds in suspended mode for disabled archives.
1461 build_queue = build.queueBuild(suspended=not archive.enabled)
1462 Store.of(build).flush()
1463 if logger is not None:
1464 logger.debug(
1465 "Created %s [%d] in %s (%d)"
1466 % (build.title, build.id, build.archive.displayname,
1467 build_queue.lastscore))
1468 return new_builds
14411469
=== modified file 'lib/lp/soyuz/tests/test_build_set.py'
--- lib/lp/soyuz/tests/test_build_set.py 2014-11-06 03:33:36 +0000
+++ lib/lp/soyuz/tests/test_build_set.py 2014-11-06 05:30:00 +0000
@@ -256,6 +256,16 @@
256 self.distroseries.nominatedarchindep = self.distroseries['sparc']256 self.distroseries.nominatedarchindep = self.distroseries['sparc']
257 self.addFakeChroots(self.distroseries)257 self.addFakeChroots(self.distroseries)
258258
259 self.distroseries2 = self.factory.makeDistroSeries(
260 distribution=self.distro, name="dumb")
261 for name, arch in (('avr', self.avr), ('sparc', self.sparc),
262 ('x32', self.x32)):
263 self.factory.makeDistroArchSeries(
264 architecturetag=name, processor=arch,
265 distroseries=self.distroseries2, supports_virtualized=True)
266 self.distroseries2.nominatedarchindep = self.distroseries2['x32']
267 self.addFakeChroots(self.distroseries2)
268
259 def getPubSource(self, architecturehintlist):269 def getPubSource(self, architecturehintlist):
260 """Return a mock source package publishing record for the archive270 """Return a mock source package publishing record for the archive
261 and architecture used in this testcase.271 and architecture used in this testcase.
@@ -281,6 +291,17 @@
281 self.assertContentEqual(expected.items(), actual.items())291 self.assertContentEqual(expected.items(), actual.items())
282 self.assertEqual(len(actual), len(builds))292 self.assertEqual(len(actual), len(builds))
283293
294 def completeBuilds(self, builds, success_map):
295 for build in builds:
296 success_or_failure = success_map.get(
297 build.distro_arch_series.architecturetag, None)
298 if success_or_failure is not None:
299 build.updateStatus(
300 BuildStatus.FULLYBUILT if success_or_failure
301 else BuildStatus.FAILEDTOBUILD)
302 del success_map[build.distro_arch_series.architecturetag]
303 self.assertContentEqual([], success_map)
304
284 def test__getAllowedArchitectures_restricted(self):305 def test__getAllowedArchitectures_restricted(self):
285 """Test _getAllowedArchitectures doesn't return unrestricted306 """Test _getAllowedArchitectures doesn't return unrestricted
286 archs.307 archs.
@@ -353,6 +374,88 @@
353 builds = self.createBuilds(spr, self.distroseries)374 builds = self.createBuilds(spr, self.distroseries)
354 self.assertBuildsMatch({'sparc': True, 'avr': False}, builds)375 self.assertBuildsMatch({'sparc': True, 'avr': False}, builds)
355376
377 def test_createForSource_arch_indep_from_scratch(self):
378 """createForSource() sets arch_indep=True on builds for the
379 nominatedarchindep architecture when no builds already exist.
380 """
381 spr = self.factory.makeSourcePackageRelease(architecturehintlist='any')
382 builds = self.createBuilds(spr, self.distroseries)
383 self.assertBuildsMatch({'sparc': True, 'avr': False}, builds)
384
385 def test_createForSource_any_with_nai_change(self):
386 # A new non-arch-indep build is created for a new
387 # nominatedarchindep architecture if arch-indep has already
388 # built elsewhere.
389 #
390 # This is most important when copying with binaries between
391 # series with different nominatedarchdep (bug #1350208).
392 spr = self.factory.makeSourcePackageRelease(architecturehintlist='any')
393 builds = self.createBuilds(spr, self.distroseries)
394 self.assertBuildsMatch({'sparc': True, 'avr': False}, builds)
395 self.completeBuilds(builds, {'sparc': True, 'avr': True})
396 # The new nominatedarchindep needs to be built, but we already
397 # have arch-indep binaries so arch_indep is False.
398 new_builds = self.createBuilds(spr, self.distroseries2)
399 self.assertBuildsMatch({'x32': False}, new_builds)
400
401 def test_createForSource_any_with_nai_change_and_fail(self):
402 # When the previous arch-indep build has failed, and
403 # nominatedarchindep has changed in the new series, the new
404 # nominatedarchindep has arch_indep=True while the other arch
405 # has arch_indep=False.
406 spr = self.factory.makeSourcePackageRelease(architecturehintlist='any')
407 builds = self.createBuilds(spr, self.distroseries)
408 self.assertBuildsMatch({'sparc': True, 'avr': False}, builds)
409 self.completeBuilds(builds, {'sparc': False, 'avr': True})
410 # The new nominatedarchindep needs to be built, and the previous
411 # nominatedarchindep build failed. We end up with two new
412 # builds, and arch_indep on nominatedarchindep.
413 new_builds = self.createBuilds(spr, self.distroseries2)
414 self.assertBuildsMatch({'x32': True, 'sparc': False}, new_builds)
415
416 def test_createForSource_all_with_nai_change(self):
417 # If we only need arch-indep binaries and they've already built
418 # successfully, no build is created for the new series, even if
419 # nominatedarchindep has changed.
420 spr = self.factory.makeSourcePackageRelease(architecturehintlist='all')
421 builds = self.createBuilds(spr, self.distroseries)
422 self.assertBuildsMatch({'sparc': True}, builds)
423 self.completeBuilds(builds, {'sparc': True})
424 # Despite there being no build for the new nominatedarchindep,
425 # the old arch-indep build is sufficient and no new record is
426 # created.
427 new_builds = self.createBuilds(spr, self.distroseries2)
428 self.assertBuildsMatch({}, new_builds)
429
430 def test_createForSource_all_with_nai_change_and_fail(self):
431 # If the previous arch-indep sole build failed, a new arch-indep
432 # build is created for nominatedarchindep.
433 spr = self.factory.makeSourcePackageRelease(architecturehintlist='all')
434 builds = self.createBuilds(spr, self.distroseries)
435 self.assertBuildsMatch({'sparc': True}, builds)
436 self.completeBuilds(builds, {'sparc': False})
437 # Despite there being no build for the new nominatedarchindep,
438 # the old arch-indep build is sufficient and no new record is
439 # created.
440 new_builds = self.createBuilds(spr, self.distroseries2)
441 self.assertBuildsMatch({'x32': True}, new_builds)
442
443 def test_createForSource_all_and_other_archs(self):
444 # If a source package specifies both 'all' and a set of
445 # architectures that doesn't include nominatedarchindep,
446 # arch_indep is set on the available DistroArchSeries with the
447 # oldest Processor.
448 # This is mostly a hack to avoid hardcoding a preference for
449 # the faster x86-family architectures, so we don't accidentally
450 # build documentation on hppa.
451 spr = self.factory.makeSourcePackageRelease(
452 architecturehintlist='all sparc avr')
453 builds = self.createBuilds(spr, self.distroseries2)
454 self.assertBuildsMatch({'sparc': False, 'avr': True}, builds)
455 self.completeBuilds(builds, {'sparc': True, 'avr': True})
456 new_builds = self.createBuilds(spr, self.distroseries)
457 self.assertBuildsMatch({}, new_builds)
458
356459
357class TestFindBuiltOrPublishedBySourceAndArchive(TestCaseWithFactory):460class TestFindBuiltOrPublishedBySourceAndArchive(TestCaseWithFactory):
358 """Tests for findBuiltOrPublishedBySourceAndArchive()."""461 """Tests for findBuiltOrPublishedBySourceAndArchive()."""