Skip to content

Commit 3a5f385

Browse files
committed
concurrent grafting
1 parent 344df64 commit 3a5f385

File tree

1 file changed

+36
-22
lines changed

1 file changed

+36
-22
lines changed

src/auditwheel/repair.py

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from contextlib import ExitStack
34
import itertools
45
import logging
56
import os
@@ -12,6 +13,7 @@
1213
from os.path import isabs
1314
from pathlib import Path
1415
from subprocess import check_call
16+
from concurrent.futures import ThreadPoolExecutor, Future, as_completed
1517

1618
from auditwheel.patcher import ElfPatcher
1719

@@ -64,6 +66,12 @@ def repair_wheel(
6466
raise ValueError(msg)
6567

6668
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] = {}
6775

6876
# here, fn is a path to an ELF file (lib or executable) in
6977
# the wheel, and v['libs'] contains its required libs
@@ -80,54 +88,60 @@ def repair_wheel(
8088
)
8189
raise ValueError(msg)
8290

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)
8694
soname_map[soname] = (new_soname, new_path)
8795
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)
90100

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)
95105

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)
99111

100112
# we grafted in a bunch of libraries and modified their sonames, but
101113
# they may have internal dependencies (DT_NEEDED) on one another, so
102114
# we need to update those records so each now knows about the new
103115
# name of the other.
116+
as_completed(copy_works.values())
104117
for _, path in soname_map.values():
105118
needed = elf_read_dt_needed(path)
106119
replacements = []
107120
for n in needed:
108121
if n in soname_map:
109122
replacements.append((n, soname_map[n][0]))
110123
if replacements:
111-
patcher.replace_needed(path, *replacements)
124+
pool.submit(patcher.replace_needed, path, *replacements)
112125

113126
if update_tags:
114127
ctx.out_wheel = add_platforms(ctx, abis, get_replace_platforms(abis[0]))
115128

116129
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()
120135

121136
return ctx.out_wheel
122137

123138

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)
128142

129143

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]:
131145
"""Graft a shared library from the system into the wheel and update the
132146
relevant links.
133147
@@ -151,7 +165,7 @@ def copylib(src_path: Path, dest_dir: Path, patcher: ElfPatcher) -> tuple[str, P
151165
new_soname = src_name
152166

153167
dest_path = dest_dir / new_soname
154-
if dest_path.exists():
168+
if dry or dest_path.exists():
155169
return new_soname, dest_path
156170

157171
logger.debug("Grafting: %s -> %s", src_path, dest_path)

0 commit comments

Comments
 (0)