Skip to content

Commit

Permalink
finufft: remove setup.py reliance on ext_modules
Browse files Browse the repository at this point in the history
  • Loading branch information
blackwer committed Dec 12, 2023
1 parent c4fd0ec commit 4780cc9
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 79 deletions.
48 changes: 13 additions & 35 deletions python/finufft/finufft/_finufft.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')

Expand Down
69 changes: 25 additions & 44 deletions python/finufft/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -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 <finufft.h>
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 ###########
Expand All @@ -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',
Expand All @@ -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)

0 comments on commit 4780cc9

Please sign in to comment.