Exemplo n.º 1
0
def append_rpath_within_wheel(lib_name: str, rpath: str, wheel_base_dir: str,
                              patcher: ElfPatcher) -> None:
    """Add a new rpath entry to a file while preserving as many existing
    rpath entries as possible.

    In order to preserve an rpath entry it must:

    1) Point to a location within wheel_base_dir.
    2) Not be a duplicate of an already-existing rpath entry.
    """
    if not isabs(lib_name):
        lib_name = abspath(lib_name)
    lib_dir = dirname(lib_name)
    if not isabs(wheel_base_dir):
        wheel_base_dir = abspath(wheel_base_dir)

    def is_valid_rpath(rpath: str) -> bool:
        return _is_valid_rpath(rpath, lib_dir, wheel_base_dir)

    old_rpaths = patcher.get_rpath(lib_name)
    rpaths = filter(is_valid_rpath, old_rpaths.split(':'))
    # Remove duplicates while preserving ordering
    # Fake an OrderedSet using OrderedDict
    rpath_set = OrderedDict([(old_rpath, '') for old_rpath in rpaths])
    rpath_set[rpath] = ''

    patcher.set_rpath(lib_name, ':'.join(rpath_set))
Exemplo n.º 2
0
def repair_wheel(wheel_path: str, abi: str, lib_sdir: str, out_dir: str,
                 update_tags: bool, patcher: ElfPatcher) -> Optional[str]:

    external_refs_by_fn = get_wheel_elfdata(wheel_path)[1]

    # Do not repair a pure wheel, i.e. has no external refs
    if not external_refs_by_fn:
        return

    soname_map = {}  # type: Dict[str, str]
    if not isabs(out_dir):
        out_dir = abspath(out_dir)

    wheel_fname = basename(wheel_path)

    with InWheelCtx(wheel_path) as ctx:
        ctx.out_wheel = pjoin(out_dir, wheel_fname)

        dest_dir = WHEEL_INFO_RE(wheel_fname).group('name') + lib_sdir

        if not exists(dest_dir):
            os.mkdir(dest_dir)

        # here, fn is a path to a python extension library in
        # the wheel, and v['libs'] contains its required libs
        for fn, v in external_refs_by_fn.items():

            ext_libs = v[abi]['libs']  # type: Dict[str, str]
            for soname, src_path in ext_libs.items():
                if src_path is None:
                    raise ValueError(('Cannot repair wheel, because required '
                                      'library "%s" could not be located') %
                                     soname)

                new_soname, new_path = copylib(src_path, dest_dir, patcher)
                soname_map[soname] = (new_soname, new_path)
                patcher.replace_needed(fn, soname, new_soname)

            if len(ext_libs) > 0:
                new_rpath = os.path.relpath(dest_dir, os.path.dirname(fn))
                new_rpath = os.path.join('$ORIGIN', new_rpath)
                patcher.set_rpath(fn, new_rpath)

        # we grafted in a bunch of libraries and modified their sonames, but
        # they may have internal dependencies (DT_NEEDED) on one another, so
        # we need to update those records so each now knows about the new
        # name of the other.
        for old_soname, (new_soname, path) in soname_map.items():
            needed = elf_read_dt_needed(path)
            for n in needed:
                if n in soname_map:
                    patcher.replace_needed(path, n, soname_map[n][0])

        if update_tags:
            ctx.out_wheel = add_platforms(ctx, [abi],
                                          get_replace_platforms(abi))
    return ctx.out_wheel
Exemplo n.º 3
0
def copylib(src_path: str, dest_dir: str,
            patcher: ElfPatcher) -> Tuple[str, str]:
    """Graft a shared library from the system into the wheel and update the
    relevant links.

    1) Copy the file from src_path to dest_dir/
    2) Rename the shared object from soname to soname.<unique>
    3) If the library has a RUNPATH/RPATH, clear it and set RPATH to point to
    its new location.
    """
    # Copy the a shared library from the system (src_path) into the wheel
    # if the library has a RUNPATH/RPATH we clear it and set RPATH to point to
    # its new location.

    with open(src_path, 'rb') as f:
        shorthash = hashfile(f)[:8]

    src_name = os.path.basename(src_path)
    base, ext = src_name.split('.', 1)
    if not base.endswith('-%s' % shorthash):
        new_soname = f'{base}-{shorthash}.{ext}'
    else:
        new_soname = src_name

    dest_path = os.path.join(dest_dir, new_soname)
    if os.path.exists(dest_path):
        return new_soname, dest_path

    logger.debug('Grafting: %s -> %s', src_path, dest_path)
    rpaths = elf_read_rpaths(src_path)
    shutil.copy2(src_path, dest_path)
    statinfo = os.stat(dest_path)
    if not statinfo.st_mode & stat.S_IWRITE:
        os.chmod(dest_path, statinfo.st_mode | stat.S_IWRITE)

    patcher.set_soname(dest_path, new_soname)

    if any(itertools.chain(rpaths['rpaths'], rpaths['runpaths'])):
        patcher.set_rpath(dest_path, dest_dir)

    return new_soname, dest_path
Exemplo n.º 4
0
def copylib(src_path: str, dest_dir: str,
            patcher: ElfPatcher) -> Tuple[str, str]:
    # Do NOT hash filename to make it unique in the particular case of boost
    # python modules, since otherwise it will be impossible to share a common
    # registery, which is necessary for cross module interoperability.
    if "libboost_python" in src_path:
        src_name = os.path.basename(src_path)
        dest_path = os.path.join(dest_dir, src_name)
        if os.path.exists(dest_path):
            return src_name, dest_path

        logger.debug('Grafting: %s -> %s', src_path, dest_path)
        shutil.copy2(src_path, dest_path)
        rpaths = elf_read_rpaths(src_path)
        statinfo = os.stat(dest_path)
        if not statinfo.st_mode & stat.S_IWRITE:
            os.chmod(dest_path, statinfo.st_mode | stat.S_IWRITE)
        patcher.set_soname(dest_path, src_name)
        if any(itertools.chain(rpaths['rpaths'], rpaths['runpaths'])):
            patcher.set_rpath(dest_path, dest_dir)

        return src_name, dest_path
    return copylib_orig(src_path, dest_dir, patcher)