diff --git a/setup.py b/setup.py index c0e1d0b..8e18791 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,9 @@ #!/usr/bin/env python -# encoding: utf-8 - -# setup.py -# only if building in place: ``python setup.py build_ext --inplace`` import os -import re -import platform import shutil +import platform import setuptools -import subprocess +from setuptools.command.build_ext import build_ext ####### # This forces wheels to be platform specific @@ -25,93 +20,79 @@ class BinaryDistribution(Distribution): def has_ext_modules(foo): return True ####### - - -def run_meson_build(staging_dir): - prefix = os.path.join(os.getcwd(), staging_dir) - purelibdir = "." - - # check if meson extra args are specified - meson_args = "" - if "MESON_ARGS" in os.environ: - meson_args = os.environ["MESON_ARGS"] - # A weird add-on on mac github action runners needs to be removed - if meson_args.find("buildtype") >= 0: meson_args = "" - - if platform.system() == "Windows": - if not "FC" in os.environ: - os.environ["FC"] = "gfortran" - if not "CC" in os.environ: - os.environ["CC"] = "gcc" - - # configure - meson_path = shutil.which("meson") - if meson_path is None: - raise OSError("The meson command cannot be found on the system") - - meson_call = [meson_path, "setup", staging_dir, "--wipe", - f"--prefix={prefix}", f"-Dpython.purelibdir={purelibdir}", - f"-Dpython.platlibdir={purelibdir}", meson_args] - meson_call = [m for m in meson_call if m != ""] - print(meson_call) - p1 = subprocess.run(meson_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - os.makedirs(staging_dir, exist_ok=True) - setup_log = os.path.join(staging_dir, "setup.log") - with open(setup_log, "wb") as f: - f.write(p1.stdout) - if p1.returncode != 0: - with open(setup_log, "r") as f: - print(f.read()) - raise OSError(meson_call, f"The meson setup command failed! Check the log at {setup_log} for more information.") - - # build - meson_call = [meson_path, "compile", "-vC", staging_dir] - meson_call = [m for m in meson_call if m != ""] - print(meson_call) - p2 = subprocess.run(meson_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - compile_log = os.path.join(staging_dir, "compile.log") - with open(compile_log, "wb") as f: - f.write(p2.stdout) - if p2.returncode != 0: - with open(compile_log, "r") as f: - print(f.read()) - raise OSError(meson_call, f"The meson compile command failed! Check the log at {compile_log} for more information.") - +this_dir = os.path.abspath(os.path.dirname(__file__)) +staging_dir = os.path.join(this_dir, "meson_build") +build_dir = os.path.join(this_dir, "build") def copy_shared_libraries(): build_path = os.path.join(staging_dir, "pyframe3dd") for root, _dirs, files in os.walk(build_path): - for file in files: - # move pyframe3dd to just under staging_dir - if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll", ".mod")): - if ".so.p" in root or ".pyd.p" in root: # excludes intermediate object files - continue - file_path = os.path.join(root, file) - new_path = str(file_path) - match = re.search(staging_dir, new_path) - new_path = new_path[match.span()[1] + 1 :] + for f in files: + if f.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): + file_path = os.path.join(root, f) + new_path = str(file_path).replace(staging_dir + os.sep, "") print(f"Copying build file {file_path} -> {new_path}") - shutil.move(file_path, new_path) - + shutil.copy(file_path, new_path) +####### +class MesonExtension(setuptools.Extension): + + def __init__(self, name, sourcedir="", **kwa): + setuptools.Extension.__init__(self, name, sources=[], **kwa) + self.sourcedir = os.path.abspath(sourcedir) + +class MesonBuildExt(build_ext): + + def copy_extensions_to_source(self): + newext = [] + for ext in self.extensions: + if isinstance(ext, MesonExtension): continue + newext.append( ext ) + self.extensions = newext + super().copy_extensions_to_source() + + def build_extension(self, ext): + if not isinstance(ext, MesonExtension): + super().build_extension(ext) + + else: + + # Ensure that Meson is present and working + try: + self.spawn(["meson", "--version"]) + except OSError: + raise RuntimeError("Cannot find meson executable") + + # check if meson extra args are specified + meson_args = "" + if "MESON_ARGS" in os.environ: + meson_args = os.environ["MESON_ARGS"] + + if platform.system() == "Windows": + if "FC" not in os.environ: + os.environ["FC"] = "gfortran" + if "CC" not in os.environ: + os.environ["CC"] = "gcc" + + purelibdir = "." + configure_call = ["meson", "setup", staging_dir, "--wipe", + f"-Dpython.purelibdir={purelibdir}", f"--prefix={staging_dir}", + f"-Dpython.platlibdir={purelibdir}"] + meson_args.split() + configure_call = [m for m in configure_call if m.strip() != ""] + print(configure_call) + + build_call = ["meson", "compile", "-vC", staging_dir] + print(build_call) + + self.build_temp = build_dir + + self.spawn(configure_call) + self.spawn(build_call) + copy_shared_libraries() + + if __name__ == "__main__": - # This is where the meson build system will install to, it is then - # used as the sources for setuptools - staging_dir = "meson_build" - - # this keeps the meson build system from running more than once - if "dist" not in str(os.path.abspath(__file__)): - cwd = os.getcwd() - run_meson_build(staging_dir) - os.chdir(cwd) - copy_shared_libraries() - - init_file = os.path.join("pyframe3dd", "__init__.py") - #__version__ = re.findall( - # r"""__version__ = ["']+([0-9\.]*)["']+""", - # open(init_file).read(), - #)[0] - - setuptools.setup(cmdclass={'bdist_wheel': bdist_wheel}, distclass=BinaryDistribution) - -#os.environ['NPY_DISTUTILS_APPEND_FLAGS'] = '1' + setuptools.setup(cmdclass={"bdist_wheel": bdist_wheel, "build_ext": MesonBuildExt}, + distclass=BinaryDistribution, + ext_modules=[ MesonExtension("pyframe3dd", this_dir) ], + )