# HG changeset patch # User Andrew Dalke # Date 1669800880 -3600 # Wed Nov 30 10:34:40 2022 +0100 # Node ID 8024b496bec0e30d998ba508924974cd84f9e2ba # Parent 1e32504f628793dd8d4362d5f773b11dd9dc91d9 switched from static determination of the library options to using build_ext.get_ext_fullpath(). diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1,36 +1,25 @@ import sys +import pathlib from setuptools import setup, Extension -import sysconfig +from setuptools.command.build_ext import build_ext from Cython.Build import cythonize -#### System configuration information -# A value like '.cpython-39-x86_64-linux-gnu.so' -# or '.cpython-39-darwin.so' or '.so' -# Used to determine the shared library filename. -EXT_SUFFIX = sysconfig.get_config_var("EXT_SUFFIX") -EXT_INFIX = EXT_SUFFIX.rpartition(".")[0] - -# A value like 'linux-x86_64', 'macosx-10.14-x86_64', or -# 'freebsd-12.2-RELEASE-p9-amd64' -platform = sysconfig.get_platform() - -# The default location of the build directory. -# (This assumes the directory was not specified on the command-line.) -plat_specifier = "%s-%d.%d" % (platform, *sys.version_info[:2]) - -# Figure out the platform-specific linker arguments so the new -# extensions can access the C functions in the core library. -if sys.platform == "darwin": - libcore_link_args = [] -else: - # The compiler needs the -L to find libcore in the build directory. - # The run-time loader needs an '$ORIGIN' rpath to find libcore in the install directory. - libcore_link_args = [f"-Lbuild/lib.{plat_specifier}/abcd", f"-labcd_core{EXT_INFIX}", "-Wl,-rpath,$ORIGIN"] +# Version 1.1 of this package tried to figure out the extra_link_args +# statically, before calling setup(), based on the platform name and +# version. This ended up being non-portable. A conda-based Python used +# the subdirectory "lib.linux-x86_64-cpython-39" where I expected +# "lib.linux-x86_64-3.9". +# +# Version 1.2 switched to a "build_ext" extension which uses +# get_ext_fullpath() to get the output library file and from +# that determine the library path and name. #### Extension configuration information - -# This starts with 'lib' so I can use the '-l' flag. + +# This module starts with 'lib' so I can use the '-l' flag in the +# extra_link_args of the other modules. + module1 = Extension( "abcd.libabcd_core", sources = ["abcd.c"], @@ -42,7 +31,6 @@ "abcd.name", sources = ["abcd_name.c"], depends = ["abcd.h"], - extra_link_args = libcore_link_args, ) # A Cython extension @@ -50,15 +38,53 @@ "abcd.add", sources = ["abcd_add.pyx"], depends = ["abcd.h"], - extra_link_args = libcore_link_args, ) +# Configure the internal dependencies for each package. +# Specify that module X depends on modules [A, B ...]. +# In this case, module2 and module3 depend on module1. +internal_libraries = { + "abcd.name": ["abcd.libabcd_core"], + "abcd.add": ["abcd.libabcd_core"], +} + +#### extend the "build" command so it adds the appropriate dependencies + +# Given the output path for a given module, figure out the +# "-L" and "-l" arguments for extra_link_args. + +# This are needed for Linux-based OSes, but not Darwin. + +def get_library_link_args(path): + libdir = path.parent # Used for "-L". + stem = path.stem # Used for "-l". + + # remove the leading "lib" prefix, so we can use "-l" + assert stem[:3] == "lib", stem # double-checking + libname = stem[3:] + + # The run-time loader needs an '$ORIGIN' rpath to find the library in the + # install directory. + return [f"-L{libdir}", f"-l{libname}", "-Wl,-rpath,$ORIGIN"] + + +class build_ext_with_libraries(build_ext): + def build_extensions(self): + if sys.platform != "darwin": + # Update the extra_link_args + for e in self.extensions: + internal_libs = internal_libraries.get(e.name, []) + for internal_lib in internal_libs: + path = pathlib.Path(self.get_ext_fullpath("abcd.libabcd_core")) + e.extra_link_args.extend(get_library_link_args(path)) + super().build_extensions() + #### setup() setup( name = "abcd", - version = "1.1", - description = "example of a shared library used by C/Python and Cython extensions", + version = "1.2", + description = "example of having C/Python and Cython extensions depend on an included shared library", author = "Andrew Dalke", packages = ["abcd"], ext_modules = [ @@ -67,4 +93,5 @@ ] + cythonize([ module3, ]), + cmdclass = {"build_ext": build_ext_with_libraries}, )