1
1
from __future__ import annotations
2
2
3
+ from contextlib import ExitStack
3
4
import itertools
4
5
import logging
5
6
import os
12
13
from os .path import isabs
13
14
from pathlib import Path
14
15
from subprocess import check_call
16
+ from concurrent .futures import ThreadPoolExecutor , Future , as_completed
15
17
16
18
from auditwheel .patcher import ElfPatcher
17
19
@@ -64,6 +66,12 @@ def repair_wheel(
64
66
raise ValueError (msg )
65
67
66
68
dest_dir = Path (match .group ("name" ) + lib_sdir )
69
+ if not dest_dir .exists ():
70
+ dest_dir .mkdir ()
71
+
72
+ pool = ThreadPoolExecutor ()
73
+ copy_works : dict [str , Future ] = {}
74
+ replace_works : dict [str , Future ] = {}
67
75
68
76
# here, fn is a path to an ELF file (lib or executable) in
69
77
# the wheel, and v['libs'] contains its required libs
@@ -80,54 +88,60 @@ def repair_wheel(
80
88
)
81
89
raise ValueError (msg )
82
90
83
- if not dest_dir . exists ():
84
- dest_dir . mkdir ()
85
- new_soname , new_path = copylib ( src_path , dest_dir , patcher )
91
+ new_soname , new_path = copylib ( src_path , dest_dir , patcher , dry = True )
92
+ if not new_path . exists () and str ( new_path ) not in copy_works :
93
+ copy_works [ str ( new_path )] = pool . submit ( copylib , src_path , dest_dir , patcher )
86
94
soname_map [soname ] = (new_soname , new_path )
87
95
replacements .append ((soname , new_soname ))
88
- if replacements :
89
- patcher .replace_needed (fn , * replacements )
96
+
97
+ def _inner_replace ():
98
+ if replacements :
99
+ patcher .replace_needed (fn , * replacements )
90
100
91
- if len (ext_libs ) > 0 :
92
- new_fn = fn
93
- if _path_is_script (fn ):
94
- new_fn = _replace_elf_script_with_shim (match .group ("name" ), fn )
101
+ if len (ext_libs ) > 0 :
102
+ new_fn = fn
103
+ if _path_is_script (fn ):
104
+ new_fn = _replace_elf_script_with_shim (match .group ("name" ), fn )
95
105
96
- new_rpath = os .path .relpath (dest_dir , new_fn .parent )
97
- new_rpath = os .path .join ("$ORIGIN" , new_rpath )
98
- append_rpath_within_wheel (new_fn , new_rpath , ctx .name , patcher )
106
+ new_rpath = os .path .relpath (dest_dir , new_fn .parent )
107
+ new_rpath = os .path .join ("$ORIGIN" , new_rpath )
108
+ append_rpath_within_wheel (new_fn , new_rpath , ctx .name , patcher )
109
+
110
+ replace_works [fn ] = pool .submit (_inner_replace )
99
111
100
112
# we grafted in a bunch of libraries and modified their sonames, but
101
113
# they may have internal dependencies (DT_NEEDED) on one another, so
102
114
# we need to update those records so each now knows about the new
103
115
# name of the other.
116
+ as_completed (copy_works .values ())
104
117
for _ , path in soname_map .values ():
105
118
needed = elf_read_dt_needed (path )
106
119
replacements = []
107
120
for n in needed :
108
121
if n in soname_map :
109
122
replacements .append ((n , soname_map [n ][0 ]))
110
123
if replacements :
111
- patcher .replace_needed ( path , * replacements )
124
+ pool . submit ( patcher .replace_needed , path , * replacements )
112
125
113
126
if update_tags :
114
127
ctx .out_wheel = add_platforms (ctx , abis , get_replace_platforms (abis [0 ]))
115
128
116
129
if strip :
117
- libs_to_strip = [path for (_ , path ) in soname_map .values ()]
118
- extensions = external_refs_by_fn .keys ()
119
- strip_symbols (itertools .chain (libs_to_strip , extensions ))
130
+ for lib , future in itertools .chain (copy_works .items (), replace_works .items ()):
131
+ logger .info ("Stripping symbols from %s" , lib )
132
+ then (future , check_call , ["strip" , "-s" , lib ])
133
+
134
+ pool .shutdown ()
120
135
121
136
return ctx .out_wheel
122
137
123
138
124
- def strip_symbols (libraries : Iterable [Path ]) -> None :
125
- for lib in libraries :
126
- logger .info ("Stripping symbols from %s" , lib )
127
- check_call (["strip" , "-s" , lib ])
139
+ def then (pool : ThreadPoolExecutor , future : Future , * args , ** kwargs ):
140
+ future .result ()
141
+ pool .submit (* args , ** kwargs )
128
142
129
143
130
- def copylib (src_path : Path , dest_dir : Path , patcher : ElfPatcher ) -> tuple [str , Path ]:
144
+ def copylib (src_path : Path , dest_dir : Path , patcher : ElfPatcher , dry : bool = False ) -> tuple [str , Path ]:
131
145
"""Graft a shared library from the system into the wheel and update the
132
146
relevant links.
133
147
@@ -151,7 +165,7 @@ def copylib(src_path: Path, dest_dir: Path, patcher: ElfPatcher) -> tuple[str, P
151
165
new_soname = src_name
152
166
153
167
dest_path = dest_dir / new_soname
154
- if dest_path .exists ():
168
+ if dry or dest_path .exists ():
155
169
return new_soname , dest_path
156
170
157
171
logger .debug ("Grafting: %s -> %s" , src_path , dest_path )
0 commit comments