Building extensions with hardening flags (via manylinux)

I’m fairly sure it’s a feature - at least, it was done on purpose. It’s bad practice to just copy over some compile flags from one of one’s dependencies from first principles. sysconfig.get_config_var(...) is discouraged API[1] in CPython, CPython can be built with different CFLAGS by different distributors, and we have a mix of C and C++ extension modules but sysconfig.get_config_var('CXXFLAGS') does not exist.

You can also check what your local interpreter(s) give back, there’s lots of stuff in there that’s just random and unrelated to building extension modules. On my macOS machine:

# system python CFLAGS
% python3 -c "import sysconfig; print(sysconfig.get_config_var('CFLAGS'))"
-Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -iwithsysroot/System/Library/Frameworks/System.framework/PrivateHeaders -iwithsysroot/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/Headers -arch arm64 -arch x86_64 -Werror=implicit-function-declaration -Wno-error=unreachable-code -Wno-error=unused-but-set-variable -Wno-error=cast-function-type-mismatch -Wno-unknown-warning-option

# conda-forge python CFLAGS
% python3 -c "import sysconfig; print(sysconfig.get_config_var('CFLAGS'))"         
-fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -O2 -Wall  -fPIC  -O2  -isystem /Users/rgommers/code/pixi-dev-scipystack/scipy/scipy/.pixi/envs/build/include   -arch arm64   -fPIC  -O2  -isystem /Users/rgommers/code/pixi-dev-scipystack/scipy/scipy/.pixi/envs/build/include   -arch arm64

Very little of that is related to extension modules; from -O2 to lots of warning-related flags, as well as duplicated flags - it’s a mess. It gets worse when you compare across platform; if I check on Linux aarch64 I get -O3 instead of -O2 and -flto is included, which will break quite a few packages that aren’t LTO-ready.

Not much is documented either way, and what’s there tends to be conflicting with each other. The split between CFLAGS and CFLAGS_NODIST in the configure docs and the existence of python3-config --cflags are hints at there being intent of reuse, but I read it as outdated setuptools-specific code.

After writing this, I did a bit more searching and found PEP 831. The most compelling evidence for CFLAGS reuse is this section. The authors knew though that not all build systems use the sysconfig values, see the Ecosystem Impact section[2].

I think we need something new (preferably in code, but docs-only would already help) which contains only the flags essential to or strongly recommended for extension module building. There’s only a couple, and they’re already included in build-details.json (libpython.link_extensions, abi.extension_suffix, c_api.headers, see PEP 739). That looks like the best place to add a new key, and add frame pointer and -fcf-protection flags as values. PEP 831 is also a nice model of how to propose adding a new default compile flag to extension modules - a PEP seems appropriate, rather than manylinux trying to add something that isn’t specific to distributing on PyPI.


  1. We had a lot of discussions previously, and my understanding is that while get_config_var is public, there’s no guarantees on contents of configure flags captured at build time. There’s a desire, and open issues, for a proper design of public API for what Python packages/users may need. ↩︎

  2. See also the bold in the abstract for “Strongly recommend that all build systems in the Python ecosystem build with frame pointers by default” ↩︎

5 Likes