From 4780cc9c5b30f00f96ded3653ccb4df7cd9bb9d0 Mon Sep 17 00:00:00 2001 From: Robert Blackwell Date: Tue, 12 Dec 2023 12:01:13 -0500 Subject: [PATCH] finufft: remove setup.py reliance on ext_modules --- python/finufft/finufft/_finufft.py | 48 ++++++--------------- python/finufft/setup.py | 69 +++++++++++------------------- 2 files changed, 38 insertions(+), 79 deletions(-) diff --git a/python/finufft/finufft/_finufft.py b/python/finufft/finufft/_finufft.py index 0f3c35bf6..c36b9f416 100644 --- a/python/finufft/finufft/_finufft.py +++ b/python/finufft/finufft/_finufft.py @@ -11,16 +11,6 @@ import warnings import platform -# While imp is deprecated, it is currently the inspection solution -# that works for all versions of Python 2 and 3. -# One day if that changes, can be replaced -# with importlib.find_spec. -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - import imp - -import numpy as np - from ctypes import c_double from ctypes import c_int from ctypes import c_float @@ -33,33 +23,21 @@ c_double_p = ctypes.POINTER(c_double) c_longlong_p = ctypes.POINTER(c_longlong) -# TODO: See if there is a way to improve this so it is less hacky. -lib = None -# Try to load a local library directly. -try: - lib = ctypes.cdll.LoadLibrary('libfinufft.so') -except OSError: - pass - -# Should that not work, try to find the full path of a packaged lib. -# The packaged lib should have a py/platform decorated name, -# and be rpath'ed the true FINUFFT library through the Extension and wheel -# systems. +# Attempt to load library, first from package install, then from path as fallback try: - if lib is None: - # Find the library. - fh = imp.find_module('finufft/finufftc')[0] - # Get the full path for the ctypes loader. - if platform.system() == 'Windows': - os.environ["PATH"] += os.pathsep + os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(fh.name))),'finufft') - full_lib_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(fh.name))),'finufft','libfinufft.dll') - else: - full_lib_path = os.path.realpath(fh.name) - fh.close() # Be nice and close the open file handle. - - # Load the library, - # which rpaths the libraries we care about. + pkgroot = os.path.dirname(__file__) + if platform.system() == 'Windows': + os.environ["PATH"] += os.pathsep + pkgroot + basename = "libfinufft.dll" + else: + basename = "libfinufft.so" + + full_lib_path = os.path.join(pkgroot, 'libfinufft.dll') + try: lib = ctypes.cdll.LoadLibrary(full_lib_path) + except: + lib = ctypes.cdll.LoadLibrary(basename) + except Exception: raise ImportError('Failed to find a suitable finufft library') diff --git a/python/finufft/setup.py b/python/finufft/setup.py index 11fd32196..758b1c3a0 100644 --- a/python/finufft/setup.py +++ b/python/finufft/setup.py @@ -7,12 +7,12 @@ __version__ = '2.2.0.dev0' -from setuptools import setup, Extension +from setuptools import setup import os import platform from pathlib import Path - -from tempfile import mkstemp +import shutil +import glob finufft_dir = os.environ.get('FINUFFT_DIR') @@ -22,44 +22,34 @@ finufft_dir = Path(__file__).resolve().parents[2] # Set include and library paths relative to FINUFFT root directory. -inc_dir = os.path.join(finufft_dir, 'include') lib_dir = os.path.join(finufft_dir, 'lib') lib_dir_cmake = os.path.join(finufft_dir, 'build') # lib may be only here # Read in long description from README.md. with open(os.path.join(finufft_dir, 'python', 'finufft', 'README.md'), 'r') as f: - long_description = f.read() - -finufft_dlib = 'finufft' + long_description = f.read() -# Windows does not have the concept of rpath and as a result, MSVC crashes if -# supplied with one. -if platform.system() != "Windows": - runtime_library_dirs = [lib_dir, lib_dir_cmake] +# Capture library and supporting libraries on windows. Ignores libcufinufft +if platform.system() == "Windows": + lib_name = 'libfinufft.dll' + ext_glob = "lib[!c]*.dll" else: - runtime_library_dirs = [] - -# For certain platforms (e.g. Ubuntu 20.04), we need to create a dummy source -# that calls one of the functions in the FINUFFT dynamic library. The reason -# is that these platforms override the default --no-as-needed flag for ld, -# which means that the linker will only link to those dynamic libraries for -# which there are unresolved symbols in the object files. Since we do not have -# a real source, the result is that no dynamic libraries are linked. To -# prevent this, we create a dummy source so that the library will link as -# expected. -fd, source_filename = mkstemp(suffix='.c', text=True) - -with open(fd, 'w') as f: - f.write( \ -""" -#include - -void PyInit_finufftc(void) { - finufft_opts opt; + # Mac and linux both build with .so + lib_name = 'libfinufft.so' + ext_glob = "lib[!c]*.so" + +if os.path.isfile(os.path.join(lib_dir, lib_name)): + lib_dir = lib_dir +elif os.path.isfile(os.path.join(lib_dir_cmake, lib_name)): + lib_dir = lib_dir_cmake +else: + raise FileNotFoundError("Unable to find suitable finufft library") - finufft_default_opts(&opt); -} -""") +# setuptools will only copy files from inside package, so put them there +# use glob to copy supporting libraries in windows +libfiles = glob.glob(os.path.join(lib_dir, ext_glob)) +for src in libfiles: + shutil.copy2(src, 'finufft') ########## SETUP ########### @@ -74,6 +64,8 @@ long_description_content_type='text/markdown', license="Apache 2", packages=['finufft'], + package_dir={'': '.'}, + package_data={'finufft': [ext_glob]}, classifiers=[ 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python :: 3', @@ -85,15 +77,4 @@ install_requires=['numpy>=1.12.0'], python_requires='>=3.6', zip_safe=False, - py_modules=['finufft.finufftc'], - ext_modules=[ - Extension(name='finufft.finufftc', - sources=[source_filename], - include_dirs=[inc_dir, '/usr/local/include'], - library_dirs=[lib_dir, lib_dir_cmake, '/usr/local/lib'], - libraries=[finufft_dlib], - runtime_library_dirs=runtime_library_dirs) - ] ) - -os.unlink(source_filename)