Skip to content

Commit 80d04af

Browse files
author
peng.li24
committed
refactor: single DEB for header-only lib; backend selected by consumer via NUMPYCPP_STD_ONLY
- CMakeLists.txt: one DEB (numpycpp-dev-<ver>-Linux.deb) — no flavor suffix - numpycpp-config.cmake.in: cmake config propagates -ldl (bitexact) or -DNUMPYCPP_STD_ONLY (std) automatically based on consumer's NUMPYCPP_STD_ONLY - CI: single package job builds one DEB - README: document both backends with find_package examples
1 parent 4e2b5e4 commit 80d04af

4 files changed

Lines changed: 75 additions & 56 deletions

File tree

.github/workflows/ci.yml

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
# CI: build and test both backends on every push/PR.
22
#
3-
# Two jobs run in parallel:
3+
# Two test jobs run in parallel:
44
# test-bitexact — NUMPYCPP_STD_ONLY=OFF (default)
55
# All 900 tests verify IEEE-754 bit-identical results vs numpy.
66
# Requires: numpy .so loaded (OpenBLAS + SVML dlsym), AVX-512 capable CPU.
77
#
88
# test-std — NUMPYCPP_STD_ONLY=ON
9-
# Same API, pure <cmath> + C++ loops. Tests run with atol/rtol tolerance.
9+
# Same API, pure <cmath> + C++ loops. ~423 precision-independent tests.
1010
# No external math dependencies.
1111
#
12-
# DEB packaging (flavor appears after version number in filename):
13-
# cmake -DNUMPYCPP_STD_ONLY=OFF → numpycpp-dev-<ver>-bitexact-Linux.deb
14-
# cmake -DNUMPYCPP_STD_ONLY=ON → numpycpp-dev-<ver>-std-Linux.deb
12+
# DEB packaging: single DEB (header-only — backend is consumer's compile choice)
13+
# cmake .. → numpycpp-dev-<ver>-Linux.deb
1514

1615
name: CI
1716

@@ -110,31 +109,26 @@ jobs:
110109
cd tests
111110
python -m pytest test_all.py -q --tb=short --no-header -k "not (test_unary_math or test_dot or test_norm or test_matmul or TestEinsum or test_avx512_boundary or test_nan_passthrough or test_nan_mixed or test_domain or signed_zero or _inf or test_sign_inf)"
112111
113-
# ── Job 3: package both DEBs ─────────────────────────────────────────────────
112+
# ── Job 3: package DEB ───────────────────────────────────────────────────────
114113
package:
115-
name: Package DEBs
114+
name: Package DEB
116115
runs-on: ubuntu-latest
117116
needs: [test-bitexact, test-std]
118117

119118
steps:
120119
- uses: actions/checkout@v4
121120

122-
- name: Build bitexact DEB
121+
- name: Build DEB
122+
# Header-only: both backends ship in one DEB.
123+
# Backend is selected by the consumer at cmake configure time
124+
# via -DNUMPYCPP_STD_ONLY=ON/OFF.
123125
run: |
124-
cmake -S . -B build_bitexact -DNUMPYCPP_STD_ONLY=OFF
125-
cmake --build build_bitexact --target deb
126-
ls build_bitexact/numpycpp-dev-*-bitexact-*.deb
126+
cmake -S . -B build_deb
127+
cmake --build build_deb --target deb
128+
ls build_deb/numpycpp-dev-*.deb
127129
128-
- name: Build std DEB
129-
run: |
130-
cmake -S . -B build_std_deb -DNUMPYCPP_STD_ONLY=ON
131-
cmake --build build_std_deb --target deb
132-
ls build_std_deb/numpycpp-dev-*-std-*.deb
133-
134-
- name: Upload DEBs as artifacts
130+
- name: Upload DEB as artifact
135131
uses: actions/upload-artifact@v4
136132
with:
137-
name: numpycpp-debs
138-
path: |
139-
build_bitexact/*.deb
140-
build_std_deb/*.deb
133+
name: numpycpp-dev-deb
134+
path: build_deb/numpycpp-dev-*.deb

CMakeLists.txt

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,31 +79,21 @@ install(DIRECTORY example/
7979
)
8080

8181
# ---- CPack DEB packaging ----------------------------------------------------
82-
# Package name and file name differ by backend flavor.
83-
# File naming: numpycpp-dev-<version>-<flavor>-Linux.deb
84-
# bitexact: numpycpp-dev-1.21.2-bitexact-Linux.deb
85-
# std: numpycpp-dev-1.21.2-std-Linux.deb
86-
# Package name keeps the flavor so both DEBs can coexist on the same machine.
87-
if(NUMPYCPP_STD_ONLY)
88-
set(_NUMPYCPP_DEB_FLAVOR "std")
89-
set(_NUMPYCPP_DEB_SUMMARY "C++ numpy-API header-only library — pure std backend (perf-first)")
90-
else()
91-
set(_NUMPYCPP_DEB_FLAVOR "bitexact")
92-
set(_NUMPYCPP_DEB_SUMMARY "C++ numpy-API header-only library — bit-exact backend (SVML + BLAS)")
93-
endif()
94-
95-
set(CPACK_PACKAGE_NAME "numpycpp-dev-${_NUMPYCPP_DEB_FLAVOR}")
82+
# Single DEB — header-only library, backend is a consumer compile-time choice.
83+
# File: numpycpp-dev-<version>-Linux.deb
84+
set(CPACK_PACKAGE_NAME "numpycpp-dev")
9685
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
9786
set(CPACK_PACKAGE_VENDOR "miaobyte")
9887
set(CPACK_PACKAGE_CONTACT "miaobyte")
99-
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${_NUMPYCPP_DEB_SUMMARY}")
88+
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
89+
"C++ numpy-API header-only library (bitexact + std backends, C++17)")
10090
set(CPACK_GENERATOR "DEB")
10191
set(CPACK_DEB_COMPONENT_INSTALL OFF)
10292
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "miaobyte")
10393
set(CPACK_DEBIAN_PACKAGE_SECTION "libdevel")
10494
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
10595
set(CPACK_DEBIAN_FILE_NAME
106-
"numpycpp-dev-${CPACK_PACKAGE_VERSION}-${_NUMPYCPP_DEB_FLAVOR}-Linux.deb")
96+
"numpycpp-dev-${CPACK_PACKAGE_VERSION}-Linux.deb")
10797
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
10898
# Wipe the old include tree on install/upgrade so stale headers cannot accumulate.
10999
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/preinst")
@@ -113,7 +103,7 @@ include(CPack)
113103
# Convenience target
114104
add_custom_target(deb
115105
COMMAND cpack -G DEB
116-
COMMENT "Packaging numpycpp-dev-${_NUMPYCPP_DEB_FLAVOR}-${CPACK_PACKAGE_VERSION}-Linux.deb"
106+
COMMENT "Packaging numpycpp-dev-${CPACK_PACKAGE_VERSION}-Linux.deb"
117107
)
118108

119109
message(STATUS "numpycpp v${PROJECT_VERSION} (header-only C++ library)")

README.md

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,34 +69,50 @@ Download the [latest `.deb` release](https://github.com/array2d/numpycpp/release
6969
7070
```bash
7171
mkdir build && cd build
72-
cmake .. -DCMAKE_BUILD_TYPE=Release
72+
cmake ..
7373
make deb
7474
sudo dpkg -i numpycpp-dev-*.deb
7575
```
7676

77-
Headers are installed to `/usr/include/numpycpp/` along with CMake config. Consuming projects use:
77+
Headers are installed to `/usr/include/numpycpp/` along with a CMake config that supports both backends.
78+
79+
**CMake — bitexact backend (default)**
80+
81+
```cmake
82+
find_package(numpycpp REQUIRED)
83+
target_link_libraries(myapp PRIVATE numpycpp::numpycpp)
84+
# cmake propagates -ldl automatically — no extra flags needed
85+
```
86+
87+
**CMake — std backend**
7888

7989
```cmake
90+
set(NUMPYCPP_STD_ONLY ON) # set BEFORE find_package
8091
find_package(numpycpp REQUIRED)
8192
target_link_libraries(myapp PRIVATE numpycpp::numpycpp)
93+
# cmake propagates -DNUMPYCPP_STD_ONLY automatically — no extra flags needed
8294
```
8395

8496
**pybind11_add_module users**
8597

8698
With certain CMake / pybind11 version combinations, `pybind11_add_module` may lose IMPORTED targets
87-
during generation (target exists at configure time but disappears at generate time).
88-
If you hit this, use the variables-based fallback:
99+
during generation. If you hit this, use the variables-based fallback:
89100

90101
```cmake
102+
set(NUMPYCPP_STD_ONLY OFF) # or ON for std backend
91103
find_package(numpycpp REQUIRED)
92104
pybind11_add_module(mymodule module.cpp)
93105
target_include_directories(mymodule PRIVATE ${numpycpp_INCLUDE_DIRS})
94106
target_compile_features(mymodule PRIVATE cxx_std_17)
107+
# bitexact: add manually → target_link_libraries(mymodule PRIVATE dl)
108+
# std: add manually → target_compile_definitions(mymodule PRIVATE NUMPYCPP_STD_ONLY)
95109
```
96110

97111
**Manual (header-only)**
98112

99113
Add `-Ipath/to/numpycpp` to your compiler flags and include the headers directly. No build step, no copy required.
114+
- Bitexact backend: add `-ldl` at link time (no other flags needed at `-O2`; see compiler flags table below)
115+
- Std backend: add `-DNUMPYCPP_STD_ONLY` (no `-ldl` needed)
100116

101117
### Testing
102118

@@ -119,6 +135,10 @@ numpycpp ships two interchangeable math backends selected via a single cmake fla
119135
All public APIs (`numpy::exp`, `numpy::dot`, `numpy::einsum`, …) are identical;
120136
only the internal implementation and precision guarantee differ.
121137

138+
The library is **header-only** — both backends live in the same installed headers.
139+
The backend is a **consumer compile-time choice**, not an install-time choice.
140+
One DEB installs everything; `NUMPYCPP_STD_ONLY` selects the backend.
141+
122142
```cmake
123143
cmake -DNUMPYCPP_STD_ONLY=OFF .. # default — bit-exact backend
124144
cmake -DNUMPYCPP_STD_ONLY=ON .. # std / performance-first backend
@@ -130,7 +150,7 @@ cmake -DNUMPYCPP_STD_ONLY=ON .. # std / performance-first backend
130150
| **Dot / matmul** | OpenBLAS ILP64 via `dlsym` | Pure C++ loops (auto-vectorised) |
131151
| **Precision vs numpy** | **IEEE 754 bit-identical** | 0–2 ULP (not bit-exact) |
132152
| **External deps** | libdl + numpy `.so` loaded | **None** — pure C++17 |
133-
| **DEB package** | `numpycpp-dev-<ver>-bitexact-Linux.deb` | `numpycpp-dev-<ver>-std-Linux.deb` |
153+
| **DEB package** | same `numpycpp-dev-<ver>-Linux.deb` | same `numpycpp-dev-<ver>-Linux.deb` |
134154
| **cmake propagation** | `target_link_libraries(… dl)` | `target_compile_definitions(… NUMPYCPP_STD_ONLY)` |
135155

136156
#### Compiler flags — bitexact backend (`NUMPYCPP_STD_ONLY=OFF`)
@@ -169,7 +189,7 @@ target_compile_options(<target> PRIVATE
169189

170190
| Flag | Status | Why |
171191
|------|:------:|-----|
172-
| `NUMPYCPP_STD_ONLY` | **required** | Selects `std_math_backend.h` + `std_linalg_backend.h` instead of SVML/BLAS bridges. Propagated automatically when using `find_package(numpycpp)` with the std DEB. |
192+
| `NUMPYCPP_STD_ONLY` | **required** | Selects `std_math_backend.h` + `std_linalg_backend.h` instead of SVML/BLAS bridges. Set via `cmake -DNUMPYCPP_STD_ONLY=ON` or `set(NUMPYCPP_STD_ONLY ON)` before `find_package`. |
173193
| `-O3 -march=native` | recommended | Enables full auto-vectorisation of the C++ loops (exp/dot/gemm). Without optimisation, std backend is slow. |
174194
| `-ffp-contract=off` | **not needed** | FMA contraction is welcome in std mode — improves precision and performance of gemm/dot. |
175195
| `-mavx512f -mprefer-vector-width=256` | **not needed** | SVML bridge not compiled in; no ZMM-trap risk. `-march=native` selects appropriate SIMD automatically. |

numpycpp-config.cmake.in

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
@PACKAGE_INIT@
22

3-
# Manually create the imported target (no auto-generated targets file).
4-
# This avoids cmake_policy(PUSH/POP) scope issues present in CMake's
5-
# install(EXPORT)-generated targets files on CMake >= 3.20.
3+
# numpycpp -- header-only C++ numpy API library.
64
#
7-
# Use a non-namespaced INTERFACE IMPORTED target as the real target,
8-
# plus a namespaced ALIAS for conventional CMake usage. pybind11_add_module
9-
# has a CMake-level bug with namespaced (::) targets during generation,
10-
# so users hitting that can link against the bare "numpycpp" target instead.
5+
# Backend selection (set BEFORE find_package, or via cmake -D):
6+
# cmake -DNUMPYCPP_STD_ONLY=OFF .. # bitexact (default): SVML + OpenBLAS via dlsym
7+
# cmake -DNUMPYCPP_STD_ONLY=ON .. # std: pure <cmath> + C++ loops, no dlsym
8+
#
9+
# The flag is propagated automatically to every target that links numpycpp:
10+
# bitexact → INTERFACE_LINK_LIBRARIES dl
11+
# std → INTERFACE_COMPILE_DEFINITIONS NUMPYCPP_STD_ONLY
12+
13+
# Non-namespaced + namespaced alias.
14+
# pybind11_add_module users: if the :: target is lost during generation, use
15+
# target_include_directories(mymod PRIVATE ${numpycpp_INCLUDE_DIRS})
1116
add_library(numpycpp INTERFACE IMPORTED)
1217

1318
set_target_properties(numpycpp PROPERTIES
@@ -18,10 +23,20 @@ set_target_properties(numpycpp PROPERTIES
1823

1924
add_library(numpycpp::numpycpp ALIAS numpycpp)
2025

21-
# Fallback variables for environments where CMake targets are unreliable
22-
# (e.g. pybind11_add_module may lose targets during generation with
23-
# certain CMake / pybind11 version combinations).
24-
# Usage: target_include_directories(mymod PRIVATE ${numpycpp_INCLUDE_DIRS})
26+
# Propagate the correct backend flags to every consumer automatically.
27+
if(NUMPYCPP_STD_ONLY)
28+
# std backend: no external dependencies
29+
set_property(TARGET numpycpp APPEND PROPERTY
30+
INTERFACE_COMPILE_DEFINITIONS NUMPYCPP_STD_ONLY)
31+
message(STATUS "numpycpp: std backend (pure <cmath>, no dlsym)")
32+
else()
33+
# bitexact backend (default): libdl needed for dlsym -> numpy .so at runtime
34+
set_property(TARGET numpycpp APPEND PROPERTY
35+
INTERFACE_LINK_LIBRARIES dl)
36+
message(STATUS "numpycpp: bitexact backend (SVML + OpenBLAS via dlsym, -ldl)")
37+
endif()
38+
39+
# Fallback variables (pybind11_add_module target-loss workaround)
2540
set(numpycpp_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/include/numpycpp")
2641

2742
check_required_components(numpycpp)

0 commit comments

Comments
 (0)