switched from static determination of the library options to using build_ext.get_ext_fullpath().
1 files changed, 57 insertions(+), 30 deletions(-)

M setup.py
M setup.py +57 -30
@@ 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 @@ module2 = Extension(
     "abcd.name",
     sources = ["abcd_name.c"],
     depends = ["abcd.h"],
-    extra_link_args = libcore_link_args,
     )
 
 # A Cython extension

          
@@ 50,15 38,53 @@ module3 = Extension(
     "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 @@ setup(
         ] + cythonize([
             module3,
         ]),
+    cmdclass = {"build_ext": build_ext_with_libraries},
     )