Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions .github/workflows/conda-forge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: conda-forge BLAS

# Builds against conda-forge's BLAS packages, which ship no vendor CMake config
# (Windows) and split CBLAS into libcblas (Linux), as the sparsediffpy
# conda-forge recipe does.

on:
push:
branches: [main, development]
pull_request:
branches: [main, development]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]

defaults:
run:
shell: bash -el {0}

steps:
- uses: actions/checkout@v5

- uses: conda-incubator/setup-miniconda@v3
with:
miniforge-version: latest
activate-environment: build
conda-remove-defaults: "true"

- name: Install conda-forge packages (Linux)
if: runner.os == 'Linux'
run: >
conda install -y cmake ninja c-compiler blas-devel
"libblas=*=*netlib" "libcblas=*=*netlib"

- name: Install conda-forge packages (Windows)
if: runner.os == 'Windows'
run: conda install -y cmake openblas libopenblas

- name: Configure (Linux)
if: runner.os == 'Linux'
run: >
cmake -B build -S . -G Ninja
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_PREFIX_PATH="$CONDA_PREFIX"
-DCMAKE_C_FLAGS="-isystem $CONDA_PREFIX/include"
-DBLA_VENDOR=Generic

- name: Configure (Windows)
if: runner.os == 'Windows'
run: >
cmake -B build -S .
-DCMAKE_PREFIX_PATH="$CONDA_PREFIX/Library"

- name: Show BLAS selection
run: grep -E '^(BLAS_|CBLAS_LIBRARY|OPENBLAS_|OpenBLAS_DIR)' build/CMakeCache.txt || true

- name: Build
run: cmake --build build --config Release

- name: Run tests
run: cd build && ctest -C Release --output-on-failure

# Like conda-forge's pinning, build against netlib so the binary records
# the switchable libblas.so.3/libcblas.so.3 SONAMEs (the openblas variant
# is a symlink to libopenblas.so.0).
- name: Check libcblas is linked explicitly (Linux)
if: runner.os == 'Linux'
run: |
readelf -d build/all_tests | grep NEEDED
readelf -d build/all_tests | grep -q 'libcblas\.so\.3'

- name: Re-run tests after switching to OpenBLAS (Linux)
if: runner.os == 'Linux'
run: |
conda install -y "libblas=*=*openblas" "libcblas=*=*openblas"
cd build && ctest -C Release --output-on-failure
45 changes: 42 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,22 @@ else()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
endif()

# MSVC warnings-as-errors: on for standalone builds, off for Python builds so
# downstream toolchains (e.g. conda-forge) aren't broken by new MSVC warnings
if(SKBUILD)
set(_sde_werror_default OFF)
else()
set(_sde_werror_default ON)
endif()
option(SDE_WARNINGS_AS_ERRORS "Treat MSVC warnings as errors (/WX)"
${_sde_werror_default})

# Warning flags (compiler-specific)
if(MSVC)
add_compile_options(/W4 /WX)
add_compile_options(/W4)
if(SDE_WARNINGS_AS_ERRORS)
add_compile_options(/WX)
endif()
else()
add_compile_options(
-Wall # Enable most warnings
Expand Down Expand Up @@ -66,10 +79,36 @@ if(APPLE)
find_library(ACCELERATE_FRAMEWORK Accelerate)
target_link_libraries(dnlp_diff ${ACCELERATE_FRAMEWORK})
elseif(MSVC)
find_package(OpenBLAS CONFIG REQUIRED)
target_link_libraries(dnlp_diff OpenBLAS::OpenBLAS)
find_package(OpenBLAS CONFIG QUIET)
if(TARGET OpenBLAS::OpenBLAS)
# vcpkg
target_link_libraries(dnlp_diff OpenBLAS::OpenBLAS)
else()
# e.g. conda-forge's openblas ships openblas.lib and
# include/openblas/cblas.h, but no imported target
find_library(OPENBLAS_LIBRARY NAMES openblas libopenblas REQUIRED)
get_filename_component(_sde_openblas_libdir "${OPENBLAS_LIBRARY}" DIRECTORY)
get_filename_component(_sde_openblas_prefix "${_sde_openblas_libdir}" DIRECTORY)
find_path(OPENBLAS_INCLUDE_DIR cblas.h
HINTS "${_sde_openblas_prefix}/include"
PATH_SUFFIXES openblas REQUIRED)
target_link_libraries(dnlp_diff ${OPENBLAS_LIBRARY})
target_include_directories(dnlp_diff PUBLIC ${OPENBLAS_INCLUDE_DIR})
endif()
else()
find_package(BLAS REQUIRED)
# conda-forge splits the CBLAS interface into libcblas next to libblas. Link
# it explicitly (and before BLAS) so the package keeps working when users
# switch BLAS implementations, even if the build-time libblas exports cblas_*.
if(BLAS_LIBRARIES)
list(GET BLAS_LIBRARIES 0 _sde_blas_lib)
get_filename_component(_sde_blas_dir "${_sde_blas_lib}" DIRECTORY)
find_library(CBLAS_LIBRARY NAMES cblas
HINTS "${_sde_blas_dir}" NO_DEFAULT_PATH)
endif()
if(CBLAS_LIBRARY)
target_link_libraries(dnlp_diff ${CBLAS_LIBRARY})
endif()
target_link_libraries(dnlp_diff ${BLAS_LIBRARIES})
endif()

Expand Down
Loading