From aedace0c277a4a130078d61a53095aaaffd35ab7 Mon Sep 17 00:00:00 2001 From: Marcel Loose Date: Tue, 23 Apr 2024 12:48:50 +0200 Subject: [PATCH] Use scikit-build-core (#263) * WIP: Use scikit-build-core First working version. May still need a bit of tweaking. * WIP: Add check for valid data directory * WIP: Create separate CMakeLists.txt files Created separate `CMakeLists.txt` files for the `src` and `tests` directory. Removed unused environment variables in `pyproject.toml`. * WIP: Do not immediately stop on error Try to configure as much as possible, so that more than one error can be reported. * WIP: Do not repeat yourself The same CMake settings need to be applied to all libraries. Do this in a `foreach` loop that loops over all targets. * WIP: Set indentation to 2 spaces for CMakeLists.txt files * WIP: Run cmake-format Formatted the `CMakeLists.txt` files using `cmake-format`. * WIP: Remove deprecated Travis configuration * WIP: Exclude unwanted file from source distribution * WIP: Update README * WIP: Do not use editable install Editable installs don't work with `pip` and `pyproject.toml`. * WIP: Install pkg-config The new build system uses `pkg-config` to locate Casacore. At it to the list of packages that must be installed. * WIP: Disable kern-based GitHub workflow jobs * WIP: Use correct docker image on quay.io * WIP: Fix py3_casacore_master job Based `py3_casacore_master` on `ubuntu:24.04` and fixed the job. * WIP: Replace nose with pytest Replaced `nose` with the more modern `pytest` and `pytest-cov` for measuring test coverage. Coverage report is sent to stdout. * WIP: Remove coverage badge The coverage badge pointed to a page that hasn't been updated since 2017. It has been removed. TODO: perform coverage tests and add a new badge. * WIP: Remove commented lines in workflow * WIP: Add extra project info to pyproject.toml * WIP: Fix incorrect license in classifiers * WIP: Add C++ programming language to classifiers * Since Cascore < 3.5 doesn't come with pkg-config files, we need to fall-back to using good-ole CMake modules. * Install lapack development package Not sure why, but when using `FindCasacore.cmake` (adopted from `schaapcommon`), we need to have `liblapack-dev` installed. * Depend on ubuntu:latest Do not depend on an explicit Ubunut version, use `latest` instead. * Uppercase "Casacore_" in all CMakeLists.txt files Forgot to uppercase _all_ references to variable names `Casacore_*`. * Only link to needed libraries Only link to the libraries that are actually needed by passing the `--as-needed` option to the linker. * Undo unnecessary changes Undid some unnecessary changes to some of the no longer used GitHub workflows. These changes wouldn't have had an effect anyway. * Update README.rst Point to main WCSlib page, instead of tar ball. --- .github/workflows/linux.yml | 2 - .github/workflows/py3_casacore_master.docker | 28 +- .github/workflows/py3_casacore_v3.4.docker | 2 +- .github/workflows/py3_kern7.docker | 2 +- .gitignore | 3 + .travis.yml | 41 --- CMakeLists.txt | 41 +++ MANIFEST.in | 6 - README.rst | 27 +- cmake/FindCFITSIO.cmake | 74 +++++ cmake/FindCasacore.cmake | 265 ++++++++++++++++ cmake/FindWCSLIB.cmake | 70 +++++ pyproject.toml | 73 ++++- setup.py | 311 ------------------- src/CMakeLists.txt | 39 +++ tests/CMakeLists.txt | 7 + tests/requirements.txt | 6 +- 17 files changed, 598 insertions(+), 399 deletions(-) delete mode 100644 .travis.yml create mode 100644 CMakeLists.txt delete mode 100644 MANIFEST.in create mode 100644 cmake/FindCFITSIO.cmake create mode 100644 cmake/FindCasacore.cmake create mode 100644 cmake/FindWCSLIB.cmake delete mode 100755 setup.py create mode 100644 src/CMakeLists.txt create mode 100644 tests/CMakeLists.txt diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e2cae6d5..224c0cc4 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -12,9 +12,7 @@ jobs: strategy: matrix: dist: - - py3_kern7 - py3_casacore_master - - py3_casacore_v3.4 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/py3_casacore_master.docker b/.github/workflows/py3_casacore_master.docker index a3cbad7f..bae72c2e 100644 --- a/.github/workflows/py3_casacore_master.docker +++ b/.github/workflows/py3_casacore_master.docker @@ -1,19 +1,19 @@ -FROM quay.io/casacore/casacore:master -USER root -RUN docker-apt-install \ +FROM ubuntu:latest +RUN apt-get update -y +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \ + build-essential \ + casacore-dev \ libboost-python-dev \ libcfitsio-dev \ - wcslib-dev \ - python3-all \ + liblapack-dev \ python3-dev \ - python3-numpy \ - python3-setuptools \ - python3-six \ - python3-pip \ - python3-nose + python3-venv \ + wcslib-dev ADD . /src WORKDIR /src -RUN pip3 install -e . -RUN pip3 install -r tests/requirements.txt -RUN nosetests3 --with-coverage --verbose --cover-package=casacore -RUN python3 setup.py clean +RUN python3 -m venv venv && \ + . venv/bin/activate && \ + pip install . && \ + cd tests && \ + pip install -r requirements.txt && \ + pytest --cov-report term --cov=casacore diff --git a/.github/workflows/py3_casacore_v3.4.docker b/.github/workflows/py3_casacore_v3.4.docker index ac00188c..29441411 100644 --- a/.github/workflows/py3_casacore_v3.4.docker +++ b/.github/workflows/py3_casacore_v3.4.docker @@ -13,6 +13,6 @@ RUN docker-apt-install \ python3-nose ADD . /src WORKDIR /src -RUN pip3 install -e . +RUN pip3 install . RUN pip3 install -r tests/requirements.txt RUN nosetests3 --with-coverage --verbose --cover-package=casacore diff --git a/.github/workflows/py3_kern7.docker b/.github/workflows/py3_kern7.docker index ddc93877..7a8b2563 100644 --- a/.github/workflows/py3_kern7.docker +++ b/.github/workflows/py3_kern7.docker @@ -15,6 +15,6 @@ RUN docker-apt-install \ python3-nose ADD . /src WORKDIR /src -RUN pip3 install -e . +RUN pip3 install . RUN pip3 install -r tests/requirements.txt RUN nosetests3 --with-coverage --verbose --cover-package=casacore diff --git a/.gitignore b/.gitignore index 5bdbca5c..c0105bb3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,13 @@ casacore/*/_*.so python_casacore.egg-info/ casacore.egg-info/ *~ +*.bak +*.log .coverage .virtualenv/ .virtualenv3/ .venv*/ venv/ +wheelhouse/ htmlcov *.so diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 17be3a03..00000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -language: python -os: linux -dist: xenial -jobs: - include: - - env: TARGET=py2_casacore_v3.2 - - env: TARGET=py3_casacore_v3.2 - - env: TARGET=py2_casacore_master - - env: TARGET=py3_casacore_master - - env: TARGET=py2-kern - - env: TARGET=py3-kern - - env: TARGET=pep8 - - env: TARGET=mypy - allow_failures: - - env: TARGET=py2-kern - - env: TARGET=py3-kern - - env: TARGET=pep8 - - env: TARGET=mypy -services: - - docker -before_install: - - .travis/before_install.sh -install: - - .travis/install.sh -script: - - .travis/script.sh -deploy: - provider: pypi - username: gijzelaerr - password: - secure: cdrrma3XaFjtv4lHvag6dfhSbKBF8iLmVgPQEjXP8qa+WxHnkHObkyraYAFGqThDY/0lBdrBm7Og6l1JfAcSA2BjdPQYxujP8KEoVicPwlgwEJ5LZo4HqympWVk33APvbcYNw7K/CwEXNJCCD1tDiO4pdwkPAWuKlnYUVfZq2yI= - skip_existing: true - on: - tags: true - repo: casacore/python-casacore -after_success: - coveralls -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/08a570c12a3afa37d8e2 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..313cc226 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.15...3.26) +project(python-casacore) + +find_package( + Python + COMPONENTS Interpreter Development.Module + REQUIRED) + +# Find Casacore and its dependencies +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) +find_package(Casacore REQUIRED) +find_package(Boost REQUIRED COMPONENTS python) + +# If environment variable CASACORE_DATA is set, assume it points to a directory +# containing the cascacore data files, and install its contents. +if(DEFINED ENV{CASACORE_DATA}) + foreach(_dir ephemerides geodetic) + if(NOT IS_DIRECTORY $ENV{CASACORE_DATA}/${_dir}) + message( + SEND_ERROR + "Directory $ENV{CASACORE_DATA}/${_dir} does not exist. " + "Does environment variable CASACORE_DATA point to a valid data directory?" + ) + endif() + endforeach() + install( + DIRECTORY $ENV{CASACORE_DATA} + DESTINATION casacore + COMPONENT data) +else() + message( + WARNING + "Environment variable CASACORE_DATA is not defined. " + "Casacore data files will not be included in the python-casacore package." + ) +endif() + +add_subdirectory(src) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 1f5744cb..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include *.rst *.md -include casacore/.aipsrc -graft casacore/data -graft doc -graft src -graft tests diff --git a/README.rst b/README.rst index 308d1110..01c32f47 100644 --- a/README.rst +++ b/README.rst @@ -8,10 +8,14 @@ a c++ library used in radio astronomy. Python-casacore replaces the old The python-casacore documentation can be found on `casacore.github.io/python-casacore `_. -.. image:: https://travis-ci.org/casacore/python-casacore.svg?branch=master - :target: https://travis-ci.org/casacore/python-casacore -.. image:: https://coveralls.io/repos/github/casacore/python-casacore/badge.svg?branch=master - :target: https://coveralls.io/github/casacore/python-casacore?branch=master +Build status +------------ + +.. image:: https://github.com/casacore/python-casacore/actions/workflows/linux.yml/badge.svg + :target: https://github.com/casacore/python-casacore/actions/workflows/linux.yml +.. image:: https://github.com/casacore/python-casacore/actions/workflows/osx.yml/badge.svg + :target: https://github.com/casacore/python-casacore/actions/workflows/osx.yml + Installation ============ @@ -38,16 +42,17 @@ from source install these requirements: -* `setuptools `_ * `Casacore `__ * `Boost-python `_ -* `numpy `_ +* `numpy `_ * `cfitsio `_ +* `wcslib `_ +* `pip `_ On ubuntu you can install these with:: - $ apt-get install casacore-dev python-numpy \ - python-setuptools libboost-python-dev libcfitsio3-dev wcslib-dev + $ apt-get install casacore-dev libboost-python-dev python3-numpy \ + libcfitsio3-dev wcslib-dev python3-pip * compile and install:: @@ -55,13 +60,13 @@ On ubuntu you can install these with:: * or if you are installing from the source repository:: - $ python ./setup.py install - + $ pip install . + * If the compilation fails you might need to help the compiler find the paths to the boost and casacore libraries and headers. You can control this with the `CFLAGS` environment variable. For example on OS X when using homebrew and clang you need to do something like this:: - + CFLAGS="-std=c++11 \ -I/usr/local/Cellar/boost/1.68.0/include/ \ -I/usr/local/include/ \ diff --git a/cmake/FindCFITSIO.cmake b/cmake/FindCFITSIO.cmake new file mode 100644 index 00000000..a7497012 --- /dev/null +++ b/cmake/FindCFITSIO.cmake @@ -0,0 +1,74 @@ +# - Try to find CFITSIO. +# Variables used by this module: +# CFITSIO_ROOT_DIR - CFITSIO root directory +# Variables defined by this module: +# CFITSIO_FOUND - system has CFITSIO +# CFITSIO_INCLUDE_DIR - the CFITSIO include directory (cached) +# CFITSIO_INCLUDE_DIRS - the CFITSIO include directories +# (identical to CFITSIO_INCLUDE_DIR) +# CFITSIO_LIBRARY - the CFITSIO library (cached) +# CFITSIO_LIBRARIES - the CFITSIO libraries +# (identical to CFITSIO_LIBRARY) +# CFITSIO_VERSION_STRING the found version of CFITSIO, padded to 3 digits + +# Copyright (C) 2009 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see . +# +# $Id$ + +if(NOT CFITSIO_FOUND) + + find_path(CFITSIO_INCLUDE_DIR fitsio.h + HINTS ${CFITSIO_ROOT_DIR} PATH_SUFFIXES include include/cfitsio + include/libcfitsio0) + + if(CFITSIO_INCLUDE_DIR) + FILE(READ "${CFITSIO_INCLUDE_DIR}/fitsio.h" CFITSIO_H) + set(CFITSIO_VERSION_REGEX ".*#define CFITSIO_VERSION[^0-9]*([0-9]+)\\.([0-9]+).*") + if ("${CFITSIO_H}" MATCHES ${CFITSIO_VERSION_REGEX}) + # Pad CFITSIO minor version to three digit because 3.181 is older than 3.35 + STRING(REGEX REPLACE ${CFITSIO_VERSION_REGEX} + "\\1.\\200" CFITSIO_VERSION_STRING "${CFITSIO_H}") + STRING(SUBSTRING ${CFITSIO_VERSION_STRING} 0 5 CFITSIO_VERSION_STRING) + STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\1" CFITSIO_VERSION_MAJOR ${CFITSIO_VERSION_STRING}) + # CFITSIO_VERSION_MINOR will contain 80 for 3.08, 181 for 3.181 and 200 for 3.2 + STRING(REGEX REPLACE "^([0-9]+)[.]0*([0-9]+)" "\\2" CFITSIO_VERSION_MINOR ${CFITSIO_VERSION_STRING}) + else () + set(CFITSIO_VERSION_STRING "Unknown") + endif() + endif(CFITSIO_INCLUDE_DIR) + + find_library(CFITSIO_LIBRARY cfitsio + HINTS ${CFITSIO_ROOT_DIR} PATH_SUFFIXES lib) + find_library(M_LIBRARY m) + mark_as_advanced(CFITSIO_INCLUDE_DIR CFITSIO_LIBRARY M_LIBRARY) + + if(CMAKE_VERSION VERSION_LESS "2.8.3") + find_package_handle_standard_args(CFITSIO DEFAULT_MSG + CFITSIO_LIBRARY M_LIBRARY CFITSIO_INCLUDE_DIR) + else () + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(CFITSIO + REQUIRED_VARS CFITSIO_LIBRARY M_LIBRARY CFITSIO_INCLUDE_DIR + VERSION_VAR CFITSIO_VERSION_STRING) + endif () + + set(CFITSIO_INCLUDE_DIRS ${CFITSIO_INCLUDE_DIR}) + set(CFITSIO_LIBRARIES ${CFITSIO_LIBRARY} ${M_LIBRARY}) + +endif(NOT CFITSIO_FOUND) diff --git a/cmake/FindCasacore.cmake b/cmake/FindCasacore.cmake new file mode 100644 index 00000000..fc3f1311 --- /dev/null +++ b/cmake/FindCasacore.cmake @@ -0,0 +1,265 @@ +# - Try to find Casacore include dirs and libraries +# Usage: +# find_package(Casacore [REQUIRED] [COMPONENTS components...]) +# Valid components are: +# casa, coordinates, derivedmscal, fits, images, lattices, meas, measures, +# mirlib, ms, msfits, python3, scimath, scimath_f, tables +# +# Note that most components are dependent on other (more basic) components. +# In that case, it suffices to specify the "top-level" components; dependent +# components will be searched for automatically. +# +# The dependency tree can be generated using the script get_casacore_deps.sh. +# For this, you need to have a complete casacore installation, built with shared +# libraries, at your disposal. +# +# The dependencies in this macro were generated against casacore release 1.7.0. +# +# Variables used by this module: +# CASACORE_ROOT_DIR - Casacore root directory. +# BLAS_LIBS - override BLAS library +# LAPACK_LIBS - override LAPACK library +# +# Variables defined by this module: +# CASACORE_FOUND - System has Casacore, which means that the +# include dir was found, as well as all +# libraries specified (not cached) +# CASACORE_INCLUDE_DIR - Casacore include directory (cached) +# CASACORE_INCLUDE_DIRS - Casacore include directories (not cached) +# identical to CASACORE_INCLUDE_DIR +# CASACORE_LIBRARIES - The Casacore libraries (not cached) +# CASA_${COMPONENT}_LIBRARY - The absolute path of Casacore library +# "component" (cached) +# HAVE_AIPSPP - True if system has Casacore (cached) +# for backward compatibility with AIPS++ +# HAVE_CASACORE - True if system has Casacore (cached) +# identical to CASACORE_FOUND +# TAQL_EXECUTABLE - The absolute path of the TaQL executable +# (cached) +# +# ATTENTION: The component names need to be in lower case, just as the +# casacore library names. However, the CMake variables use all upper case. + +# Copyright (C) 2020 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: GPL-3.0-or-later + +# - casacore_resolve_dependencies(_result) +# +# Resolve the Casacore library dependencies for the given components. +# The list of dependent libraries will be returned in the variable result. +# It is sorted from least dependent to most dependent library, so it can be +# directly fed to the linker. +# +# Usage: casacore_resolve_dependencies(result components...) +# +macro(casacore_resolve_dependencies _result) + set(${_result} ${ARGN}) + set(_index 0) + # Do a breadth-first search through the dependency graph; append to the + # result list the dependent components for each item in that list. + # Duplicates will be removed later. + while(1) + list(LENGTH ${_result} _length) + if(NOT _index LESS _length) + break() + endif(NOT _index LESS _length) + list(GET ${_result} ${_index} item) + list(APPEND ${_result} ${Casacore_${item}_DEPENDENCIES}) + math(EXPR _index "${_index}+1") + endwhile(1) + # Remove all duplicates in the current result list, while retaining only the + # last of each duplicate. + list(REVERSE ${_result}) + list(REMOVE_DUPLICATES ${_result}) + list(REVERSE ${_result}) +endmacro(casacore_resolve_dependencies _result) + + +# - casacore_find_library(_name) +# +# Search for the library ${_name}. +# If library is found, add it to CASACORE_LIBRARIES; if not, add ${_name} +# to CASACORE_MISSING_COMPONENTS and set CASACORE_FOUND to false. +# +# Usage: casacore_find_library(name) +# +macro(casacore_find_library _name) + string(TOUPPER ${_name} _NAME) + find_library(${_NAME}_LIBRARY ${_name} + HINTS ${CASACORE_ROOT_DIR} PATH_SUFFIXES lib) + mark_as_advanced(${_NAME}_LIBRARY) + if(${_NAME}_LIBRARY) + list(APPEND CASACORE_LIBRARIES ${${_NAME}_LIBRARY}) + else(${_NAME}_LIBRARY) + set(CASACORE_FOUND FALSE) + list(APPEND CASACORE_MISSING_COMPONENTS ${_name}) + endif(${_NAME}_LIBRARY) +endmacro(casacore_find_library _name) + + +# - casacore_find_package(_name) +# +# Search for the package ${_name}. +# If the package is found, add the contents of ${_name}_INCLUDE_DIRS to +# CASACORE_INCLUDE_DIRS and ${_name}_LIBRARIES to CASACORE_LIBRARIES. +# +# If Casacore itself is required, then, strictly speaking, the packages it +# requires must be present. However, when linking against static libraries +# they may not be needed. One can override the REQUIRED setting by switching +# CASACORE_MAKE_REQUIRED_EXTERNALS_OPTIONAL to ON. Beware that this might cause +# compile and/or link errors. +# +# Usage: casacore_find_package(name [REQUIRED]) +# +macro(casacore_find_package _name) + if("${ARGN}" MATCHES "^REQUIRED$" AND + Casacore_FIND_REQUIRED AND + NOT CASACORE_MAKE_REQUIRED_EXTERNALS_OPTIONAL) + find_package(${_name} REQUIRED) + else() + find_package(${_name}) + endif() + if(${_name}_FOUND) + list(APPEND CASACORE_INCLUDE_DIRS ${${_name}_INCLUDE_DIRS}) + list(APPEND CASACORE_LIBRARIES ${${_name}_LIBRARIES}) + endif(${_name}_FOUND) +endmacro(casacore_find_package _name) + +# Define the Casacore components. +set(Casacore_components + casa + coordinates + derivedmscal + fits + images + lattices + meas + measures + mirlib + ms + msfits + python3 + scimath + scimath_f + tables +) + +# Define the Casacore components' inter-dependencies. +set(Casacore_casa_DEPENDENCIES) +set(Casacore_coordinates_DEPENDENCIES fits measures casa) +set(Casacore_derivedmscal_DEPENDENCIES ms measures tables casa) +set(Casacore_fits_DEPENDENCIES measures tables casa) +set(Casacore_images_DEPENDENCIES coordinates mirlib lattices fits measures scimath tables casa) +set(Casacore_lattices_DEPENDENCIES scimath tables casa) +set(Casacore_meas_DEPENDENCIES measures tables casa) +set(Casacore_measures_DEPENDENCIES tables casa) +set(Casacore_mirlib_DEPENDENCIES) +set(Casacore_ms_DEPENDENCIES measures scimath tables casa) +set(Casacore_msfits_DEPENDENCIES ms fits measures scimath tables casa) +set(Casacore_python3_DEPENDENCIES casa) +set(Casacore_scimath_DEPENDENCIES scimath_f casa) +set(Casacore_scimath_f_DEPENDENCIES) +set(Casacore_tables_DEPENDENCIES casa) + +# Initialize variables. +set(CASACORE_FOUND FALSE) +set(CASACORE_DEFINITIONS) +set(CASACORE_LIBRARIES) +set(CASACORE_MISSING_COMPONENTS) + +# Search for the header file first. +if(NOT CASACORE_INCLUDE_DIR) + find_path(CASACORE_INCLUDE_DIR casacore/casa/aips.h + HINTS ${CASACORE_ROOT_DIR} PATH_SUFFIXES include) + mark_as_advanced(CASACORE_INCLUDE_DIR) +endif(NOT CASACORE_INCLUDE_DIR) + +# Fallback for systems that have old casacore installed in directory not called 'casacore' +# This fallback can be removed once we move to casacore 2.0 which always puts headers in 'casacore' +if(NOT CASACORE_INCLUDE_DIR) + find_path(CASACORE_INCLUDE_DIR casa/aips.h + HINTS ${CASACORE_ROOT_DIR} PATH_SUFFIXES include) + mark_as_advanced(CASACORE_INCLUDE_DIR) +endif(NOT CASACORE_INCLUDE_DIR) + +if(NOT CASACORE_INCLUDE_DIR) + set(CASACORE_ERROR_MESSAGE "Casacore: unable to find the header file casa/aips.h.\nPlease set CASACORE_ROOT_DIR to the root directory containing Casacore.") +else(NOT CASACORE_INCLUDE_DIR) + # We've found the header file; let's continue. + set(CASACORE_FOUND TRUE) + # Note that new Casacore uses #include, while + # LOFAR still uses #include. Hence use both in -I path. + set(CASACORE_INCLUDE_DIRS ${CASACORE_INCLUDE_DIR} ${CASACORE_INCLUDE_DIR}/casacore) + + # Search for some often used binaries. + find_program(TAQL_EXECUTABLE taql + HINTS ${CASACORE_ROOT_DIR}/bin) + mark_as_advanced(TAQL_EXECUTABLE) + + # If the user specified components explicity, use that list; otherwise we'll + # assume that the user wants to use all components. + if(NOT Casacore_FIND_COMPONENTS) + set(Casacore_FIND_COMPONENTS ${Casacore_components}) + endif(NOT Casacore_FIND_COMPONENTS) + + # Get a list of all dependent Casacore libraries that need to be found. + casacore_resolve_dependencies(_find_components ${Casacore_FIND_COMPONENTS}) + + # Find the library for each component, and handle external dependencies + foreach(_comp ${_find_components}) + casacore_find_library(casa_${_comp}) + if(${_comp} STREQUAL casa) + casacore_find_package(HDF5) + casacore_find_library(m) + list(APPEND CASACORE_LIBRARIES ${CMAKE_DL_LIBS}) + elseif(${_comp} STREQUAL coordinates) + casacore_find_package(WCSLIB REQUIRED) + elseif(${_comp} STREQUAL fits) + casacore_find_package(CFITSIO REQUIRED) + elseif(${_comp} STREQUAL scimath_f) + # If only looking for LAPACK, no library will be added if LAPACK + # is part of BLAS, as it is customary nowadays. So look for both + # to avoid confusing linker errors on symbols used in headers/templates. + if(DEFINED ENV{BLAS_LIBS}) + set(BLAS_FOUND YES) + list(APPEND CASACORE_LIBRARIES $ENV{BLAS_LIBS}) + else() + casacore_find_package(BLAS REQUIRED) + endif() + if(DEFINED ENV{LAPACK_LIBS}) + set(LAPACK_FOUND YES) + list(APPEND CASACORE_LIBRARIES $ENV{LAPACK_LIBS}) + else() + casacore_find_package(LAPACK REQUIRED) + endif() + endif(${_comp} STREQUAL casa) + endforeach(_comp ${_find_components}) +endif(NOT CASACORE_INCLUDE_DIR) + +# Set HAVE_CASACORE; and HAVE_AIPSPP (for backward compatibility with AIPS++). +if(CASACORE_FOUND) + set(HAVE_CASACORE TRUE CACHE INTERNAL "Define if Casacore is installed") + set(HAVE_AIPSPP TRUE CACHE INTERNAL "Define if AIPS++/Casacore is installed") +endif(CASACORE_FOUND) + +# Compose diagnostic message if not all necessary components were found. +if(CASACORE_MISSING_COMPONENTS) + set(CASACORE_ERROR_MESSAGE "Casacore: the following components could not be found:\n ${CASACORE_MISSING_COMPONENTS}") +endif(CASACORE_MISSING_COMPONENTS) + +# Print diagnostics. +if(CASACORE_FOUND) + if(NOT Casacore_FIND_QUIETLY) + message(STATUS "Found the following Casacore components: ") + foreach(_comp ${_find_components}) + string(TOUPPER casa_${_comp} _COMP) + message(STATUS " ${_comp}: ${${_COMP}_LIBRARY}") + endforeach(_comp ${_find_components}) + endif(NOT Casacore_FIND_QUIETLY) +else(CASACORE_FOUND) + if(Casacore_FIND_REQUIRED) + message(FATAL_ERROR "${CASACORE_ERROR_MESSAGE}") + else(Casacore_FIND_REQUIRED) + message(STATUS "${CASACORE_ERROR_MESSAGE}") + endif(Casacore_FIND_REQUIRED) +endif(CASACORE_FOUND) diff --git a/cmake/FindWCSLIB.cmake b/cmake/FindWCSLIB.cmake new file mode 100644 index 00000000..43270843 --- /dev/null +++ b/cmake/FindWCSLIB.cmake @@ -0,0 +1,70 @@ +# - Try to find WCSLIB: the FITS "World Coordinate System" library +# Variables used by this module: +# WCSLIB_ROOT_DIR - WCSLIB root directory +# Variables defined by this module: +# WCSLIB_FOUND - system has WCSLIB +# WCSLIB_INCLUDE_DIR - the WCSLIB include directory (cached) +# WCSLIB_INCLUDE_DIRS - the WCSLIB include directories +# (identical to WCSLIB_INCLUDE_DIR) +# WCSLIB_LIBRARY - the WCSLIB library (cached) +# WCSLIB_LIBRARIES - the WCSLIB libraries +# (identical to WCSLIB_LIBRARY) +# WCSLIB_VERSION_STRING the found version of WCSLIB + +# Copyright (C) 2009 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see . +# +# $Id$ + +if(NOT WCSLIB_FOUND) + + find_path(WCSLIB_INCLUDE_DIR wcslib/wcsconfig.h + HINTS ${WCSLIB_ROOT_DIR} PATH_SUFFIXES include) + + if(WCSLIB_INCLUDE_DIR) + FILE(READ "${WCSLIB_INCLUDE_DIR}/wcslib/wcsconfig.h" WCSLIB_H) + set(WCSLIB_VERSION_REGEX ".*#define WCSLIB_VERSION[^0-9]*([0-9]+)\\.([0-9]+).*") + if ("${WCSLIB_H}" MATCHES ${WCSLIB_VERSION_REGEX}) + STRING(REGEX REPLACE ${WCSLIB_VERSION_REGEX} + "\\1.\\2" WCSLIB_VERSION_STRING "${WCSLIB_H}") + STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\1" WCSLIB_VERSION_MAJOR ${WCSLIB_VERSION_STRING}) + STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\2" WCSLIB_VERSION_MINOR ${WCSLIB_VERSION_STRING}) + else () + set(WCSLIB_VERSION_STRING "Unknown") + endif () + endif(WCSLIB_INCLUDE_DIR) + + find_library(WCSLIB_LIBRARY wcs + HINTS ${WCSLIB_ROOT_DIR} PATH_SUFFIXES lib) + find_library(M_LIBRARY m) + mark_as_advanced(WCSLIB_INCLUDE_DIR WCSLIB_LIBRARY M_LIBRARY) + + if(CMAKE_VERSION VERSION_LESS "2.8.3") + find_package_handle_standard_args(WCSLIB DEFAULT_MSG + WCSLIB_LIBRARY M_LIBRARY WCSLIB_INCLUDE_DIR) + else () + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(WCSLIB + REQUIRED_VARS WCSLIB_LIBRARY M_LIBRARY WCSLIB_INCLUDE_DIR + VERSION_VAR WCSLIB_VERSION_STRING) + endif () + + set(WCSLIB_INCLUDE_DIRS ${WCSLIB_INCLUDE_DIR}) + set(WCSLIB_LIBRARIES ${WCSLIB_LIBRARY} ${M_LIBRARY}) + +endif(NOT WCSLIB_FOUND) diff --git a/pyproject.toml b/pyproject.toml index 26695031..bf71b569 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,18 +1,61 @@ +######################### +# build-system settings # +######################### + [build-system] requires = [ - "build", - "cmake>=3.18", - "oldest-supported-numpy", - "setuptools", - "wheel", + "scikit-build-core>=0.8", +] +build-backend = "scikit_build_core.build" + + +#################### +# project settings # +#################### + +[project] +name = "python-casacore" +version = "3.5.3" +description = "A wrapper around CASACORE, the radio astronomy library" +keywords = ["pyrap", "casacore", "utilities", "astronomy"] +license = {file = "LICENSE"} +readme = {file = "README.rst", content-type = "test/x-rst"} +requires-python = ">=3.7" +authors = [ + {name = "Malte Marquarding", email = "Malte.Marquarding@gmail.com"}, + {name = "Ger van Diepen", email = "gervandiepen@gmail.com"}, + {name = "Gijs Molenaar", email = "gijs@pythonic.nl"}, + {name = "Tammo Jan Dijkema", email = "dijkema@astron.nl"}, + {name = "Marcel Loose", email = "loose@astron.nl"}, +] +classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", + "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Programming Language :: C++", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Astronomy", +] +dependencies = [ + "numpy", + "six", ] +[project.urls] +Homepage = "https://github.com/casacore/python-casacore" + + +######################### +# cibuildwheel settings # +######################### + [tool.cibuildwheel] build = "cp3{7,8,9,10,11,12}-*_x86_64" build-verbosity = 1 environment = """ \ - CFLAGS="-I/usr/include/cfitsio" \ - LD_LIBRARY_PATH=/opt/boost/lib \ + CXXFLAGS="-I/usr/include/cfitsio" \ """ test-command = "cd {package}/tests && pytest" test-requires = "pytest" @@ -49,3 +92,19 @@ manylinux-x86_64-image = "quay.io/casacore/casacore:py311_master" [[tool.cibuildwheel.overrides]] select="cp312-*" manylinux-x86_64-image = "quay.io/casacore/casacore:py312_master" + + +######################### +# scikit-build settings # +######################### + +[tool.scikit-build] +cmake.version = ">=3.15" +ninja.version = ">=1.5" +logging.level = "INFO" +install.components = ["data", "libraries"] +sdist.exclude = [".github", ".gitignore"] +wheel.packages = ["casacore", "pyrap"] + +[tool.scikit-build.cmake.define] +BUILD_TESTING = "ON" diff --git a/setup.py b/setup.py deleted file mode 100755 index 844b7ec3..00000000 --- a/setup.py +++ /dev/null @@ -1,311 +0,0 @@ -#!/usr/bin/env python -""" -Setup script for the CASACORE python wrapper. -""" -import os -import subprocess -import sys -import warnings -from setuptools import setup, Extension, find_packages, find_namespace_packages -from distutils.sysconfig import get_config_vars -from distutils.command import build_ext as build_ext_module -from distutils import ccompiler -from distutils.version import LooseVersion -import argparse -import ctypes -from os.path import join, dirname - -from casacore import __version__, __mincasacoreversion__ - -no_boost_error = """ -Could not find a Python boost library! Please use your package manager to install boost. - -Or install it manually: - -http://boostorg.github.io/python/doc/html/index.html -""" - -no_casacore_error = """Could not find Casacore! - -Casacore is a critical requirement. Please install Casacore using a package manager or install it manually. -You can find installation instructions on: - - https://github.com/casacore/casacore - -If you have Casacore installed in a non default location, you need to specify the location: - -$ python setup.py build_ext -I/opt/casacore/include:/other/include/path -L/opt/casacore/lib - -Don't give up! -""" - - -def find_library_file(libname): - """ - Try to get the directory of the specified library. - It adds to the search path the library paths given to distutil's build_ext. - """ - # Use a dummy argument parser to get user specified library dirs - parser = argparse.ArgumentParser(add_help=False) - parser.add_argument("--library-dirs", "-L", default='') - args, unknown = parser.parse_known_args() - lib_dirs = args.library_dirs.split(':') - - if 'LD_LIBRARY_PATH' in os.environ: - lib_dirs += os.environ['LD_LIBRARY_PATH'].split(':') - # Look for Homebrewed libraries - try: - homebrew_prefix = subprocess.run( - ['brew', '--prefix'], - capture_output=True, - check=True, - text=True - ).stdout.strip() - lib_dirs.append(join(homebrew_prefix, 'lib')) - except (subprocess.CalledProcessError, FileNotFoundError): - pass - # Append default search path (not a complete list) - lib_dirs += [join(sys.prefix, 'lib'), - '/usr/local/lib', - '/usr/lib64', - '/usr/lib', - '/usr/lib/x86_64-linux-gnu'] - - compiler = ccompiler.new_compiler() - return compiler.find_library_file(lib_dirs, libname) - - -def read(fname): - return open(join(dirname(__file__), fname)).read() - - -def find_boost(): - """ - Find the name and path of boost-python - - Returns: - library_name, e.g. 'boost_python-py36' (a guess if boost is not found) - library_dir, e.g. '/opt/local/boost/lib' ('' if boost is not found) - include_dir, e.g. '/opt/local/boost/include' ('' if boost is not found) - """ - short_version = "{}{}".format(sys.version_info[0], sys.version_info[1]) - major_version = str(sys.version_info[0]) - - # Prefer libraries with python version in their name over unversioned variants - boostlibnames = ['boost_python-py' + short_version, - 'boost_python' + short_version, - 'boost_python' + major_version, - 'boost_python', - ] - # The -mt (multithread) extension is used on macOS but not Linux. - # Look for it first to avoid ending up with a single-threaded version. - boostlibnames = sum([[name + '-mt', name] for name in boostlibnames], []) - - for libboostname in boostlibnames: - found_lib = find_library_file(libboostname) - if found_lib: - libdir = dirname(found_lib) - includedir = join(dirname(libdir), "include") - return libboostname, libdir, includedir - - warnings.warn(no_boost_error) - return boostlibnames[0], '', '' - - -def find_casacore_version(): - """ - Find the version of casacore, or None if it's not found - """ - if sys.version_info[0] == 2: - casa_python = 'casa_python' - else: - casa_python = 'casa_python3' - - # Find casacore libpath - libcasacasa = find_library_file('casa_casa') - - casacoreversion = None - if libcasacasa: - # Get version number from casacore - try: - libcasa = ctypes.cdll.LoadLibrary(libcasacasa) - getCasacoreVersion = libcasa.getVersion - getCasacoreVersion.restype = ctypes.c_char_p - casacoreversion = getCasacoreVersion().decode('utf-8') - except: - # getVersion was fixed in casacore 2.3.0 - pass - - return casacoreversion - - -def find_casacore(): - """ - Find the name and path of casacore - - Returns: - library_name, e.g. 'casa_python3' - library_dir, e.g. '/opt/local/casacore/lib' ('' if casacore is not found) - include_dir, e.g. '/opt/local/casacore/include' ('' if casacore is not found) - """ - if sys.version_info[0] == 2: - casa_python = 'casa_python' - else: - casa_python = 'casa_python3' - - # Find casacore libpath - libcasacasa = find_library_file('casa_casa') - - libdir = '' - includedir = '' - if libcasacasa: - libdir = dirname(libcasacasa) - includedir = join(dirname(libdir), "include") - else: - warnings.warn(no_casacore_error) - - return casa_python, libdir, includedir - - -def get_extensions(): - boost_python_libname, boost_python_libdir, boost_python_includedir = find_boost() - casa_python_libname, casa_libdir, casa_includedir = find_casacore() - - extension_metas = ( - # name, sources, depends, libraries - ( - "casacore.fitting._fitting", - ["src/fit.cc", "src/fitting.cc"], - ["src/fitting.h"], - ['casa_scimath', 'casa_scimath_f', boost_python_libname, casa_python_libname], - ), - ( - "casacore.functionals._functionals", - ["src/functional.cc", "src/functionals.cc"], - ["src/functionals.h"], - ['casa_scimath', 'casa_scimath_f', boost_python_libname, casa_python_libname], - ), - ( - "casacore.images._images", - ["src/images.cc", "src/pyimages.cc"], - ["src/pyimages.h"], - ['casa_images', 'casa_coordinates', - 'casa_fits', 'casa_lattices', 'casa_measures', - 'casa_scimath', 'casa_scimath_f', 'casa_tables', 'casa_mirlib', - boost_python_libname, casa_python_libname] - ), - ( - "casacore.measures._measures", - ["src/pymeas.cc", "src/pymeasures.cc"], - ["src/pymeasures.h"], - ['casa_measures', 'casa_scimath', 'casa_scimath_f', 'casa_tables', - boost_python_libname, casa_python_libname] - ), - ( - "casacore.quanta._quanta", - ["src/quanta.cc", "src/quantamath.cc", "src/quantity.cc", - "src/quantvec.cc"], - ["src/quanta.h"], - ["casa_casa", boost_python_libname, casa_python_libname], - ), - ( - "casacore.tables._tables", - ["src/pytable.cc", "src/pytableindex.cc", "src/pytableiter.cc", - "src/pytablerow.cc", "src/tables.cc", "src/pyms.cc"], - ["src/tables.h"], - ['casa_derivedmscal', 'casa_meas', 'casa_ms', 'casa_tables', boost_python_libname, casa_python_libname], - ), - ( - "casacore._tConvert", - ["tests/tConvert.cc"], - [], - [boost_python_libname, casa_python_libname], - ) - ) - - extensions = [] - for meta in extension_metas: - name, sources, depends, libraries = meta - - # Add dependency on casacore libraries to trigger rebuild at casacore update - for library in libraries: - if library and 'casa' in library: - found_lib = find_library_file(library) - if found_lib: - depends = depends + [found_lib] - - library_dirs = [lib for lib in (boost_python_libdir, - casa_libdir) if lib] - include_dirs = [inc for inc in (boost_python_includedir, - casa_includedir) if inc] - - extensions.append(Extension(name=name, sources=sources, - depends=depends, libraries=libraries, - library_dirs=library_dirs, - include_dirs=include_dirs, - # Since casacore 3.0.0 we have to be C++11 - extra_compile_args=['-std=c++11'])) - return extensions - - -# remove the strict-prototypes warning during compilation -(opt,) = get_config_vars('OPT') -os.environ['OPT'] = " ".join( - flag for flag in opt.split() if flag != '-Wstrict-prototypes' -) - - -def create_symlink(src_dir, dest_dir): - """ - Create a symbolic link from `src_dir` to `dest_dir`, unless `dest_dir` - already exists. - Return `dest_dir` upon success, or an empty string upon failure. - """ - if os.path.islink(dest_dir): - os.remove(dest_dir) - try: - os.symlink(src_dir, dest_dir) - except FileExistsError: - pass - except (FileNotFoundError, TypeError): - return "" - return dest_dir - - -class my_build_ext(build_ext_module.build_ext): - def run(self): - casacoreversion = find_casacore_version() - if casacoreversion is not None and LooseVersion(casacoreversion) < LooseVersion(__mincasacoreversion__): - errorstr = "Your casacore version is too old. Minimum is " + __mincasacoreversion__ + \ - ", you have " + casacoreversion - if casacoreversion == "2.5.0": - errorstr += " or 3.0.0 (which shipped in KERN5, incorrectly reporting itself as 2.5.0)" - raise RuntimeError(errorstr) - - build_ext_module.build_ext.run(self) - -setup(name='python-casacore', - version=__version__, - description='A wrapper around CASACORE, the radio astronomy library', - install_requires=['numpy', 'six'], - author='Gijs Molenaar', - author_email='gijs@pythonic.nl', - url='https://github.com/casacore/python-casacore', - keywords=['pyrap', 'casacore', 'utilities', 'astronomy'], - long_description=read('README.rst'), - long_description_content_type='text/x-rst', - packages=find_packages() + find_namespace_packages(include=["casacore.data.*"]), - include_package_data=True, - # We need to bring the casacore data files in scope of the python package. - # There is no need to copy the files, creating a symlink suffices. Environment - # variable `CASACORE_DATA` must point to the directory containing the data files. - # If `CASACORE_DATA` is not set, or points to a non-existing directory, no data - # will be added to the python package. If `casacore/data` already exists and is - # not a symlink, then no attempt is made to create a symlink; the contents of - # `casacore/data` will be used instead. - package_data={ - "casacore.data": [create_symlink(os.getenv("CASACORE_DATA"), "casacore/data")] - }, - ext_modules=get_extensions(), - cmdclass={'build_ext': my_build_ext}, - license='LGPL') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..377285b4 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,39 @@ +python_add_library(_fitting MODULE WITH_SOABI fit.cc fitting.cc) + +python_add_library(_functionals MODULE WITH_SOABI functional.cc functionals.cc) + +python_add_library(_images MODULE WITH_SOABI images.cc pyimages.cc) + +python_add_library(_measures MODULE WITH_SOABI pymeas.cc pymeasures.cc) + +python_add_library( + _quanta + MODULE + WITH_SOABI + quanta.cc + quantamath.cc + quantity.cc + quantvec.cc) + +python_add_library( + _tables + MODULE + WITH_SOABI + pytable.cc + pytableindex.cc + pytableiter.cc + pytablerow.cc + tables.cc + pyms.cc) + +get_directory_property(_targets BUILDSYSTEM_TARGETS) +foreach(_target ${_targets}) + string(REGEX REPLACE "^_" "casacore/" _destination "${_target}") + target_include_directories(${_target} PRIVATE ${Boost_INCLUDE_DIRS} + ${CASACORE_INCLUDE_DIRS}) + target_link_directories(${_target} PRIVATE ${CASACORE_LIBRARY_DIRS}) + target_link_libraries(${_target} PRIVATE ${CASACORE_LIBRARIES}) + target_link_options(${_target} PRIVATE "LINKER:--as-needed") + install(TARGETS ${_target} LIBRARY DESTINATION ${_destination} + COMPONENT libraries) +endforeach() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..669d4b4b --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +python_add_library(_tConvert MODULE WITH_SOABI tConvert.cc) +target_include_directories(_tConvert PRIVATE ${Boost_INCLUDE_DIRS} + ${CASACORE_INCLUDE_DIRS}) +target_link_directories(_tConvert PRIVATE ${CASACORE_LIBRARY_DIRS}) +target_link_libraries(_tConvert PRIVATE ${CASACORE_LIBRARIES}) +target_link_options(_tConvert PRIVATE "LINKER:--as-needed") +install(TARGETS _tConvert LIBRARY DESTINATION casacore COMPONENT libraries) diff --git a/tests/requirements.txt b/tests/requirements.txt index 55f1bf7d..c75c448b 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,5 +1 @@ -pytest -coverage -coveralls -travis-sphinx - +pytest-cov