diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83fa8ce..d744548 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,18 +7,18 @@ on: jobs: tests: name: Run tests - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install cherab-solps and dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ccae05c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.21) +project(${SKBUILD_PROJECT_NAME} LANGUAGES C) + +find_package( + Python + COMPONENTS Interpreter Development.Module NumPy + REQUIRED) + +# Cython support uses cython-cmake 0.2.0: +# https://pypi.org/project/cython-cmake/. +# We use a vendored copy as upstream doesn't support Python 3.7, but this +# isn't a problem as the vendored files are cmake not python files. +# Ironically the vendoring scripts are the only part of the project which +# would potentially cause Python incompatibilities... +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +find_package(Cython MODULE REQUIRED VERSION 3.0) +include(UseCython) + +# Out-of-tree build means Cython needs to be told to look in +# the source tree for pxd files. +set(CYTHON_ARGS -3 -Wextra -I${CMAKE_SOURCE_DIR}) + +# Don't use deprecated Numpy API. +add_compile_definitions(NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) + +# Demo files are stored separately and must be copied manually. +file(GLOB_RECURSE demos RELATIVE ${CMAKE_SOURCE_DIR} "${CMAKE_SOURCE_DIR}/demos/*") +foreach(demo_file ${demos}) + get_filename_component(dir ${demo_file} DIRECTORY) + install(FILES ${demo_file} DESTINATION ${SKBUILD_DATA_DIR}/share/cherab/demos/solps/${dir}) +endforeach() + +# Dynamic options ported over from original setup.py. +# CMake's caching means when we use option() here along with +# -Ccmake.define.= (pypa-build) +# or --config-settings=cmake.define.= (pip), +# these persist across rebuilds until specified again on the command line with +# different values. +# +# This means --force won't work properly, since specifying --force for two builds in +# succession would be treated as the same build (the cache value doesn't change) and +# would not lead to calling cython at all. For other options such as --profile, +# again they will only be turned on or off if the command line arguments are changed. +# +# TODO: decide whether CMake defines or environment variables are the better approach +# for dynamic configuration. + +option(profile "Enable profiling in Cython modules." OFF) +if(profile) + list(APPEND CYTHON_ARGS -Xprofile=True) +endif() + +option(line-profile "Enable line-by-line profiling in Cython modules." OFF) +if(line-profile) + list(APPEND CYTHON_ARGS -Xlinetrace=True) + add_compile_definitions(CYTHON_TRACE=1) + add_compile_definitions(CYTHON_TRACE_NOGIL=1) +endif() + +# An example of using an environment variable instead of a CMake option. +# Leave this undocumented for now: do we really want to include support for +# annotations? Probably fine to just use `cython --annotate` on individual files. +if(DEFINED ENV{CYTHON_ANNOTATE}) + list(APPEND CYTHON_ARGS --annotate) +endif() + +# Cython extensions are all stored in cherab/solps and subdirectories. Note: all +# modifications to CYTHON_ARGS should be done before the call to add_subdirectory to +# ensure subdirectories pick up the final list of arguments. +add_subdirectory(cherab/solps) diff --git a/README.md b/README.md index ef09fb3..630a6b9 100644 --- a/README.md +++ b/README.md @@ -49,15 +49,38 @@ This will pull in `cherab-core`, `raysect` `numpy` and other dependencies, then ### Developers -For developing the code, it is recommended to use local checkouts of `cherab-core` and `raysect`, as well as `cherab-solps`. Development should be done against the `development` branch of this repository, and any modifications submitted as pull requests to be merged back into `development`. -To install the package in develop mode, so that local changes are immediately visible without needing to reinstall, install with: +To install the package in editable mode, so that local changes are immediately visible without needing to reinstall, install the project dependencies into your development environment. +You should also enable auto rebuilds. +From the cherab-solps directory, run: ``` -pip install -e +pip install -r requirements.txt +pip install --no-build-isolation --config-settings=editable.rebuild=true -e . ``` -If you are modifying Cython files you will need to run `./dev/build.sh` from this directory in order to rebuild the extension modules. -They will then be used when Python is restarted. +If you are modifying Cython files these will then be automatically rebuilt and the modified versions used when Python is restarted. +Pure Python files will be automatically included in the distribution as long as they have been added to Git, but if you add any Cython files you will need to add (or modify) a CMakeLists.txt file in the same directory as the new files to ensure these modules are built and included in the distribution. +See the existing CMakeLists.txt files for examples of how to do this. +Also note that when adding new Cython files you will need to re-run the above `pip install` command to ensure these new modules will be available in the editable install. + +#### Profiling + +It is possible to turn on profiling and line tracing support in the Cython extensions, which may be useful for performance optimisation. +These features do incur a performance overhead so they are disabled by default. + +To enable function-level profiling after installing the project in editable mode (see above), reinstall it with the following `pip` command: + +``` +pip install --no-build-isolation --config-settings=editable.rebuild=true --config-settings=cmake.define.profile=ON -e +``` + +To enable line-by-line profiling, use: + +``` +pip install --no-build-isolation --config-settings=editable.rebuild=true --config-settings=cmake.define.line-profile=ON -e +``` + +**Important:** the profile and line-profile settings will persist across subsequent (manual or automatic) rebuilds until they are turned off by running `pip install` with the corresponding definition set to `OFF`. diff --git a/cherab/__init__.py b/cherab/__init__.py deleted file mode 100644 index bfb1512..0000000 --- a/cherab/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas -# -# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the -# European Commission - subsequent versions of the EUPL (the "Licence"); -# You may not use this work except in compliance with the Licence. -# You may obtain a copy of the Licence at: -# -# https://joinup.ec.europa.eu/software/page/eupl5 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. -# -# See the Licence for the specific language governing permissions and limitations -# under the Licence. - -__import__('pkg_resources').declare_namespace(__name__) \ No newline at end of file diff --git a/cherab/solps/CMakeLists.txt b/cherab/solps/CMakeLists.txt new file mode 100644 index 0000000..bf44139 --- /dev/null +++ b/cherab/solps/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_path(RELATIVE_PATH CMAKE_CURRENT_BINARY_DIR BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE libdir) + +cython_transpile(solps_2d_functions.pyx LANGUAGE C OUTPUT_VARIABLE solps_2d_functions_c) +python_add_library(solps_2d_functions MODULE "${solps_2d_functions_c}" WITH_SOABI) +target_link_libraries(solps_2d_functions PRIVATE Python::NumPy) + +install(TARGETS solps_2d_functions DESTINATION ${libdir}) + +add_subdirectory(models) diff --git a/cherab/solps/models/CMakeLists.txt b/cherab/solps/models/CMakeLists.txt new file mode 100644 index 0000000..29d8a84 --- /dev/null +++ b/cherab/solps/models/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_path(RELATIVE_PATH CMAKE_CURRENT_BINARY_DIR BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE libdir) + +cython_transpile(line_emitter.pyx LANGUAGE C OUTPUT_VARIABLE line_emitter_c) +python_add_library(line_emitter MODULE "${line_emitter_c}" WITH_SOABI) +target_link_libraries(line_emitter PRIVATE Python::NumPy) + +install(TARGETS line_emitter DESTINATION ${libdir}) diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake new file mode 100644 index 0000000..7507dd7 --- /dev/null +++ b/cmake/FindCython.cmake @@ -0,0 +1,113 @@ +#.rst: +# +# Find ``cython`` executable. +# +# This module will set the following variables in your project: +# +# ``CYTHON_EXECUTABLE`` +# path to the ``cython`` program +# +# ``CYTHON_VERSION`` +# version of ``cython`` +# +# ``CYTHON_FOUND`` +# true if the program was found +# +# And the following target: +# +# ``Cython::Cython`` +# The Cython executable +# +# A range of versions is supported on CMake 3.19+. See also UseCython. +# +# For more information on the Cython project, see https://cython.org/. +# +# *Cython is a language that makes writing C extensions for the Python language +# as easy as Python itself.* +# +#============================================================================= +# Copyright 2011 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +# SPDX-License-Identifier: Apache-2.0 + +# Use the Cython executable that lives next to the Python executable +# if it is a local installation. +if(Python_EXECUTABLE) + get_filename_component(_python_path ${Python_EXECUTABLE} PATH) +elseif(Python3_EXECUTABLE) + get_filename_component(_python_path ${Python3_EXECUTABLE} PATH) +elseif(DEFINED PYTHON_EXECUTABLE) + get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH) +endif() + +if(DEFINED _python_path) + find_program(CYTHON_EXECUTABLE + NAMES cython cython.bat cython3 + HINTS ${_python_path} + DOC "path to the cython executable") +else() + find_program(CYTHON_EXECUTABLE + NAMES cython cython.bat cython3 + DOC "path to the cython executable") +endif() + +if(CYTHON_EXECUTABLE) + set(CYTHON_version_command "${CYTHON_EXECUTABLE}" --version) + + execute_process(COMMAND ${CYTHON_version_command} + OUTPUT_VARIABLE CYTHON_version_output + ERROR_VARIABLE CYTHON_version_error + RESULT_VARIABLE CYTHON_version_result + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE) + + if(NOT ${CYTHON_version_result} EQUAL 0) + set(_error_msg "Command \"${CYTHON_version_command}\" failed with") + set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}") + message(FATAL_ERROR "${_error_msg}") + elseif("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)") + set(CYTHON_VERSION "${CMAKE_MATCH_1}") + elseif("${CYTHON_version_error}" MATCHES "^[Cc]ython version ([^,]+)") + set(CYTHON_VERSION "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Invalid Cython version output") + endif() +endif() + +include(FindPackageHandleStandardArgs) + +if(CMAKE_VERSION VERSION_LESS 3.19) + set(_handle_version_range) +else() + set(_handle_version_range HANDLE_VERSION_RANGE) +endif() + +find_package_handle_standard_args(Cython + REQUIRED_VARS CYTHON_EXECUTABLE + VERSION_VAR ${CYTHON_VERSION} + ${_handle_version_range} +) + +if(CYTHON_FOUND) + if(NOT DEFINED Cython::Cython) + add_executable(Cython::Cython IMPORTED) + set_target_properties(Cython::Cython PROPERTIES + IMPORTED_LOCATION "${CYTHON_EXECUTABLE}" + ) + endif() +endif() + +mark_as_advanced(CYTHON_EXECUTABLE) diff --git a/cmake/UseCython.cmake b/cmake/UseCython.cmake new file mode 100644 index 0000000..08198b4 --- /dev/null +++ b/cmake/UseCython.cmake @@ -0,0 +1,251 @@ +#.rst: +# +# The following functions are defined: +# +# .. cmake:command:: Cython_transpile +# +# Create custom rules to generate the source code for a Python extension module +# using cython. +# +# Cython_transpile( +# [LANGUAGE C | CXX] +# [CYTHON_ARGS ...] +# [OUTPUT ] +# [OUTPUT_VARIABLE ]) +# +# Options: +# +# ``LANGUAGE [C | CXX]`` +# Force the generation of either a C or C++ file. Recommended; will attempt +# to be deduced if not specified, defaults to C unless only CXX is enabled. +# +# ``CYTHON_ARGS `` +# Specify additional arguments for the cythonization process. Will default to +# the ``CYTHON_ARGS`` variable if not specified. +# +# ``OUTPUT `` +# Specify a specific path for the output file as ````. By +# default, this will output into the current binary dir. A depfile will be +# created alongside this file as well. +# +# ``OUTPUT_VARIABLE `` +# Set the variable ```` in the parent scope to the path to the +# generated source file. +# +# Defined variables: +# +# ```` +# The path of the generated source file. +# +# Usage example: +# +# .. code-block:: cmake +# +# find_package(Cython) +# include(UseCython) +# +# Cython_transpile(_hello.pyx +# OUTPUT_VARIABLE _hello_source_files +# ) +# +# Python_add_library(_hello +# MODULE "${_hello_source_files}" +# WITH_SOABI +# ) +# +# +#============================================================================= +# Copyright 2011 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +# SPDX-License-Identifier: Apache-2.0 + +if(CMAKE_VERSION VERSION_LESS "3.8") + # CMake 3.7 required for DEPFILE + # CMake 3.8 required for COMMAND_EXPAND_LISTS + message(FATAL_ERROR "CMake 3.8 required for COMMAND_EXPAND_LISTS") +endif() + +function(Cython_transpile) + set(_options ) + set(_one_value LANGUAGE OUTPUT OUTPUT_VARIABLE) + set(_multi_value CYTHON_ARGS) + + cmake_parse_arguments(_args + "${_options}" + "${_one_value}" + "${_multi_value}" + ${ARGN} + ) + + if(DEFINED CYTHON_EXECUTABLE) + set(_cython_command "${CYTHON_EXECUTABLE}") + elseif(DEFINED Python_EXECUTABLE) + set(_cython_command "${Python_EXECUTABLE}" -m cython) + elseif(DEFINED Python3_EXECUTABLE) + set(_cython_command "${Python3_EXECUTABLE}" -m cython) + else() + message(FATAL_ERROR "Cython executable not found") + endif() + + # Default to CYTHON_ARGS if argument not specified + if(NOT _args_CYTHON_ARGS AND DEFINED CYTHON_ARGS) + set(_args_CYTHON_ARGS "${CYTHON_ARGS}") + endif() + + # Get input + set(_source_files ${_args_UNPARSED_ARGUMENTS}) + list(LENGTH _source_files input_length) + if(NOT input_length EQUAL 1) + message(FATAL_ERROR "One and only one input file must be specified, got '${_source_files}'") + endif() + + function(_transpile _source_file generated_file language) + + if(language STREQUAL "C") + set(_language_arg "") + elseif(language STREQUAL "CXX") + set(_language_arg "--cplus") + else() + message(FATAL_ERROR "_transpile language must be one of C or CXX") + endif() + + set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) + + # Generated depfile is expected to have the ".dep" extension and be located along + # side the generated source file. + set(_depfile ${generated_file}.dep) + set(_depfile_arg "-M") + + # Normalize the input path + get_filename_component(_source_file "${_source_file}" ABSOLUTE) + + # Pretty-printed output names + file(RELATIVE_PATH generated_file_relative + "${CMAKE_BINARY_DIR}" "${generated_file}") + file(RELATIVE_PATH source_file_relative + "${CMAKE_SOURCE_DIR}" "${_source_file}") + set(comment "Generating ${_language} source '${generated_file_relative}' from '${source_file_relative}'") + + # Get output directory to ensure its exists + get_filename_component(output_directory "${generated_file}" DIRECTORY) + + get_source_file_property(pyx_location ${_source_file} LOCATION) + + # Add the command to run the compiler. + add_custom_command( + OUTPUT "${generated_file}" + COMMAND + "${CMAKE_COMMAND}" -E make_directory "${output_directory}" + COMMAND + ${_cython_command} + ${_language_arg} + "${_args_CYTHON_ARGS}" + ${_depfile_arg} + "${pyx_location}" + --output-file "${generated_file}" + COMMAND_EXPAND_LISTS + MAIN_DEPENDENCY + "${_source_file}" + DEPFILE + "${_depfile}" + VERBATIM + COMMENT "${comment}" + ) + endfunction() + + function(_set_output _input_file _language _output_var) + if(_language STREQUAL "C") + set(_language_extension "c") + elseif(_language STREQUAL "CXX") + set(_language_extension "cxx") + else() + message(FATAL_ERROR "_set_output language must be one of C or CXX") + endif() + + # Can use cmake_path for CMake 3.20+ + # cmake_path(GET _input_file STEM basename) + get_filename_component(_basename "${_input_file}" NAME_WE) + + if(IS_ABSOLUTE "${_input_file}") + file(RELATIVE_PATH _input_relative "${CMAKE_CURRENT_SOURCE_DIR}" "${_input_file}") + else() + set(_input_relative "${_input_file}") + endif() + + get_filename_component(_output_relative_dir "${_input_relative}" DIRECTORY) + string(REPLACE "." "_" _output_relative_dir "${_output_relative_dir}") + if(_output_relative_dir) + set(_output_relative_dir "${_output_relative_dir}/") + endif() + + set(${_output_var} "${CMAKE_CURRENT_BINARY_DIR}/${_output_relative_dir}${_basename}.${_language_extension}" PARENT_SCOPE) + endfunction() + + set(generated_files) + + list(GET _source_files 0 _source_file) + + # Set target language + set(_language ${_args_LANGUAGE}) + if(NOT _language) + get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES) + if("C" IN_LIST _languages AND "CXX" IN_LIST _languages) + # Try to compute language. Returns falsy if not found. + _cython_compute_language(_language ${_source_file}) + elseif("C" IN_LIST _languages) + # If only C is enabled globally, assume C + set(_language "C") + elseif("CXX" IN_LIST _languages) + # Likewise for CXX + set(_language "CXX") + else() + message(FATAL_ERROR "LANGUAGE keyword required if neither C nor CXX enabled globally") + endif() + endif() + + if(NOT _language MATCHES "^(C|CXX)$") + message(FATAL_ERROR "Cython_transpile LANGUAGE must be one of C or CXX") + endif() + + # Place the cython files in the current binary dir if no path given + if(NOT _args_OUTPUT) + _set_output("${_source_file}" ${_language} _args_OUTPUT) + elseif(NOT IS_ABSOLUTE "${_args_OUTPUT}") + set(_args_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${_args_OUTPUT}") + endif() + + set(generated_file "${_args_OUTPUT}") + _transpile("${_source_file}" "${generated_file}" ${_language}) + list(APPEND generated_files "${generated_file}") + + # Output variable only if set + if(_args_OUTPUT_VARIABLE) + set(_output_variable ${_args_OUTPUT_VARIABLE}) + set(${_output_variable} "${generated_files}" PARENT_SCOPE) + endif() + +endfunction() + +function(_cython_compute_language OUTPUT_VARIABLE FILENAME) + file(READ "${FILENAME}" FILE_CONTENT) + # Check for compiler directive similar to "# distutils: language = c++" + # See https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html#declare-a-var-with-the-wrapped-c-class + set(REGEX_PATTERN [=[^[[:space:]]*#[[:space:]]*distutils:.*language[[:space:]]*=[[:space:]]*(c\\+\\+|c)]=]) + string(REGEX MATCH "${REGEX_PATTERN}" MATCH_RESULT "${FILE_CONTENT}") + string(TOUPPER "${MATCH_RESULT}" LANGUAGE_NAME) + string(REPLACE "+" "X" LANGUAGE_NAME "${LANGUAGE_NAME}") + set(${OUTPUT_VARIABLE} ${LANGUAGE_NAME} PARENT_SCOPE) +endfunction() diff --git a/dev/build.sh b/dev/build.sh deleted file mode 100755 index 7f2b7a2..0000000 --- a/dev/build.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -CORES=`nproc --all` - -echo "Rebuilding CHERAB extension modules (in place)..." -python setup.py build_ext -j$CORES --inplace $1 $2 $3 $4 $5 diff --git a/dev/build_wheels.sh b/dev/build_wheels.sh index 377b4fd..7d78c84 100644 --- a/dev/build_wheels.sh +++ b/dev/build_wheels.sh @@ -1,30 +1,18 @@ #!/bin/sh -# Run in the PyPA manylinux1 docker container to produce wheels suitable for PyPI. +# Run in the PyPA manylinux2010 docker container to produce wheels suitable for PyPI. # Ensure that the path to the cherab-solps repository is mounted at /cherab-solps. +# Python 3.10 and later should use the manylinux2014 docker container instead. -# Due to the lack of manylinux1 wheels available for many of the dependencies, we -# install build-time dependencies manually using the last versions with wheels, -# then run build without build-time isolation. +# Set cmake.args=--fresh to ensure no cached configuration from previous builds is used. -/opt/python/cp37-cp37m/bin/python -m venv /tmp/venv --clear -. /tmp/venv/bin/activate -pip install --prefer-binary "numpy==1.14.6" "raysect==0.7.1" build wheel "cherab==1.3.0" "scipy<1.6" "matplotlib<3.3" "cython==3.0a5" -python -m build -n . - -/opt/python/cp38-cp38/bin/python -m venv /tmp/venv --clear -. /tmp/venv/bin/activate -pip install --prefer-binary "numpy==1.17.5" "raysect==0.7.1" build wheel "cherab==1.3.0" "scipy<1.8" "matplotlib<3.6" "cython==3.0a5" -python -m build -n . - -/opt/python/cp39-cp39/bin/python -m venv /tmp/venv --clear -. /tmp/venv/bin/activate -pip install --prefer-binary "numpy==1.19.5" "raysect==0.7.1" build wheel "cherab==1.3.0" "scipy<1.8" "matplotlib<3.6" "cython==3.0a5" -python -m build -n . +/opt/python/cp37-cp37m/bin/python -m build . -Ccmake.args=--fresh +/opt/python/cp38-cp38/bin/python -m build . -Ccmake.args=--fresh +/opt/python/cp39-cp39/bin/python -m build . -Ccmake.args=--fresh for wheel in dist/*.whl do auditwheel repair "$wheel" done -# Upload the manylinux1 wheels, along with the sdist in dist/, using twine. +# Upload the manylinux wheels, along with the sdist in dist/, using twine. diff --git a/dev/test.sh b/dev/test.sh index 6132427..547a1b2 100755 --- a/dev/test.sh +++ b/dev/test.sh @@ -1,3 +1,6 @@ #!/bin/bash -python -m unittest $1 $2 $3 $4 $5 +# Project must have been installed in editable mode for this to work. +# For namespace package discovery need to pass the path to the part of +# the namespace that has an __init__.py as the start directory. +python -m unittest discover -s cherab.solps -t . $1 $2 $3 $4 $5 diff --git a/pyproject.toml b/pyproject.toml index 3d17489..7b39891 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,60 @@ [build-system] -requires = ["setuptools", "oldest-supported-numpy", "cython~=3.0", "raysect==0.8.1.*", "cherab==1.5.*"] -build-backend="setuptools.build_meta" +requires = [ + "scikit-build-core", + # "cython-cmake", # Vendored to support Python 3.7 builds. + "cython~=3.0", + # "oldest-supported-numpy; python_version < '3.9'", + # "numpy~=2.0; python_version >= '3.9'", + # Cherab currently requires numpy < 2, so this package does too. + "oldest-supported-numpy", + "raysect==0.8.1.*", + "cherab==1.5.*", +] +build-backend="scikit_build_core.build" + +[project] +name = "cherab-solps" +version = "1.3.0.dev2" +dependencies = [ + "raysect == 0.8.1.*", + "cherab == 1.5.*", + "matplotlib", + "scipy", +] +description = "Cherab spectroscopy framework: SOLPS submodule" +readme = "README.md" +license = {file = "LICENCE.txt"} +requires-python = ">= 3.7" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "Intended Audience :: Education", + "Intended Audience :: Developers", + "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Programming Language :: Cython", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Physics", +] + +[project.urls] +homepage = "https://github.com/cherab" +documentation = "https://www.cherab.info" +repository = "https://github.com/cherab/solps" +issues = "https://github.com/cherab/solps/issues" + +[tool.scikit-build] +cmake.version = ">=3.21" +minimum-version = "0.10" +sdist.exclude = [ + ".github", + ".gitignore", + "dev", +] +wheel.exclude = [ + "**.pyx", + "CMakeLists.txt", +] +wheel.packages = ["cherab"] +build-dir = "build/{state}" +build.verbose = true # For development, show the build commands. diff --git a/requirements.txt b/requirements.txt index 27e2c31..45f28f1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +scikit-build-core +oldest-supported-numpy cherab==1.5.* raysect==0.8.1.* cython~=3.0 diff --git a/setup.py b/setup.py deleted file mode 100644 index fa882c8..0000000 --- a/setup.py +++ /dev/null @@ -1,84 +0,0 @@ -from collections import defaultdict -from setuptools import setup, find_packages, Extension -from Cython.Build import cythonize -import sys -import numpy -import os -import os.path as path -from pathlib import Path - -force = False -profile = False - -if "--force" in sys.argv: - force = True - del sys.argv[sys.argv.index("--force")] - -if "--profile" in sys.argv: - profile = True - del sys.argv[sys.argv.index("--profile")] - -compilation_includes = [".", numpy.get_include()] - -setup_path = path.dirname(path.abspath(__file__)) - -# build extension list -extensions = [] -for root, dirs, files in os.walk(setup_path): - for file in files: - if path.splitext(file)[1] == ".pyx": - pyx_file = path.relpath(path.join(root, file), setup_path) - module = path.splitext(pyx_file)[0].replace("/", ".") - extensions.append(Extension(module, [pyx_file], include_dirs=compilation_includes),) - -cython_directives = {"language_level": 3} -if profile: - cython_directives["profile"] = True - - -with open("README.md") as f: - long_description = f.read() - -# Include demos in a separate directory in the distribution as data_files. -demo_parent_path = Path("share/cherab/demos/solps") -data_files = defaultdict(list) -demos_source = Path("demos") -for item in demos_source.rglob("*"): - if item.is_file(): - install_dir = demo_parent_path / item.parent.relative_to(demos_source) - data_files[str(install_dir)].append(str(item)) -data_files = list(data_files.items()) - -setup( - name="cherab-solps", - version="1.3.0.dev1", - license="EUPL 1.1", - namespace_packages=['cherab'], - description="Cherab spectroscopy framework: SOLPS submodule", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Science/Research", - "Intended Audience :: Education", - "Intended Audience :: Developers", - "Natural Language :: English", - "Operating System :: POSIX :: Linux", - "Programming Language :: Cython", - "Programming Language :: Python :: 3", - "Topic :: Scientific/Engineering :: Physics", - ], - url="https://github.com/cherab", - project_urls=dict( - Tracker="https://github.com/cherab/solps/issues", - Documentation="https://cherab.github.io/documentation/", - ), - long_description=long_description, - long_description_content_type="text/markdown", - packages=find_packages(), - package_data={"": [ - "**/*.pyx", "**/*.pxd", # Needed to build Cython extensions. - ], - }, - data_files=data_files, - install_requires=["raysect==0.8.1.*", "cherab==1.5.*"], - ext_modules=cythonize(extensions, force=force, compiler_directives=cython_directives), -)