-
Notifications
You must be signed in to change notification settings - Fork 84
/
fix_xacc_rpaths.py
200 lines (166 loc) · 7.64 KB
/
fix_xacc_rpaths.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import os,sys,pkg_resources
import re
from os.path import isfile, exists, abspath, basename
import shutil
import itertools
import functools
import argparse
from auditwheel.policy import (load_policies, get_policy_name, get_priority_by_name,
POLICY_PRIORITY_HIGHEST)
from os.path import exists, relpath, dirname, basename, abspath, isabs
from os.path import join as pjoin
from subprocess import check_call, check_output, CalledProcessError
from distutils.spawn import find_executable
from typing import Optional
from os.path import join as pjoin
from auditwheel.policy import get_replace_platforms
from auditwheel.wheeltools import InWheelCtx, add_platforms
from auditwheel.wheel_abi import get_wheel_elfdata
from auditwheel.elfutils import elf_read_rpaths, is_subdir, elf_read_dt_needed
from auditwheel.hashfile import hashfile
from auditwheel.repair import patchelf_set_rpath, verify_patchelf, copylib
def xacc_repair_wheel(wheel_path: str, abi: str, lib_sdir: str, out_dir: str,
update_tags: bool) -> Optional[str]:
external_refs_by_fn = get_wheel_elfdata(wheel_path)[1]
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)
# 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():
# pkg_root should resolve to like numpy/ or scipy/
# note that it's possible for the wheel to contain
# more than one pkg, which is why we detect the pkg root
# for each fn.
pkg_root = fn.split(os.sep)[0]
if pkg_root == fn:
# this file is an extension that's not contained in a
# directory -- just supposed to be directly in site-packages
dest_dir = lib_sdir + pkg_root.split('.')[0]
else:
dest_dir = pjoin(pkg_root, lib_sdir)
if not exists(dest_dir):
os.mkdir(dest_dir)
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)
soname_map[soname] = (new_soname, new_path)
check_call(['patchelf', '--replace-needed', soname, new_soname, fn])
if len(ext_libs) > 0:
patchelf_set_rpath(fn, dest_dir)
# we grafted in a bunch of libraries and modifed 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:
check_call(['patchelf', '--replace-needed', n, soname_map[n][0], path])
check_call(['patchelf', '--force-rpath', '--set-rpath', '$ORIGIN/.libs:$ORIGIN/lib', 'xacc/_pyxacc.so'])
if update_tags:
ctx.out_wheel = add_platforms(ctx, [abi],
get_replace_platforms(abi))
return ctx.out_wheel
def configure_parser(sub_parsers):
policy_names = [p['name'] for p in load_policies()]
highest_policy = get_policy_name(POLICY_PRIORITY_HIGHEST)
help = "Vendor in external shared library dependencies of a wheel."
p = sub_parsers.add_parser('repair', help=help, description=help)
p.add_argument('WHEEL_FILE', help='Path to wheel file.')
p.add_argument('-f',
'--force',
help='Override symbol version ABI check',
action='store_true')
p.add_argument(
'--plat',
dest='PLAT',
help='Desired target platform. (default: "%s")' % highest_policy,
choices=policy_names,
default=highest_policy)
p.add_argument('-L',
'--lib-sdir',
dest='LIB_SDIR',
help=('Subdirectory in packages to store copied libraries.'
' (default: ".libs")'),
default='.libs')
p.add_argument('-w',
'--wheel-dir',
dest='WHEEL_DIR',
type=abspath,
help=('Directory to store delocated wheels (default:'
' "wheelhouse/")'),
default='wheelhouse/')
p.add_argument('--no-update-tags',
dest='UPDATE_TAGS',
action='store_false',
help=('Do not update the wheel filename tags and WHEEL info'
' to match the repaired platform tag.'),
default=True)
p.set_defaults(func=execute)
def execute(args, p):
import os
from distutils.spawn import find_executable
from auditwheel.wheel_abi import analyze_wheel_abi
if not isfile(args.WHEEL_FILE):
p.error('cannot access %s. No such file' % args.WHEEL_FILE)
if find_executable('patchelf') is None:
p.error('cannot find the \'patchelf\' tool, which is required')
print('Repairing %s' % basename(args.WHEEL_FILE))
if not exists(args.WHEEL_DIR):
os.makedirs(args.WHEEL_DIR)
wheel_abi = analyze_wheel_abi(args.WHEEL_FILE)
reqd_tag = get_priority_by_name(args.PLAT)
if (reqd_tag > get_priority_by_name(wheel_abi.sym_tag)):
msg = ('cannot repair "%s" to "%s" ABI because of the presence '
'of too-recent versioned symbols. You\'ll need to compile '
'the wheel on an older toolchain.' %
(args.WHEEL_FILE, args.PLAT))
p.error(msg)
if (reqd_tag > get_priority_by_name(wheel_abi.ucs_tag)):
msg = ('cannot repair "%s" to "%s" ABI because it was compiled '
'against a UCS2 build of Python. You\'ll need to compile '
'the wheel against a wide-unicode build of Python.' %
(args.WHEEL_FILE, args.PLAT))
p.error(msg)
out_wheel = xacc_repair_wheel(args.WHEEL_FILE,
abi=args.PLAT,
lib_sdir=args.LIB_SDIR,
out_dir=args.WHEEL_DIR,
update_tags=args.UPDATE_TAGS)
if out_wheel is not None:
print('\nFixed-up wheel written to %s' % out_wheel)
def main():
dist = pkg_resources.get_distribution('auditwheel')
version = 'auditwheel %s installed at %s (python %s)' % (
dist.version, dist.location, sys.version[:3])
p = argparse.ArgumentParser(description='Fix XACC Linux Wheel.')
p.set_defaults(prog=os.path.basename(sys.argv[0]))
p.add_argument('-V', '--version', action='version', version=version)
p.add_argument("-v",
"--verbose",
action='count',
dest='verbose',
default=0,
help='Give more output. Option is additive')
sub_parsers = p.add_subparsers(metavar='command', dest='cmd')
configure_parser(sub_parsers)
args = p.parse_args()
if not hasattr(args, 'func'):
p.print_help()
return
try:
rval = args.func(args, p)
except:
# TODO(rmcgibbo): nice message
raise
return rval
if __name__ == '__main__':
sys.exit(main())