From b323da1f8f8e02a45b91374a86f8f5ace214ff91 Mon Sep 17 00:00:00 2001 From: Garrett Barter Date: Sat, 16 Sep 2023 21:17:57 -0600 Subject: [PATCH] catching up documentation and improving installation robustness --- README.md | 7 ++- docs/installation.rst | 15 ++--- pyproject.toml | 130 +++++++++++++++++++++++++++++++++++++++++- setup.py | 90 ++++++++++++----------------- 4 files changed, 176 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index c7eea8e..f1e72a9 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,14 @@ See local documentation in the `docs`-directory or access the online version at ## Prerequisites -pyMAP requires a Fortran compiler +CCBlade execution requires: `numpy`, `scipy`, `openmdao` +CCBlade installation requires: `meson`, `ninja`, `gfortran` ## Installation -For detailed installation instructions of WISDEM modules see or to install CCBlade by itself do: +CCBlade is available as a [WISDEM](https://github.com/WISDEM/WISDEM) module and WISDEM is both pip-installable and conda-installable. For building CCBlade from source as a standalone library, first make sure that you have the necessary prerequisites installed. After cloning the repository, do: - $ python setup.py install + $ pip install CCBlade ## Run Unit Tests diff --git a/docs/installation.rst b/docs/installation.rst index cca3a93..376aac3 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,18 +1,15 @@ Installation ------------ -.. admonition:: Prerequisites - :class: warning +CCBlade is available as a `WISDEM `_ module and WISDEM is both pip-installable and conda-installable. For building CCBlade from source as a standalone library, first make sure that you have the necessary prerequisites installed. After cloning the repository, do: - Fortran compiler, NumPy, SciPy, zope.interface - -Download either CCBlade.py-|release|.tar.gz or CCBlade.py-|release|.zip and uncompress/unpack it. +.. code-block:: bash -Install CCBlade with the following command. + $ pip install CCBlade -.. code-block:: bash +The pre-requisites for running CCBlade include: `numpy`, `scipy`, `openmdao` - $ python setup.py install +The pre-requisites for building CCBlade from source include: `meson`, `ninja`, `gfortran` To check if installation was successful run the unit tests for the NREL 5-MW model @@ -37,5 +34,5 @@ A current copy of the documentation for this code is also available online at ht .. note:: - The CCBlade installation also installs the module `AirfoilPrep.py `_. Although it is not strictly necessary to use AirfoilPrep.py with CCBlade, its inclusion is convenient when working with AeroDyn input files or doing any aerodynamic preprocessing of airfoil data. If you wish to do more with AirfoilPrep.py please see its documentation `here `_. + The CCBlade installation also installs the `AirfoilPrep` module. Although it is not strictly necessary to use AirfoilPrep.py with CCBlade, its inclusion is convenient when working with AeroDyn input files or doing any aerodynamic preprocessing of airfoil data. diff --git a/pyproject.toml b/pyproject.toml index df7c3e3..876729c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,131 @@ [build-system] -requires = ["setuptools", "meson>=0.60.0", "numpy","ninja"] +requires = ["setuptools", "meson>=1.1", "numpy","ninja","wheel"] build-backend = "setuptools.build_meta" + +[project] +name = "CCBlade" +version = "1.3.1" +description = "Blade element momentum aerodynamics for wind turbines" +readme = "README.md" +requires-python = ">=3.9" +license = {text = "Apache-2.0"} +keywords = ["wind", "turbine", "mdao", "design", "optimization"] +authors = [ + {name = "NREL WISDEM Team", email = "systems.engineering@nrel.gov" } +] +maintainers = [ + {name = "NREL WISDEM Team", email = "systems.engineering@nrel.gov" } +] +classifiers = [ # Optional + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + "Development Status :: 4 - Beta", + + # Indicate who your project is intended for + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering", + + "License :: OSI Approved :: Apache Software License", + + # Specify the Python versions you support here. In particular, ensure + # that you indicate you support Python 3. These classifiers are *not* + # checked by "pip install". See instead "python_requires" below. + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Fortran", +] + +dependencies = [ + "numpy", + "scipy", + "openmdao", +] + +# List additional groups of dependencies here (e.g. development +# dependencies). Users will be able to install these using the "extras" +# syntax, for example: +# +# $ pip install sampleproject[dev] +# +# Similar to `dependencies` above, these must be valid existing +# projects. +[project.optional-dependencies] # Optional +dev = ["meson", "ninja"] +test = ["pytest"] + +# List URLs that are relevant to your project +# +# This field corresponds to the "Project-URL" and "Home-Page" metadata fields: +# https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use +# https://packaging.python.org/specifications/core-metadata/#home-page-optional +# +# Examples listed include a pattern for specifying where the package tracks +# issues, where the source is hosted, where to say thanks to the package +# maintainers, and where to support the project financially. The key is +# what's used to render the link text on PyPI. +[project.urls] # Optional +"Homepage" = "https://github.com/WISDEM/CCBlade" + +# 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 = ["docs", "test"] +namespaces = true + +[tool.setuptools.package-data] +# If there are data files included in your packages that need to be +# installed, specify them here. +"*" = ["*.yaml", "*.xlsx", "*.txt", "*.so", "*.lib", "*.pyd", "*.pdb", "*.dylib", "*.dll", "*.exe"] + +[tool.black] +line-length = 120 +target-version = ['py39'] +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' + +[tool.isort] +# https://github.com/PyCQA/isort +multi_line_output = "3" +include_trailing_comma = true +force_grid_wrap = false +use_parentheses = true +line_length = "120" +sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] +known_first_party = ["wisdem"] +length_sort = "1" +profile = "black" +skip_glob = ['__init__.py'] +atomic = true +#lines_after_imports = 2 +#lines_between_types = 1 +#src_paths=isort,test + +[tool.cibuildwheel] +skip = ["cp36-*", "cp37-*", "cp38-*", "*-win32", "*arm64", "pp*"] +build-frontend = "build" + diff --git a/setup.py b/setup.py index e598c99..a49021c 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,22 @@ import setuptools import subprocess +####### +# 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 +####### + def run_meson_build(staging_dir): prefix = os.path.join(os.getcwd(), staging_dir) @@ -19,6 +35,8 @@ def run_meson_build(staging_dir): 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: @@ -28,14 +46,15 @@ def run_meson_build(staging_dir): # configure meson_path = shutil.which("meson") - meson_call = ( - f"{meson_path} setup {staging_dir} --prefix={prefix} " - + f"-Dpython.purelibdir={purelibdir} -Dpython.platlibdir={purelibdir} {meson_args}" - ) - sysargs = meson_call.split(" ") - sysargs = [arg for arg in sysargs if arg != ""] - print(sysargs) - p1 = subprocess.run(sysargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + 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: @@ -43,23 +62,20 @@ def run_meson_build(staging_dir): if p1.returncode != 0: with open(setup_log, "r") as f: print(f.read()) - raise OSError(sysargs, f"The meson setup command failed! Check the log at {setup_log} for more information.") + raise OSError(meson_call, f"The meson setup command failed! Check the log at {setup_log} for more information.") # build - meson_call = f"{meson_path} compile -vC {staging_dir}" - sysargs = meson_call.split(" ") - sysargs = [arg for arg in sysargs if arg != ""] - print(sysargs) - p2 = subprocess.run(sysargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + 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( - sysargs, f"The meson compile command failed! Check the log at {compile_log} for more information." - ) + raise OSError(meson_call, f"The meson compile command failed! Check the log at {compile_log} for more information.") def copy_shared_libraries(): @@ -67,7 +83,7 @@ def copy_shared_libraries(): for root, _dirs, files in os.walk(build_path): for file in files: # move ccblade to just under staging_dir - if file.endswith((".so", ".lib", ".pyd", ".pdb", ".dylib", ".dll")): + 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) @@ -75,7 +91,7 @@ def copy_shared_libraries(): match = re.search(staging_dir, new_path) new_path = new_path[match.span()[1] + 1 :] print(f"Copying build file {file_path} -> {new_path}") - shutil.copy(file_path, new_path) + shutil.move(file_path, new_path) if __name__ == "__main__": @@ -90,43 +106,11 @@ def copy_shared_libraries(): os.chdir(cwd) copy_shared_libraries() - #docs_require = "" - #req_txt = os.path.join("doc", "requirements.txt") - #if os.path.isfile(req_txt): - # with open(req_txt) as f: - # docs_require = f.read().splitlines() - init_file = os.path.join("ccblade", "__init__.py") #__version__ = re.findall( # r"""__version__ = ["']+([0-9\.]*)["']+""", # open(init_file).read(), #)[0] - setuptools.setup( - name='CCBlade', - version='1.3.0', - description='Blade element momentum aerodynamics for wind turbines', - long_description="CCBlade is a Python package that provides a blade element momentum theory solution for wind turbine blade design", - author='NREL WISDEM Team', - author_email='systems.engineering@nrel.gov', - install_requires=[ - "numpy", - "scipy", - "openmdao", - ], - extras_require={ - "testing": ["pytest"], - }, - python_requires=">=3.8", - packages=["ccblade"], - package_data={"": ["*.yaml", "*.so", "*.lib", "*.pyd", "*.pdb", "*.dylib", "*.dll"]}, - license='Apache License, Version 2.0', - zip_safe=False, - ) - -#os.environ['NPY_DISTUTILS_APPEND_FLAGS'] = '1' - -# package_data={'ccblade': []}, -# ext_modules=[Extension('ccblade._bem', -# sources=[os.path.join('ccblade','src','bem.f90')], -# extra_compile_args=['-O2','-fPIC','-std=c11'])], + setuptools.setup(cmdclass={'bdist_wheel': bdist_wheel}, distclass=BinaryDistribution) +