|
1 | 1 | import itertools
|
2 | 2 | import logging
|
3 | 3 | import os
|
| 4 | +import platform |
4 | 5 | import re
|
5 | 6 | import stat
|
6 | 7 | import shutil
|
7 |
| -from os.path import exists, basename, abspath, isabs |
| 8 | +from collections import OrderedDict |
| 9 | +from os.path import exists, basename, abspath, isabs, dirname |
8 | 10 | from os.path import join as pjoin
|
9 | 11 | from typing import Dict, Optional
|
10 | 12 |
|
11 | 13 | from auditwheel.patcher import ElfPatcher
|
12 |
| -from .elfutils import elf_read_rpaths, elf_read_dt_needed |
| 14 | +from .elfutils import elf_read_rpaths, elf_read_dt_needed, is_subdir |
13 | 15 | from .hashfile import hashfile
|
14 | 16 | from .policy import get_replace_platforms
|
15 | 17 | from .wheel_abi import get_wheel_elfdata
|
@@ -66,7 +68,7 @@ def repair_wheel(wheel_path: str, abi: str, lib_sdir: str, out_dir: str,
|
66 | 68 | if len(ext_libs) > 0:
|
67 | 69 | new_rpath = os.path.relpath(dest_dir, os.path.dirname(fn))
|
68 | 70 | new_rpath = os.path.join('$ORIGIN', new_rpath)
|
69 |
| - patcher.set_rpath(fn, new_rpath) |
| 71 | + append_rpath_within_wheel(fn, new_rpath, ctx.name, patcher) |
70 | 72 |
|
71 | 73 | # we grafted in a bunch of libraries and modified their sonames, but
|
72 | 74 | # they may have internal dependencies (DT_NEEDED) on one another, so
|
@@ -124,3 +126,68 @@ def copylib(src_path, dest_dir, patcher):
|
124 | 126 | patcher.set_rpath(dest_path, dest_dir)
|
125 | 127 |
|
126 | 128 | return new_soname, dest_path
|
| 129 | + |
| 130 | + |
| 131 | +def append_rpath_within_wheel(lib_name: str, |
| 132 | + rpath: str, |
| 133 | + wheel_base_dir: str, |
| 134 | + patcher: ElfPatcher) -> None: |
| 135 | + """Add a new rpath entry to a file while preserving as many existing |
| 136 | + rpath entries as possible. |
| 137 | +
|
| 138 | + In order to preserve an rpath entry it must: |
| 139 | +
|
| 140 | + 1) Point to a location within wheel_base_dir. |
| 141 | + 2) Not be a duplicate of an already-existing rpath entry. |
| 142 | + """ |
| 143 | + if not isabs(lib_name): |
| 144 | + lib_name = abspath(lib_name) |
| 145 | + lib_dir = dirname(lib_name) |
| 146 | + if not isabs(wheel_base_dir): |
| 147 | + wheel_base_dir = abspath(wheel_base_dir) |
| 148 | + |
| 149 | + def is_valid_rpath(rpath: str) -> bool: |
| 150 | + return _is_valid_rpath(rpath, lib_dir, wheel_base_dir) |
| 151 | + |
| 152 | + old_rpaths = patcher.get_rpath(lib_name) |
| 153 | + rpaths = filter(is_valid_rpath, old_rpaths.split(':')) |
| 154 | + # Remove duplicates while preserving ordering |
| 155 | + # Fake an OrderedSet using OrderedDict |
| 156 | + rpaths = OrderedDict([(old_rpath, '') for old_rpath in rpaths]) |
| 157 | + rpaths[rpath] = '' |
| 158 | + |
| 159 | + patcher.set_rpath(lib_name, ':'.join(rpaths)) |
| 160 | + |
| 161 | + |
| 162 | +def _is_valid_rpath(rpath: str, |
| 163 | + lib_dir: str, |
| 164 | + wheel_base_dir: str) -> bool: |
| 165 | + full_rpath_entry = _resolve_rpath_tokens(rpath, lib_dir) |
| 166 | + if not isabs(full_rpath_entry): |
| 167 | + logger.debug('rpath entry {} could not be resolved to an absolute ' |
| 168 | + 'path -- discarding it.'.format(rpath)) |
| 169 | + return False |
| 170 | + elif not is_subdir(full_rpath_entry, wheel_base_dir): |
| 171 | + logger.debug('rpath entry {} points outside the wheel -- discarding ' |
| 172 | + 'it.'.format(rpath)) |
| 173 | + return False |
| 174 | + else: |
| 175 | + logger.debug('Preserved rpath entry {}'.format(rpath)) |
| 176 | + return True |
| 177 | + |
| 178 | + |
| 179 | +def _resolve_rpath_tokens(rpath: str, |
| 180 | + lib_base_dir: str) -> str: |
| 181 | + # See https://www.man7.org/linux/man-pages/man8/ld.so.8.html#DESCRIPTION |
| 182 | + if platform.architecture()[0] == '64bit': |
| 183 | + system_lib_dir = 'lib64' |
| 184 | + else: |
| 185 | + system_lib_dir = 'lib' |
| 186 | + system_processor_type = platform.machine() |
| 187 | + token_replacements = {'ORIGIN': lib_base_dir, |
| 188 | + 'LIB': system_lib_dir, |
| 189 | + 'PLATFORM': system_processor_type} |
| 190 | + for token, target in token_replacements.items(): |
| 191 | + rpath = rpath.replace('${}'.format(token), target) # $TOKEN |
| 192 | + rpath = rpath.replace('${{{}}}'.format(token), target) # ${TOKEN} |
| 193 | + return rpath |
0 commit comments