コード例 #1
0
    def test_converted_package_installation(self):
        """
        Install a converted package on the test system and verify that it works.

        This test only runs on Travis CI, it's a functional test that uses
        py2deb to convert a Python package to a Debian package, installs
        that package on the local system and verifies that the system wide
        Python installation can successfully import the installed package.
        """
        if os.environ.get('TRAVIS') != 'true':
            self.skipTest(
                "This test should only be run on Travis CI! (set $TRAVIS_CI=true to override)"
            )
        with TemporaryDirectory() as directory:
            version = '1.1.8'
            # Run the conversion command.
            converter = self.create_isolated_converter()
            converter.set_repository(directory)
            archives, relationships = converter.convert(
                ['setproctitle==%s' % version])
            # Find and install the generated *.deb archive.
            pathname = find_package_archive(
                archives, fix_name_prefix('python-setproctitle'))
            execute('dpkg', '--install', pathname, sudo=True)
            # Verify that the installed package can be imported.
            interpreter = '/usr/bin/%s' % python_version()
            output = execute(interpreter,
                             '-c',
                             '; '.join([
                                 'import setproctitle',
                                 'print(setproctitle.__version__)',
                             ]),
                             capture=True)
            assert output == version
コード例 #2
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)
コード例 #3
0
    def test_conversion_of_simple_package(self):
        """
        Convert a simple Python package without any dependencies.

        Converts coloredlogs_ and sanity checks the result. Performs several static
        checks on the metadata and contents of the resulting package archive.

        .. _coloredlogs: https://pypi.org/project/coloredlogs
        """
        # Use a temporary directory as py2deb's repository directory so that we
        # can easily find the *.deb archive generated by py2deb.
        with TemporaryDirectory() as directory:
            # Run the conversion twice to check that existing archives are not overwritten.
            last_modified_time = 0
            for i in range(2):
                # Prepare a control file to be patched.
                control_file = os.path.join(directory, 'control')
                with open(control_file, 'w') as handle:
                    handle.write('Depends: vim\n')
                # Run the conversion command.
                exit_code, output = run_cli(
                    main,
                    '--verbose',
                    '--yes',
                    '--repository=%s' % directory,
                    '--report-dependencies=%s' % control_file,
                    'coloredlogs==0.5',
                )
                assert exit_code == 0
                # Check that the control file was patched.
                control_fields = load_control_file(control_file)
                assert control_fields['Depends'].matches('vim')
                assert control_fields['Depends'].matches(
                    fix_name_prefix('python-coloredlogs'), '0.5')
                # Find the generated Debian package archive.
                archives = glob.glob('%s/*.deb' % directory)
                logger.debug("Found generated archive(s): %s", archives)
                assert len(archives) == 1
                # Verify that existing archives are not overwritten.
                if not last_modified_time:
                    # Capture the last modified time of the archive in the first iteration.
                    last_modified_time = os.path.getmtime(archives[0])
                else:
                    # Verify the last modified time of the archive in the second iteration.
                    assert last_modified_time == os.path.getmtime(archives[0])
                # Use deb-pkg-tools to inspect the generated package.
                metadata, contents = inspect_package(archives[0])
                logger.debug("Metadata of generated package: %s",
                             dict(metadata))
                logger.debug("Contents of generated package: %s",
                             dict(contents))
                # Check the package metadata.
                assert metadata['Package'] == fix_name_prefix(
                    'python-coloredlogs')
                assert metadata['Version'].startswith('0.5')
                assert metadata['Architecture'] == 'all'
                # There should be exactly one dependency: some version of Python.
                assert metadata['Depends'].matches(python_version())
                # Don't care about the format here as long as essential information is retained.
                assert 'Peter Odding' in metadata['Maintainer']
                assert '*****@*****.**' in metadata['Maintainer']
                # Check the package contents.
                # Check for the two *.py files that should be installed by the package.
                assert find_file(
                    contents,
                    '/usr/lib/py*/dist-packages/coloredlogs/__init__.py')
                assert find_file(
                    contents,
                    '/usr/lib/py*/dist-packages/coloredlogs/converter.py')
                # Make sure the file ownership and permissions are sane.
                archive_entry = find_file(
                    contents,
                    '/usr/lib/py*/dist-packages/coloredlogs/__init__.py')
                assert archive_entry.owner == 'root'
                assert archive_entry.group == 'root'
                assert archive_entry.permissions == '-rw-r--r--'
コード例 #4
0
ファイル: package.py プロジェクト: anjith2006/py2deb
    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)