예제 #1
0
def test_construct_bash_launcher():
    linker, library_path, executable = '../lib/ld-linux.so.2', '../lib/', 'grep'
    script_content = construct_bash_launcher(linker=linker,
                                             library_path=library_path,
                                             executable=executable)
    assert script_content.startswith('#! /bin/bash\n')
    assert linker in script_content
    assert executable in script_content
예제 #2
0
def create_unpackaged_bundle(executables, rename=[], ldd='ldd'):
    """Creates a temporary directory containing the unpackaged contents of the bundle."""
    root_directory = tempfile.mkdtemp(prefix='exodus-bundle-')
    try:
        # Make the top-level bundle directories.
        bin_directory = os.path.join(root_directory, 'bin')
        os.makedirs(bin_directory)
        lib_directory = os.path.join(root_directory, 'lib')
        os.makedirs(lib_directory)
        bundles_directory = os.path.join(root_directory, 'bundles')

        # Loop through and package each executable.
        assert len(executables), 'No executables were specified.'
        assert len(executables) >= len(rename), \
            'More renamed options were included than executables.'
        # Pad the rename's so that they have the same length for the `zip()` call.
        rename = rename + [None for i in range(len(executables) - len(rename))]
        for name, executable in zip(rename, map(resolve_binary, executables)):
            # Make the bundle subdirectories for this executable.
            binary_name = (name or os.path.basename(executable)).replace(
                os.sep, '')
            binary_hash = sha256_hash(executable)
            bundle_directory = os.path.join(bundles_directory, binary_hash)
            bundle_bin_directory = os.path.join(bundle_directory, 'bin')
            os.makedirs(bundle_bin_directory)
            bundle_lib_directory = os.path.join(bundle_directory, 'lib')
            os.makedirs(bundle_lib_directory)

            # Copy over the library dependencies and link them.
            dependencies = find_all_library_dependencies(ldd, executable)
            for dependency in dependencies:
                # Create the `lib/{hash}` library file.
                dependency_name = os.path.basename(dependency)
                dependency_hash = sha256_hash(dependency)
                dependency_path = os.path.join(lib_directory, dependency_hash)
                if not os.path.exists(dependency_path):
                    shutil.copy(dependency, dependency_path)

                # Create a link to the actual library from inside the bundle lib directory.
                bundle_dependency_link = os.path.join(bundle_lib_directory,
                                                      dependency_name)
                relative_dependency_path = os.path.relpath(
                    dependency_path, bundle_lib_directory)
                if not os.path.exists(bundle_dependency_link):
                    os.symlink(relative_dependency_path,
                               bundle_dependency_link)
                else:
                    link_destination = os.readlink(bundle_dependency_link)
                    link_destination = os.path.join(bundle_lib_directory,
                                                    link_destination)
                    # This is only a problem if the duplicate libraries have different content.
                    if os.path.normpath(link_destination) != os.path.normpath(
                            dependency_path):
                        raise LibraryConflictError(
                            'A library called "%s" was linked more than once.'
                            % dependency_name)

            # Copy over the executable.
            bundle_executable_path = os.path.join(bundle_bin_directory,
                                                  binary_name)
            shutil.copy(executable, bundle_executable_path)

            # Construct the launcher.
            linker_candidates = list(
                filter(lambda candidate: candidate.startswith('ld-'),
                       (os.path.basename(dependency)
                        for dependency in dependencies)))
            assert len(linker_candidates) > 0, 'No linker candidates found.'
            assert len(
                linker_candidates) < 2, 'Multiple linker candidates found.'
            [linker] = linker_candidates
            # Try a c launcher first and fallback.
            try:
                launcher_path = '%s-launcher' % bundle_executable_path
                launcher_content = construct_binary_launcher(
                    linker=linker, binary=binary_name)
                with open(launcher_path, 'wb') as f:
                    f.write(launcher_content)
            except CompilerNotFoundError:
                logger.warn((
                    'Installing either the musl or diet C libraries will result in more efficient '
                    'launchers (currently using bash fallbacks instead).'))
                launcher_path = '%s-launcher.sh' % bundle_executable_path
                launcher_content = construct_bash_launcher(linker=linker,
                                                           binary=binary_name)
                with open(launcher_path, 'w') as f:
                    f.write(launcher_content)
            shutil.copymode(bundle_executable_path, launcher_path)
            executable_link = os.path.join(bin_directory, binary_name)
            relative_launcher_path = os.path.relpath(launcher_path,
                                                     bin_directory)
            os.symlink(relative_launcher_path, executable_link)

        return root_directory
    except:  # noqa: E722
        shutil.rmtree(root_directory)
        raise
예제 #3
0
    def create_launcher(self,
                        working_directory,
                        bundle_root,
                        linker_basename,
                        symlink_basename,
                        shell_launcher=False):
        """Creates a launcher at `source` for `destination`.

        Note:
            If an `entry_point` has been specified, it will also be created.
        Args:
            working_directory (str): The root that the `destination` will be joined with.
            bundle_root (str): The root that `source` will be joined with.
            linker_basename (str): The basename of the linker to place in the same directory.
            symlink_basename (str): The basename of the symlink to the actual executable.
            shell_launcher (bool, optional): Forces the use of shell script launcher instead of
                attempting to compile first using musl or diet c.
        Returns:
            str: The normalized and absolute path to the launcher.
        """
        destination_path = os.path.join(working_directory, self.destination)
        source_path = os.path.join(bundle_root, self.source)

        # Create the symlink.
        source_parent = os.path.dirname(source_path)
        if not os.path.exists(source_parent):
            os.makedirs(source_parent)
        relative_destination_path = os.path.relpath(destination_path,
                                                    source_parent)
        symlink_path = os.path.join(source_parent, symlink_basename)
        os.symlink(relative_destination_path, symlink_path)
        executable = os.path.join('.', symlink_basename)

        # Copy over the linker.
        linker_path = os.path.join(source_parent, linker_basename)
        if not os.path.exists(linker_path):
            shutil.copy(self.elf.linker_file.path, linker_path)
        else:
            assert filecmp.cmp(self.elf.linker_file.path, linker_path), \
                'The "%s" linker file already exists and has differing contents.' % linker_path
        linker = os.path.join('.', linker_basename)

        # Construct the library path
        original_file_parent = os.path.dirname(self.path)
        library_paths = os.environ.get('LD_LIBRARY_PATH', '').split(':')
        library_paths += [
            '/lib64', '/usr/lib64', '/lib', '/usr/lib', '/lib32', '/usr/lib32'
        ]
        for dependency in self.elf.dependencies:
            library_paths.append(os.path.dirname(dependency.path))
        relative_library_paths = []
        for directory in library_paths:
            if not len(directory):
                continue

            # Get the actual absolute path for the library directory.
            directory = os.path.normpath(os.path.abspath(directory))
            if self.chroot:
                directory = os.path.join(self.chroot,
                                         os.path.relpath(directory, '/'))

            # Convert it into a path relative to the launcher/source.
            relative_library_path = os.path.relpath(directory,
                                                    original_file_parent)
            if relative_library_path not in relative_library_paths:
                relative_library_paths.append(relative_library_path)
        library_path = ':'.join(relative_library_paths)

        # Determine whether this is a "full" linker (*e.g.* GNU linker).
        with open(self.elf.linker_file.path, 'rb') as f:
            linker_content = f.read()
            full_linker = (linker_content.find(b'inhibit-rpath') > -1)

        # Try a c launcher first and fallback.
        try:
            if shell_launcher:
                raise CompilerNotFoundError()

            launcher_content = construct_binary_launcher(
                linker=linker,
                library_path=library_path,
                executable=executable,
                full_linker=full_linker)
            with open(source_path, 'wb') as f:
                f.write(launcher_content)
        except CompilerNotFoundError:
            if not shell_launcher:
                logger.warning((
                    'Installing either the musl or diet C libraries will result in more efficient '
                    'launchers (currently using bash fallbacks instead).'))
            launcher_content = construct_bash_launcher(
                linker=linker,
                library_path=library_path,
                executable=executable,
                full_linker=full_linker)
            with open(source_path, 'w') as f:
                f.write(launcher_content)
        shutil.copymode(self.path, source_path)

        return os.path.normpath(os.path.abspath(source_path))
예제 #4
0
def test_construct_bash_launcher():
    linker, binary = 'ld-linux.so.2', 'grep'
    script_content = construct_bash_launcher(linker=linker, binary=binary)
    assert script_content.startswith('#! /bin/bash\n')
    assert linker in script_content
    assert binary in script_content