Skip to content

Commit 52c9c18

Browse files
committed
[GR-60074] Fix wheelbuilder shell scripts and windows dll bundling.
PullRequest: graalpython/3590
2 parents 9c2af57 + e331c1c commit 52c9c18

File tree

6 files changed

+50
-150
lines changed

6 files changed

+50
-150
lines changed

scripts/wheelbuilder/build_wheels.py

Lines changed: 25 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import importlib
5252
import os
5353
import re
54+
import shlex
5455
import shutil
5556
import subprocess
5657
import sys
@@ -107,6 +108,7 @@ def build_wheels(pip):
107108
packages_to_build = set()
108109
with open(join(dirname(__file__), "packages.txt")) as f:
109110
for line in f.readlines():
111+
line = line.strip()
110112
name, version = line.split("==")
111113
if not packages_selected or name in packages_selected or line in packages_selected:
112114
packages_to_build.add(line)
@@ -131,7 +133,11 @@ def build_wheels(pip):
131133
env["PATH"] = abspath(dirname(pip)) + os.pathsep + env["PATH"]
132134
env["VIRTUAL_ENV"] = abspath(dirname(dirname(pip)))
133135
print("Building", name, version, "with", script, flush=True)
134-
subprocess.check_call([script, version], shell=True, env=env)
136+
if sys.platform == "win32":
137+
cmd = [script, version] # Python's subprocess.py does the quoting we need
138+
else:
139+
cmd = f"{shlex.quote(script)} {version}"
140+
subprocess.check_call(cmd, shell=True, env=env)
135141
if not len(glob("*.whl")) > whl_count:
136142
print("Building wheel for", name, version, "after", script, "did not", flush=True)
137143
subprocess.check_call([pip, "wheel", spec])
@@ -140,152 +146,26 @@ def build_wheels(pip):
140146
subprocess.check_call([pip, "wheel", spec])
141147

142148

143-
_warned_dlls = []
144-
145-
146-
def repair_wheels_windows(output_dir, wheels):
147-
import pefile
148-
from machomachomangler.pe import redll
149-
150-
def resolve_dll_src(dll):
151-
# search for dependencies in system directories
152-
dll_search_paths = [
153-
os.environ["WINDIR"],
154-
join(os.environ["WINDIR"], "System32"),
155-
*os.environ["PATH"].split(";"),
156-
]
157-
ignored_dlls = [
158-
# These DLLs are just provided by Windows.
159-
# This list is probably incomplete.
160-
r"advapi32\.dll",
161-
r"advapires32\.dll",
162-
r"atl.*\.dll",
163-
r"comctl32\.dll",
164-
r"comdlg32\.dll",
165-
r"crtdll\.dll",
166-
r"gdi32\.dll",
167-
r"hal.*\.dll",
168-
r"imm32\.dll",
169-
r"iphlpapi\.dll",
170-
r"kernel32\.dll",
171-
r"kernelbase\.dll",
172-
r"msvbvm60\.dll",
173-
r"msvcirt\.dll",
174-
r"msvcrt?.*\.dll",
175-
r"netapi32\.dll",
176-
r"ntdll\.dll",
177-
r"ole32\.dll",
178-
r"pdh\.dll",
179-
r"powrprof\.dll",
180-
r"psapi\.dll",
181-
r"rpcrt4\.dll",
182-
r"sechost\.dll",
183-
r"shell32\.dll",
184-
r"shlwapi\.dll",
185-
r"shscrap\.dll",
186-
r"ucrtbase\.dll",
187-
r"user32\.dll",
188-
r"version\.dll",
189-
r"winmm\.dll",
190-
r"ws2_32\.dll",
191-
# These match DLLs that provide API sets.
192-
# See https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets
193-
r"api-ms-win-.*\.dll",
194-
r"ext-ms-win-.*\.dll",
195-
# These match DLLs that we provide in GraalPy
196-
r"python.*\.dll",
197-
# These are the DLLs typically linked when building with MSVC. See
198-
# https://learn.microsoft.com/en-us/cpp/windows/determining-which-dlls-to-redistribute
199-
# When these are included, the user should install the latest
200-
# redist package from
201-
# https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist
202-
# However, https://aka.ms/vs/17/redist.txt lists the libraries
203-
# which can be included in application distributions.
204-
r"concrt.*\.dll",
205-
r"mfc.*\.dll",
206-
r"msvcp.*\.dll",
207-
r"vcamp.*\.dll",
208-
r"vccorlib.*\.dll",
209-
r"vcomp.*\.dll",
210-
r"vcruntime.*\.dll",
211-
]
212-
if not dll:
213-
return
214-
if any(re.match(pat, basename(dll), re.IGNORECASE) for pat in ignored_dlls):
215-
if dll not in _warned_dlls:
216-
print("Not including", dll, flush=True)
217-
_warned_dlls.append(dll)
218-
return
219-
if isabs(dll):
220-
return dll
221-
for search_path in dll_search_paths:
222-
if exists(src := join(search_path, dll)):
223-
return src
224-
225-
def resolve_dll_target(dll, dependent, checksum):
226-
return join(dirname(dependent), f"{checksum}.{basename(dll)}")
227-
228-
def filehash(files):
229-
sha1 = hashlib.sha1()
230-
for file in files:
231-
with open(file, mode="rb") as f:
232-
sha1.update(f.read())
233-
return sha1.hexdigest()[:8]
234-
235-
for whl in wheels:
236-
with TemporaryDirectory() as name:
237-
with zipfile.ZipFile(whl) as f:
238-
f.extractall(name)
239-
240-
# find all pyd files and recursively copy dependencies
241-
dlls = glob(f"{name}/**/*.pyd", recursive=True)
242-
checksum = filehash(dlls)
243-
dependents_to_dependencies = {}
244-
while dlls:
245-
dll = dlls.pop()
246-
with pefile.PE(dll) as pe:
247-
pe_info = pe.dump_dict()
248-
for syms in pe_info.get("Imported symbols", []):
249-
for sym in syms:
250-
if dep_src := resolve_dll_src(sym.get("DLL", b"").decode("utf-8")):
251-
if not exists(dep_tgt := resolve_dll_target(dep_src, dll, checksum)):
252-
print("Including", dep_src, "as", dep_tgt, flush=True)
253-
shutil.copy(dep_src, dep_tgt)
254-
dlls.append(dep_tgt)
255-
dependents_to_dependencies.setdefault(dll, []).append(dep_src)
256-
257-
for dll, dependencies in dependents_to_dependencies.items():
258-
mapping = {}
259-
for dep_src in dependencies:
260-
mapping[basename(dep_src).encode("utf-8")] = basename(
261-
resolve_dll_target(dep_src, dll, checksum)
262-
).encode("utf-8")
263-
with open(dll, mode="rb") as f:
264-
data = f.read()
265-
print(
266-
"Rewriting\n\t",
267-
"\n\t".join([k.decode("utf-8") for k in mapping.keys()]),
268-
"\n\t->\n\t",
269-
"\n\t".join([v.decode("utf-8") for v in mapping.values()]),
270-
"\nin",
271-
dll,
272-
)
273-
data = redll(data, mapping)
274-
with open(dll, mode="wb") as f:
275-
f.write(data)
276-
277-
os.makedirs(output_dir, exist_ok=True)
278-
if exists(whl_tgt := join(output_dir, whl)):
279-
os.unlink(whl_tgt)
280-
shutil.make_archive(whl_tgt, "zip", name)
281-
os.rename(f"{whl_tgt}.zip", whl_tgt)
282-
283-
284149
def repair_wheels():
285150
if sys.platform == "win32":
286-
ensure_installed("machomachomangler")
287-
ensure_installed("pefile")
288-
repair_wheels_windows("wheelhouse", glob("*.whl"))
151+
ensure_installed("delvewheel")
152+
env = os.environ.copy()
153+
env["PYTHONUTF8"] = "1"
154+
subprocess.check_call(
155+
[
156+
sys.executable,
157+
"-m",
158+
"delvewheel",
159+
"repair",
160+
"-v",
161+
"--exclude",
162+
"python-native.dll",
163+
"-w",
164+
"wheelhouse",
165+
*glob("*.whl"),
166+
],
167+
env=env,
168+
)
289169
elif sys.platform == "linux":
290170
ensure_installed("auditwheel")
291171
subprocess.check_call(

scripts/wheelbuilder/darwin/scipy.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,8 @@ if [ -n "$GITHUB_RUN_ID" ]; then
4242
export PKG_CONFIG_PATH=/opt/homebrew/opt/openblas/lib/pkgconfig
4343
fi
4444
export FFLAGS=-fallow-argument-mismatch
45-
pip wheel "scipy==$1"
45+
if [ -n "$1" ]; then
46+
pip wheel "scipy==$1"
47+
else
48+
pip wheel scipy
49+
fi

scripts/wheelbuilder/darwin/torch.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ if [ -n "$GITHUB_RUN_ID" ]; then
4444
fi
4545
export MAX_JOBS=4
4646
export BUILD_TEST=0
47-
pip wheel "torch==$1"
47+
if [ -n "$1" ]; then
48+
pip wheel "torch==$1"
49+
else
50+
pip wheel torch
51+
fi

scripts/wheelbuilder/linux/scipy.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,8 @@ if command -v manylinux-interpreters 2>&1 >/dev/null; then
4141
dnf install -y gcc-toolset-12-gcc-gfortran openblas-devel
4242
fi
4343
export FFLAGS=-fallow-argument-mismatch
44-
pip wheel "scipy==$1"
44+
if [ -n "$1" ]; then
45+
pip wheel "scipy==$1"
46+
else
47+
pip wheel scipy
48+
fi

scripts/wheelbuilder/linux/tensorflow.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,9 @@ curl -L https://github.com/bazelbuild/bazel/releases/download/6.4.0/bazel-6.4.0-
4747
chmod +x tmp_bazel/bazel
4848
export PATH=$(pwd)/tmp_bazel/:$PATH
4949
bazel --version
50-
pip wheel "tensorflow==$1"
50+
if [ -n "$1" ]; then
51+
pip wheel "tensorflow==$1"
52+
else
53+
pip wheel tensorflow
54+
fi
5155
rm -rf tmp_bazel

scripts/wheelbuilder/linux/torch.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@ if command -v manylinux-interpreters 2>&1 >/dev/null; then
4343
fi
4444
export MAX_JOBS=4
4545
export BUILD_TEST=0
46-
pip wheel "torch==$1"
46+
if [ -n "$1" ]; then
47+
pip wheel "torch==$1"
48+
else
49+
pip wheel torch
50+
fi

0 commit comments

Comments
 (0)