Skip to content

Commit 0649b17

Browse files
Strip versioned Xcode path from build flags (#414)
## Summary We get reports of users failing to build extension modules on macOS due to the presence of a hardcoded Xcode path. Here's one example: ![bafkreih5iht5nuyhkuzrs3npkio5smggpcrwmwirjr7cq7wiftyx6f77xe-1](https://github.com/user-attachments/assets/7062ab4e-0bbe-4547-a983-0d58b5bf5c2c) I wasn't able to reproduce this locally... until I set `CFLAGS` in my environment (to anything). It turns out that CPython already strips `-isysroot` on macOS, but _not_ if `CFLAGS` is set. See: https://github.com/python/cpython/blob/a03efb533a58fd13fb0cc7f4a5c02c8406a407bd/Lib/_osx_support.py#L331. (I find this a bit surprising -- it should probably _still_ strip the values that are provided via `_sysconfigdata_t_darwin_darwin.py`, and only _not_ strip values that are set explicitly via a `CFLAGS` environment variable? But alas.) This PR modifies our macOS builds to always strip `-isysroot`. We're effectively already doing this for users that _don't_ have any `CFLAGS` set globally, and the current value is almost always going to be wrong. ## Test Plan I confirmed that `CFLAGS="-Wunreachable-code" uv pip install cffi==1.17.1 --python 3.13t --no-cache --verbose` succeeds with a build from this branch, but fails on `main`.
1 parent 985eb18 commit 0649b17

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

cpython-unix/build-cpython.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ fi
587587
# that a) it works on as many machines as possible b) doesn't leak details
588588
# about the build environment, which is non-portable.
589589
cat > ${ROOT}/hack_sysconfig.py << EOF
590+
import json
590591
import os
591592
import sys
592593
import sysconfig
@@ -628,6 +629,41 @@ def replace_in_all(search, replace):
628629
replace_in_file(SYSCONFIGDATA, search, replace)
629630
630631
632+
def format_sysconfigdata():
633+
"""Reformat the sysconfigdata file to avoid implicit string concatenations.
634+
635+
In some Python versions, the sysconfigdata file contains implicit string
636+
concatenations that extend over multiple lines, which make string replacement
637+
much harder. This function reformats the file to avoid this issue.
638+
639+
See: https://github.com/python/cpython/blob/a03efb533a58fd13fb0cc7f4a5c02c8406a407bd/Mac/BuildScript/build-installer.py#L1360C1-L1385C15.
640+
"""
641+
with open(SYSCONFIGDATA, "rb") as fh:
642+
data = fh.read()
643+
644+
globals_dict = {}
645+
locals_dict = {}
646+
exec(data, globals_dict, locals_dict)
647+
build_time_vars = locals_dict['build_time_vars']
648+
649+
with open(SYSCONFIGDATA, "wb") as fh:
650+
fh.write(b'# system configuration generated and used by the sysconfig module\n')
651+
fh.write(('build_time_vars = %s' % json.dumps(build_time_vars, indent=4)).encode("utf-8"))
652+
fh.close()
653+
654+
655+
# Format sysconfig to ensure that string replacements take effect.
656+
format_sysconfigdata()
657+
658+
# Remove the Xcode path from the compiler flags.
659+
#
660+
# CPython itself will drop this from `sysconfig.get_config_var("CFLAGS")` and
661+
# similar calls, but _not_ if `CFLAGS` is set in the environment (regardless of
662+
# the `CFLAGS` value). It will almost always be wrong, so we drop it unconditionally.
663+
xcode_path = os.getenv("APPLE_SDK_PATH")
664+
if xcode_path:
665+
replace_in_all("-isysroot %s" % xcode_path, "")
666+
631667
# -fdebug-default-version is Clang only. Strip so compiling works on GCC.
632668
replace_in_all("-fdebug-default-version=4", "")
633669

cpython-unix/build.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ def add_target_env(env, build_platform, target_triple, build_env):
186186
if not os.path.exists(sdk_path):
187187
raise Exception("macOS SDK path %s does not exist" % sdk_path)
188188

189+
env["APPLE_SDK_PATH"] = sdk_path
190+
189191
# Grab the version from the SDK so we can put it in PYTHON.json.
190192
sdk_settings_path = pathlib.Path(sdk_path) / "SDKSettings.json"
191193
with sdk_settings_path.open("rb") as fh:

0 commit comments

Comments
 (0)