Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 5f77b74

Browse files
authored
Try building ABI3 wheels for cpython (#14253)
1 parent 4dd7aa3 commit 5f77b74

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

.ci/scripts/auditwheel_wrapper.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#!/usr/bin/env python
2+
# Copyright 2022 The Matrix.org Foundation C.I.C.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# Wraps `auditwheel repair` to first check if we're repairing a potentially abi3
17+
# compatible wheel, if so rename the wheel before repairing it.
18+
19+
import argparse
20+
import os
21+
import subprocess
22+
from typing import Optional
23+
from zipfile import ZipFile
24+
25+
from packaging.tags import Tag
26+
from packaging.utils import parse_wheel_filename
27+
from packaging.version import Version
28+
29+
30+
def check_is_abi3_compatible(wheel_file: str) -> None:
31+
"""Check the contents of the built wheel for any `.so` files that are *not*
32+
abi3 compatible.
33+
"""
34+
35+
with ZipFile(wheel_file, "r") as wheel:
36+
for file in wheel.namelist():
37+
if not file.endswith(".so"):
38+
continue
39+
40+
if not file.endswith(".abi3.so"):
41+
raise Exception(f"Found non-abi3 lib: {file}")
42+
43+
44+
def cpython(wheel_file: str, name: str, version: Version, tag: Tag) -> str:
45+
"""Replaces the cpython wheel file with a ABI3 compatible wheel"""
46+
47+
if tag.abi == "abi3":
48+
# Nothing to do.
49+
return wheel_file
50+
51+
check_is_abi3_compatible(wheel_file)
52+
53+
abi3_tag = Tag(tag.interpreter, "abi3", tag.platform)
54+
55+
dirname = os.path.dirname(wheel_file)
56+
new_wheel_file = os.path.join(
57+
dirname,
58+
f"{name}-{version}-{abi3_tag}.whl",
59+
)
60+
61+
os.rename(wheel_file, new_wheel_file)
62+
63+
print("Renamed wheel to", new_wheel_file)
64+
65+
return new_wheel_file
66+
67+
68+
def main(wheel_file: str, dest_dir: str, archs: Optional[str]) -> None:
69+
"""Entry point"""
70+
71+
# Parse the wheel file name into its parts. Note that `parse_wheel_filename`
72+
# normalizes the package name (i.e. it converts matrix_synapse ->
73+
# matrix-synapse), which is not what we want.
74+
_, version, build, tags = parse_wheel_filename(os.path.basename(wheel_file))
75+
name = os.path.basename(wheel_file).split("-")[0]
76+
77+
if len(tags) != 1:
78+
# We expect only a wheel file with only a single tag
79+
raise Exception(f"Unexpectedly found multiple tags: {tags}")
80+
81+
tag = next(iter(tags))
82+
83+
if build:
84+
# We don't use build tags in Synapse
85+
raise Exception(f"Unexpected build tag: {build}")
86+
87+
# If the wheel is for cpython then convert it into an abi3 wheel.
88+
if tag.interpreter.startswith("cp"):
89+
wheel_file = cpython(wheel_file, name, version, tag)
90+
91+
# Finally, repair the wheel.
92+
if archs is not None:
93+
# If we are given archs then we are on macos and need to use
94+
# `delocate-listdeps`.
95+
subprocess.run(["delocate-listdeps", wheel_file], check=True)
96+
subprocess.run(
97+
["delocate-wheel" "--require-archs", archs, "-w", dest_dir, wheel_file],
98+
check=True,
99+
)
100+
else:
101+
subprocess.run(["auditwheel", "repair", "-w", dest_dir, wheel_file], check=True)
102+
103+
104+
if __name__ == "__main__":
105+
parser = argparse.ArgumentParser(description="Tag wheel as abi3 and repair it.")
106+
107+
parser.add_argument(
108+
"--wheel-dir",
109+
"-w",
110+
metavar="WHEEL_DIR",
111+
help="Directory to store delocated wheels",
112+
required=True,
113+
)
114+
115+
parser.add_argument(
116+
"--require-archs",
117+
metavar="archs",
118+
default=None,
119+
)
120+
121+
parser.add_argument(
122+
"wheel_file",
123+
metavar="WHEEL_FILE",
124+
)
125+
126+
args = parser.parse_args()
127+
128+
wheel_file = args.wheel_file
129+
wheel_dir = args.wheel_dir
130+
archs = args.require_archs
131+
132+
main(wheel_file, wheel_dir, archs)

changelog.d/14253.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Build ABI3 wheels for cpython.

pyproject.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,12 @@ environment= { PATH = "$PATH:$HOME/.cargo/bin" }
330330
before-build = "rm -rf {project}/build"
331331
build-frontend = "build"
332332
test-command = "python -c 'from synapse.synapse_rust import sum_as_string; print(sum_as_string(1, 2))'"
333+
334+
335+
[tool.cibuildwheel.linux]
336+
# Wrap the repair command to correctly rename the built cpython wheels as ABI3.
337+
repair-wheel-command = "./.ci/scripts/auditwheel_wrapper.py -w {dest_dir} {wheel}"
338+
339+
[tool.cibuildwheel.macos]
340+
# Wrap the repair command to correctly rename the built cpython wheels as ABI3.
341+
repair-wheel-command = "./.ci/scripts/auditwheel_wrapper.py --require-archs {delocate_archs} -w {dest_dir} {wheel}"

0 commit comments

Comments
 (0)