From 25ecc040d007a55e4b5c30fa739054b52c1aacac Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 18 Nov 2021 10:18:44 +0200 Subject: [PATCH] bpo-45573: Introduce extension module flags in Makefile (GH-29594) ``configure`` now uses a standardized format to forward state, compiler flags, and linker flags to ``Makefile``, ``setup.py``, and ``Modules/Setup``. ``makesetup`` use the new variables by default if a module line does not contain any compiler or linker flags. ``setup.py`` has a new function ``addext()``. For a module ``egg``, configure adds: * ``MODULE_EGG`` with value yes, missing, disabled, or n/a * ``MODULE_EGG_CFLAGS`` * ``MODULE_EGG_LDFLAGS`` ``Makefile.pre.in`` may also provide ``MODULE_EGG_DEPS`` that lists dependencies such as header files and static libs. Signed-off-by: Christian Heimes --- Makefile.pre.in | 18 +- .../2021-11-17-19-02-51.bpo-45573.GMNdun.rst | 3 + Modules/Setup | 15 +- Modules/makesetup | 10 +- aclocal.m4 | 50 +++++ configure | 208 ++++++++++++++++++ configure.ac | 62 ++++++ setup.py | 172 +++++++-------- 8 files changed, 435 insertions(+), 103 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2021-11-17-19-02-51.bpo-45573.GMNdun.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 1535cabdade..55336d24ed1 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -201,12 +201,26 @@ RUNSHARED= @RUNSHARED@ # ensurepip options ENSUREPIP= @ENSUREPIP@ +# Internal static libraries +LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a +LIBEXPAT_A= Modules/expat/libexpat.a + # OpenSSL options for setup.py so sysconfig can pick up AC_SUBST() vars. OPENSSL_INCLUDES=@OPENSSL_INCLUDES@ OPENSSL_LIBS=@OPENSSL_LIBS@ OPENSSL_LDFLAGS=@OPENSSL_LDFLAGS@ OPENSSL_RPATH=@OPENSSL_RPATH@ +# Module compiler and linker flags +# yes: module is available +# missing: build dependency is missing +# disabled: module is disabled +# n/a: module is not available on the current platform +# MODULE_EGG=yes # yes, missing, disabled, n/a +# MODULE_EGG_CFLAGS= +# MODULE_EGG_LDFLAGS= +@MODULE_BLOCK@ + # Default zoneinfo.TZPATH. Added here to expose it in sysconfig.get_config_var TZPATH=@TZPATH@ @@ -535,8 +549,6 @@ LIBMPDEC_HEADERS= \ $(srcdir)/Modules/_decimal/libmpdec/typearith.h \ $(srcdir)/Modules/_decimal/libmpdec/umodarith.h -LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a - ########################################################################## # pyexpat's expat library @@ -562,8 +574,6 @@ LIBEXPAT_HEADERS= \ Modules/expat/xmltok.h \ Modules/expat/xmltok_impl.h -LIBEXPAT_A= Modules/expat/libexpat.a - ######################################################################### # Rules diff --git a/Misc/NEWS.d/next/Build/2021-11-17-19-02-51.bpo-45573.GMNdun.rst b/Misc/NEWS.d/next/Build/2021-11-17-19-02-51.bpo-45573.GMNdun.rst new file mode 100644 index 00000000000..688b22c3e48 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2021-11-17-19-02-51.bpo-45573.GMNdun.rst @@ -0,0 +1,3 @@ +``configure`` now uses a unified format to set state, compiler flags, and +linker flags in Makefile. The new macro ``PY_STDLIB_MOD`` sets three +variables that are consumed by ``Modules/Setup`` and ``setup.py``. diff --git a/Modules/Setup b/Modules/Setup index 608866d9ced..414c6af4b69 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -42,6 +42,15 @@ # You can also use any Make variable that is detected by configure and # defined in Makefile.pre.in, e.g. OpenSSL flags $(OPENSSL_INCLUDES). # +# Rules generated by makesetup use additional variables: +# +# - All source file rules have a dependency on $(PYTHON_HEADERS) and on +# optional variable $(MODULES_{mod_upper}_DEPS). +# - If no and no arguments are given, then makesetup +# defaults to $(MODULES_{mod_upper}_CFLAGS) cppargs and +# $(MODULES_{mod_upper}_LDFLAGS) libraries. The variables are typically +# defined by configure. +# # The build process works like this: # # 1. Build all modules that are declared as static in Modules/Setup, @@ -149,7 +158,7 @@ time timemodule.c #_contextvars _contextvarsmodule.c #_csv _csv.c #_datetime _datetimemodule.c -#_decimal _decimal/_decimal.c $(DECIMAL_CFLAGS) $(DECIMAL_LDFLAGS) +#_decimal _decimal/_decimal.c #_heapq _heapqmodule.c #_json _json.c #_lsprof _lsprof.c rotatingtree.c @@ -172,8 +181,8 @@ time timemodule.c #select selectmodule.c # XML -#_elementtree _elementtree.c $(EXPAT_CFLAGS) -#pyexpat pyexpat.c $(EXPAT_CFLAGS) $(EXPAT_LDFLAGS) +#_elementtree _elementtree.c +#pyexpat pyexpat.c # hashing builtins #_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/makesetup b/Modules/makesetup index a8817fffb7c..2335724e804 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -154,6 +154,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | cpps= libs= mods= + mods_upper= skip= for arg in $line do @@ -194,11 +195,17 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | *.*) echo 1>&2 "bad word $arg in $line" exit 1;; -u) skip=libs; libs="$libs -u";; - [a-zA-Z_]*) mods="$mods $arg";; + [a-zA-Z_]*) + mods="$mods $arg" + mods_upper=$(echo $mods | tr '[a-z]' '[A-Z]');; *) echo 1>&2 "bad word $arg in $line" exit 1;; esac done + if test -z "$cpps" -a -z "$libs"; then + cpps="\$(MODULE_${mods_upper}_CFLAGS)" + libs="\$(MODULE_${mods_upper}_LDFLAGS)" + fi case $doconfig in yes) LIBS="$LIBS $libs" @@ -245,7 +252,6 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | *) cc="$cc \$(PY_BUILTIN_MODULE_CFLAGS)";; esac - mods_upper=$(echo $mods | tr '[a-z]' '[A-Z]') # force rebuild when header file or module build flavor (static/shared) is changed rule="$obj: $src \$(MODULE_${mods_upper}_DEPS) \$(PYTHON_HEADERS) Modules/config.c; $cc $cpps -c $src -o $obj" echo "$rule" >>$rulesf diff --git a/aclocal.m4 b/aclocal.m4 index 2f1bd37528c..6a33c0cc9d9 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -619,3 +619,53 @@ AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 2006-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + diff --git a/configure b/configure index 53dc4a23f80..ab5afbf0b8a 100755 --- a/configure +++ b/configure @@ -623,6 +623,13 @@ ac_includes_default="\ #endif" ac_subst_vars='LTLIBOBJS +MODULE_BLOCK +MODULE__DECIMAL_FALSE +MODULE__DECIMAL_TRUE +MODULE__ELEMENTTREE_FALSE +MODULE__ELEMENTTREE_TRUE +MODULE_PYEXPAT_FALSE +MODULE_PYEXPAT_TRUE TEST_MODULES LIBRARY_DEPS STATIC_LIBPYTHON @@ -19196,6 +19203,183 @@ $as_echo "no" >&6; } fi +case $ac_sys_system in #( + AIX) : + py_stdlib_not_available="_scproxy spwd" ;; #( + VxWorks*) : + py_stdlib_not_available="_scproxy _crypt termios grp" ;; #( + Darwin) : + py_stdlib_not_available="ossaudiodev spwd" ;; #( + CYGWIN*) : + py_stdlib_not_available="_scproxy nis" ;; #( + QNX*) : + py_stdlib_not_available="_scproxy nis" ;; #( + FreeBSD*) : + py_stdlib_not_available="_scproxy spwd" ;; #( + *) : + py_stdlib_not_available="_scproxy" + ;; +esac + + +MODULE_BLOCK= + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module pyexpat" >&5 +$as_echo_n "checking for stdlib extension module pyexpat... " >&6; } + case $py_stdlib_not_available in #( + *pyexpat*) : + py_cv_module_pyexpat=n/a ;; #( + *) : + + if true; then : + if true; then : + py_cv_module_pyexpat=yes +else + py_cv_module_pyexpat=missing +fi +else + py_cv_module_pyexpat=disabled + +fi + + ;; +esac + as_fn_append MODULE_BLOCK "MODULE_PYEXPAT=$py_cv_module_pyexpat$as_nl" + if test "x$py_cv_module_pyexpat" = xyes; then : + + as_fn_append MODULE_BLOCK "MODULE_PYEXPAT_CFLAGS=$LIBEXPAT_CFLAGS$as_nl" + as_fn_append MODULE_BLOCK "MODULE_PYEXPAT_LDFLAGS=$LIBEXPAT_LDFLAGS$as_nl" + if true; then + MODULE_PYEXPAT_TRUE= + MODULE_PYEXPAT_FALSE='#' +else + MODULE_PYEXPAT_TRUE='#' + MODULE_PYEXPAT_FALSE= +fi + + +else + + if false; then + MODULE_PYEXPAT_TRUE= + MODULE_PYEXPAT_FALSE='#' +else + MODULE_PYEXPAT_TRUE='#' + MODULE_PYEXPAT_FALSE= +fi + + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module_pyexpat" >&5 +$as_echo "$py_cv_module_pyexpat" >&6; } + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _elementtree" >&5 +$as_echo_n "checking for stdlib extension module _elementtree... " >&6; } + case $py_stdlib_not_available in #( + *_elementtree*) : + py_cv_module__elementtree=n/a ;; #( + *) : + + if true; then : + if true; then : + py_cv_module__elementtree=yes +else + py_cv_module__elementtree=missing +fi +else + py_cv_module__elementtree=disabled + +fi + + ;; +esac + as_fn_append MODULE_BLOCK "MODULE__ELEMENTTREE=$py_cv_module__elementtree$as_nl" + if test "x$py_cv_module__elementtree" = xyes; then : + + as_fn_append MODULE_BLOCK "MODULE__ELEMENTTREE_CFLAGS=$LIBEXPAT_CFLAGS$as_nl" + as_fn_append MODULE_BLOCK "MODULE__ELEMENTTREE_LDFLAGS=$as_nl" + if true; then + MODULE__ELEMENTTREE_TRUE= + MODULE__ELEMENTTREE_FALSE='#' +else + MODULE__ELEMENTTREE_TRUE='#' + MODULE__ELEMENTTREE_FALSE= +fi + + +else + + if false; then + MODULE__ELEMENTTREE_TRUE= + MODULE__ELEMENTTREE_FALSE='#' +else + MODULE__ELEMENTTREE_TRUE='#' + MODULE__ELEMENTTREE_FALSE= +fi + + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__elementtree" >&5 +$as_echo "$py_cv_module__elementtree" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _decimal" >&5 +$as_echo_n "checking for stdlib extension module _decimal... " >&6; } + case $py_stdlib_not_available in #( + *_decimal*) : + py_cv_module__decimal=n/a ;; #( + *) : + + if true; then : + if true; then : + py_cv_module__decimal=yes +else + py_cv_module__decimal=missing +fi +else + py_cv_module__decimal=disabled + +fi + + ;; +esac + as_fn_append MODULE_BLOCK "MODULE__DECIMAL=$py_cv_module__decimal$as_nl" + if test "x$py_cv_module__decimal" = xyes; then : + + as_fn_append MODULE_BLOCK "MODULE__DECIMAL_CFLAGS=$LIBMPDEC_CFLAGS$as_nl" + as_fn_append MODULE_BLOCK "MODULE__DECIMAL_LDFLAGS=$LIBMPDEC_LDFLAGS$as_nl" + if true; then + MODULE__DECIMAL_TRUE= + MODULE__DECIMAL_FALSE='#' +else + MODULE__DECIMAL_TRUE='#' + MODULE__DECIMAL_FALSE= +fi + + +else + + if false; then + MODULE__DECIMAL_TRUE= + MODULE__DECIMAL_FALSE='#' +else + MODULE__DECIMAL_TRUE='#' + MODULE__DECIMAL_FALSE= +fi + + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__decimal" >&5 +$as_echo "$py_cv_module__decimal" >&6; } + + +# substitute multiline block, must come after last PY_STDLIB_MOD() + # generate output files ac_config_files="$ac_config_files Makefile.pre Misc/python.pc Misc/python-embed.pc Misc/python-config.sh" @@ -19312,6 +19496,30 @@ LTLIBOBJS=$ac_ltlibobjs +if test -z "${MODULE_PYEXPAT_TRUE}" && test -z "${MODULE_PYEXPAT_FALSE}"; then + as_fn_error $? "conditional \"MODULE_PYEXPAT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${MODULE_PYEXPAT_TRUE}" && test -z "${MODULE_PYEXPAT_FALSE}"; then + as_fn_error $? "conditional \"MODULE_PYEXPAT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${MODULE__ELEMENTTREE_TRUE}" && test -z "${MODULE__ELEMENTTREE_FALSE}"; then + as_fn_error $? "conditional \"MODULE__ELEMENTTREE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${MODULE__ELEMENTTREE_TRUE}" && test -z "${MODULE__ELEMENTTREE_FALSE}"; then + as_fn_error $? "conditional \"MODULE__ELEMENTTREE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${MODULE__DECIMAL_TRUE}" && test -z "${MODULE__DECIMAL_FALSE}"; then + as_fn_error $? "conditional \"MODULE__DECIMAL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${MODULE__DECIMAL_TRUE}" && test -z "${MODULE__DECIMAL_FALSE}"; then + as_fn_error $? "conditional \"MODULE__DECIMAL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 diff --git a/configure.ac b/configure.ac index f43030e4810..43c8f768c18 100644 --- a/configure.ac +++ b/configure.ac @@ -5936,6 +5936,68 @@ else fi AC_SUBST(TEST_MODULES) +dnl Modules that are not available on some platforms +dnl AIX has shadow passwords, but access is not via getspent() +dnl VxWorks does not provide crypt() function +AS_CASE([$ac_sys_system], + [AIX], [py_stdlib_not_available="_scproxy spwd"], + [VxWorks*], [py_stdlib_not_available="_scproxy _crypt termios grp"], + [Darwin], [py_stdlib_not_available="ossaudiodev spwd"], + [CYGWIN*], [py_stdlib_not_available="_scproxy nis"], + [QNX*], [py_stdlib_not_available="_scproxy nis"], + [FreeBSD*], [py_stdlib_not_available="_scproxy spwd"], + [py_stdlib_not_available="_scproxy"] +) + +dnl _MODULE_BLOCK_ADD([VAR], [VALUE]) +dnl internal: adds $1=quote($2) to MODULE_BLOCK +AC_DEFUN([_MODULE_BLOCK_ADD], [AS_VAR_APPEND([MODULE_BLOCK], ["$1=_AS_QUOTE([$2])$as_nl"])]) +MODULE_BLOCK= + +dnl Check for stdlib extension modules +dnl PY_STDLIB_MOD([NAME], [ENABLED-TEST], [SUPPORTED-TEST], [CFLAGS], [LDFLAGS]) +dnl sets MODULE_$NAME based on $py_stdlib_not_available, ENABLED-TEST, +dnl and SUPPORTED_TEST. ENABLED-TEST and SUPPORTED-TEST default to true if +dnl empty. +dnl n/a: $NAME in $py_stdlib_not_available (not available on platform) +dnl yes: enabled and supported +dnl missing: enabled and not supported +dnl disabled: not enabled +dnl sets MODULE_$NAME_CFLAGS and MODULE_$NAME_LDFLAGS +AC_DEFUN([PY_STDLIB_MOD], [ + AC_MSG_CHECKING([for stdlib extension module $1]) + m4_pushdef([modcond], [MODULE_]m4_toupper([$1]))dnl + m4_pushdef([modstate], [py_cv_module_$1])dnl + AS_CASE([$py_stdlib_not_available], + [*$1*], [modstate=n/a], + [ + AS_IF(m4_ifblank([$2], [true], [$2]), + [AS_IF([m4_ifblank([$3], [true], [$3])], [modstate=yes], [modstate=missing])], + [modstate=disabled] + ) + ] + ) + _MODULE_BLOCK_ADD(modcond, [$modstate]) + AS_VAR_IF([modstate], [yes], [ + _MODULE_BLOCK_ADD([MODULE_]m4_toupper([$1])[_CFLAGS], [$4]) + _MODULE_BLOCK_ADD([MODULE_]m4_toupper([$1])[_LDFLAGS], [$5]) + AM_CONDITIONAL(modcond, [true]) + ], [ + AM_CONDITIONAL(modcond, [false]) + ]) + AC_MSG_RESULT([$modstate]) + m4_popdef([modcond])dnl + m4_popdef([modstate])dnl +]) + +dnl _elementtree loads libexpat via CAPI hook in pyexpat +PY_STDLIB_MOD([pyexpat], [], [], [$LIBEXPAT_CFLAGS], [$LIBEXPAT_LDFLAGS]) +PY_STDLIB_MOD([_elementtree], [], [], [$LIBEXPAT_CFLAGS], []) + +PY_STDLIB_MOD([_decimal], [], [], [$LIBMPDEC_CFLAGS], [$LIBMPDEC_LDFLAGS]) + +# substitute multiline block, must come after last PY_STDLIB_MOD() +AC_SUBST([MODULE_BLOCK]) # generate output files AC_CONFIG_FILES(Makefile.pre Misc/python.pc Misc/python-embed.pc Misc/python-config.sh) diff --git a/setup.py b/setup.py index 915169e6863..8b9566e26bf 100644 --- a/setup.py +++ b/setup.py @@ -364,57 +364,6 @@ def find_module_file(module, dirlist): return os.path.abspath(os.path.join(dirs[0], module)) -def parse_cflags(flags): - """Parse a string with compiler flags (-I, -D, -U, extra) - - Distutils appends extra args to the compiler arguments. Some flags like - -I must appear earlier. Otherwise the pre-processor picks up files - from system inclue directories. - """ - include_dirs = [] - define_macros = [] - undef_macros = [] - extra_compile_args = [] - if flags is not None: - # shlex.split(None) reads from stdin - for token in shlex.split(flags): - switch = token[0:2] - value = token[2:] - if switch == '-I': - include_dirs.append(value) - elif switch == '-D': - key, _, val = value.partition("=") - if not val: - val = None - define_macros.append((key, val)) - elif switch == '-U': - undef_macros.append(value) - else: - extra_compile_args.append(token) - - return include_dirs, define_macros, undef_macros, extra_compile_args - - -def parse_ldflags(flags): - """Parse a string with linker flags (-L, -l, extra)""" - library_dirs = [] - libraries = [] - extra_link_args = [] - if flags is not None: - # shlex.split(None) reads from stdin - for token in shlex.split(flags): - switch = token[0:2] - value = token[2:] - if switch == '-L': - library_dirs.append(value) - elif switch == '-l': - libraries.append(value) - else: - extra_link_args.append(token) - - return library_dirs, libraries, extra_link_args - - class PyBuildExt(build_ext): def __init__(self, dist): @@ -433,6 +382,74 @@ class PyBuildExt(build_ext): def add(self, ext): self.extensions.append(ext) + def addext(self, ext, *, update_flags=True): + """Add extension with Makefile MODULE_{name} support + """ + if update_flags: + self.update_extension_flags(ext) + + state = sysconfig.get_config_var(f"MODULE_{ext.name.upper()}") + if state == "yes": + self.extensions.append(ext) + elif state == "disabled": + self.disabled_configure.append(ext.name) + elif state == "missing": + self.missing.append(ext.name) + elif state == "n/a": + # not available on current platform + pass + else: + # not migrated to MODULE_{name} yet. + self.extensions.append(ext) + + def update_extension_flags(self, ext): + """Update extension flags with module CFLAGS and LDFLAGS + + Reads MODULE_{name}_CFLAGS and _LDFLAGS + + Distutils appends extra args to the compiler arguments. Some flags like + -I must appear earlier, otherwise the pre-processor picks up files + from system inclue directories. + """ + upper_name = ext.name.upper() + # Parse compiler flags (-I, -D, -U, extra args) + cflags = sysconfig.get_config_var(f"MODULE_{upper_name}_CFLAGS") + if cflags: + for token in shlex.split(cflags): + switch = token[0:2] + value = token[2:] + if switch == '-I': + ext.include_dirs.append(value) + elif switch == '-D': + key, _, val = value.partition("=") + if not val: + val = None + ext.define_macros.append((key, val)) + elif switch == '-U': + ext.undef_macros.append(value) + else: + ext.extra_compile_args.append(token) + + # Parse linker flags (-L, -l, extra objects, extra args) + ldflags = sysconfig.get_config_var(f"MODULE_{upper_name}_LDFLAGS") + if ldflags: + for token in shlex.split(ldflags): + switch = token[0:2] + value = token[2:] + if switch == '-L': + ext.library_dirs.append(value) + elif switch == '-l': + ext.libraries.append(value) + elif ( + token[0] != '-' and + token.endswith(('.a', '.o', '.so', '.sl', '.dylib')) + ): + ext.extra_objects.append(token) + else: + ext.extra_link_args.append(token) + + return ext + def set_srcdir(self): self.srcdir = sysconfig.get_config_var('srcdir') if not self.srcdir: @@ -1527,32 +1544,11 @@ class PyBuildExt(build_ext): # # More information on Expat can be found at www.libexpat.org. # - cflags = parse_cflags(sysconfig.get_config_var("EXPAT_CFLAGS")) - include_dirs, define_macros, undef_macros, extra_compile_args = cflags - # ldflags includes either system libexpat or full path to - # our static libexpat.a. - ldflags = parse_ldflags(sysconfig.get_config_var("EXPAT_LDFLAGS")) - library_dirs, libraries, extra_link_args = ldflags - - self.add(Extension('pyexpat', - include_dirs=include_dirs, - define_macros=define_macros, - undef_macros=undef_macros, - extra_compile_args=extra_compile_args, - library_dirs=library_dirs, - libraries=libraries, - extra_link_args=extra_link_args, - sources=['pyexpat.c'])) + self.addext(Extension('pyexpat', sources=['pyexpat.c'])) # Fredrik Lundh's cElementTree module. Note that this also # uses expat (via the CAPI hook in pyexpat). - self.add(Extension('_elementtree', - include_dirs=include_dirs, - define_macros=define_macros, - undef_macros=undef_macros, - extra_compile_args=extra_compile_args, - # no EXPAT_LDFLAGS - sources=['_elementtree.c'])) + self.addext(Extension('_elementtree', sources=['_elementtree.c'])) def detect_multibytecodecs(self): # Hye-Shik Chang's CJKCodecs modules. @@ -2046,26 +2042,14 @@ class PyBuildExt(build_ext): def detect_decimal(self): # Stefan Krah's _decimal module - sources = ['_decimal/_decimal.c'] - - cflags = parse_cflags(sysconfig.get_config_var("DECIMAL_CFLAGS")) - include_dirs, define_macros, undef_macros, extra_compile_args = cflags - # ldflags includes either system libmpdec or full path to - # our static libmpdec.a. - ldflags = parse_ldflags(sysconfig.get_config_var("DECIMAL_LDFLAGS")) - library_dirs, libraries, extra_link_args = ldflags - - # Uncomment for extra functionality: - #define_macros.append(('EXTRA_FUNCTIONALITY', 1)) - self.add(Extension('_decimal', - include_dirs=include_dirs, - define_macros=define_macros, - undef_macros=undef_macros, - extra_compile_args=extra_compile_args, - library_dirs=library_dirs, - libraries=libraries, - extra_link_args=extra_link_args, - sources=sources)) + self.addext( + Extension( + '_decimal', + ['_decimal/_decimal.c'], + # Uncomment for extra functionality: + # define_macros=[('EXTRA_FUNCTIONALITY', 1)] + ) + ) def detect_openssl_hashlib(self): # Detect SSL support for the socket module (via _ssl)