Skip to content

Commit 130fdd2

Browse files
cibuildwheel-bot[bot]henryiiimayeut
authored
feat: free-threaded Python for macOS, MACOSX_DEPLOYMENT_TARGET updates (#1854)
* Update dependencies * feat: macos free-threading Signed-off-by: Henry Schreiner <[email protected]> * fix: set min target to 10.13 for 3.13 and floor it always Signed-off-by: Henry Schreiner <[email protected]> * fix: tests and corrected pypy mins Signed-off-by: Henry Schreiner <[email protected]> --------- Signed-off-by: Henry Schreiner <[email protected]> Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> Co-authored-by: Henry Schreiner <[email protected]> Co-authored-by: Matthieu Darbois <[email protected]>
1 parent ed12054 commit 130fdd2

File tree

8 files changed

+105
-49
lines changed

8 files changed

+105
-49
lines changed

.github/workflows/test.yml

+2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ jobs:
8282
output-dir: wheelhouse
8383
env:
8484
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
85+
CIBW_FREE_THREADED_SUPPORT: 1
86+
CIBW_PRERELEASE_PYTHONS: 1
8587

8688
- name: Run a sample build (GitHub Action, only)
8789
uses: ./

cibuildwheel/macos.py

+44-10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
detect_ci_provider,
3434
download,
3535
find_compatible_wheel,
36+
free_thread_enable_313,
3637
get_build_verbosity_extra_flags,
3738
get_pip_version,
3839
install_certifi_script,
@@ -115,12 +116,13 @@ def get_python_configurations(
115116
return python_configurations
116117

117118

118-
def install_cpython(tmp: Path, version: str, url: str) -> Path:
119-
installation_path = Path(f"/Library/Frameworks/Python.framework/Versions/{version}")
119+
def install_cpython(tmp: Path, version: str, url: str, free_threading: bool) -> Path:
120+
ft = "T" if free_threading else ""
121+
installation_path = Path(f"/Library/Frameworks/Python{ft}.framework/Versions/{version}")
120122
with FileLock(CIBW_CACHE_PATH / f"cpython{version}.lock"):
121123
installed_system_packages = call("pkgutil", "--pkgs", capture_stdout=True).splitlines()
122124
# if this version of python isn't installed, get it from python.org and install
123-
python_package_identifier = f"org.python.Python.PythonFramework-{version}"
125+
python_package_identifier = f"org.python.Python.Python{ft}Framework-{version}"
124126
if python_package_identifier not in installed_system_packages:
125127
if detect_ci_provider() is None:
126128
# if running locally, we don't want to install CPython with sudo
@@ -137,13 +139,22 @@ def install_cpython(tmp: Path, version: str, url: str) -> Path:
137139
# download the pkg
138140
download(url, pkg_path)
139141
# install
140-
call("sudo", "installer", "-pkg", pkg_path, "-target", "/")
142+
args = []
143+
if version.startswith("3.13"):
144+
# Python 3.13 is the first version to have a free-threading option
145+
args += ["-applyChoiceChangesXML", str(free_thread_enable_313.resolve())]
146+
call("sudo", "installer", "-pkg", pkg_path, *args, "-target", "/")
141147
pkg_path.unlink()
142148
env = os.environ.copy()
143149
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
144-
call(installation_path / "bin" / "python3", install_certifi_script, env=env)
145150

146-
return installation_path / "bin" / "python3"
151+
if free_threading:
152+
call(installation_path / f"bin/python{version}t", "-m", "ensurepip", env=env)
153+
call(installation_path / f"bin/python{version}t", install_certifi_script, env=env)
154+
else:
155+
call(installation_path / "bin/python3", install_certifi_script, env=env)
156+
157+
return installation_path / "bin" / (f"python{version}t" if free_threading else "python3")
147158

148159

149160
def install_pypy(tmp: Path, url: str) -> Path:
@@ -172,13 +183,19 @@ def setup_python(
172183
implementation_id = python_configuration.identifier.split("-")[0]
173184
log.step(f"Installing Python {implementation_id}...")
174185
if implementation_id.startswith("cp"):
175-
base_python = install_cpython(tmp, python_configuration.version, python_configuration.url)
186+
free_threading = "t-macos" in python_configuration.identifier
187+
base_python = install_cpython(
188+
tmp, python_configuration.version, python_configuration.url, free_threading
189+
)
190+
176191
elif implementation_id.startswith("pp"):
177192
base_python = install_pypy(tmp, python_configuration.url)
178193
else:
179194
msg = "Unknown Python implementation"
180195
raise ValueError(msg)
181-
assert base_python.exists()
196+
assert (
197+
base_python.exists()
198+
), f"{base_python.name} not found, has {list(base_python.parent.iterdir())}"
182199

183200
log.step("Setting up build environment...")
184201
venv_path = tmp / "venv"
@@ -244,8 +261,25 @@ def setup_python(
244261
# Set MACOSX_DEPLOYMENT_TARGET, if the user didn't set it.
245262
# For arm64, the minimal deployment target is 11.0.
246263
# On x86_64 (or universal2), use 10.9 as a default.
247-
# PyPy defaults to 10.7, causing inconsistencies if it's left unset.
248-
env.setdefault("MACOSX_DEPLOYMENT_TARGET", "11.0" if config_is_arm64 else "10.9")
264+
# CPython 3.13 needs 10.13.
265+
if config_is_arm64:
266+
default_target = "11.0"
267+
elif Version(python_configuration.version) >= Version("3.13"):
268+
default_target = "10.13"
269+
elif python_configuration.identifier.startswith("pp") and Version(
270+
python_configuration.version
271+
) >= Version("3.9"):
272+
default_target = "10.15"
273+
else:
274+
default_target = "10.9"
275+
env.setdefault("MACOSX_DEPLOYMENT_TARGET", default_target)
276+
277+
# This is a floor, it can't be set lower than the default_target.
278+
if Version(env["MACOSX_DEPLOYMENT_TARGET"]) < Version(default_target):
279+
log.warning(
280+
f"Bumping MACOSX_DEPLOYMENT_TARGET ({env['MACOSX_DEPLOYMENT_TARGET']}) to the minimum required ({default_target})."
281+
)
282+
env["MACOSX_DEPLOYMENT_TARGET"] = default_target
249283

250284
if python_configuration.version not in {"3.6", "3.7"}:
251285
if config_is_arm64:

cibuildwheel/resources/build-platforms.toml

+15-12
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,12 @@ python_configurations = [
123123
{ identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
124124
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
125125
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
126-
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
127-
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
128-
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
126+
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
127+
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
128+
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
129+
{ identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
130+
{ identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
131+
{ identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b2-macos11.pkg" },
129132
{ identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" },
130133
{ identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
131134
{ identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
@@ -149,18 +152,18 @@ python_configurations = [
149152
{ identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
150153
{ identifier = "cp311-win32", version = "3.11.9", arch = "32" },
151154
{ identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
152-
{ identifier = "cp312-win32", version = "3.12.3", arch = "32" },
153-
{ identifier = "cp312-win_amd64", version = "3.12.3", arch = "64" },
154-
{ identifier = "cp313-win32", version = "3.13.0-b1", arch = "32" },
155-
{ identifier = "cp313t-win32", version = "3.13.0-b1", arch = "32" },
156-
{ identifier = "cp313-win_amd64", version = "3.13.0-b1", arch = "64" },
157-
{ identifier = "cp313t-win_amd64", version = "3.13.0-b1", arch = "64" },
155+
{ identifier = "cp312-win32", version = "3.12.4", arch = "32" },
156+
{ identifier = "cp312-win_amd64", version = "3.12.4", arch = "64" },
157+
{ identifier = "cp313-win32", version = "3.13.0-b2", arch = "32" },
158+
{ identifier = "cp313t-win32", version = "3.13.0-b2", arch = "32" },
159+
{ identifier = "cp313-win_amd64", version = "3.13.0-b2", arch = "64" },
160+
{ identifier = "cp313t-win_amd64", version = "3.13.0-b2", arch = "64" },
158161
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
159162
{ identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
160163
{ identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
161-
{ identifier = "cp312-win_arm64", version = "3.12.3", arch = "ARM64" },
162-
{ identifier = "cp313-win_arm64", version = "3.13.0-b1", arch = "ARM64" },
163-
{ identifier = "cp313t-win_arm64", version = "3.13.0-b1", arch = "ARM64" },
164+
{ identifier = "cp312-win_arm64", version = "3.12.4", arch = "ARM64" },
165+
{ identifier = "cp313-win_arm64", version = "3.13.0-b2", arch = "ARM64" },
166+
{ identifier = "cp313t-win_arm64", version = "3.13.0-b2", arch = "ARM64" },
164167
{ identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" },
165168
{ identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
166169
{ identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<array>
5+
<dict>
6+
<key>attributeSetting</key>
7+
<integer>1</integer>
8+
<key>choiceAttribute</key>
9+
<string>selected</string>
10+
<key>choiceIdentifier</key>
11+
<string>org.python.Python.PythonTFramework-3.13</string>
12+
</dict>
13+
</array>
14+
</plist>

cibuildwheel/resources/pinned_docker_images.cfg

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
[x86_64]
22
manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
33
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
4-
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-06-03-e195670
4+
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-06-06-99f15a7
55
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
6-
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024-06-03-e195670
7-
musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024-06-03-e195670
8-
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2024-06-03-e195670
6+
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024-06-06-99f15a7
7+
musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024-06-06-99f15a7
8+
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2024-06-06-99f15a7
99

1010
[i686]
1111
manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
1212
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
13-
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-06-03-e195670
13+
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-06-06-99f15a7
1414
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
15-
musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024-06-03-e195670
16-
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2024-06-03-e195670
15+
musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024-06-06-99f15a7
16+
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2024-06-06-99f15a7
1717

1818
[pypy_x86_64]
1919
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
20-
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-06-03-e195670
20+
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-06-06-99f15a7
2121
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
22-
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024-06-03-e195670
22+
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024-06-06-99f15a7
2323

2424
[pypy_i686]
2525
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
26-
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-06-03-e195670
26+
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-06-06-99f15a7
2727
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
2828

2929
[aarch64]

cibuildwheel/util.py

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060

6161
install_certifi_script: Final[Path] = resources_dir / "install_certifi.py"
6262

63+
free_thread_enable_313: Final[Path] = resources_dir / "free-threaded-enable-313.xml"
64+
6365
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
6466

6567

test/test_macos_archs.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*utils.expected_wheels("spam", "0.1.0", machine_arch="arm64", include_universal2=True),
1515
}
1616

17-
DEPLOYMENT_TARGET_TOO_LOW_WARNING = "[WARNING] MACOSX_DEPLOYMENT_TARGET is set to a lower value"
17+
DEPLOYMENT_TARGET_TOO_LOW_WARNING = "Bumping MACOSX_DEPLOYMENT_TARGET"
1818

1919

2020
def get_xcode_version() -> tuple[int, int]:

test/utils.py

+17-16
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,11 @@ def cibuildwheel_run(
137137
return wheels
138138

139139

140-
def _get_arm64_macosx_deployment_target(macosx_deployment_target: str) -> str:
140+
def _floor_macosx(*args: str) -> str:
141141
"""
142-
The first version of macOS that supports arm is 11.0. So the wheel tag
143-
cannot contain an earlier deployment target, even if
144-
MACOSX_DEPLOYMENT_TARGET sets it.
142+
Make sure a deployment target is not less than some value.
145143
"""
146-
version_tuple = tuple(map(int, macosx_deployment_target.split(".")))
147-
if version_tuple <= (11, 0):
148-
return "11.0"
149-
else:
150-
return macosx_deployment_target
144+
return max(args, key=lambda x: tuple(map(int, x.split("."))))
151145

152146

153147
def expected_wheels(
@@ -202,9 +196,8 @@ def expected_wheels(
202196
"cp311-cp311",
203197
"cp312-cp312",
204198
"cp313-cp313",
199+
"cp313-cp313t",
205200
]
206-
if platform != "macos":
207-
python_abi_tags.append("cp313-cp313t")
208201

209202
if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
210203
python_abi_tags += [
@@ -223,6 +216,7 @@ def expected_wheels(
223216
"cp311-cp311",
224217
"cp312-cp312",
225218
"cp313-cp313",
219+
"cp313-cp313t",
226220
"pp38-pypy38_pp73",
227221
"pp39-pypy39_pp73",
228222
"pp310-pypy310_pp73",
@@ -282,12 +276,19 @@ def expected_wheels(
282276

283277
elif platform == "macos":
284278
if machine_arch == "arm64":
285-
arm64_macosx_deployment_target = _get_arm64_macosx_deployment_target(
286-
macosx_deployment_target
287-
)
288-
platform_tags = [f'macosx_{arm64_macosx_deployment_target.replace(".", "_")}_arm64']
279+
arm64_macosx = _floor_macosx(macosx_deployment_target, "11.0")
280+
platform_tags = [f'macosx_{arm64_macosx.replace(".", "_")}_arm64']
289281
else:
290-
platform_tags = [f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64']
282+
if python_abi_tag.startswith("pp") and not python_abi_tag.startswith(
283+
("pp37", "pp38")
284+
):
285+
pypy_macosx = _floor_macosx(macosx_deployment_target, "10.15")
286+
platform_tags = [f'macosx_{pypy_macosx.replace(".", "_")}_x86_64']
287+
elif python_abi_tag.startswith("cp313"):
288+
pypy_macosx = _floor_macosx(macosx_deployment_target, "10.13")
289+
platform_tags = [f'macosx_{pypy_macosx.replace(".", "_")}_x86_64']
290+
else:
291+
platform_tags = [f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64']
291292

292293
if include_universal2:
293294
platform_tags.append(

0 commit comments

Comments
 (0)