From 065f61915e435c6a731e35ce2936b73739575a2e Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 06:08:59 -0700 Subject: [PATCH 1/8] try this out --- meson.build | 7 +-- pyframe3dd/meson.build | 14 ++++-- pyproject.toml | 36 ++++++---------- setup.py | 98 ------------------------------------------ 4 files changed, 25 insertions(+), 130 deletions(-) delete mode 100644 setup.py diff --git a/meson.build b/meson.build index 322e666..8566733 100644 --- a/meson.build +++ b/meson.build @@ -24,12 +24,7 @@ endif # https://mesonbuild.com/Python-module.html # Here we differentiate from the python used by meson, py3_command, and that python target, py3_target. This is useful # when cross compiling like on conda-forge -py_mod = import('python') -if get_option('python_target') != '' - py3 = py_mod.find_installation(get_option('python_target')) -else - py3 = py_mod.find_installation('python') -endif +py3 = import('python').find_installation(pure: false) py3_dep = py3.dependency() message(py3.path()) diff --git a/pyframe3dd/meson.build b/pyframe3dd/meson.build index 2d7e310..bcb71be 100644 --- a/pyframe3dd/meson.build +++ b/pyframe3dd/meson.build @@ -1,3 +1,12 @@ +python_sources = [ + '__init__.py', + 'pyframe3dd.py', +] + +py3.install_sources( + python_sources, + subdir: 'pyframe3dd', +) sources = ['src/py_main.c', 'src/py_structs.h', @@ -19,9 +28,8 @@ sources = ['src/py_main.c', # We need to build a shared library NOT A PYTHON EXTENSION # The ctypes wrapper handles the extension part. # If the interface was done purely in C, then need the python extension. -shared_library('_pyframe3dd', sources, +temp = shared_library('_pyframe3dd', sources, name_prefix: '', include_directories: 'src', - dependencies : py3_dep, + install_dir: py3.get_install_dir() / 'pyframe3dd', install : true) - diff --git a/pyproject.toml b/pyproject.toml index b5ffed2..0106273 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools", "meson>=1.1", "ninja", "wheel"] -build-backend = "setuptools.build_meta" +requires = ["numpy", "ninja", "meson>=1.1", "meson-python", "wheel"] +build-backend = "mesonpy" [project] name = "WISDEM-pyFrame3DD" -version = "1.0.1" +version = "1.1.1" description = "Python bindings to Frame3DD, a code for static and dynamic structural analysis of 2D and 3D frames and trusses, with permission from Prof Henri Gavin" readme = "README.md" requires-python = ">=3.9" @@ -36,8 +36,14 @@ classifiers = [ # Optional "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Fortran", + "Programming Language :: C", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Operating System :: POSIX", + "Operating System :: Unix", + "Operating System :: MacOS", ] dependencies = [ @@ -70,28 +76,12 @@ test = ["pytest"] "Homepage" = "https://github.com/WISDEM/pyFrame3DD" "Project" = "https://frame3dd.sourceforge.net" -# This is configuration specific to the `setuptools` build backend. -# If you are using a different build backend, you will need to change this. -[tool.setuptools] -zip-safe = false -include-package-data = true - -#[tool.setuptools.packages] -#find = {} - -[tool.setuptools.packages.find] -#where = ["wisdem"] -exclude = ["test", "examples"] -namespaces = true - -[tool.setuptools.package-data] -# If there are data files included in your packages that need to be -# installed, specify them here. -"*" = ["*.so", "*.lib", "*.pyd", "*.pdb", "*.dylib", "*.dll"] +[tool.meson-python.args] +install = ['--tags=runtime,python-runtime,bin'] [tool.black] line-length = 120 -target-version = ['py39'] +target-version = ['py311'] include = '\.pyi?$' exclude = ''' /( diff --git a/setup.py b/setup.py deleted file mode 100644 index 8e18791..0000000 --- a/setup.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -import os -import shutil -import platform -import setuptools -from setuptools.command.build_ext import build_ext - -####### -# This forces wheels to be platform specific -from setuptools.dist import Distribution -from wheel.bdist_wheel import bdist_wheel as _bdist_wheel - -class bdist_wheel(_bdist_wheel): - def finalize_options(self): - _bdist_wheel.finalize_options(self) - self.root_is_pure = False - -class BinaryDistribution(Distribution): - """Distribution which always forces a binary package with platform name""" - def has_ext_modules(foo): - return True -####### -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 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.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__": - setuptools.setup(cmdclass={"bdist_wheel": bdist_wheel, "build_ext": MesonBuildExt}, - distclass=BinaryDistribution, - ext_modules=[ MesonExtension("pyframe3dd", this_dir) ], - ) From 47d4639ab2e20e43eb012dbc5120aa7af3717abf Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 10:59:26 -0700 Subject: [PATCH 2/8] more robust name finding --- meson.build | 2 -- pyframe3dd/__init__.py | 3 +- pyframe3dd/pyframe3dd.py | 64 +++++++++++++++++++++++++--------------- pyproject.toml | 1 + 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/meson.build b/meson.build index 8566733..bee62cc 100644 --- a/meson.build +++ b/meson.build @@ -31,5 +31,3 @@ message(py3.path()) message(py3.get_install_dir()) subdir('pyframe3dd') - - diff --git a/pyframe3dd/__init__.py b/pyframe3dd/__init__.py index 3439576..8f76d42 100644 --- a/pyframe3dd/__init__.py +++ b/pyframe3dd/__init__.py @@ -1,4 +1,5 @@ from .pyframe3dd import Frame, StaticLoadCase, NodeData, ReactionData, ElementData, Options -#import frame3dd + + diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index 53298f4..36d45af 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -12,25 +12,43 @@ import math from ctypes import POINTER, c_int, c_double, Structure, pointer from collections import namedtuple +import platform import os -#from distutils.sysconfig import get_config_var - -from sys import platform - -libext = None #get_config_var('EXT_SUFFIX') -if libext is None or libext == '': - if platform == "linux" or platform == "linux2": - libext = '.so' - elif platform == "darwin": - libext = '.dylib' - #libext = '.so' - elif platform == "win32": - libext = '.dll' - #libext = '.pyd' - elif platform == "cygwin": - libext = '.dll' - -libname = '_pyframe3dd' + libext +import sysconfig +import sys + +if platform.system() == "Windows": + lib_ext = ".dll" +elif platform.system() == "Darwin": + lib_ext = ".dylib" +else: + lib_ext = ".so" + +libname = '_pyframe3dd' + lib_ext + +pyframe3dd_dir = os.path.dirname( os.path.abspath(__file__) ) + +lib_opt = [os.path.join(pyframe3dd_dir, libname), # pip installs (regular and editable) + os.path.join(os.path.dirname( os.path.dirname( pyframe3dd_dir )), "local", "lib", libname), # WEIS library + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), libname), # conda installs + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "lib", libname), # conda installs + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "Library", "lib", libname), # conda installs + os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", "lib", libname), # system-wide pip installs + os.path.join( sysconfig.get_config_var("userbase"), "lib", "python", "site-packages", "pyframe3dd", libname), # system wide local + ] + +for p in sys.meta_path: + if "_build_path" in p.__dict__: + lib_opt += [os.path.join(p._build_path, "pyframe3dd", libname)] + +lib_path = None +for p in lib_opt: + if os.path.exists(p): + lib_path = str(p) + break + +if lib_path is None: + raise Exception(f"Cannot find {libname} in {lib_opt}") c_int_p = POINTER(c_int) c_double_p = POINTER(c_double) @@ -446,12 +464,10 @@ def __init__(self, nodes, reactions, elements, options): # load c module - mydir = os.path.dirname(os.path.realpath(__file__)) # get path to this file - try: - self._pyframe3dd = np.ctypeslib.load_library(libname, mydir) - except: - mydir = os.path.abspath(os.path.dirname(mydir)) - self._pyframe3dd = np.ctypeslib.load_library(libname, mydir) + #mydir = impresources.files(pyframe3dd) + #with impresources.as_file(mydir) as f: + # print(f) + self._pyframe3dd = np.ctypeslib.load_library(libname, os.path.dirname(lib_path)) self._pyframe3dd.run.argtypes = [POINTER(C_Nodes), POINTER(C_Reactions), POINTER(C_Elements), POINTER(C_OtherElementData), c_int, POINTER(C_LoadCase), diff --git a/pyproject.toml b/pyproject.toml index 0106273..229e5d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,7 @@ test = ["pytest"] "Project" = "https://frame3dd.sourceforge.net" [tool.meson-python.args] +setup = ['--python.install-env=auto'] install = ['--tags=runtime,python-runtime,bin'] [tool.black] From aaa4c15231ab09e6e11cf869e6789e5f7837e963 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 10:59:41 -0700 Subject: [PATCH 3/8] build options --- .github/workflows/CI_pyFrame3DD.yml | 5 ++--- environment.yml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 26966f2..bd6aa8f 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -37,11 +37,10 @@ jobs: - name: Pip Install pyFrame3DD env: - MESON_ARGS: -Dpython_target=${{ steps.cp.outputs.python-path }} CC: '${{ steps.install_cc.outputs.cc }}' CXX: '${{ steps.install_cc.outputs.cxx }}' run: | - '${{ steps.cp.outputs.python-path }}' -m pip install -vv .[test] + '${{ steps.cp.outputs.python-path }}' -m pip install -v .[test] #- name: Setup tmate session # uses: mxschmitt/action-tmate@v3 @@ -105,7 +104,7 @@ jobs: env: MESON_ARGS: "" run: | - python -m pip install . + python -m pip install . -v - name: Test run run: | diff --git a/environment.yml b/environment.yml index 7274e6b..be8f8df 100644 --- a/environment.yml +++ b/environment.yml @@ -4,9 +4,9 @@ channels: dependencies: - meson + - meson-python - ninja - numpy - pip - python - pytest - - setuptools From 2c46c2c5eb1c862c7fd2ca9ed5a74e910b52066a Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 11:32:00 -0700 Subject: [PATCH 4/8] try this --- pyframe3dd/pyframe3dd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index 36d45af..96265ec 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -33,7 +33,7 @@ os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "lib", libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "Library", "lib", libname), # conda installs - os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", "lib", libname), # system-wide pip installs + os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", libname), # system-wide pip installs os.path.join( sysconfig.get_config_var("userbase"), "lib", "python", "site-packages", "pyframe3dd", libname), # system wide local ] From 3fa93cd52c34760e4ce6fc535d1a1b4482e52267 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 14:25:37 -0700 Subject: [PATCH 5/8] test editable too --- .github/workflows/CI_pyFrame3DD.yml | 25 +++++++++++++++++++++++-- README.md | 15 ++++++++++++++- pyframe3dd/pyframe3dd.py | 1 + 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index bd6aa8f..90b0977 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -51,6 +51,18 @@ jobs: - name: Test run run: | '${{ steps.cp.outputs.python-path }}' -m pytest test + + - name: Editable Pip Install pyFrame3DD + env: + CC: '${{ steps.install_cc.outputs.cc }}' + CXX: '${{ steps.install_cc.outputs.cxx }}' + run: | + '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd + '${{ steps.cp.outputs.python-path }}' -m pip install -e --no-build-isolation . + + - name: Editable Test run + run: | + '${{ steps.cp.outputs.python-path }}' -m pytest test build_conda: @@ -93,13 +105,11 @@ jobs: run: | conda install -y compilers - # Install - name: Debug run: | conda list printenv - # Install - name: Conda Install pyFrame3DD env: MESON_ARGS: "" @@ -109,4 +119,15 @@ jobs: - name: Test run run: | python -m pytest test + + - name: Editable Conda Install pyFrame3DD + env: + MESON_ARGS: "" + run: | + python -m pip uninstall pyframe3dd + python -m pip install -e --no-build-isolation . -v + + - name: Editable Test run + run: | + python -m pytest test diff --git a/README.md b/README.md index a1bd6fe..93c803e 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,25 @@ There is example code that shows usage contained in ``examples/exB.py``. This f pyFrame3DD requires a C compiler -## Install +## Install (as a library) For detailed installation instructions of WISDEM modules see or to install pyFrame3DD by itself do: $ pip install WISDEM-pyFrame3DD + +## Install (from source) + +If you would like to build the project locally from source for easier access to the underlying methods and tests, do: + + $ git clone https://github.com/WISDEM/pyFrame3DD.git + $ cd pyFrame3DD + $ pip install . + +If developer/editable mode, do the same `git clone` step, but on install do: + + $ pip install -e --no-build-isolation . + ## Unit Tests $ pytest test diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index 96265ec..cfff4d8 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -31,6 +31,7 @@ lib_opt = [os.path.join(pyframe3dd_dir, libname), # pip installs (regular and editable) os.path.join(os.path.dirname( os.path.dirname( pyframe3dd_dir )), "local", "lib", libname), # WEIS library os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), libname), # conda installs + os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "pyframe3dd", libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "lib", libname), # conda installs os.path.join(os.path.dirname( sysconfig.get_path('stdlib') ), "Library", "lib", libname), # conda installs os.path.join( sysconfig.get_path('platlib'), "pyframe3dd", libname), # system-wide pip installs From a3558ed23567a0bbb40c276e62857c8156c72f38 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 14:50:12 -0700 Subject: [PATCH 6/8] change order --- .github/workflows/CI_pyFrame3DD.yml | 4 ++-- README.md | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 90b0977..6ffff5d 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -58,7 +58,7 @@ jobs: CXX: '${{ steps.install_cc.outputs.cxx }}' run: | '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd - '${{ steps.cp.outputs.python-path }}' -m pip install -e --no-build-isolation . + '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e . - name: Editable Test run run: | @@ -125,7 +125,7 @@ jobs: MESON_ARGS: "" run: | python -m pip uninstall pyframe3dd - python -m pip install -e --no-build-isolation . -v + python -m pip install --no-build-isolation -e . -v - name: Editable Test run run: | diff --git a/README.md b/README.md index 93c803e..dd0a604 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,10 @@ If you would like to build the project locally from source for easier access to If developer/editable mode, do the same `git clone` step, but on install do: - $ pip install -e --no-build-isolation . + $ pip install --no-build-isolation -e . + +The `--no-build-isolation` option is important per [Meson guidelines](https://meson-python.readthedocs.io/en/latest/how-to-guides/editable-installs.html). + ## Unit Tests From 6c43ad55bcc911fe060006102808c6c689a17b1f Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 15:14:33 -0700 Subject: [PATCH 7/8] swapping order of installs --- .github/workflows/CI_pyFrame3DD.yml | 24 ++++++++++++------------ pyframe3dd/pyframe3dd.py | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 6ffff5d..1396a64 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -35,11 +35,23 @@ jobs: python-version: ${{ matrix.python-version }} update-environment: true + - name: Editable Pip Install pyFrame3DD + env: + CC: '${{ steps.install_cc.outputs.cc }}' + CXX: '${{ steps.install_cc.outputs.cxx }}' + run: | + '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e .[test] + + - name: Editable Test run + run: | + '${{ steps.cp.outputs.python-path }}' -m pytest test + - name: Pip Install pyFrame3DD env: CC: '${{ steps.install_cc.outputs.cc }}' CXX: '${{ steps.install_cc.outputs.cxx }}' run: | + '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd '${{ steps.cp.outputs.python-path }}' -m pip install -v .[test] #- name: Setup tmate session @@ -51,18 +63,6 @@ jobs: - name: Test run run: | '${{ steps.cp.outputs.python-path }}' -m pytest test - - - name: Editable Pip Install pyFrame3DD - env: - CC: '${{ steps.install_cc.outputs.cc }}' - CXX: '${{ steps.install_cc.outputs.cxx }}' - run: | - '${{ steps.cp.outputs.python-path }}' -m pip uninstall pyframe3dd - '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e . - - - name: Editable Test run - run: | - '${{ steps.cp.outputs.python-path }}' -m pytest test build_conda: diff --git a/pyframe3dd/pyframe3dd.py b/pyframe3dd/pyframe3dd.py index cfff4d8..4605645 100644 --- a/pyframe3dd/pyframe3dd.py +++ b/pyframe3dd/pyframe3dd.py @@ -38,6 +38,7 @@ os.path.join( sysconfig.get_config_var("userbase"), "lib", "python", "site-packages", "pyframe3dd", libname), # system wide local ] +# For Meson's editable installs for p in sys.meta_path: if "_build_path" in p.__dict__: lib_opt += [os.path.join(p._build_path, "pyframe3dd", libname)] From 9b42df3320ca9f0e350a76c04ab6d194e5e67d93 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Wed, 17 Jan 2024 15:16:47 -0700 Subject: [PATCH 8/8] try again --- .github/workflows/CI_pyFrame3DD.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI_pyFrame3DD.yml b/.github/workflows/CI_pyFrame3DD.yml index 1396a64..9978d6f 100644 --- a/.github/workflows/CI_pyFrame3DD.yml +++ b/.github/workflows/CI_pyFrame3DD.yml @@ -40,6 +40,8 @@ jobs: CC: '${{ steps.install_cc.outputs.cc }}' CXX: '${{ steps.install_cc.outputs.cxx }}' run: | + '${{ steps.cp.outputs.python-path }}' -m pip install --upgrade pip + '${{ steps.cp.outputs.python-path }}' -m pip install meson-python meson numpy ninja wheel '${{ steps.cp.outputs.python-path }}' -m pip install --no-build-isolation -e .[test] - name: Editable Test run