diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98f1428808..7c7f327e2e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,8 +48,7 @@ jobs: name: build run: | conda activate "${{ steps.reqs.outputs.envname }}" - cmake -S . -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONDA_BUILD=ON -DCMAKE_INSTALL_PREFIX="$CONDA_PREFIX" - cmake --build ./build --target install + pip install . - name: test run: | conda activate "${{ steps.reqs.outputs.envname }}" @@ -81,10 +80,7 @@ jobs: python-version: ${{ matrix.python-version }} environment-file: scripts/requirements-test.yml activate-environment: cil_dev - - name: build - run: | - cmake -S . -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONDA_BUILD=ON -DCMAKE_INSTALL_PREFIX="$CONDA_PREFIX" - cmake --build ./build --target install + - run: pip install . - name: test run: python -m unittest discover -v ./Wrappers/Python/test conda-matrix: @@ -179,11 +175,7 @@ jobs: conda-merge ../scripts/requirements-test.yml docs_environment.yml > environment.yml conda env update -n test conda list - - name: build cil - working-directory: . - run: | - cmake -S . -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONDA_BUILD=ON -DCMAKE_INSTALL_PREFIX="$CONDA_PREFIX" - cmake --build ./build --target install + - run: pip install .. - name: checkout docs uses: actions/checkout@v4 with: diff --git a/.gitignore b/.gitignore index db3970f85a..81b4942ab4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ -/build/ +/build* __pycache__/ -/Wrappers/Python/build/ /Wrappers/Python/cil/version.py -/build* +/Wrappers/Python/cil-*.dist-info/ +/Wrappers/Python/cil/include/ +/Wrappers/Python/cil/lib/ *.vscode* *.egg* diff --git a/CHANGELOG.md b/CHANGELOG.md index ffad4c4127..93488ba57d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - Added tests for Normaliser processor - Update minimum cmake version to 3.5 - Dependencies: + - Move from CMake to `pip install` (#2145) - matplotlib-base is an optional dependency, instead of required (#2093) - `unittest-parametrize has been added as a dependency for tests (#1990) - olefile and dxchange are an optional dependency, instead of required (#2149) diff --git a/CMakeLists.txt b/CMakeLists.txt index 261c61cb66..1054e3d78c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,10 @@ else() cmake_minimum_required(VERSION 3.5) endif() -project(cil LANGUAGES CXX) +if(NOT SKBUILD_PROJECT_NAME) + set(SKBUILD_PROJECT_NAME cil) +endif() +project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX) cmake_policy(PUSH) @@ -46,6 +49,5 @@ if(NOT DEFINED CMAKE_BUILD_TYPE) endif() add_subdirectory(src/Core) -add_subdirectory(Wrappers/Python) cmake_policy(POP) diff --git a/Dockerfile b/Dockerfile index 5dc5f25bca..5c508e0d71 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,5 @@ ENV TENSORBOARD_PROXY_URL=/user-redirect/proxy/6006/ # build & install CIL COPY --chown="${NB_USER}" . src -RUN cmake -S ./src -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONDA_BUILD=ON -DCMAKE_INSTALL_PREFIX="${CONDA_DIR}" \ - && cmake --build ./build --target install \ - && rm -rf src build \ +RUN pip install ./src && rm -rf src \ && fix-permissions "${CONDA_DIR}" /home/${NB_USER} diff --git a/README.md b/README.md index 5cdd6b3dea..75e80d2e23 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ conda create --name cil -c conda-forge -c https://software.repos.intel.com/pytho ``` where: + - `astra-toolbox=*=py*` enables CIL support for [ASTRA toolbox](http://www.astra-toolbox.com) CPU projector (2D Parallel beam only) (GPLv3 license) - `astra-toolbox=*=cuda*` (requires an NVIDIA GPU) enables CIL support for [ASTRA toolbox](http://www.astra-toolbox.com) GPU projectors (GPLv3 license) - `tigre` (requires an NVIDIA GPU) enables support for [TIGRE](https://github.com/CERN/TIGRE) toolbox projectors (BSD license) @@ -86,7 +87,8 @@ docker run --rm --gpus all -p 8888:8888 -it ghcr.io/tomographicimaging/cil:lates ## Getting Started with CIL ### CIL Training -We typically run training courses at least twice a year - check https://ccpi.ac.uk/training/ for our upcoming events! + +We typically run training courses at least twice a year - check for our upcoming events! ### CIL on binder @@ -116,60 +118,45 @@ The use of `--recurse-submodule` is necessary if the user wants the examples dat git submodule update --init --recursive ``` -### Build dependencies +### Building with `pip` + +#### Install Dependencies To create a conda environment with all the dependencies for building CIL run the following shell script: ```sh -bash scripts/create_local_env_for_cil_development.sh +bash ./scripts/create_local_env_for_cil_development.sh ``` Or with the CIL build and test dependencies: ```sh -bash scripts/create_local_env_for_cil_development.sh -t +bash ./scripts/create_local_env_for_cil_development.sh -t ``` -And then install CIL in to this environment using CMake. +And then install CIL in to this environment using `pip`. Alternatively, one can use the `scripts/requirements-test.yml` to create a conda environment with all the appropriate dependencies on any OS, using the following command: ```sh -conda env create -f scripts/requirements-test.yml +conda env create -f ./scripts/requirements-test.yml ``` -### Build with CMake - -CMake and a C++ compiler are required to build the source code. Let's suppose that the user is in the source directory, then the following commands should work: +#### Build CIL -```sh -cmake -S . -B ./build -DCMAKE_INSTALL_PREFIX= -DPython_EXECUTABLE= -cmake --build ./build --target install -``` +A C++ compiler is required to build the source code. Let's suppose that the user is in the source directory, then the following commands should work: -If targeting an active conda environment then the `` can be set to the `CONDA_PREFIX` environment variable (e.g. `${CONDA_PREFIX}` in Bash, or `%CONDA_PREFIX%` in the Anaconda Prompt on Windows). Similarly, `` can be set to the active conda environment by passing `${CONDA_PREFIX}/bin/python` on linux or `%CONDA_PREFIX%\python` in windows. - -#### Linux -```sh -cmake -S . -B ./build -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX} -DPython_EXECUTABLE=${CONDA_PREFIX}/bin/python -``` -#### Windows ```sh -cmake -S . -B .\build -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX% -DPython_EXECUTABLE=%CONDA_PREFIX%\python +pip install --no-deps . ``` -If not installing to a conda environment then the user will also need to set the locations of the IPP library and includes, and the path to CIL. - -By default the location of the IPP library and includes is `${CMAKE_INSTALL_PREFIX}/lib` and `${CMAKE_INSTALL_PREFIX}/include` respectively. To pass the location of the IPP library and headers please pass the following parameters: +If not installing inside a conda environment, then the user might need to set the locations of optional libraries: ```sh -cmake -S . -B ./build -DCMAKE_INSTALL_PREFIX= -DPython_EXECUTABLE= -DIPP_ROOT= +pip install . -Ccmake.define.IPP_ROOT="" -Ccmake.define.OpenMP_ROOT="" ``` -The user will then need to add the path `/lib` to the environment variable `PATH` or `LD_LIBRARY_PATH`, depending on system OS. - - ### Building with Docker In the repository root, simply update submodules and run `docker build`: @@ -179,7 +166,6 @@ git submodule update --init --recursive docker build . -t ghcr.io/tomographicimaging/cil ``` - ### Testing One installed, CIL functionality can be tested using the following command: @@ -189,10 +175,8 @@ export TESTS_FORCE_GPU=1 # optional, makes GPU test failures noisy python -m unittest discover -v ./Wrappers/Python/test ``` - ## Citing CIL - If you use CIL in your research, please include citations to **both** the software on Zenodo, and a CIL paper: E. Pasca, J. S. Jørgensen, E. Papoutsellis, E. Ametova, G. Fardell, K. Thielemans, L. Murgatroyd, M. Duff and H. Robarts (2023)
@@ -200,7 +184,6 @@ Core Imaging Library (CIL)
Zenodo [software archive]
**DOI:** https://doi.org/10.5281/zenodo.4746198
- In most cases, the first CIL paper will be the appropriate choice: J. S. Jørgensen, E. Ametova, G. Burca, G. Fardell, E. Papoutsellis, E. Pasca, K. Thielemans, M. Turner, R. Warr, W. R. B. Lionheart and P. J. Withers (2021)
diff --git a/Wrappers/Python/CMakeLists.txt b/Wrappers/Python/CMakeLists.txt deleted file mode 100644 index 9cb60e0f60..0000000000 --- a/Wrappers/Python/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -find_package(Python COMPONENTS Interpreter REQUIRED) - -if(PYTHON_DEST_DIR) - file(TO_CMAKE_PATH "${PYTHON_DEST_DIR}" PYTHON_DEST) -endif() - -message("CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}") -if(NOT DEFINED) - set(CMAKE_BUILD_TYPE "RelWithDebInfo") -endif() - -# TODO: add to add_custom_command DEPENDS the list of python files of the project. -# file(GLOB_RECURSE DEPS ${CMAKE_CURRENT_SOURCE_DIR}/cil/*.py ) -message("SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CIL: $ENV{SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CIL}") -message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") -if(PYTHON_DEST_DIR) - message(STATUS "CIL install using ${Python_EXECUTABLE} -m pip install -t ${PYTHON_DEST}") - add_custom_target(pythonsetup ALL - COMMAND ${Python_EXECUTABLE} -m pip install ${CMAKE_SOURCE_DIR} -t ${PYTHON_DEST} --upgrade - DEPENDS cilacc) -else() - message(STATUS "CIL install using ${Python_EXECUTABLE} -m pip install") - add_custom_target(pythonsetup ALL - COMMAND ${Python_EXECUTABLE} -m pip install ${CMAKE_SOURCE_DIR} --upgrade - DEPENDS cilacc) -endif() -# install the data if not a conda build. With conda the data is in the dependency package cil-data -if (NOT CONDA_BUILD) - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/cil) -endif() diff --git a/Wrappers/Python/cil/framework/cilacc.py b/Wrappers/Python/cil/framework/cilacc.py index fa96ba4c51..88e7046dc0 100644 --- a/Wrappers/Python/cil/framework/cilacc.py +++ b/Wrappers/Python/cil/framework/cilacc.py @@ -16,18 +16,10 @@ # Authors: # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt import ctypes -import platform -from ctypes import util -# check for the extension +from pathlib import Path -if platform.system() == 'Linux': - dll = 'libcilacc.so' -elif platform.system() == 'Windows': - dll_file = 'cilacc.dll' - dll = util.find_library(dll_file) -elif platform.system() == 'Darwin': - dll = 'libcilacc.dylib' -else: - raise ValueError('Not supported platform, ', platform.system()) - -cilacc = ctypes.cdll.LoadLibrary(dll) +try: + cilacc_path = next((Path(__file__).parent.parent / 'lib').resolve().glob("*cilacc.*")) +except StopIteration: + raise FileNotFoundError("cilacc library not found") +cilacc = ctypes.cdll.LoadLibrary(str(cilacc_path)) diff --git a/Wrappers/Python/cil/optimisation/operators/GradientOperator.py b/Wrappers/Python/cil/optimisation/operators/GradientOperator.py index ae6bbc6922..56dd9b1b1b 100644 --- a/Wrappers/Python/cil/optimisation/operators/GradientOperator.py +++ b/Wrappers/Python/cil/optimisation/operators/GradientOperator.py @@ -18,7 +18,7 @@ from cil.optimisation.operators import LinearOperator from cil.optimisation.operators import FiniteDifferenceOperator -from cil.framework import BlockGeometry, ImageGeometry +from cil.framework import BlockGeometry, ImageGeometry, cilacc import logging from cil.utilities.multiprocessing import NUM_THREADS import numpy as np @@ -258,7 +258,7 @@ def adjoint(self, x, out=None): out.fill(tmp) else: out += tmp - return out + return out else: tmp = self.domain_geometry().allocate() for i, axis_index in enumerate(self.ind): @@ -267,20 +267,7 @@ def adjoint(self, x, out=None): tmp += self.FD.adjoint(x.get_item(i)) return tmp -import ctypes, platform -from ctypes import util -# check for the extension -if platform.system() == 'Linux': - dll = 'libcilacc.so' -elif platform.system() == 'Windows': - dll_file = 'cilacc.dll' - dll = util.find_library(dll_file) -elif platform.system() == 'Darwin': - dll = 'libcilacc.dylib' -else: - raise ValueError('Not supported platform, ', platform.system()) - -cilacc = ctypes.cdll.LoadLibrary(dll) +import ctypes c_float_p = ctypes.POINTER(ctypes.c_float) @@ -457,7 +444,7 @@ def adjoint(self, x, out=None): status = self.fd(out_p, *args) if status != 0: raise RuntimeError('Call to C gradient operator failed') - + out.fill(ndout) #reset input data @@ -466,4 +453,3 @@ def adjoint(self, x, out=None): ndx[i]*= el return out - diff --git a/docs/source/developer_guide.rst b/docs/source/developer_guide.rst index ea78b8bb7e..f1472925f9 100644 --- a/docs/source/developer_guide.rst +++ b/docs/source/developer_guide.rst @@ -137,8 +137,7 @@ Example: cd CIL sh scripts/create_local_env_for_cil_development_tests.sh -n NUMPY_VERSION -p PYTHON_VERSION -e ENVIRONMENT_NAME conda activate ENVIRONMENT_NAME - cmake -S . -B ./build -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX} - cmake --build ./build --target install + pip install . cd docs conda update -n base -c defaults conda conda env update -f docs_environment.yml # with the name field set to ENVIRONMENT_NAME diff --git a/pyproject.toml b/pyproject.toml index 24adcdac4a..846644715e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,20 @@ [build-system] -requires = ["setuptools>=64", "setuptools_scm>=8"] -build-backend = "setuptools.build_meta" +requires = [ + "setuptools>=64", + "setuptools_scm>=8", + "scikit-build-core>=0.10", + #"ipp-devel==2021.12.*", # PyPI conflicts with conda package +] +build-backend = "scikit_build_core.build" + +[tool.scikit-build] +minimum-version = "build-system.requires" +cmake.version = ">=3.16" +metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" +sdist.include = ["Wrappers/Python/cil/version.py"] +sdist.exclude = ["scripts", "docs", "Wrappers/Python/data", "Wrappers/Python/test", ".*"] +[tool.scikit-build.wheel.packages] +cil = "Wrappers/Python/cil" [tool.setuptools_scm] version_file = "Wrappers/Python/cil/version.py" diff --git a/recipe/bld.bat b/recipe/bld.bat index 696c6a1870..97fb90517d 100644 --- a/recipe/bld.bat +++ b/recipe/bld.bat @@ -4,13 +4,5 @@ set SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CIL=%PKG_VERSION% if not "%GIT_DESCRIBE_NUMBER%"=="0" ( set SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CIL=%PKG_VERSION%.dev%GIT_DESCRIBE_NUMBER%+%GIT_DESCRIBE_HASH% ) - -:: -G "Visual Studio 16 2019" specifies the the generator -:: -T v142 specifies the toolset - -cmake -S "%RECIPE_DIR%\.." -B "%SRC_DIR%\build_framework" -G "Visual Studio 16 2019" -T "v142" -DCONDA_BUILD=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBRARY_LIB=%CONDA_PREFIX%\lib -DLIBRARY_INC=%CONDA_PREFIX% -DCMAKE_INSTALL_PREFIX=%PREFIX% -DPython_EXECUTABLE=%CONDA_PREFIX%\python - -if errorlevel 1 exit 1 - -cmake --build "%SRC_DIR%\build_framework" --target install --config RelWithDebInfo +pip install . --no-deps if errorlevel 1 exit 1 diff --git a/recipe/build.sh b/recipe/build.sh index 0a1c1c629f..872bff219b 100755 --- a/recipe/build.sh +++ b/recipe/build.sh @@ -1,23 +1,16 @@ #!/usr/bin/env bash set -euxo pipefail +extra_args="-G Ninja" if test $(python -c "from __future__ import print_function; import platform; print(platform.system())") = Darwin ; then echo "Darwin" - extra_args="-DOPENMP_LIBRARIES=${CONDA_PREFIX}/lib -DOPENMP_INCLUDES=${CONDA_PREFIX}/include" + extra_args="$extra_args -DOPENMP_LIBRARIES=${CONDA_PREFIX}/lib -DOPENMP_INCLUDES=${CONDA_PREFIX}/include" else echo "something else" - extra_args="" fi export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CIL="${PKG_VERSION}" if test "${GIT_DESCRIBE_NUMBER}" != "0"; then export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CIL="${PKG_VERSION}.dev${GIT_DESCRIBE_NUMBER}+${GIT_DESCRIBE_HASH}" fi -cmake ${RECIPE_DIR}/../ ${extra_args} \ - -DCONDA_BUILD=ON \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DLIBRARY_LIB=${CONDA_PREFIX}/lib \ - -DLIBRARY_INC=${CONDA_PREFIX}/include \ - -DCMAKE_INSTALL_PREFIX=${PREFIX} \ - -DPython_EXECUTABLE=${CONDA_PREFIX}/bin/python -cmake --build . --target install +pip install . --no-deps -Ccmake.args="${extra_args}" diff --git a/recipe/meta.yaml b/recipe/meta.yaml index a84600143d..7f2bb87005 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -34,7 +34,7 @@ test: - pip install unittest-parametrize - python -m unittest discover -v -s Wrappers/Python/test # [win] - python -m unittest discover -s Wrappers/Python/test -v # [not win] - + requirements: build: @@ -50,9 +50,11 @@ requirements: - _openmp_mutex # [linux] - openmp # [osx] - vc 14 # [win] - - ipp-include {{ ipp_version }} - - ipp-devel {{ ipp_version }} - - ipp {{ ipp_version }} + - ninja # [not win] + - ipp-include {{ ipp_version }} + - ipp-devel {{ ipp_version }} + - ipp {{ ipp_version }} + - scikit-build-core >=0.10 run: - python diff --git a/src/Core/CMakeLists.txt b/src/Core/CMakeLists.txt index 82f0c8e0b0..51aaa92851 100644 --- a/src/Core/CMakeLists.txt +++ b/src/Core/CMakeLists.txt @@ -26,7 +26,7 @@ find_package(OpenMP REQUIRED) add_definitions(${OpenMP_CXX_FLAGS}) if (WIN32) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Ddll_EXPORTS") + add_definitions("/Ddll_EXPORTS") endif() if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNUCC") @@ -68,29 +68,8 @@ add_library(cilacc SHARED ${cilacc_SOURCES}) target_link_libraries(cilacc ${cilacc_LIBRARIES}) include_directories(cilacc PUBLIC ${cilacc_INCLUDES}) -## Install -#include(GNUInstallDirs) -#install(TARGETS cilacc -# RUNTIME DESTINATION bin -# LIBRARY DESTINATION lib -# ARCHIVE DESTINATION lib -# CONFIGURATIONS ${CMAKE_BUILD_TYPE} -# ) - -if (UNIX) -message ("I'd install into ${CMAKE_INSTALL_PREFIX}/lib") install(TARGETS cilacc - LIBRARY DESTINATION lib - CONFIGURATIONS ${CMAKE_BUILD_TYPE} - ) -elseif(WIN32) -message ("I'd install into ${CMAKE_INSTALL_PREFIX} lib bin") - install(TARGETS cilacc - RUNTIME DESTINATION Library/bin - ARCHIVE DESTINATION lib - CONFIGURATIONS ${CMAKE_BUILD_TYPE} - ) -endif() - -install(DIRECTORY ${PROJECT_SOURCE_DIR}/src/Core/include/ - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/cil) + LIBRARY DESTINATION ${SKBUILD_PROJECT_NAME}/lib + RUNTIME DESTINATION ${SKBUILD_PROJECT_NAME}/lib) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION ${SKBUILD_PROJECT_NAME}/include/${SKBUILD_PROJECT_NAME})