# SPDX-License-Identifier: MIT # # Copyright (C) 2017 Francis Deslauriers # Copyright (C) 2020 Jérémie Galarneau import sys import os import shutil import subprocess # Distutils was removed in Python 3.12, use setuptools as an alternative. if sys.version_info >= (3, 12): from setuptools import setup, Extension else: from distutils.core import setup, Extension # Starting with Debian's Python 3.10, the default install scheme is # 'posix_local' which is a Debian specific scheme based on 'posix_prefix' but # with an added 'local' prefix. This is the default so users doing system wide # manual installations of python modules end up in '/usr/local'. This # interferes with our autotools based install which already defaults to # '/usr/local' and expect a provided prefix to be used verbatim. # # Monkeypatch sysconfig to override this scheme and use 'posix_prefix' instead. if sys.version_info >= (3, 10): import sysconfig original_get_preferred_scheme = sysconfig.get_preferred_scheme def our_get_preferred_scheme(key): scheme = original_get_preferred_scheme(key) if scheme == "posix_local": return "posix_prefix" else: return scheme sysconfig.get_preferred_scheme = our_get_preferred_scheme else: import distutils.sysconfig as sysconfig PY_PATH_WARN_MSG = """ -------------------------------------WARNING------------------------------------ The install directory used:\n ({})\nis not included in your PYTHONPATH. To add this directory to your Python search path permanently you can add the following command to your .bashrc/.zshrc: export PYTHONPATH="${{PYTHONPATH}}:{}" -------------------------------------------------------------------------------- """ original_get_config_vars = sysconfig.get_config_vars def get_cflags(): cflags = os.environ.get("CFLAGS") if cflags is None: [cflags] = original_get_config_vars("CFLAGS") return cflags # distutils performs a similar transformation step on LDSHARED on # darwin to use the overridden CC as the default command for LDSHARED # (see distutils' customize_compiler() step in the sysconfig module). # # This takes it a step further by using our own LDFLAGS (when available) # along with the overridden compiler and ensure that flags that are unsupported # by either the Python interprter's CC or the overridden CC don't cause a # build failure. def get_ldshared(): cc = os.environ.get("CC") ldflags = os.environ.get("LDFLAGS") [py_cc] = original_get_config_vars("CC") [py_ldshared] = original_get_config_vars("LDSHARED") if not py_ldshared.startswith(py_cc): return py_ldshared if cc and ldflags: return "{} -shared {}".format(cc, ldflags) elif cc: return cc + py_ldshared[len(py_cc) :] elif ldflags: return py_cc + py_ldshared[len(py_cc) :] else: return py_ldshared def our_get_config_vars(*args): overridden_config_vars_funcs = { "CFLAGS": get_cflags, "LDSHARED": get_ldshared, } if len(args) == 0: # Return a dict with all config vars. all_config_vars = original_get_config_vars() for name in overridden_config_vars_funcs: all_config_vars[name] = overridden_config_vars_funcs[name]() return all_config_vars else: # Return a list with the requested config vars. subset_config_vars = [] for name in args: if name in overridden_config_vars_funcs: subset_config_vars.append(overridden_config_vars_funcs[name]()) else: subset_config_vars.append(original_get_config_vars(name)[0]) return subset_config_vars sysconfig.get_config_vars = our_get_config_vars # Returns 'True' when running on a MinGW system. def is_mingw(): return sys.platform == "win32" and shutil.which("cygpath") != None # On MinGW systems run 'cygpath -m' on 'path', on other systems return 'path' as-is. def cygpath_m(path: str): if is_mingw(): return subprocess.check_output( 'cygpath -m "{}"'.format(path), shell=True, encoding="utf-8" ).strip("\n") return path # On MinGW systems, check CFLAGS and CPPFLAGS for absolute include paths # (starts with '-I/') and convert them to valid Windows paths using cygpath. if is_mingw(): for flagvar in ["CFLAGS", "CPPFLAGS"]: cur_flags = os.getenv(flagvar) if cur_flags != None: new_flags = "" for flag in cur_flags.split(): if flag.startswith("-I/"): flag = "-I{}".format(cygpath_m(flag[2:])) new_flags += " {}".format(flag) os.environ[flagvar] = new_flags def main(): babeltrace_ext = Extension( "bt2._native_bt", sources=[ "bt2/native_bt.c", cygpath_m("@srcdir@/bt2/logging.c"), ], libraries=["babeltrace2", "glib-2.0"], extra_objects=[ "@top_builddir@/src/autodisc/.libs/libautodisc.a", "@top_builddir@/src/logging/.libs/liblogging.a", "@top_builddir@/src/common/.libs/libcommon.a", "@top_builddir@/src/py-common/.libs/libpy-common.a", "@top_builddir@/src/string-format/.libs/libstring-format.a", ], ) dist = setup( name="bt2", version="@PACKAGE_VERSION@", description="Babeltrace 2 Python Bindings", packages=["bt2"], package_dir={"bt2": "bt2"}, options={ "build": {"build_base": "build", "build_lib": "build/build_lib"}, "build_ext": {"build_lib": "build/build_lib"}, }, url="https://babeltrace.org/", ext_modules=[babeltrace_ext], license="MIT", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: The MIT License", "Programming Language :: Python :: 3" "Topic :: System :: Logging", ], ) # After the installation, we check that the install directory is included in # the Python search path and we print a warning message when it's not. # We need to do this because Python search path differs depending on the distro # and some distros don't include any /usr/local/ in the search path. This is # also useful for out-of-tree installs and tests. # It's only relevant to make this check on the `install` command. if "install" in dist.command_obj: install_dir = dist.command_obj["install"].install_libbase if install_dir not in sys.path: # We can't consider this an error because if affects every # distro differently. We only warn the user that some # extra configuration is needed to use the bindings print(PY_PATH_WARN_MSG.format(install_dir, install_dir)) if __name__ == "__main__": main()