Skip to content
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

Building and linking C extensions in a post-distutils world #99942

Open
eli-schwartz opened this issue Dec 2, 2022 · 2 comments
Open

Building and linking C extensions in a post-distutils world #99942

eli-schwartz opened this issue Dec 2, 2022 · 2 comments
Assignees
Labels
3.12 bugs and security fixes 3.13 bugs and security fixes docs Documentation in the Doc dir topic-sysconfig type-feature A feature request or enhancement

Comments

@eli-schwartz
Copy link
Contributor

eli-schwartz commented Dec 2, 2022

With the removal of distutils from 3.12 alphas, I've recently taken a new look at the use of distutils within https://mesonbuild.com in the hopes that we were finally unblocked and could migrate to sysconfig.

  • install schemes turn out to finally be viable since 3.10.3, when the deb_system scheme patch on Debian operating systems for distutils was patched into sysconfig. This hard blocker is gone, thank G-d.
  • linking to libpython itself may or may not be an issue, originally I thought it was, but now I think it may not be.

How can a C-extension supporting PEP 517 build backend know the best way to compile and link?

python <3.12

Meson has some code: https://github.com/mesonbuild/meson/blob/master/mesonbuild/modules/python.py#L338-L407

Here's the interesting bit:

def links_against_libpython():
    from distutils.core import Distribution, Extension
    cmd = Distribution().get_command_obj('build_ext')
    cmd.ensure_finalized()
    return bool(cmd.get_libraries(Extension('dummy', [])))

python >=3.8

In bpo-36721, bpo-21536 etc the above code changes from returning True to False, on many Unix platforms.

New additional methods suitable for getting C extension arguments are available. Well, mostly suitable.

  • sysconfig.get_config_var('LIBPYTHON')

this is configured into:

  • pkg-config --cflags --libs python3
  • python-config --embed

There's a couple problems with this:

  • on Cygwin and Android, LIBPYTHON and thus pkg-config is hardcoded to link to libpython, but distutils only does so when Py_ENABLE_SHARED
  • none of it works on Windows, which since it is built with PCBuild, doesn't have Makefile config vars (and also / consequently? distributes neither of the latter two)
  • pkg-config may not always be installed, so we want a fallback for that
  • python-config has tons of CPython build-time junk so we cannot use it

...

It feels uncomfortably like there's still way too much undocumented magic here.

@vstinner, I assume the Cygwin/Android inconsistency is probably a configure.ac bug that should behave the same way distutils does/did? I can provide a patch to fix it but would like confirmation of which side to fix.

@FFY00, I think in the long term, sysconfig should expose a function that returns cflags / libs required to build an extension (and works on Windows without _generate_posix_vars, and doesn't include lots of -O3 -pipe -fstack-protector-strong -fdiagnostics-color=always and more -- i.e. works like pkg-config, not like python-config). Even without the question of "whether to link to libpython", there's a 140-line class for figuring out what the name of the library is, which directory to find it in, and what the include directory is too. Of course, this is specific to the case where pkg-config is not available, and most of it is for the Windows case.

Linked PRs

@eli-schwartz eli-schwartz added the type-feature A feature request or enhancement label Dec 2, 2022
@CAM-Gerlach CAM-Gerlach added the 3.12 bugs and security fixes label Dec 2, 2022
eli-schwartz added a commit to eli-schwartz/cpython that referenced this issue Dec 20, 2022
…roid/cygwin

In commit 254b309 a previous change to
avoid linking to libpython was partially reverted for Android (and later
Cygwin as well), to add back the link flags. This was applied to
distutils and to python-config.sh, but not to python.pc.

Add it back to python.pc as well.
eli-schwartz added a commit to eli-schwartz/cpython that referenced this issue Dec 20, 2022
…roid/cygwin

In commit 254b309 a previous change to
avoid linking to libpython was partially reverted for Android (and later
Cygwin as well), to add back the link flags. This was applied to
distutils and to python-config.sh, but not to python.pc.

Add it back to python.pc as well.
eli-schwartz added a commit to eli-schwartz/cpython that referenced this issue Dec 30, 2022
…roid/cygwin

In commit 254b309 a previous change to
avoid linking to libpython was partially reverted for Android (and later
Cygwin as well), to add back the link flags. This was applied to
distutils and to python-config.sh, but not to python.pc.

Add it back to python.pc as well.
eli-schwartz added a commit to eli-schwartz/cpython that referenced this issue Dec 30, 2022
…/android

On shared build configurations, Cygwin and Android need to link to
libpython. See bpo-21536.

This was corrected in distutils with an explicit check to only link when
libpython is built shared, but implemented in configure.ac
unconditionally. The correct approach is to follow distutils, which
includes a comment regarding why:
- on Android, a shared libpython is RTLD_LOCAL, and thus available only
  to the main executable, not exported to loaded modules, but a static
  libpython *is* the main executable and thus exported
eli-schwartz added a commit to eli-schwartz/cpython that referenced this issue Jan 12, 2023
…/android

On shared build configurations, Cygwin and Android need to link to
libpython. See bpo-21536.

This was corrected in distutils with an explicit check to only link when
libpython is built shared, but implemented in configure.ac
unconditionally. The correct approach is to follow distutils, which
includes a comment regarding why:

- on Android, a shared libpython is RTLD_LOCAL, and thus available only
  to the main executable, not exported to loaded modules, but a static
  libpython *is* the main executable and thus exported

- on Cygwin, symbols in shared libraries must be resolved at link time

It's actually not clear to me what to do for Cygwin. Cygwin doesn't
actually build static libpython successfully. If it did, then extensions
probably need to be linked to the import library for the executable,
rather than the full static library. So omitting this here is probably
correct, but incomplete. Either way, it won't matter until other fixes
are made.
eli-schwartz added a commit to eli-schwartz/cpython that referenced this issue Jan 12, 2023
…/android

On shared build configurations, Cygwin and Android need to link to
libpython. See bpo-21536.

This was corrected in distutils with an explicit check to only link when
libpython is built shared, but implemented in configure.ac
unconditionally. The correct approach is to follow distutils, which
includes a comment regarding why:

- on Android, a shared libpython is RTLD_LOCAL, and thus available only
  to the main executable, not exported to loaded modules, but a static
  libpython *is* the main executable and thus exported

- on Cygwin, symbols in shared libraries must be resolved at link time

It's actually not clear to me what to do for Cygwin. Cygwin doesn't
actually build static libpython successfully. If it did, then extensions
probably need to be linked to the import library for the executable,
rather than the full static library. So omitting this here is probably
correct, but incomplete. Either way, it won't matter until other fixes
are made.
@zooba
Copy link
Member

zooba commented Jan 12, 2023

The Windows case really isn't complicated because the linking is all set up to isolate the two sides of the ABI. The only thing that's going to bleed through is C runtime state when you mix a debug build with a release build (which use two different CRT instances). Virtually nobody actually has a debug build of CPython, and those who do will know because the filenames are different, so there's nothing really to pick up from the interpreter.

Now, the complicated bit is finding the compiler, in effect, this bit:

root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
if not root:
return None, None
try:
path = subprocess.check_output([
os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
"-latest",
"-prerelease",
"-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"-property", "installationPath",
"-products", "*",
], encoding="mbcs", errors="strict").strip()
except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
return None, None

This is assuming you want to compile with MSVC. If you want to compile an extension with a different compiler (many of which will work, since the ABI is well defined), you'll need different code. Again, this is best left to the tool figuring out how to run the build - one of the main reasons for dropping distutils is so that compiler options don't get trapped by the CPython release cycle.

Otherwise, the location of Python's header files (typically {prefix}\Include) and import libraries (typically {prefix}\libs) are all you need. They should both be in sysconfig somewhere already, but since you can't use that when cross-compiling, we either need a new data file containing the paths (which I'm in favour of, but it wasn't popular last time we discussed it) or to just stick with the assumptions tools use today.

@eli-schwartz
Copy link
Contributor Author

They should both be in sysconfig somewhere already, but since you can't use that when cross-compiling, we either need a new data file containing the paths (which I'm in favour of, but it wasn't popular last time we discussed it) or to just stick with the assumptions tools use today.

There's also the undocumented $_PYTHON_SYSCONFIGDATA_NAME environment variable, which is unfortunately only available on posix. But POSIX systems do often make the assumption that they can convince e.g. distutils to cross compile by setting that environment variable before running any build tool (pip, python -m build, python setup.py ...).

miss-islington pushed a commit that referenced this issue Feb 22, 2023
…nfigure.ac (GH-100356)

In commit 254b309 a previous change to avoid linking to libpython was partially reverted for Android (and later Cygwin as well), to add back the link flags. This was applied to distutils and to python-config.sh, but not to python.pc.

Add it back to python.pc as well.

Automerge-Triggered-By: GH:gpshead
carljm added a commit to carljm/cpython that referenced this issue Feb 23, 2023
* main: (76 commits)
  Fix syntax error in struct doc example (python#102160)
  pythongh-99108: Import MD5 and SHA1 from HACL* (python#102089)
  pythonGH-101777: `queue.rst`: use 2 spaces after a period to be consistent. (python#102143)
  Few coverage nitpicks for the cmath module (python#102067)
  pythonGH-100982: Restrict `FOR_ITER_RANGE` to a single instruction to allow instrumentation. (pythonGH-101985)
  pythongh-102135: Update turtle docs to rename wikipedia demo to rosette (python#102137)
  pythongh-99942: python.pc on android/cygwin should link to libpython per configure.ac (pythonGH-100356)
  pythongh-95672 fix typo SkitTest to SkipTest (pythongh-102119)
  pythongh-101936: Update the default value of fp from io.StringIO to io.BytesIO (pythongh-102100)
  pythongh-102008: simplify test_except_star by using sys.exception() instead of sys.exc_info() (python#102009)
  pythongh-101903: Remove obsolete undefs for previously removed macros Py_EnterRecursiveCall and Py_LeaveRecursiveCall (python#101923)
  pythongh-100556: Improve clarity of `or` docs (python#100589)
  pythongh-101777: Make `PriorityQueue` docs slightly clearer (python#102026)
  pythongh-101965: Fix usage of Py_EnterRecursiveCall return value in _bisectmodule.c (pythonGH-101966)
  pythongh-101578: Amend exception docs (python#102057)
  pythongh-101961 fileinput.hookcompressed should not set the encoding value for the binary mode (pythongh-102068)
  pythongh-102056: Fix a few bugs in error handling of exception printing code (python#102078)
  pythongh-102011: use sys.exception() instead of sys.exc_info() in docs where possible (python#102012)
  pythongh-101566: Sync with zipp 3.14. (pythonGH-102018)
  pythonGH-99818: improve the documentation for zipfile.Path and Traversable (pythonGH-101589)
  ...
@erlend-aasland erlend-aasland added docs Documentation in the Doc dir 3.13 bugs and security fixes labels Jan 5, 2024
python-sidebar pushed a commit to python-sidebar/Python-Documentation-Fork-With-TOC that referenced this issue Sep 1, 2024
…per configure.ac (pythonGH-100356)

In commit 254b309 a previous change to avoid linking to libpython was partially reverted for Android (and later Cygwin as well), to add back the link flags. This was applied to distutils and to python-config.sh, but not to python.pc.

Add it back to python.pc as well.

Automerge-Triggered-By: GH:gpshead
python-sidebar pushed a commit to python-sidebar/Python-Documentation-Fork-With-TOC that referenced this issue Sep 1, 2024
…per configure.ac (pythonGH-100356)

In commit 254b309 a previous change to avoid linking to libpython was partially reverted for Android (and later Cygwin as well), to add back the link flags. This was applied to distutils and to python-config.sh, but not to python.pc.

Add it back to python.pc as well.

Automerge-Triggered-By: GH:gpshead
JelleZijlstra pushed a commit to JelleZijlstra/cpython that referenced this issue Sep 10, 2024
…per configure.ac (pythonGH-100356)

In commit 254b309 a previous change to avoid linking to libpython was partially reverted for Android (and later Cygwin as well), to add back the link flags. This was applied to distutils and to python-config.sh, but not to python.pc.

Add it back to python.pc as well.

Automerge-Triggered-By: GH:gpshead
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.12 bugs and security fixes 3.13 bugs and security fixes docs Documentation in the Doc dir topic-sysconfig type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

5 participants