← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/debug-subcomponent into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/debug-subcomponent into lp:launchpad with lp:~wgrant/launchpad/maybe-publish-debug-symbols-respeck as a prerequisite.

Commit message:
Publish ddebs to a new main/debug subcomponent, like main/debian-installer.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/debug-subcomponent/+merge/163889

Publish ddebs to a new foo/debug subcomponent, as we already publish udebs to foo/debian-installer. Only create those indices when publish_debug_symbols is enabled.

I also tweaked the apt-ftparchive debian-installer implementation to actually check the file type, rather than the section. I've confirmed that this won't actually change anything in the primary archive.
-- 
https://code.launchpad.net/~wgrant/launchpad/debug-subcomponent/+merge/163889
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/debug-subcomponent into lp:launchpad.
=== modified file 'lib/lp/archivepublisher/model/ftparchive.py'
--- lib/lp/archivepublisher/model/ftparchive.py	2013-02-26 23:00:56 +0000
+++ lib/lp/archivepublisher/model/ftparchive.py	2013-05-15 10:50:59 +0000
@@ -27,7 +27,10 @@
 from lp.services.database.stormexpr import Concatenate
 from lp.services.librarian.model import LibraryFileAlias
 from lp.services.osutils import write_file
-from lp.soyuz.enums import PackagePublishingStatus
+from lp.soyuz.enums import (
+    BinaryPackageFormat,
+    PackagePublishingStatus,
+    )
 from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
 from lp.soyuz.model.binarypackagename import BinaryPackageName
 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
@@ -100,6 +103,16 @@
 
 """
 
+EXT_TO_SUBCOMPONENT = {
+    'udeb': 'debian-installer',
+    'ddeb': 'debug',
+    }
+
+SUBCOMPONENT_TO_EXT = {
+    'debian-installer': 'udeb',
+    'debug': 'ddeb',
+    }
+
 
 class AptFTPArchiveFailure(Exception):
     """Failure while running apt-ftparchive."""
@@ -209,12 +222,15 @@
         suite = distroseries.getSuite(pocket)
 
         # Create empty override lists.
-        for path in (
-            (comp, ),
+        needed_paths = [
+            (comp,),
             ("extra", comp),
-            (comp, "debian-installer"),
             (comp, "src"),
-            ):
+            ]
+        for sub_comp in self.publisher.subcomponents:
+            needed_paths.append((comp, sub_comp))
+
+        for path in needed_paths:
             write_file(os.path.join(
                 self._config.overrideroot,
                 ".".join(("override", suite) + path)), "")
@@ -231,7 +247,8 @@
         for arch in arch_tags:
             # Touch more file lists for the archs.
             touch_list(comp, "binary-" + arch)
-            touch_list(comp, "debian-installer", "binary-" + arch)
+            for sub_comp in self.publisher.subcomponents:
+                touch_list(comp, sub_comp, "binary-" + arch)
 
     #
     # Override Generation
@@ -276,8 +293,9 @@
         """Fetch override information about all published binaries.
 
         The override information consists of tuples with 'binaryname',
-        'component', 'section', 'architecture' and 'priority' strings and
-        'phased_update_percentage' integers, in this order.
+        'component', 'section', 'architecture' and 'priority' strings,
+        'binpackageformat' enum, 'phased_update_percentage' integer, in this
+        order.
 
         :param distroseries: target `IDistroSeries`
         :param pocket: target `PackagePublishingPocket`
@@ -306,17 +324,26 @@
         if len(architectures_ids) == 0:
             return EmptyResultSet()
 
+        conditions = [
+            BinaryPackagePublishingHistory.archive == self.publisher.archive,
+            BinaryPackagePublishingHistory.distroarchseriesID.is_in(
+                architectures_ids),
+            BinaryPackagePublishingHistory.pocket == pocket,
+            BinaryPackagePublishingHistory.status ==
+                PackagePublishingStatus.PUBLISHED,
+            ]
+        if not self.publisher.archive.publish_debug_symbols:
+            conditions.append(
+                BinaryPackageRelease.binpackageformat
+                    != BinaryPackageFormat.DDEB)
+
         result_set = store.using(*origins).find(
             (BinaryPackageName.name, Component.name, Section.name,
              DistroArchSeries.architecturetag,
              BinaryPackagePublishingHistory.priority,
+             BinaryPackageRelease.binpackageformat,
              BinaryPackagePublishingHistory.phased_update_percentage),
-            BinaryPackagePublishingHistory.archive == self.publisher.archive,
-            BinaryPackagePublishingHistory.distroarchseriesID.is_in(
-                architectures_ids),
-            BinaryPackagePublishingHistory.pocket == pocket,
-            BinaryPackagePublishingHistory.status ==
-                PackagePublishingStatus.PUBLISHED)
+            *conditions)
 
         return result_set.order_by(Desc(BinaryPackagePublishingHistory.id))
 
@@ -357,12 +384,14 @@
         """
         # This code is tested in soyuz-set-of-uploads, and in
         # test_ftparchive.
+        from lp.archivepublisher.publishing import FORMAT_TO_SUBCOMPONENT
 
         # overrides[component][src/bin] = sets of tuples
         overrides = defaultdict(lambda: defaultdict(set))
 
         def updateOverride(packagename, component, section, archtag=None,
-                           priority=None, phased_update_percentage=None):
+                           priority=None, binpackageformat=None,
+                           phased_update_percentage=None):
             """Generates and packs tuples of data required for overriding.
 
             If archtag is provided, it's a binary tuple; otherwise, it's a
@@ -383,17 +412,18 @@
             # duplicated overrides.
             if archtag:
                 priority = priority.title.lower()
-                # We pick up debian-installer packages here, although they
+                # We pick up subcomponent packages here, although they
                 # do not need phased updates (and adding the
                 # phased_update_percentage would complicate
                 # generateOverrideForComponent).
-                if section.endswith("debian-installer"):
-                    override['d-i'].add((packagename, priority, section))
-                else:
+                subcomp = FORMAT_TO_SUBCOMPONENT.get(binpackageformat)
+                if subcomp is None:
                     package_arch = "%s/%s" % (packagename, archtag)
                     override['bin'].add((
                         package_arch, priority, section,
                         phased_update_percentage))
+                elif subcomp in self.publisher.subcomponents:
+                    override[subcomp].add((packagename, priority, section))
             else:
                 override['src'].add((packagename, section))
 
@@ -419,7 +449,6 @@
         """Generates overrides for a specific component."""
         src_overrides = sorted(overrides[component]['src'])
         bin_overrides = sorted(overrides[component]['bin'])
-        di_overrides = sorted(overrides[component]['d-i'])
 
         # Set up filepaths for the overrides we read
         extra_extra_overrides = os.path.join(self._config.miscroot,
@@ -433,9 +462,6 @@
                                      "override.%s.%s" % (suite, component))
         ef_override = os.path.join(self._config.overrideroot,
                                    "override.%s.extra.%s" % (suite, component))
-        di_override = os.path.join(self._config.overrideroot,
-                                   "override.%s.%s.debian-installer" %
-                                   (suite, component))
         source_override = os.path.join(self._config.overrideroot,
                                        "override.%s.%s.src" %
                                        (suite, component))
@@ -498,8 +524,14 @@
             sf.close()
 
         _outputSimpleOverrides(source_override, src_overrides)
-        if di_overrides:
-            _outputSimpleOverrides(di_override, di_overrides)
+
+        for subcomp in self.publisher.subcomponents:
+            sub_overrides = sorted(overrides[component][subcomp])
+            if sub_overrides:
+                sub_path = os.path.join(
+                    self._config.overrideroot,
+                    "override.%s.%s.%s" % (suite, component, subcomp))
+                _outputSimpleOverrides(sub_path, sub_overrides)
 
     #
     # File List Generation
@@ -571,6 +603,11 @@
                 PackagePublishingStatus.PUBLISHED,
             ]
 
+        if not self.publisher.archive.publish_debug_symbols:
+            select_conditions.append(
+                BinaryPackageRelease.binpackageformat
+                    != BinaryPackageFormat.DDEB)
+
         result_set = store.find(
             columns, *(join_conditions + select_conditions))
         return result_set.order_by(
@@ -639,36 +676,26 @@
     def writeFileList(self, arch, file_names, dr_pocketed, component):
         """Output file lists for a series and architecture.
 
-        This includes a debian-installer file list.
+        This includes the subcomponent file lists.
         """
-        files = []
-        di_files = []
-        f_path = os.path.join(self._config.overrideroot,
-                              "%s_%s_%s" % (dr_pocketed, component, arch))
-        f = file(f_path, "w")
+        files = defaultdict(list)
         for name in file_names:
-            if name.endswith(".udeb"):
-                # Note the name for output later
-                di_files.append(name)
-            else:
-                files.append(name)
-        files.sort(key=package_name)
-        f.write("\n".join(files))
-        f.write("\n")
-        f.close()
+            files[EXT_TO_SUBCOMPONENT.get(name.rsplit('.', 1)[1])].append(name)
 
-        # Once again, some d-i stuff to write out...
-        self.log.debug(
-            "Writing d-i file list for %s/%s/%s" % (
-                dr_pocketed, component, arch))
-        di_overrides = os.path.join(
-            self._config.overrideroot,
-            "%s_%s_debian-installer_%s" % (dr_pocketed, component, arch))
-        f = open(di_overrides, "w")
-        di_files.sort(key=package_name)
-        f.write("\n".join(di_files))
-        f.write("\n")
-        f.close()
+        lists = (
+            [(None, 'regular', '%s_%s_%s' % (dr_pocketed, component, arch))]
+            + [(subcomp, subcomp,
+                '%s_%s_%s_%s' % (dr_pocketed, component, subcomp, arch))
+               for subcomp in self.publisher.subcomponents])
+        for subcomp, desc, filename in lists:
+            self.log.debug(
+                "Writing %s file list for %s/%s/%s" % (
+                    desc, dr_pocketed, component, arch))
+            path = os.path.join(self._config.overrideroot, filename)
+            with open(path, "w") as f:
+                files[subcomp].sort(key=package_name)
+                f.write("\n".join(files[subcomp]))
+                f.write("\n")
 
     #
     # Config Generation
@@ -748,19 +775,20 @@
 
         if archs:
             for component in comps:
-                apt_config.write(STANZA_TEMPLATE % {
-                    "LISTPATH": self._config.overrideroot,
-                    "DISTRORELEASEONDISK": "%s/%s" % (suite, component),
-                    "DISTRORELEASEBYFILE": "%s_%s" % (suite, component),
-                    "DISTRORELEASE": "%s.%s" % (suite, component),
-                    "ARCHITECTURES": " ".join(archs),
-                    "SECTIONS": "debian-installer",
-                    "EXTENSIONS": ".udeb",
-                    "CACHEINSERT": "debian-installer-",
-                    "DISTS": os.path.basename(self._config.distsroot),
-                    "HIDEEXTRA": "// ",
-                    "LONGDESCRIPTION": "true",
-                    })
+                for subcomp in self.publisher.subcomponents:
+                    apt_config.write(STANZA_TEMPLATE % {
+                        "LISTPATH": self._config.overrideroot,
+                        "DISTRORELEASEONDISK": "%s/%s" % (suite, component),
+                        "DISTRORELEASEBYFILE": "%s_%s" % (suite, component),
+                        "DISTRORELEASE": "%s.%s" % (suite, component),
+                        "ARCHITECTURES": " ".join(archs),
+                        "SECTIONS": subcomp,
+                        "EXTENSIONS": '.%s' % SUBCOMPONENT_TO_EXT[subcomp],
+                        "CACHEINSERT": "%s-" % subcomp,
+                        "DISTS": os.path.basename(self._config.distsroot),
+                        "HIDEEXTRA": "// ",
+                        "LONGDESCRIPTION": "true",
+                        })
 
         # XXX: 2006-08-24 kiko: Why do we do this directory creation here?
         for comp in comps:
@@ -771,5 +799,6 @@
                 safe_mkdir(os.path.join(component_path, "i18n"))
             for arch in archs:
                 safe_mkdir(os.path.join(component_path, "binary-" + arch))
-                safe_mkdir(os.path.join(
-                    component_path, "debian-installer", "binary-" + arch))
+                for subcomp in self.publisher.subcomponents:
+                    safe_mkdir(os.path.join(
+                        component_path, subcomp, "binary-" + arch))

=== modified file 'lib/lp/archivepublisher/publishing.py'
--- lib/lp/archivepublisher/publishing.py	2013-05-03 19:18:26 +0000
+++ lib/lp/archivepublisher/publishing.py	2013-05-15 10:50:59 +0000
@@ -68,6 +68,12 @@
 GLOBAL_PUBLISHER_LOCK = 'launchpad-publisher.lock'
 
 
+FORMAT_TO_SUBCOMPONENT = {
+    BinaryPackageFormat.UDEB: 'debian-installer',
+    BinaryPackageFormat.DDEB: 'debug',
+    }
+
+
 def reorder_components(components):
     """Return a list of the components provided.
 
@@ -234,6 +240,15 @@
         return (not self.allowed_suites or
                 (distroseries.name, pocket) in self.allowed_suites)
 
+    @property
+    def subcomponents(self):
+        subcomps = []
+        if self.archive.purpose != ArchivePurpose.PARTNER:
+            subcomps.append('debian-installer')
+        if self.archive.publish_debug_symbols:
+            subcomps.append('debug')
+        return subcomps
+
     def A_publish(self, force_publishing):
         """First step in publishing: actual package publishing.
 
@@ -433,33 +448,33 @@
 
             self.log.debug("Generating Packages for %s" % arch_path)
 
+            indices = {}
             package_index_root = os.path.join(
                 self._config.distsroot, suite_name, component.name, arch_path)
-            package_index = RepositoryIndexFile(
+            indices[None] = RepositoryIndexFile(
                 package_index_root, self._config.temproot, 'Packages')
 
-            di_index_root = os.path.join(
-                self._config.distsroot, suite_name, component.name,
-                'debian-installer', arch_path)
-            di_index = RepositoryIndexFile(
-                di_index_root, self._config.temproot, 'Packages')
+            for subcomp in self.subcomponents:
+                sub_index_root = os.path.join(
+                    self._config.distsroot, suite_name, component.name,
+                    subcomp, arch_path)
+                indices[subcomp] = RepositoryIndexFile(
+                    sub_index_root, self._config.temproot, 'Packages')
 
             for bpp in distroseries.getBinaryPackagePublishing(
                     arch.architecturetag, pocket, component, self.archive):
+                subcomp = FORMAT_TO_SUBCOMPONENT.get(
+                    bpp.binarypackagerelease.binpackageformat)
+                if subcomp not in indices:
+                    # Skip anything that we're not generating indices
+                    # for, eg. ddebs where publish_debug_symbols is
+                    # disabled.
+                    continue
                 stanza = bpp.getIndexStanza().encode('utf-8') + '\n\n'
-                if (bpp.binarypackagerelease.binpackageformat in
-                    (BinaryPackageFormat.DEB, BinaryPackageFormat.DDEB)):
-                    package_index.write(stanza)
-                elif (bpp.binarypackagerelease.binpackageformat ==
-                      BinaryPackageFormat.UDEB):
-                    di_index.write(stanza)
-                else:
-                    self.log.debug(
-                        "Cannot publish %s because it is not a DEB or "
-                        "UDEB file" % bpp.displayname)
+                indices[subcomp].write(stanza)
 
-            package_index.close()
-            di_index.close()
+            for index in indices.itervalues():
+                index.close()
 
     def cannotModifySuite(self, distroseries, pocket):
         """Return True if the distroseries is stable and pocket is release."""
@@ -659,14 +674,12 @@
         """Write out a Release file for an architecture in a suite."""
         file_stub = 'Packages'
         arch_path = 'binary-' + arch_name
-        # Only the primary and PPA archives have debian-installer.
-        if self.archive.purpose != ArchivePurpose.PARTNER:
-            # Set up the debian-installer paths for main_archive.
-            # d-i paths are nested inside the component.
-            di_path = os.path.join(
-                component, "debian-installer", arch_path)
-            di_file_stub = os.path.join(di_path, file_stub)
-            all_series_files.update(get_suffixed_indices(di_file_stub))
+
+        for subcomp in self.subcomponents:
+            # Set up the subcomponent paths.
+            sub_path = os.path.join(component, subcomp, arch_path)
+            sub_file_stub = os.path.join(sub_path, file_stub)
+            all_series_files.update(get_suffixed_indices(sub_file_stub))
         self._writeSuiteArchOrSource(
             distroseries, pocket, component, 'Packages', arch_name, arch_path,
             all_series_files)

=== modified file 'lib/lp/archivepublisher/tests/test_ftparchive.py'
--- lib/lp/archivepublisher/tests/test_ftparchive.py	2013-02-27 01:17:51 +0000
+++ lib/lp/archivepublisher/tests/test_ftparchive.py	2013-05-15 10:50:59 +0000
@@ -28,6 +28,7 @@
     DevNullLogger,
     )
 from lp.soyuz.enums import (
+    BinaryPackageFormat,
     PackagePublishingPriority,
     PackagePublishingStatus,
     )
@@ -56,6 +57,7 @@
 
     def __init__(self, archive):
         self.archive = archive
+        self.subcomponents = ['debian-installer']
 
     def isAllowed(self, distroseries, pocket):
         return True
@@ -144,12 +146,22 @@
             self._logger, self._config, self._dp, self._distribution,
             self._publisher)
 
-    def _publishDefaultOverrides(self, fa, component,
-                                 phased_update_percentage=None):
-        source_overrides = FakeSelectResult([('foo', component, 'misc')])
+    def _setUpSampleDataFTPArchiveHandler(self):
+        # Reconfigure FTPArchiveHandler to retrieve sampledata records.
+        fa = self._setUpFTPArchiveHandler()
+        ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
+        hoary = ubuntu.getSeries('hoary')
+        fa.distro = ubuntu
+        fa.publisher.archive = hoary.main_archive
+        return fa, hoary
+
+    def _publishDefaultOverrides(self, fa, component, section='misc',
+                                 phased_update_percentage=None,
+                                 binpackageformat=BinaryPackageFormat.DEB):
+        source_overrides = FakeSelectResult([('foo', component, section)])
         binary_overrides = FakeSelectResult([(
-            'foo', component, 'misc', 'i386', PackagePublishingPriority.EXTRA,
-            phased_update_percentage)])
+            'foo', component, section, 'i386', PackagePublishingPriority.EXTRA,
+            binpackageformat, phased_update_percentage)])
         fa.publishOverrides('hoary-test', source_overrides, binary_overrides)
 
     def _publishDefaultFileLists(self, fa, component):
@@ -161,13 +173,7 @@
     def test_getSourcesForOverrides(self):
         # getSourcesForOverrides returns a list of tuples containing:
         # (sourcename, component, section)
-
-        # Reconfigure FTPArchiveHandler to retrieve sampledata overrides.
-        fa = self._setUpFTPArchiveHandler()
-        ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
-        hoary = ubuntu.getSeries('hoary')
-        fa.publisher.archive = hoary.main_archive
-
+        fa, hoary = self._setUpSampleDataFTPArchiveHandler()
         published_sources = fa.getSourcesForOverrides(
             hoary, PackagePublishingPocket.RELEASE)
 
@@ -188,20 +194,15 @@
         # getBinariesForOverrides returns a list of tuples containing:
         # (sourcename, component, section, archtag, priority,
         # phased_update_percentage)
-
-        # Reconfigure FTPArchiveHandler to retrieve sampledata overrides.
-        fa = self._setUpFTPArchiveHandler()
-        ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
-        hoary = ubuntu.getSeries('hoary')
-        fa.publisher.archive = hoary.main_archive
-
+        fa, hoary = self._setUpSampleDataFTPArchiveHandler()
         published_binaries = fa.getBinariesForOverrides(
             hoary, PackagePublishingPocket.RELEASE)
         expectedBinaries = [
             ('pmount', 'main', 'base', 'hppa',
-             PackagePublishingPriority.EXTRA, None),
+             PackagePublishingPriority.EXTRA, BinaryPackageFormat.DEB, None),
             ('pmount', 'universe', 'editors', 'i386',
-             PackagePublishingPriority.IMPORTANT, None),
+             PackagePublishingPriority.IMPORTANT, BinaryPackageFormat.DEB,
+             None),
             ]
         self.assertEqual(expectedBinaries, list(published_binaries))
 
@@ -256,6 +257,58 @@
                 "foo/i386\tPhased-Update-Percentage\t50",
                 result_file.read().splitlines())
 
+    def test_publishOverrides_udebs(self):
+        # udeb overrides appear in a separate file.
+        fa = self._setUpFTPArchiveHandler()
+        self._publishDefaultOverrides(
+            fa, 'main', section='debian-installer',
+            binpackageformat=BinaryPackageFormat.UDEB)
+
+        # The main override file is empty.
+        path = os.path.join(self._overdir, "override.hoary-test.main")
+        with open(path) as result_file:
+            self.assertEqual('', result_file.read())
+
+        # The binary shows up in the d-i override file.
+        path = os.path.join(
+            self._overdir, "override.hoary-test.main.debian-installer")
+        with open(path) as result_file:
+            self.assertEqual(
+                ["foo\textra\tdebian-installer"],
+                result_file.read().splitlines())
+
+    def test_publishOverrides_ddebs_disabled(self):
+        # ddebs aren't indexed if Archive.publish_debug_symbols is unset.
+        fa = self._setUpFTPArchiveHandler()
+        self._publishDefaultOverrides(
+            fa, 'main', binpackageformat=BinaryPackageFormat.DDEB)
+
+        # The main override file is empty, and there's no ddeb override
+        # file.
+        stat = os.stat(os.path.join(self._overdir, "override.hoary-test.main"))
+        self.assertEqual(0, stat.st_size)
+        self.assertFalse(
+            os.path.exists(
+                os.path.join(self._overdir, "override.hoary-test.main.debug")))
+
+    def test_publishOverrides_ddebs(self):
+        # ddebs are indexed in a subcomponent if
+        # Archive.publish_debug_symbols is set.
+        fa = self._setUpFTPArchiveHandler()
+        fa.publisher.subcomponents.append('debug')
+        self._publishDefaultOverrides(
+            fa, 'main', binpackageformat=BinaryPackageFormat.DDEB)
+
+        # The main override file is empty.
+        stat = os.stat(os.path.join(self._overdir, "override.hoary-test.main"))
+        self.assertEqual(0, stat.st_size)
+
+        # The binary shows up in the debug override file.
+        path = os.path.join(self._overdir, "override.hoary-test.main.debug")
+        with open(path) as result_file:
+            self.assertEqual(
+                ["foo\textra\tmisc"], result_file.read().splitlines())
+
     def test_generateOverrides(self):
         # generateOverrides generates all the overrides from start to finish.
         self._distribution = getUtility(IDistributionSet).getByName('ubuntu')
@@ -285,14 +338,7 @@
     def test_getSourceFiles(self):
         # getSourceFiles returns a list of tuples containing:
         # (sourcename, filename, component)
-
-        # Reconfigure FTPArchiveHandler to retrieve sampledata records.
-        fa = self._setUpFTPArchiveHandler()
-        ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
-        hoary = ubuntu.getSeries('hoary')
-        fa.distro = ubuntu
-        fa.publisher.archive = hoary.main_archive
-
+        fa, hoary = self._setUpSampleDataFTPArchiveHandler()
         sources_files = fa.getSourceFiles(
             hoary, PackagePublishingPocket.RELEASE)
         expected_files = [
@@ -305,18 +351,47 @@
     def test_getBinaryFiles(self):
         # getBinaryFiles returns a list of tuples containing:
         # (sourcename, filename, component, architecture)
-
-        # Reconfigure FTPArchiveHandler to retrieve sampledata records.
-        fa = self._setUpFTPArchiveHandler()
-        ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
-        hoary = ubuntu.getSeries('hoary')
-        fa.distro = ubuntu
-        fa.publisher.archive = hoary.main_archive
-
-        binary_files = fa.getBinaryFiles(
-            hoary, PackagePublishingPocket.RELEASE)
-        expected_files = [
-            ('pmount', 'pmount_1.9-1_all.deb', 'main', 'binary-hppa'),
+        fa, hoary = self._setUpSampleDataFTPArchiveHandler()
+        binary_files = fa.getBinaryFiles(
+            hoary, PackagePublishingPocket.RELEASE)
+        expected_files = [
+            ('pmount', 'pmount_1.9-1_all.deb', 'main', 'binary-hppa'),
+            ]
+        self.assertEqual(expected_files, list(binary_files))
+
+    def makeDDEBPub(self, series):
+        self.factory.makeBinaryPackagePublishingHistory(
+            binarypackagename=u'foo', sourcepackagename='foo', version='666',
+            archive=series.main_archive, distroarchseries=series['hppa'],
+            pocket=PackagePublishingPocket.RELEASE,
+            component=u'main', with_debug=True, with_file=True,
+            status=PackagePublishingStatus.PUBLISHED)
+
+    def test_getBinaryFiles_ddebs_disabled(self):
+        # getBinaryFiles excludes ddebs unless publish_debug_symbols is
+        # enabled.
+        fa, hoary = self._setUpSampleDataFTPArchiveHandler()
+        self.makeDDEBPub(hoary)
+        binary_files = fa.getBinaryFiles(
+            hoary, PackagePublishingPocket.RELEASE)
+        expected_files = [
+            ('pmount', 'pmount_1.9-1_all.deb', 'main', 'binary-hppa'),
+            ('foo', 'foo_666_hppa.deb', 'main', 'binary-hppa'),
+            ]
+        self.assertEqual(expected_files, list(binary_files))
+
+    def test_getBinaryFiles_ddebs_enabled(self):
+        # getBinaryFiles includes ddebs if publish_debug_symbols is
+        # enabled.
+        fa, hoary = self._setUpSampleDataFTPArchiveHandler()
+        fa.publisher.archive.publish_debug_symbols = True
+        self.makeDDEBPub(hoary)
+        binary_files = fa.getBinaryFiles(
+            hoary, PackagePublishingPocket.RELEASE)
+        expected_files = [
+            ('pmount', 'pmount_1.9-1_all.deb', 'main', 'binary-hppa'),
+            ('foo', 'foo_666_hppa.deb', 'main', 'binary-hppa'),
+            ('foo', 'foo-dbgsym_666_hppa.ddeb', 'main', 'binary-hppa'),
             ]
         self.assertEqual(expected_files, list(binary_files))
 

=== modified file 'lib/lp/archivepublisher/tests/test_publisher.py'
--- lib/lp/archivepublisher/tests/test_publisher.py	2013-02-25 05:27:36 +0000
+++ lib/lp/archivepublisher/tests/test_publisher.py	2013-05-15 10:50:59 +0000
@@ -34,6 +34,7 @@
     )
 from lp.archivepublisher.utils import RepositoryIndexFile
 from lp.registry.interfaces.distribution import IDistributionSet
+from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.pocket import (
     PackagePublishingPocket,
@@ -653,6 +654,7 @@
         allowed_suites = []
 
         cprov = getUtility(IPersonSet).getByName('cprov')
+        cprov.archive.publish_debug_symbols = True
 
         archive_publisher = getPublisher(
             cprov.archive, allowed_suites, self.logger)
@@ -666,7 +668,8 @@
             pub_source=pub_source,
             description="   My leading spaces are normalised to a single "
                         "space but not trailing.  \n    It does nothing, "
-                        "though")[0]
+                        "though",
+            with_debug=True)
 
         # Ignored (deleted) source publication that will not be listed in
         # the index and a pending 'udeb' binary package.
@@ -767,6 +770,37 @@
              ''],
             index_contents)
 
+        # 'debug' too, when publish_debug_symbols is enabled.
+        index_contents = self._checkCompressedFile(
+            archive_publisher,
+            os.path.join('debug', 'binary-i386', 'Packages.bz2'),
+            os.path.join('debug', 'binary-i386', 'Packages'))
+
+        index_contents = self._checkCompressedFile(
+            archive_publisher,
+            os.path.join('debug', 'binary-i386', 'Packages.gz'),
+            os.path.join('debug', 'binary-i386', 'Packages'))
+
+        self.assertEqual(
+            ['Package: foo-bin-dbgsym',
+             'Source: foo',
+             'Priority: standard',
+             'Section: base',
+             'Installed-Size: 100',
+             'Maintainer: Foo Bar <foo@xxxxxxx>',
+             'Architecture: all',
+             'Version: 666',
+             'Filename: pool/main/f/foo/foo-bin-dbgsym_666_all.ddeb',
+             'Size: 18',
+             'MD5sum: 008409e7feb1c24a6ccab9f6a62d24c5',
+             'SHA1: 30b7b4e583fa380772c5a40e428434628faef8cf',
+             'Description: Foo app is great',
+             ' My leading spaces are normalised to a single space but not '
+             'trailing.  ',
+             ' It does nothing, though',
+             ''],
+            index_contents)
+
         # We always regenerate all Releases file for a given suite.
         self.assertTrue(
             ('breezy-autotest', PackagePublishingPocket.RELEASE) in
@@ -1502,9 +1536,11 @@
         return self.factory.makeSourcePackagePublishingHistory(
             distroseries=series, status=PackagePublishingStatus.PENDING)
 
-    def makePublisher(self, series):
-        """Create a publisher for a given distroseries."""
-        return getPublisher(series.main_archive, None, DevNullLogger())
+    def makePublisher(self, archive_or_series):
+        """Create a publisher for a given archive or distroseries."""
+        if IDistroSeries.providedBy(archive_or_series):
+            archive_or_series = archive_or_series.main_archive
+        return getPublisher(archive_or_series, None, DevNullLogger())
 
     def makeFakeReleaseData(self):
         """Create a fake `debian.deb822.Release`.
@@ -1571,3 +1607,16 @@
         self.assertEqual(1, len(timestamps))
         # The filesystem may round off subsecond parts of timestamps.
         self.assertEqual(int(now), int(list(timestamps)[0]))
+
+    def test_subcomponents(self):
+        primary = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
+        self.assertEqual(
+            ['debian-installer'],
+            self.makePublisher(primary).subcomponents)
+        primary.publish_debug_symbols = True
+        self.assertEqual(
+            ['debian-installer', 'debug'],
+            self.makePublisher(primary).subcomponents)
+
+        partner = self.factory.makeArchive(purpose=ArchivePurpose.PARTNER)
+        self.assertEqual([], self.makePublisher(partner).subcomponents)

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2013-05-14 11:00:54 +0000
+++ lib/lp/testing/factory.py	2013-05-15 10:50:59 +0000
@@ -3742,7 +3742,8 @@
                                            source_package_release=None,
                                            binpackageformat=None,
                                            sourcepackagename=None,
-                                           with_debug=False):
+                                           version=None,
+                                           with_debug=False, with_file=False):
         """Make a `BinaryPackagePublishingHistory`."""
         if distroarchseries is None:
             if archive is None:
@@ -3759,7 +3760,7 @@
                 purpose=ArchivePurpose.PRIMARY)
 
         if pocket is None:
-            pocket = self.getAnyPocket() 
+            pocket = self.getAnyPocket()
         if status is None:
             status = PackagePublishingStatus.PENDING
 
@@ -3776,10 +3777,25 @@
                 pocket=pocket, source_package_release=source_package_release,
                 sourcepackagename=sourcepackagename)
             binarypackagerelease = self.makeBinaryPackageRelease(
-                binarypackagename=binarypackagename,
+                binarypackagename=binarypackagename, version=version,
                 build=binarypackagebuild,
                 component=component, binpackageformat=binpackageformat,
                 section_name=section_name, priority=priority)
+            if with_file:
+                ext = {
+                    BinaryPackageFormat.DEB: 'deb',
+                    BinaryPackageFormat.UDEB: 'udeb',
+                    BinaryPackageFormat.DDEB: 'ddeb',
+                    }[binarypackagerelease.binpackageformat]
+                lfa = self.makeLibraryFileAlias(
+                    filename='%s_%s_%s.%s' % (
+                        binarypackagerelease.binarypackagename.name,
+                        binarypackagerelease.version,
+                        binarypackagebuild.distro_arch_series.architecturetag,
+                        ext))
+                self.makeBinaryPackageFile(
+                    binarypackagerelease=binarypackagerelease,
+                    library_file=lfa)
 
         if datecreated is None:
             datecreated = self.getUniqueDate()
@@ -3798,7 +3814,9 @@
             naked_bpph.datepublished = UTC_NOW
         if with_debug:
             debug_bpph = self.makeBinaryPackagePublishingHistory(
-                distroarchseries=distroarchseries,
+                binarypackagename=(
+                    binarypackagerelease.binarypackagename.name + '-dbgsym'),
+                version=version, distroarchseries=distroarchseries,
                 component=component, section_name=binarypackagerelease.section,
                 priority=priority, status=status,
                 scheduleddeletiondate=scheduleddeletiondate,
@@ -3806,7 +3824,8 @@
                 pocket=pocket, archive=archive,
                 source_package_release=source_package_release,
                 binpackageformat=BinaryPackageFormat.DDEB,
-                sourcepackagename=sourcepackagename)
+                sourcepackagename=sourcepackagename,
+                with_file=with_file)
             removeSecurityProxy(bpph.binarypackagerelease).debug_package = (
                 debug_bpph.binarypackagerelease)
             return bpph, debug_bpph
@@ -3881,6 +3900,8 @@
             binpackageformat = BinaryPackageFormat.DEB
         if component is None:
             component = build.source_package_release.component
+        elif isinstance(component, unicode):
+            component = getUtility(IComponentSet)[component]
         if isinstance(section_name, basestring):
             section_name = self.makeSection(section_name)
         section = section_name or build.source_package_release.section