Adds pybind11.get_source_dir() / python -m pybind11 --srcdir and a srcdir variable in pybind11.pc, so build systems such as Meson can compile src/pybind11_combined.cpp with PYBIND11_PRECOMPILED defined. Assisted-by: ClaudeCode:claude-fable-5
159 lines
5.2 KiB
Python
159 lines
5.2 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
DIR = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
# shlex/sysconfig are imported lazily below to keep `import pybind11` light
|
|
|
|
# This is the conditional used for os.path being posixpath
|
|
if "posix" in sys.builtin_module_names:
|
|
|
|
def _quote(s: str) -> str:
|
|
import shlex
|
|
|
|
return shlex.quote(s)
|
|
elif "nt" in sys.builtin_module_names:
|
|
# See https://github.com/mesonbuild/meson/blob/db22551ed9d2dd7889abea01cc1c7bba02bf1c75/mesonbuild/utils/universal.py#L1092-L1121
|
|
# and the original documents:
|
|
# https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and
|
|
# https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
|
_UNSAFE = re.compile("[ \t\n\r]")
|
|
|
|
def _quote(s: str) -> str:
|
|
if s and not _UNSAFE.search(s):
|
|
return s
|
|
|
|
# Paths cannot contain a '"' on Windows, so we don't need to worry
|
|
# about nuanced counting here.
|
|
return f'"{s}\\"' if s.endswith("\\") else f'"{s}"'
|
|
else:
|
|
|
|
def _quote(s: str) -> str:
|
|
return s
|
|
|
|
|
|
def _config(name: str) -> str:
|
|
"""A stripped sysconfig variable, or "" if unset."""
|
|
import sysconfig
|
|
|
|
return str(sysconfig.get_config_var(name) or "").strip()
|
|
|
|
|
|
def get_include(user: bool = False) -> str: # noqa: ARG001
|
|
"""
|
|
Return the path to the pybind11 include directory. The historical "user"
|
|
argument is unused, and may be removed.
|
|
"""
|
|
installed_path = os.path.join(DIR, "include")
|
|
source_path = os.path.join(os.path.dirname(DIR), "include")
|
|
return installed_path if os.path.exists(installed_path) else source_path
|
|
|
|
|
|
def get_source_dir() -> str:
|
|
"""
|
|
Return the path to the pybind11 library sources, for the optional
|
|
precompiled mode. Compile ``pybind11_combined.cpp`` (or the individual
|
|
``.cpp`` files) with ``PYBIND11_PRECOMPILED`` defined, and define that
|
|
macro for every translation unit that includes pybind11.
|
|
"""
|
|
installed_path = os.path.join(DIR, "share", "pybind11", "src")
|
|
source_path = os.path.join(os.path.dirname(DIR), "src")
|
|
if os.path.exists(installed_path):
|
|
return installed_path
|
|
if os.path.exists(source_path):
|
|
return source_path
|
|
|
|
msg = "pybind11 library sources not found (pybind11 not installed?)"
|
|
raise ImportError(msg)
|
|
|
|
|
|
def get_cmake_dir() -> str:
|
|
"""
|
|
Return the path to the pybind11 CMake module directory.
|
|
"""
|
|
cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11")
|
|
if os.path.exists(cmake_installed_path):
|
|
return cmake_installed_path
|
|
|
|
msg = "pybind11 not installed, installation required to access the CMake files"
|
|
raise ImportError(msg)
|
|
|
|
|
|
def get_pkgconfig_dir() -> str:
|
|
"""
|
|
Return the path to the pybind11 pkgconfig directory.
|
|
"""
|
|
pkgconfig_installed_path = os.path.join(DIR, "share", "pkgconfig")
|
|
if os.path.exists(pkgconfig_installed_path):
|
|
return pkgconfig_installed_path
|
|
|
|
msg = "pybind11 not installed, installation required to access the pkgconfig files"
|
|
raise ImportError(msg)
|
|
|
|
|
|
def get_include_dirs() -> list[str]:
|
|
"""
|
|
Return the unique include directories for Python and pybind11.
|
|
"""
|
|
import sysconfig
|
|
|
|
dirs = [
|
|
sysconfig.get_path("include"),
|
|
sysconfig.get_path("platinclude"),
|
|
get_include(),
|
|
]
|
|
|
|
# Make unique but preserve order
|
|
unique_dirs = []
|
|
for d in dirs:
|
|
if d and d not in unique_dirs:
|
|
unique_dirs.append(d)
|
|
return unique_dirs
|
|
|
|
|
|
def get_cflags() -> str:
|
|
"""
|
|
Return the compile flags for building a simple extension with a
|
|
Unix-style command-line compiler (GCC/Clang). Based on python-config.
|
|
"""
|
|
flags = [_quote(f"-I{d}") for d in get_include_dirs()]
|
|
# CFLAGS is a pre-composed multi-flag string, passed through as-is (like
|
|
# python-config does)
|
|
if cflags := _config("CFLAGS"):
|
|
flags.append(cflags)
|
|
flags.append("-std=c++17")
|
|
return " ".join(flags)
|
|
|
|
|
|
def get_ldflags(embed: bool = False) -> str:
|
|
"""
|
|
Return the link flags for building a simple extension (or, with
|
|
embed=True, an embedding program) with a Unix-style command-line
|
|
compiler (GCC/Clang). Based on python-config.
|
|
"""
|
|
# LDFLAGS/LIBS/SYSLIBS are pre-composed multi-flag strings, passed
|
|
# through as-is (like python-config does)
|
|
flags = [ldflags] if (ldflags := _config("LDFLAGS")) else []
|
|
|
|
if embed:
|
|
if libdir := _config("LIBDIR"):
|
|
flags.append(_quote(f"-L{libdir}"))
|
|
if not _config("Py_ENABLE_SHARED") and (libpl := _config("LIBPL")):
|
|
flags.append(_quote(f"-L{libpl}"))
|
|
abiflags = getattr(sys, "abiflags", "") or ""
|
|
flags.append(f"-lpython{_config('VERSION')}{abiflags}")
|
|
if libs := _config("LIBS"):
|
|
flags.append(libs)
|
|
if syslibs := _config("SYSLIBS"):
|
|
flags.append(syslibs)
|
|
elif sys.platform.startswith("darwin"):
|
|
flags += ["-undefined", "dynamic_lookup", "-shared"]
|
|
elif os.name == "posix":
|
|
# Linux and other Unix platforms (FreeBSD, Solaris, AIX, ...)
|
|
flags += ["-fPIC", "-shared"]
|
|
|
|
return " ".join(flags)
|