Beispiel #1
0
 def test_control_field_parsing(self):
     deb822_package = Deb822([
         'Package: python-py2deb',
         'Depends: python-deb-pkg-tools, python-pip, python-pip-accel',
         'Installed-Size: 42'
     ])
     parsed_info = parse_control_fields(deb822_package)
     self.assertEqual(
         parsed_info, {
             'Package':
             'python-py2deb',
             'Depends':
             RelationshipSet(Relationship(name=u'python-deb-pkg-tools'),
                             Relationship(name=u'python-pip'),
                             Relationship(name=u'python-pip-accel')),
             'Installed-Size':
             42
         })
     # Test backwards compatibility with the old interface where `Depends'
     # like fields were represented as a list of strings (shallow parsed).
     parsed_info['Depends'] = [unicode(r) for r in parsed_info['Depends']]
     self.assertEqual(unparse_control_fields(parsed_info), deb822_package)
     # Test compatibility with fields like `Depends' containing a string.
     parsed_info['Depends'] = deb822_package['Depends']
     self.assertEqual(unparse_control_fields(parsed_info), deb822_package)
Beispiel #2
0
 def test_control_field_parsing(self):
     """Test the parsing of control file fields."""
     deb822_package = Deb822([
         'Package: python-py2deb',
         'Depends: python-deb-pkg-tools, python-pip, python-pip-accel',
         'Installed-Size: 42'
     ])
     parsed_info = parse_control_fields(deb822_package)
     assert parsed_info == {
         'Package':
         'python-py2deb',
         'Depends':
         RelationshipSet(Relationship(name=u'python-deb-pkg-tools'),
                         Relationship(name=u'python-pip'),
                         Relationship(name=u'python-pip-accel')),
         'Installed-Size':
         42
     }
     # Test backwards compatibility with the old interface where `Depends'
     # like fields were represented as a list of strings (shallow parsed).
     parsed_info['Depends'] = [text_type(r) for r in parsed_info['Depends']]
     assert unparse_control_fields(parsed_info) == deb822_package
     # Test compatibility with fields like `Depends' containing a string.
     parsed_info['Depends'] = deb822_package['Depends']
     assert unparse_control_fields(parsed_info) == deb822_package
Beispiel #3
0
def scan_packages(repository, packages_file=None, cache=None):
    """
    A reimplementation of the ``dpkg-scanpackages -m`` command in Python.

    Updates a ``Packages`` file based on the Debian package archive(s) found in
    the given directory. Uses :py:class:`.PackageCache` to (optionally) speed
    up the process significantly by caching package metadata and hashes on
    disk. This explains why this function can be much faster than
    ``dpkg-scanpackages -m``.

    :param repository: The pathname of a directory containing Debian
                       package archives (a string).
    :param packages_file: The pathname of the ``Packages`` file to update
                          (a string). Defaults to the ``Packages`` file in
                          the given directory.
    :param cache: The :py:class:`.PackageCache` to use (defaults to ``None``).
    """
    # By default the `Packages' file inside the repository is updated.
    if not packages_file:
        packages_file = os.path.join(repository, 'Packages')
    # Update the `Packages' file.
    timer = Timer()
    package_archives = glob.glob(os.path.join(repository, '*.deb'))
    num_packages = len(package_archives)
    spinner = Spinner(total=num_packages)
    with open(packages_file, 'wb') as handle:
        for i, archive in enumerate(optimize_order(package_archives), start=1):
            fields = dict(inspect_package_fields(archive, cache=cache))
            fields.update(get_packages_entry(archive, cache=cache))
            deb822_dict = unparse_control_fields(fields)
            deb822_dict.dump(handle)
            handle.write(b'\n')
            spinner.step(label="Scanning package metadata", progress=i)
    spinner.clear()
    logger.debug("Wrote %i entries to output Packages file in %s.", num_packages, timer)
Beispiel #4
0
 def test_control_field_parsing(self):
     deb822_package = Deb822(['Package: python-py2deb',
                              'Depends: python-deb-pkg-tools, python-pip, python-pip-accel',
                              'Installed-Size: 42'])
     parsed_info = parse_control_fields(deb822_package)
     self.assertEqual(parsed_info,
                      {'Package': 'python-py2deb',
                       'Depends': RelationshipSet(
                           Relationship(name=u'python-deb-pkg-tools'),
                           Relationship(name=u'python-pip'),
                           Relationship(name=u'python-pip-accel')),
                       'Installed-Size': 42})
     # Test backwards compatibility with the old interface where `Depends'
     # like fields were represented as a list of strings (shallow parsed).
     parsed_info['Depends'] = [unicode(r) for r in parsed_info['Depends']]
     self.assertEqual(unparse_control_fields(parsed_info), deb822_package)
     # Test compatibility with fields like `Depends' containing a string.
     parsed_info['Depends'] = deb822_package['Depends']
     self.assertEqual(unparse_control_fields(parsed_info), deb822_package)
Beispiel #5
0
 def test_control_field_parsing(self):
     """Test the parsing of control file fields."""
     deb822_package = Deb822(['Package: python-py2deb',
                              'Depends: python-deb-pkg-tools, python-pip, python-pip-accel',
                              'Installed-Size: 42'])
     parsed_info = parse_control_fields(deb822_package)
     assert parsed_info == {'Package': 'python-py2deb',
                            'Depends': RelationshipSet(
                                Relationship(name=u'python-deb-pkg-tools'),
                                Relationship(name=u'python-pip'),
                                Relationship(name=u'python-pip-accel')),
                            'Installed-Size': 42}
     # Test backwards compatibility with the old interface where `Depends'
     # like fields were represented as a list of strings (shallow parsed).
     parsed_info['Depends'] = [text_type(r) for r in parsed_info['Depends']]
     assert unparse_control_fields(parsed_info) == deb822_package
     # Test compatibility with fields like `Depends' containing a string.
     parsed_info['Depends'] = deb822_package['Depends']
     assert unparse_control_fields(parsed_info) == deb822_package
Beispiel #6
0
def scan_packages(repository, packages_file=None, cache=None):
    """
    A reimplementation of the ``dpkg-scanpackages -m`` command in Python.

    Updates a ``Packages`` file based on the Debian package archive(s) found in
    the given directory. Uses :class:`.PackageCache` to (optionally) speed
    up the process significantly by caching package metadata and hashes on
    disk. This explains why this function can be much faster than
    ``dpkg-scanpackages -m``.

    :param repository: The pathname of a directory containing Debian
                       package archives (a string).
    :param packages_file: The pathname of the ``Packages`` file to update
                          (a string). Defaults to the ``Packages`` file in
                          the given directory.
    :param cache: The :class:`.PackageCache` to use (defaults to :data:`None`).
    """
    # By default the `Packages' file inside the repository is updated.
    if not packages_file:
        packages_file = os.path.join(repository, 'Packages')
    # Update the `Packages' file.
    timer = Timer()
    package_archives = glob.glob(os.path.join(repository, '*.deb'))
    num_packages = len(package_archives)
    spinner = Spinner(total=num_packages)
    with open(packages_file, 'wb') as handle:
        for i, archive in enumerate(optimize_order(package_archives), start=1):
            fields = dict(inspect_package_fields(archive, cache=cache))
            fields.update(get_packages_entry(archive, cache=cache))
            deb822_dict = unparse_control_fields(fields)
            deb822_dict.dump(handle)
            handle.write(b'\n')
            spinner.step(label="Scanning package metadata", progress=i)
    spinner.clear()
    logger.debug("Wrote %i entries to output Packages file in %s.",
                 num_packages, timer)
Beispiel #7
0
    def convert(self):
        """
        Convert current package from Python package to Debian package.

        :returns: The pathname of the generated ``*.deb`` archive.
        """
        with TemporaryDirectory(prefix='py2deb-build-') as build_directory:

            # Prepare the absolute pathname of the Python interpreter on the
            # target system. This pathname will be embedded in the first line
            # of executable scripts (including the post-installation and
            # pre-removal scripts).
            python_executable = '/usr/bin/%s' % python_version()

            # Unpack the binary distribution archive provided by pip-accel inside our build directory.
            build_install_prefix = os.path.join(
                build_directory, self.converter.install_prefix.lstrip('/'))
            self.converter.pip_accel.bdists.install_binary_dist(
                members=self.transform_binary_dist(python_executable),
                prefix=build_install_prefix,
                python=python_executable,
                virtualenv_compatible=False,
            )

            # Determine the directory (at build time) where the *.py files for
            # Python modules are located (the site-packages equivalent).
            if self.has_custom_install_prefix:
                build_modules_directory = os.path.join(build_install_prefix,
                                                       'lib')
            else:
                # The /py*/ pattern below is intended to match both /pythonX.Y/ and /pypyX.Y/.
                dist_packages_directories = glob.glob(
                    os.path.join(build_install_prefix,
                                 'lib/py*/dist-packages'))
                if len(dist_packages_directories) != 1:
                    msg = "Expected to find a single 'dist-packages' directory inside converted package!"
                    raise Exception(msg)
                build_modules_directory = dist_packages_directories[0]

            # Determine the directory (at installation time) where the *.py
            # files for Python modules are located.
            install_modules_directory = os.path.join(
                '/', os.path.relpath(build_modules_directory, build_directory))

            # Execute a user defined command inside the directory where the Python modules are installed.
            command = self.converter.scripts.get(self.python_name.lower())
            if command:
                execute(command,
                        directory=build_modules_directory,
                        logger=logger)

            # Determine the package's dependencies, starting with the currently
            # running version of Python and the Python requirements converted
            # to Debian packages.
            dependencies = [python_version()] + self.debian_dependencies

            # Check if the converted package contains any compiled *.so files.
            object_files = find_object_files(build_directory)
            if object_files:
                # Strip debugging symbols from the object files.
                strip_object_files(object_files)
                # Determine system dependencies by analyzing the linkage of the
                # *.so file(s) found in the converted package.
                dependencies += find_system_dependencies(object_files)

            # Make up some control file fields ... :-)
            architecture = self.determine_package_architecture(object_files)
            control_fields = unparse_control_fields(
                dict(package=self.debian_name,
                     version=self.debian_version,
                     maintainer=self.debian_maintainer,
                     description=self.debian_description,
                     architecture=architecture,
                     depends=dependencies,
                     priority='optional',
                     section='python'))

            # Automatically add the Mercurial global revision id when available.
            if self.vcs_revision:
                control_fields['Vcs-Hg'] = self.vcs_revision

            # Apply user defined control field overrides from `stdeb.cfg'.
            control_fields = self.load_control_field_overrides(control_fields)

            # Create the DEBIAN directory.
            debian_directory = os.path.join(build_directory, 'DEBIAN')
            os.mkdir(debian_directory)

            # Generate the DEBIAN/control file.
            control_file = os.path.join(debian_directory, 'control')
            logger.debug("Saving control file fields to %s: %s", control_file,
                         control_fields)
            with open(control_file, 'wb') as handle:
                control_fields.dump(handle)

            # Lintian is a useful tool to find mistakes in Debian binary
            # packages however Lintian checks from the perspective of a package
            # included in the official Debian repositories. Because py2deb
            # doesn't and probably never will generate such packages some
            # messages emitted by Lintian are useless (they merely point out
            # how the internals of py2deb work). Because of this we silence
            # `known to be irrelevant' messages from Lintian using overrides.
            if self.converter.lintian_ignore:
                overrides_directory = os.path.join(
                    build_directory,
                    'usr',
                    'share',
                    'lintian',
                    'overrides',
                )
                overrides_file = os.path.join(overrides_directory,
                                              self.debian_name)
                os.makedirs(overrides_directory)
                with open(overrides_file, 'w') as handle:
                    for tag in self.converter.lintian_ignore:
                        handle.write('%s: %s\n' % (self.debian_name, tag))

            # Find the alternatives relevant to the package we're building.
            alternatives = set(
                (link, path) for link, path in self.converter.alternatives
                if os.path.isfile(
                    os.path.join(build_directory, path.lstrip('/'))))

            # Generate post-installation and pre-removal maintainer scripts.
            self.generate_maintainer_script(
                filename=os.path.join(debian_directory, 'postinst'),
                python_executable=python_executable,
                function='post_installation_hook',
                package_name=self.debian_name,
                alternatives=alternatives,
                modules_directory=install_modules_directory,
                namespaces=self.namespaces)
            self.generate_maintainer_script(
                filename=os.path.join(debian_directory, 'prerm'),
                python_executable=python_executable,
                function='pre_removal_hook',
                package_name=self.debian_name,
                alternatives=alternatives,
                modules_directory=install_modules_directory,
                namespaces=self.namespaces)

            # Enable a user defined Python callback to manipulate the resulting
            # binary package before it's turned into a *.deb archive (e.g.
            # manipulate the contents or change the package metadata).
            if self.converter.python_callback:
                logger.debug("Invoking user defined Python callback ..")
                self.converter.python_callback(self.converter, self,
                                               build_directory)
                logger.debug("User defined Python callback finished!")

            return build_package(directory=build_directory,
                                 check_package=self.converter.lintian_enabled,
                                 copy_files=False)
Beispiel #8
0
    def convert(self):
        """
        Convert current package from Python package to Debian package.

        :returns: The pathname of the generated ``*.deb`` archive.
        """
        with TemporaryDirectory(prefix='py2deb-build-') as build_directory:

            # Prepare the absolute pathname of the Python interpreter on the
            # target system. This pathname will be embedded in the first line
            # of executable scripts (including the post-installation and
            # pre-removal scripts).
            python_executable = '/usr/bin/%s' % python_version()

            # Unpack the binary distribution archive provided by pip-accel inside our build directory.
            build_install_prefix = os.path.join(build_directory, self.converter.install_prefix.lstrip('/'))
            self.converter.pip_accel.bdists.install_binary_dist(
                members=self.transform_binary_dist(),
                prefix=build_install_prefix,
                python=python_executable,
                virtualenv_compatible=False,
            )

            # Determine the directory (at build time) where the *.py files for
            # Python modules are located (the site-packages equivalent).
            if self.has_custom_install_prefix:
                build_modules_directory = os.path.join(build_install_prefix, 'lib')
            else:
                dist_packages_directories = glob.glob(os.path.join(build_install_prefix, 'lib/python*/dist-packages'))
                if len(dist_packages_directories) != 1:
                    msg = "Expected to find a single 'dist-packages' directory inside converted package!"
                    raise Exception(msg)
                build_modules_directory = dist_packages_directories[0]

            # Determine the directory (at installation time) where the *.py
            # files for Python modules are located.
            install_modules_directory = os.path.join('/', os.path.relpath(build_modules_directory, build_directory))

            # Execute a user defined command inside the directory where the Python modules are installed.
            command = self.converter.scripts.get(self.python_name.lower())
            if command:
                execute(command, directory=build_modules_directory, logger=logger)

            # Determine the package's dependencies, starting with the currently
            # running version of Python and the Python requirements converted
            # to Debian packages.
            dependencies = [python_version()] + self.debian_dependencies

            # Check if the converted package contains any compiled *.so files.
            shared_object_files = self.find_shared_object_files(build_directory)
            if shared_object_files:
                # Determine system dependencies by analyzing the linkage of the
                # *.so file(s) found in the converted package.
                dependencies += self.find_system_dependencies(shared_object_files)

            # Make up some control file fields ... :-)
            architecture = self.determine_package_architecture(shared_object_files)
            control_fields = unparse_control_fields(dict(package=self.debian_name,
                                                         version=self.debian_version,
                                                         maintainer=self.debian_maintainer,
                                                         description=self.debian_description,
                                                         architecture=architecture,
                                                         depends=dependencies,
                                                         priority='optional',
                                                         section='python'))

            # Automatically add the Mercurial global revision id when available.
            if self.vcs_revision:
                control_fields['Vcs-Hg'] = self.vcs_revision

            # Apply user defined control field overrides from `stdeb.cfg'.
            control_fields = self.load_control_field_overrides(control_fields)

            # Create the DEBIAN directory.
            debian_directory = os.path.join(build_directory, 'DEBIAN')
            os.mkdir(debian_directory)

            # Generate the DEBIAN/control file.
            control_file = os.path.join(debian_directory, 'control')
            logger.debug("Saving control file fields to %s: %s", control_file, control_fields)
            with open(control_file, 'wb') as handle:
                control_fields.dump(handle)

            # Lintian is a useful tool to find mistakes in Debian binary
            # packages however Lintian checks from the perspective of a package
            # included in the official Debian repositories. Because py2deb
            # doesn't and probably never will generate such packages some
            # messages emitted by Lintian are useless (they merely point out
            # how the internals of py2deb work). Because of this we silence
            # `known to be irrelevant' messages from Lintian using overrides.
            overrides_directory = os.path.join(build_directory, 'usr', 'share',
                                               'lintian', 'overrides')
            overrides_file = os.path.join(overrides_directory, self.debian_name)
            os.makedirs(overrides_directory)
            with open(overrides_file, 'w') as handle:
                for tag in ['debian-changelog-file-missing',
                            'embedded-javascript-library',
                            'extra-license-file',
                            'unknown-control-interpreter',
                            'vcs-field-uses-unknown-uri-format']:
                    handle.write('%s: %s\n' % (self.debian_name, tag))

            # Find the alternatives relevant to the package we're building.
            alternatives = set((link, path) for link, path in self.converter.alternatives
                               if os.path.isfile(os.path.join(build_directory, path.lstrip('/'))))

            # Generate post-installation and pre-removal maintainer scripts.
            self.generate_maintainer_script(filename=os.path.join(debian_directory, 'postinst'),
                                            python_executable=python_executable,
                                            function='post_installation_hook',
                                            package_name=self.debian_name,
                                            alternatives=alternatives,
                                            modules_directory=install_modules_directory,
                                            namespaces=self.namespaces)
            self.generate_maintainer_script(filename=os.path.join(debian_directory, 'prerm'),
                                            python_executable=python_executable,
                                            function='pre_removal_hook',
                                            package_name=self.debian_name,
                                            alternatives=alternatives,
                                            modules_directory=install_modules_directory,
                                            namespaces=self.namespaces)

            # Enable a user defined Python callback to manipulate the resulting
            # binary package before it's turned into a *.deb archive (e.g.
            # manipulate the contents or change the package metadata).
            if self.converter.python_callback:
                logger.debug("Invoking user defined Python callback ..")
                self.converter.python_callback(self.converter, self, build_directory)
                logger.debug("User defined Python callback finished!")

            return build_package(directory=build_directory,
                                 check_package=self.converter.lintian_enabled,
                                 copy_files=False)
def inspect(path):
	sys.stdout.write('Inspecting: {0}\n'.format(path))
	info = repo.inspect_package_fields(path)
	info = dict(info)
	info.update(repo.get_packages_entry(path))
	return dict(control.unparse_control_fields(info))