Skip to content

Strip versioned Xcode path from build flags #414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 10, 2024
Merged

Conversation

charliermarsh
Copy link
Member

@charliermarsh charliermarsh commented Dec 8, 2024

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

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.

)

sdk_path = res.stdout.strip()
sdk_path = os.environ["APPLE_SDK_PATH"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may break local builds outside of CI which don't have APPLE_SDK_PATH set.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok -- I was partly trying to understand whether this is always set in CI or if I was missing something. Makes sense.

Comment on lines 804 to 807
sdk_path = os.environ["APPLE_SDK_PATH"]
if not os.path.exists(sdk_path):
raise Exception("macOS SDK path %s does not exist" % sdk_path)
env["APPLE_SDK_PATH"] = sdk_path
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is already performed around line 170. Do we need to check this again?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah will DRY this up once it's working as expected.

@indygreg
Copy link
Collaborator

indygreg commented Dec 8, 2024

Alternatively, consider removing the sysroot argument from compiler flags. This is probably inferred by the compiler. If it isn't in a default CPython build's cflags, just rip it out.

@charliermarsh
Copy link
Member Author

Yeah, let me check. I'm trying to understand why it doesn't appear for me when building cffi (unlike in the repro here).

It's not present in sysconfig.get_config_var:

>>> sysconfig.get_config_var("CFLAGS")
'-fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -g -O3 -Wall -arch arm64 -mmacosx-version-min=11.0 -Wno-nullability-completeness -Wno-expansion-to-defined -Wno-undef-prefix   -fPIC    -Werror=unguarded-availability-new'

But it does appear in _sysconfigdata_t_darwin_darwin.py:

'CFLAGS': '-fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -g -O3 -Wall -arch arm64 -mmacosx-version-min=11.0 -Wno-nullability-completeness -Wno-expansion-to-defined -Wno-undef-prefix  -isysroot /Applications/Xcode_15.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk -fPIC    -Werror=unguarded-availability-new',

@charliermarsh
Copy link
Member Author

Interesting, it looks like CPython itself strips these out in some cases?

https://github.com/python/cpython/blob/a03efb533a58fd13fb0cc7f4a5c02c8406a407bd/Lib/_osx_support.py#L438

@charliermarsh
Copy link
Member Author

Ahhh ok. It seems that CPython does not strip --isysroot (even from the sysconfig-provided CFLAGS) if you have a non-empty CFLAGS set in your environment, so I can reproduce the failure with:

CFLAGS="-Wunreachable-code" uv pip install cffi==1.17.1 --python 3.13t --no-cache --verbose

That's interesting. Should we just drop it then from CFLAGS et al?

@charliermarsh
Copy link
Member Author

It seems more correct to me to just drop isysroot everywhere, in that case.

@indygreg
Copy link
Collaborator

indygreg commented Dec 8, 2024

the sysroot arguments are a bit nonconventional. I use those to target a specific Xcode toolchain instead of relying on environment variables. I prefer the arguments over environment variables because they are visible in compiler flags. Env variables are often not logged nor easily preserved by build systems, like Python's sysconfig.

@charliermarsh
Copy link
Member Author

Does it seem right to you to strip it from _sysconfigdata_t_darwin_darwin.py, though? (That's my latest attempt, above.)

@charliermarsh
Copy link
Member Author

charliermarsh commented Dec 8, 2024

(That way, we retain the use of arguments over environment variables in python-build-standalone itself, but avoid shipping those unwanted paths to users. Unless I'm misunderstanding the dataflow.)

@charliermarsh
Copy link
Member Author

Okay this work on Python 3.13, but earlier versions have their _sysconfigdata_t_darwin_darwin.py formatted such that -isysroot and the path appear in separate strings within an implicit concatenation, which is super annoying... I'm tempted to re-format them prior to find-replace.

@charliermarsh charliermarsh marked this pull request as ready for review December 8, 2024 23:30
@charliermarsh charliermarsh requested a review from zanieb December 8, 2024 23:30
@charliermarsh charliermarsh changed the title Use a non-versioned Xcode path in sysconfig Strip versioned Xcode path from sysconfig Dec 8, 2024
@charliermarsh charliermarsh changed the title Strip versioned Xcode path from sysconfig Strip versioned Xcode path from build flags Dec 8, 2024


# Format sysconfig to ensure that string replacements take effect.
format_sysconfigdata()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't reformat the sysconfig data file, then -isysroot and the path that follows it appear on separate lines, as separate strings within an implicit concatenation -- and replacing them becomes much harder. It looks like this file is already formatted on 3.13, but not on prior versions, so this seems fine IMO.

globals_dict = {}
locals_dict = {}
exec(data, globals_dict, locals_dict)
build_time_vars = locals_dict['build_time_vars']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we assert there aren't other variables so this doesn't break something in the future?

@charliermarsh charliermarsh merged commit 0649b17 into main Dec 10, 2024
280 checks passed
@charliermarsh
Copy link
Member Author

(Also re-tested these manually against astral-sh/uv#9744.)

@charliermarsh charliermarsh deleted the charlie/xcode branch December 10, 2024 13:27
Copy link
Collaborator

@indygreg indygreg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


with open(SYSCONFIGDATA, "wb") as fh:
fh.write(b'# system configuration generated and used by the sysconfig module\n')
fh.write(('build_time_vars = %s' % json.dumps(build_time_vars, indent=4)).encode("utf-8"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need a sort_keys=True to make the output deterministic? If there are any dicts, it does.

charliermarsh added a commit to astral-sh/uv that referenced this pull request Dec 13, 2024
## Summary

This is equivalent to
astral-sh/python-build-standalone#414, but at
install-time, so that it affects older Python builds too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants