diff --git a/.github/workflows/test-langs.yml b/.github/workflows/test-langs.yml index 2b32dd0e..2acae11e 100644 --- a/.github/workflows/test-langs.yml +++ b/.github/workflows/test-langs.yml @@ -29,11 +29,10 @@ jobs: with: submodules: true - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true python-version: ${{ matrix.python-version }} - mamba-version: "*" channels: conda-forge channel-priority: true @@ -41,7 +40,8 @@ jobs: run: python -m pip install nox tomli - name: Install compilers - run: mamba install c-compiler cxx-compiler fortran-compiler + if: matrix.language != 'python' + run: conda install ${{ matrix.language }}-compiler cmake make - name: Run the language tests run: nox -s "test-langs-${{ matrix.python-version }}(lang='${{ matrix.language }}')" --python ${{ matrix.python-version }} --verbose diff --git a/babelizer/data/hooks/post_gen_project.py b/babelizer/data/hooks/post_gen_project.py index e6218f5a..27f2fbe6 100644 --- a/babelizer/data/hooks/post_gen_project.py +++ b/babelizer/data/hooks/post_gen_project.py @@ -114,6 +114,10 @@ def write_api_yaml(folderpath, **kwds): if "Not open source" == "{{ cookiecutter.open_source_license }}": remove_file("LICENSE") + {%- if cookiecutter.language == 'python' %} + remove_file("meson.build") + {%- endif %} + datadir = Path("meta") package_datadir = Path("{{ cookiecutter.package_name }}") / "data" if not package_datadir.exists(): diff --git a/babelizer/data/{{cookiecutter.package_name}}/.github/workflows/test.yml b/babelizer/data/{{cookiecutter.package_name}}/.github/workflows/test.yml index f69190dd..35416232 100644 --- a/babelizer/data/{{cookiecutter.package_name}}/.github/workflows/test.yml +++ b/babelizer/data/{{cookiecutter.package_name}}/.github/workflows/test.yml @@ -23,9 +23,9 @@ jobs: python-version: [{{ cookiecutter.ci.python_version | join(", ") }}] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true python-version: ${{ '{{' }} matrix.python-version {{ '}}' }} @@ -39,16 +39,15 @@ jobs: - name: Install requirements run: | - conda install mamba - mamba install --file=requirements-build.txt --file=requirements-library.txt - mamba list + conda install --file=requirements-build.txt --file=requirements-library.txt + conda list - name: Build and install package run: | pip install -e . - name: Install testing dependencies - run: mamba install --file=requirements-testing.txt + run: conda install --file=requirements-testing.txt - name: Test run: | diff --git a/babelizer/data/{{cookiecutter.package_name}}/.gitignore b/babelizer/data/{{cookiecutter.package_name}}/.gitignore index 3b80cb7c..35c0374f 100644 --- a/babelizer/data/{{cookiecutter.package_name}}/.gitignore +++ b/babelizer/data/{{cookiecutter.package_name}}/.gitignore @@ -1,114 +1,25 @@ -# Byte-compiled / optimized / DLL files +.DS_Store __pycache__/ *.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ +.ipynb_checkpoints build/ -develop-eggs/ dist/ -downloads/ -eggs/ -.eggs/ -# lib/ -lib64/ -parts/ sdist/ -var/ -wheels/ *.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports htmlcov/ -.tox/ .coverage -.coverage.* -.cache -nosetests.xml coverage.xml -*.cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# dotenv -.env - -# virtualenv +docs/_generated/ .venv -venv/ -ENV/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# nox virtual envs .nox/ +*.so +*.o +*.mod +*.smod -{% if cookiecutter.language == 'fortran' -%} +{%- if cookiecutter.language != 'python' -%} {%- for babelized_class in cookiecutter.components %} -# Fortran files generated by the babelizer -{{cookiecutter.package_name}}/lib/bmi_interoperability.mod -{{cookiecutter.package_name}}/lib/bmi_interoperability.smod -{{cookiecutter.package_name}}/lib/bmi_interoperability.o {{cookiecutter.package_name}}/lib/{{ babelized_class|lower }}.c {%- endfor %} {%- endif %} diff --git a/babelizer/data/{{cookiecutter.package_name}}/Makefile b/babelizer/data/{{cookiecutter.package_name}}/Makefile deleted file mode 100644 index 03d43225..00000000 --- a/babelizer/data/{{cookiecutter.package_name}}/Makefile +++ /dev/null @@ -1,94 +0,0 @@ -.PHONY: clean clean-test clean-pyc clean-build docs help -.DEFAULT_GOAL := help - -define BROWSER_PYSCRIPT -import os, webbrowser, sys - -try: - from urllib import pathname2url -except: - from urllib.request import pathname2url - -webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) -endef -export BROWSER_PYSCRIPT - -define PRINT_HELP_PYSCRIPT -import re, sys - -for line in sys.stdin: - match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) - if match: - target, help = match.groups() - print("%-20s %s" % (target, help)) -endef -export PRINT_HELP_PYSCRIPT - -BROWSER := python -c "$$BROWSER_PYSCRIPT" - -help: - @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) - -clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts - -clean-build: ## remove build artifacts - rm -fr build/ - rm -fr dist/ - rm -fr .eggs/ - find . -name '*.egg-info' -exec rm -fr {} + - find . -name '*.egg' -exec rm -f {} + - -clean-pyc: ## remove Python file artifacts - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - find . -name '__pycache__' -exec rm -fr {} + - -clean-test: ## remove test and coverage artifacts - rm -fr .tox/ - rm -f .coverage - rm -fr htmlcov/ - rm -fr .pytest_cache - -lint: ## check style with flake8 - flake8 {{ cookiecutter.package_name }} - -pretty: - find {{ cookiecutter.package_name }} -name '*.py' | xargs isort - black setup.py {{ cookiecutter.package_name }} - -test: ## run tests quickly with the default Python -{%- for babelized_class, component in cookiecutter.components|dictsort %} - bmi-test {{ cookiecutter.package_name }}.bmi:{{ babelized_class }} -vvv -{%- endfor %} - -test-all: ## run tests on every Python version with tox - tox - -coverage: ## check code coverage quickly with the default Python - coverage run --source {{ cookiecutter.package_name }} -m pytest - coverage report -m - coverage html - $(BROWSER) htmlcov/index.html - -docs: ## generate Sphinx HTML documentation, including API docs - rm -f docs/{{ cookiecutter.package_name }}.rst - rm -f docs/modules.rst - sphinx-apidoc -o docs/ {{ cookiecutter.package_name }} - $(MAKE) -C docs clean - $(MAKE) -C docs html - $(BROWSER) docs/_build/html/index.html - -servedocs: docs ## compile the docs watching for changes - watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . - -release: dist ## package and upload a release - twine upload dist/* - -dist: clean ## builds source and wheel package - python setup.py sdist - python setup.py bdist_wheel - ls -l dist - -install: clean ## install the package to the active Python's site-packages - pip install -e . diff --git a/babelizer/data/{{cookiecutter.package_name}}/README.rst b/babelizer/data/{{cookiecutter.package_name}}/README.rst index 39a573a8..05ccf4f9 100644 --- a/babelizer/data/{{cookiecutter.package_name}}/README.rst +++ b/babelizer/data/{{cookiecutter.package_name}}/README.rst @@ -65,20 +65,44 @@ Quickstart .. start-quickstart -To get started you will need to install the *{{ cookiecutter.package_name }}* package, which is currently distributed -on *conda-forge*. The easiest way to install *{{ cookiecutter.package_name }}* into your current environment using either *mamba* or *conda*. +To get started you will need to install the *{{ cookiecutter.package_name }}* package. +Here are two ways to do so. -.. tab:: mamba +Install from conda-forge +------------------------ - .. code:: bash +If the *{{ cookiecutter.package_name }}* package is distributed on *conda-forge*, install it into your current environment with *conda*. - mamba install {{ cookiecutter.package_name }} +.. code:: bash -.. tab:: conda + conda install -c conda-forge {{ cookiecutter.package_name }} - .. code:: bash +Install from source +------------------- + +You can build and install the *{{ cookiecutter.package_name }}* package from source using *conda* and *pip*. + +First, from the source directory, install package dependencies into your current environment with *conda*. + +.. code:: bash + + conda install -c conda-forge --file requirements.txt --file requirements-build.txt --file requirements-library.txt + +Then install the package itself with *pip*. +{%- if cookiecutter.language == 'python' %} - conda install {{ cookiecutter.package_name }} +.. code:: bash + + pip install -e . + +{%- else %} + +.. code:: bash + + pip install --no-build-isolation --editable . + +Note that for an editable install, the ``--no-build-isolation`` flag must be set. +{%- endif %} .. end-quickstart @@ -87,8 +111,8 @@ Usage .. start-usage -There are two ways to use the data components provided by this package: directly through it's Basic -Model Interface, or as a PyMT plugin. +There are two ways to use the components provided by this package: directly through its Basic +Model Interface (BMI), or as a PyMT plugin. A BMI is provided by each component in this package: {%- for babelized_class, component in cookiecutter.components|dictsort -%} diff --git a/babelizer/data/{{cookiecutter.package_name}}/meson.build b/babelizer/data/{{cookiecutter.package_name}}/meson.build new file mode 100644 index 00000000..6edc29d5 --- /dev/null +++ b/babelizer/data/{{cookiecutter.package_name}}/meson.build @@ -0,0 +1,99 @@ +project( + '{{ cookiecutter.package_name }}', +{%- if cookiecutter.language == 'c' %} + 'c', +{%- elif cookiecutter.language == 'c++' %} + 'cpp', +{%- elif cookiecutter.language == 'fortran' %} + 'fortran', +{%- endif %} + 'cython', + version: '{{ cookiecutter.package_version }}', +) + +py = import('python').find_installation(pure: false) + +{%- if cookiecutter.language == 'c' %} +compiler = meson.get_compiler('c') +{%- elif cookiecutter.language == 'c++' %} +compiler = meson.get_compiler('cpp') +{%- elif cookiecutter.language == 'fortran' %} +compiler = meson.get_compiler('fortran') +{%- endif %} + +# python_inc = py.get_path('data') / 'include' +numpy_inc = run_command( + py, + [ + '-c', + 'import numpy; print(numpy.get_include())' + ], + check: true +).stdout().strip() +incs = include_directories( + [ + '{{ cookiecutter.package_name }}/lib', + # python_inc, + numpy_inc, + ] +) + +{% set dependency_list = cookiecutter.package_requirements.split(',') -%} +deps = [ +{%- for dependency in dependency_list if dependency != '' %} + compiler.find_library('{{ dependency }}'), +{%- endfor %} +] + +# Files get copied to /site-packages/ +install_pkg_srcs = [ + '{{ cookiecutter.package_name }}/__init__.py', + '{{ cookiecutter.package_name }}/_bmi.py', + '{{ cookiecutter.package_name }}/_version.py', +] +py.install_sources( + install_pkg_srcs, + subdir: '{{ cookiecutter.package_name }}', +) + +install_lib_srcs = [ + '{{ cookiecutter.package_name }}/lib/__init__.py', +{%- for babelized_class in cookiecutter.components|list|sort %} + '{{ cookiecutter.package_name }}/lib/{{ babelized_class|lower }}.pyx', +{%- endfor %} +] +py.install_sources( + install_lib_srcs, + subdir: '{{ cookiecutter.package_name }}/lib', +) + + +{%- for babelized_class, component in cookiecutter.components|dictsort %} +py.extension_module( + '{{ babelized_class|lower }}', + [ +{%- if cookiecutter.language == 'fortran' %} + '{{ cookiecutter.package_name }}/lib/bmi_interoperability.f90', +{%- endif %} + '{{ cookiecutter.package_name }}/lib/{{ babelized_class|lower }}.pyx', + ], + dependencies: [ + dependency('{{ component.library }}', method : 'pkg-config'), + ], + include_directories: incs, + install: true, + subdir: '{{ cookiecutter.package_name }}/lib', +{%- if cookiecutter.language == 'c++' %} + override_options : ['cython_language=cpp'], +{%- endif %} +) + +install_subdir( + 'meta/{{ babelized_class }}', + install_dir: py.get_install_dir() / '{{ cookiecutter.package_name }}/data', +) + +{%- endfor %} + +# This is a temporary fix for editable installs. +run_command('cp', '-r', '{{ cookiecutter.package_name }}/data', 'build') diff --git a/babelizer/data/{{cookiecutter.package_name}}/pyproject.toml b/babelizer/data/{{cookiecutter.package_name}}/pyproject.toml index b0077dce..7df13701 100644 --- a/babelizer/data/{{cookiecutter.package_name}}/pyproject.toml +++ b/babelizer/data/{{cookiecutter.package_name}}/pyproject.toml @@ -1,5 +1,13 @@ [build-system] -requires = ["cython", "numpy", "setuptools", "wheel"] +{%- if cookiecutter.language == 'python' %} +build-backend = "setuptools.build_meta" +requires = [ + "setuptools >=61", +] +{% else %} +build-backend = "mesonpy" +requires = ["cython", "numpy", "meson-python", "wheel"] +{% endif %} [project] name = "{{cookiecutter.package_name}}" @@ -24,7 +32,7 @@ classifiers=[ ] requires-python = ">=3.10" keywords=["bmi", "pymt"] -dynamic = ["readme", "version"] +dynamic = ["version"] dependencies = [ "numpy", ] @@ -39,6 +47,9 @@ homepage = "https://github.com/{{ cookiecutter.info.github_username }}/{{ cookie [project.optional-dependencies] dev = [ + "meson", + "meson-python", + "ninja", "nox", ] docs = [ @@ -54,18 +65,11 @@ testing = [ "bmi-tester>=0.5.4", ] -[tool.setuptools.dynamic] -readme = {file = ["README.rst", "CREDITS.rst", "CHANGES.rst", "LICENSE.rst"]} -version = {attr = "{{ cookiecutter.package_name }}._version.__version__"} - -[tool.setuptools] -include-package-data = true -packages = ["{{ cookiecutter.package_name }}"] - -[tool.setuptools.package-data] -{{ cookiecutter.package_name }} = [ - "data/*", -] +{%- if cookiecutter.language == 'python' %} +[tool.setuptools.packages.find] +where = ["."] +include = ["{{cookiecutter.package_name}}*"] +{% endif %} [tool.pytest.ini_options] minversion = "5.0" @@ -107,3 +111,7 @@ underlines = "-^\"" issue_format = "`#{issue} `_" title_format = "{version} ({project_date})" wrap = true + +[tool.zest-releaser] +tag-format = "v{version}" +python-file-with-version = "{{ cookiecutter.package_name }}/_version.py" diff --git a/babelizer/data/{{cookiecutter.package_name}}/setup.cfg b/babelizer/data/{{cookiecutter.package_name}}/setup.cfg index 45fca16c..f3d7d5c3 100644 --- a/babelizer/data/{{cookiecutter.package_name}}/setup.cfg +++ b/babelizer/data/{{cookiecutter.package_name}}/setup.cfg @@ -5,6 +5,3 @@ ignore = E501 W503 max-line-length = 88 - -[zest.releaser] -tag-format = v{version} diff --git a/babelizer/data/{{cookiecutter.package_name}}/setup.py b/babelizer/data/{{cookiecutter.package_name}}/setup.py deleted file mode 100644 index 76102434..00000000 --- a/babelizer/data/{{cookiecutter.package_name}}/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup - -{%- if cookiecutter.language in ['c', 'c++', 'fortran'] %} -from setup_utils import build_ext, get_extension_modules -{% endif %} - -setup( -{%- if cookiecutter.language in ['c', 'c++', 'fortran'] %} - ext_modules=get_extension_modules(), -{%- endif %} -{%- if cookiecutter.language == 'fortran' %} - cmdclass={"build_ext": build_ext}, -{%- endif %} -) diff --git a/babelizer/data/{{cookiecutter.package_name}}/setup_utils.py b/babelizer/data/{{cookiecutter.package_name}}/setup_utils.py deleted file mode 100644 index b2bbca13..00000000 --- a/babelizer/data/{{cookiecutter.package_name}}/setup_utils.py +++ /dev/null @@ -1,123 +0,0 @@ -import contextlib -import os -import subprocess -import sys - -import numpy as np -from numpy.distutils.fcompiler import new_fcompiler -from setuptools import Extension -from setuptools.command.build_ext import build_ext as _build_ext - - -def get_compiler_flags(): - flags = { - "include_dirs": [ - np.get_include(), - os.path.join(sys.prefix, "include"), - {%- for dir in cookiecutter.build.include_dirs %} - "{{ dir|trim }}", - {% endfor %} - ], - "library_dirs": [ - {%- for libdir in cookiecutter.build.library_dirs %} - "{{ libdir|trim }}", - {% endfor %} - ], - "define_macros": [ - {%- if cookiecutter.build.define_macros -%} - {%- for item in cookiecutter.build.define_macros %} - {%- set key_value = item.split('=') %} - ("{{ key_value[0]|trim }}", "{{ key_value[1]|trim }}"),{% endfor %} - {%- endif %} - ], - "undef_macros": [ - {%- if cookiecutter.build.undef_macros -%} - {%- for macro in cookiecutter.build.undef_macros %} - "{{ macro|trim }}",{% endfor %} - {%- endif %} - ], - "extra_compile_args": [ - {%- if cookiecutter.build.extra_compile_args -%} - {%- for arg in cookiecutter.build.extra_compile_args %} - "{{ arg|trim }}",{% endfor %} - {%- endif %} - ], - {%- if cookiecutter.language == 'fortran' %} - "language": "c", - {% else %} - "language": "{{ cookiecutter.language }}", - {% endif -%} - } - - # Locate directories under Windows %LIBRARY_PREFIX%. - if sys.platform.startswith("win"): - flags["include_dirs"].append(os.path.join(sys.prefix, "Library", "include")) - flags["library_dirs"].append(os.path.join(sys.prefix, "Library", "lib")) - - return flags - - -def get_extension_modules(): - flags = get_compiler_flags() - - libraries = [ - {%- if cookiecutter.build.libraries -%} - {%- for lib in cookiecutter.build.libraries %} - "{{ lib|trim }}",{% endfor %} - {%- endif %} - ] - - ext_modules = [ - {%- for babelized_class, component in cookiecutter.components|dictsort %} - Extension( - "{{cookiecutter.package_name}}.lib.{{ babelized_class|lower }}", - ["{{cookiecutter.package_name}}/lib/{{ babelized_class|lower }}.pyx"], - libraries=libraries + ["{{ component.library }}"], - {% if cookiecutter.language == 'fortran' -%} - extra_objects=["{{cookiecutter.package_name}}/lib/bmi_interoperability.o"], - {% endif -%} - **flags, - ), - {%- endfor %} - ] - - return ext_modules - - -@contextlib.contextmanager -def as_cwd(path): - prev_cwd = os.getcwd() - os.chdir(path) - yield - os.chdir(prev_cwd) - - -def build_interoperability(): - compiler = new_fcompiler() - compiler.customize() - - flags = get_compiler_flags() - - cmd = [] - cmd.append(compiler.compiler_f90[0]) - cmd.append(compiler.compile_switch) - if sys.platform.startswith("win") is False: - cmd.append("-fPIC") - for include_dir in flags["include_dirs"]: - if os.path.isabs(include_dir) is False: - include_dir = os.path.join(sys.prefix, "include", include_dir) - cmd.append(f"-I{include_dir}") - cmd.append("bmi_interoperability.f90") - - try: - subprocess.check_call(cmd) - except subprocess.CalledProcessError: - raise - - -class build_ext(_build_ext): - - def run(self): - with as_cwd("{{cookiecutter.package_name}}/lib"): - build_interoperability() - _build_ext.run(self) diff --git a/babelizer/render.py b/babelizer/render.py index e7d4d1df..5edb0d46 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -168,8 +168,6 @@ def prettify_python(path_to_repo): module_name = meta["package"]["name"] files_to_fix = [ - path_to_repo / "setup.py", - path_to_repo / "setup_utils.py", path_to_repo / module_name / "_bmi.py", path_to_repo / module_name / "__init__.py", path_to_repo / "docs" / "conf.py", diff --git a/external/bmi-example-c b/external/bmi-example-c index 5d39005f..f47d0a65 160000 --- a/external/bmi-example-c +++ b/external/bmi-example-c @@ -1 +1 @@ -Subproject commit 5d39005f1aad3c2bb38c2661d68c8e453269cefe +Subproject commit f47d0a65edf848ccd9d57d375e9fdd90354c1f4f diff --git a/external/bmi-example-cxx b/external/bmi-example-cxx index 1c2d0902..07c3dd51 160000 --- a/external/bmi-example-cxx +++ b/external/bmi-example-cxx @@ -1 +1 @@ -Subproject commit 1c2d0902963894a96d80d852777769a8d14f0779 +Subproject commit 07c3dd518ddcd7db50e22be0db6ff3fc067053fb diff --git a/external/bmi-example-fortran b/external/bmi-example-fortran index 819757c7..98b4e339 160000 --- a/external/bmi-example-fortran +++ b/external/bmi-example-fortran @@ -1 +1 @@ -Subproject commit 819757c77a779d13f8c67431cc3d13f6f36b0dcc +Subproject commit 98b4e3394db6f2b032d8945c125e77435c6d5d03 diff --git a/external/tests/test_fortran/babel.toml b/external/tests/test_fortran/babel.toml index 60c816b9..0aaf3760 100644 --- a/external/tests/test_fortran/babel.toml +++ b/external/tests/test_fortran/babel.toml @@ -15,7 +15,7 @@ extra_compile_args = [] [package] name = "pymt_heatf" -requirements = [""] +requirements = [] [info] github_username = "pymt-lab" diff --git a/news/90.feature b/news/90.feature new file mode 100644 index 00000000..3798cf28 --- /dev/null +++ b/news/90.feature @@ -0,0 +1,3 @@ + +Use *meson* to build babelized projects with Fortran, C, and C++ sources. +Projects with Python sources are still built with *setuptools*. diff --git a/noxfile.py b/noxfile.py index bc1c44aa..e5fc20b9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -23,11 +23,7 @@ def test(session: nox.Session) -> None: session.run("pytest", *args) -@nox.session( - name="test-langs", - python=PYTHON_VERSIONS, - venv_backend="mamba", -) +@nox.session(name="test-langs", python=PYTHON_VERSIONS, venv_backend="conda") @nox.parametrize("lang", ["c", "cxx", "fortran", "python"]) def test_langs(session: nox.session, lang) -> None: datadir = ROOT / "external" / "tests" / f"test_{lang}" @@ -42,7 +38,6 @@ def test_langs(session: nox.session, lang) -> None: build_examples(session, lang) - session.conda_install("pip", "bmi-tester>=0.5.4", "cmake") session.install(".[testing]") with session.chdir(tmpdir): @@ -56,7 +51,7 @@ def test_langs(session: nox.session, lang) -> None: session.debug(f"{k}: {v!r}") with session.chdir(package): - session.run("python", "-m", "pip", "install", "-e", ".") + session.run("python", "-m", "pip", "install", ".[dev]") with session.chdir(testdir): shutil.copy(datadir / config_file, ".") @@ -85,17 +80,19 @@ def _get_package_metadata(datadir): return package, library, config_files[0] -@nox.session(name="build-examples", venv_backend="mamba") +@nox.session(name="build-examples", python=PYTHON_VERSIONS, venv_backend="conda") @nox.parametrize("lang", ["c", "cxx", "fortran", "python"]) def build_examples(session: nox.Session, lang): """Build the language examples.""" srcdir = ROOT / "external" / f"bmi-example-{lang}" - builddir = pathlib.Path(session.create_tmp()) / "_build" + tmpdir = ROOT / pathlib.Path(session.create_tmp()) + builddir = tmpdir / "_build" + instdir = pathlib.Path(session.virtualenv.location) if lang == "python": - session.conda_install("bmipy", "make") + session.install("bmipy") else: - session.conda_install(f"bmi-{lang}", "cmake", "make", "pkg-config") + session.conda_install(f"bmi-{lang}", "pkg-config") for k, v in sorted(session.env.items()): session.debug(f"{k}: {v!r}") @@ -111,9 +108,10 @@ def build_examples(session: nox.Session, lang): str(srcdir), "-B", ".", - f"-DCMAKE_INSTALL_PREFIX={session.env['CONDA_PREFIX']}", + f"-DCMAKE_INSTALL_PREFIX={instdir}", ) session.run("make", "install") + return instdir @nox.session(name="test-cli") diff --git a/pyproject.toml b/pyproject.toml index f5b36f1b..04357a5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,7 @@ docs = [ "sphinxcontrib.towncrier", ] testing = [ + "bmi-tester>=0.5.9", "coverage[toml]", "coveralls", "pytest",