Skip to content

Commit 78b142e

Browse files
committed
Make universal2 wheels on macos
1 parent 6611533 commit 78b142e

File tree

3 files changed

+85
-34
lines changed

3 files changed

+85
-34
lines changed

.github/workflows/build-macos.yml

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -57,43 +57,30 @@ jobs:
5757
# path: ${{ github.workspace }}/pygame_mac_deps_${{ matrix.macarch }}
5858

5959
build:
60-
name: ${{ matrix.macarch }}
60+
name: universal2
6161
needs: deps
62-
runs-on: ${{ matrix.os }}
63-
strategy:
64-
fail-fast: false # if a particular matrix build fails, don't skip the rest
65-
matrix:
66-
include:
67-
- { macarch: arm64, os: macos-15 }
68-
- { macarch: x86_64, os: macos-15 }
62+
runs-on: macos-15
6963

7064
env:
71-
MAC_ARCH: ${{ matrix.macarch }}
72-
73-
# Explicitly tell CIBW what the wheel arch deployment target should be
74-
# There seems to be no better way to set this than this env
75-
# We need this because our minimum is 10.11, different from default
76-
# of 10.9 on x86s
77-
# Related issue: https://github.com/pypa/cibuildwheel/issues/952
78-
_PYTHON_HOST_PLATFORM: ${{ matrix.macarch == 'x86_64' && 'macosx-10.11-x86_64' || 'macosx-11.0-arm64'}}
79-
80-
# Similarly, we need to tell CIBW that the wheel's linking steps
81-
# should be for 10.11 on x86
82-
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macarch == 'x86_64' && '10.11' || '11.0' }}
65+
# Our deployment target is 10.11, even on universal2. Technically this
66+
# is a lie for the arm64 part of the wheel (where the deployment target is
67+
# higher, at 11.0). However this is fine because with this hack our wheels
68+
# are acceptable on old intel macs.
69+
# cibuildwheel can internally raise this to a higher version if the python
70+
# version its building for requires it.
71+
MACOSX_DEPLOYMENT_TARGET: '10.11'
8372

84-
CIBW_ARCHS: ${{ matrix.macarch }}
73+
CIBW_ARCHS: universal2
8574

8675
# Setup macOS dependencies
8776
CIBW_BEFORE_ALL: |
8877
cd buildconfig/macdependencies
89-
cp -r ${{ github.workspace }}/pygame_mac_deps_${{ matrix.macarch }} ${{ github.workspace }}/pygame_mac_deps
9078
bash ./install_mac_deps.sh
91-
92-
CIBW_BEFORE_BUILD: |
93-
cp -r ${{ github.workspace }}/pygame_mac_deps_${{ matrix.macarch }} ${{ github.workspace }}/pygame_mac_deps
79+
mv ${{ github.workspace }}/pygame_mac_deps ${{ github.workspace }}/deps_moved
9480
9581
# To remove any speculations about the wheel not being self-contained
96-
CIBW_BEFORE_TEST: rm -rf ${{ github.workspace }}/pygame_mac_deps
82+
CIBW_BEFORE_BUILD: mv ${{ github.workspace }}/deps_moved ${{ github.workspace }}/pygame_mac_deps
83+
CIBW_BEFORE_TEST: mv ${{ github.workspace }}/pygame_mac_deps ${{ github.workspace }}/deps_moved
9784

9885
steps:
9986
- uses: actions/checkout@v5.0.0
@@ -102,14 +89,20 @@ jobs:
10289
uses: actions/cache@v4.3.0
10390
with:
10491
path: ~/Library/Caches/pip # This cache path is only right on mac
105-
key: pip-cache-${{ matrix.macarch }}-${{ matrix.os }}
92+
key: pip-cache-macos
10693

107-
- name: Fetch Mac deps
108-
id: macdep-cache
94+
- name: Fetch Mac deps (x86_64)
10995
uses: actions/cache@v4.3.0
11096
with:
111-
path: ${{ github.workspace }}/pygame_mac_deps_${{ matrix.macarch }}
112-
key: macdep-${{ hashFiles('buildconfig/manylinux-build/**') }}-${{ hashFiles('buildconfig/macdependencies/*.sh') }}-${{ matrix.macarch }}
97+
path: ${{ github.workspace }}/pygame_mac_deps_x86_64
98+
key: macdep-${{ hashFiles('buildconfig/manylinux-build/**') }}-${{ hashFiles('buildconfig/macdependencies/*.sh') }}-x86_64
99+
fail-on-cache-miss: true
100+
101+
- name: Fetch Mac deps (arm64)
102+
uses: actions/cache@v4.3.0
103+
with:
104+
path: ${{ github.workspace }}/pygame_mac_deps_arm64
105+
key: macdep-${{ hashFiles('buildconfig/manylinux-build/**') }}-${{ hashFiles('buildconfig/macdependencies/*.sh') }}-arm64
113106
fail-on-cache-miss: true
114107

115108
- name: Install uv for speed
@@ -122,6 +115,6 @@ jobs:
122115

123116
- uses: actions/upload-artifact@v5
124117
with:
125-
name: pygame-wheels-macos-${{ matrix.macarch }}
118+
name: pygame-wheels-macos
126119
path: ./wheelhouse/*.whl
127120
compression-level: 0 # wheels are already zip files, no need for more compression

buildconfig/macdependencies/install_mac_deps.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import shutil
6+
import subprocess
67
import sys
78
from pathlib import Path
89

@@ -30,6 +31,53 @@ def rmpath(path: Path, verbose: bool = False):
3031
shutil.rmtree(path)
3132

3233

34+
def merge_dylibs(out_dir: Path, x86_dir: Path, arm_dir: Path, verbose: bool = False):
35+
"""
36+
Merge .dylib files from x86_64 into a copy of arm64 folder.
37+
- Moves arm_dir to out_dir.
38+
- For each .dylib in x86_dir, merges it with the arm64 one if present,
39+
otherwise just copies it over.
40+
- Deletes x86_dir after merging.
41+
"""
42+
shutil.move(arm_dir, out_dir)
43+
if verbose:
44+
print(f"- Moved {arm_dir} -> {out_dir}")
45+
46+
for x86_file in x86_dir.rglob("*.dylib"):
47+
rel_path = x86_file.relative_to(x86_dir)
48+
out_file = out_dir / rel_path
49+
if out_file.is_symlink():
50+
try:
51+
target = out_file.resolve(strict=True)
52+
except (FileNotFoundError, OSError):
53+
raise RuntimeError(f"Broken or bad symlink: {rel_path}")
54+
55+
if not target.is_relative_to(out_dir):
56+
raise RuntimeError(
57+
f"Unsafe symlink: {rel_path} points to {target} (outside {out_dir})"
58+
)
59+
60+
if verbose:
61+
print(f"- Skipped symlink: {rel_path}")
62+
continue
63+
64+
if out_file.exists():
65+
subprocess.run(
66+
["lipo", "-create", "-output", out_file, out_file, x86_file], check=True
67+
)
68+
if verbose:
69+
print(f"- Merged: {rel_path}")
70+
else:
71+
out_file.parent.mkdir(parents=True, exist_ok=True)
72+
shutil.copy2(x86_file, out_file)
73+
if verbose:
74+
print(f"- Copied x86-only: {rel_path}")
75+
76+
shutil.rmtree(x86_dir)
77+
if verbose:
78+
print(f"- Deleted {x86_dir}")
79+
80+
3381
def symtree(srcdir: Path, destdir: Path, verbose: bool = False):
3482
"""
3583
This function creates symlinks pointing to srcdir, from destdir, such that
@@ -58,4 +106,12 @@ def symtree(srcdir: Path, destdir: Path, verbose: bool = False):
58106
destpath.symlink_to(path)
59107

60108

61-
symtree(Path(sys.argv[1]), Path("/usr/local"), verbose=True)
109+
if __name__ == "__main__":
110+
out_dir, x86_dir, arm_dir = map(Path, sys.argv[1:])
111+
try:
112+
merge_dylibs(out_dir, x86_dir, arm_dir, verbose=True)
113+
except RuntimeError as e:
114+
print(f"[ERROR] {e}")
115+
sys.exit(1)
116+
117+
symtree(out_dir, Path("/usr/local"), verbose=True)

buildconfig/macdependencies/install_mac_deps.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
set -e -x
33

44
bash ./clean_usr_local.sh
5-
sudo python3 install_mac_deps.py ${GITHUB_WORKSPACE}/pygame_mac_deps_${MAC_ARCH}
5+
sudo python3 install_mac_deps.py ${GITHUB_WORKSPACE}/pygame_mac_deps \
6+
${GITHUB_WORKSPACE}/pygame_mac_deps_x86_64 \
7+
${GITHUB_WORKSPACE}/pygame_mac_deps_arm64

0 commit comments

Comments
 (0)