samcore 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- samcore-0.1.0/.github/actions/setup-conda-toolchain/action.yml +93 -0
- samcore-0.1.0/.github/workflows/tests.yml +69 -0
- samcore-0.1.0/.github/workflows/wheels.yml +183 -0
- samcore-0.1.0/.gitignore +40 -0
- samcore-0.1.0/.gitmodules +6 -0
- samcore-0.1.0/CMakeLists.txt +254 -0
- samcore-0.1.0/LICENSE +21 -0
- samcore-0.1.0/PKG-INFO +220 -0
- samcore-0.1.0/README.md +207 -0
- samcore-0.1.0/cmake/_version.py.in +2 -0
- samcore-0.1.0/cmake/dependencies.cmake +44 -0
- samcore-0.1.0/cmake/samcoreConfig.cmake.in +9 -0
- samcore-0.1.0/cmake/stubgen.cmake +46 -0
- samcore-0.1.0/default.nix +32 -0
- samcore-0.1.0/executables/CMakeLists.txt +5 -0
- samcore-0.1.0/executables/bench.cpp +70 -0
- samcore-0.1.0/executables/gen_data.cpp +287 -0
- samcore-0.1.0/external/pocketfft/README.md +277 -0
- samcore-0.1.0/external/pocketfft/meson.build +12 -0
- samcore-0.1.0/external/pocketfft/pocketfft_demo.cc +87 -0
- samcore-0.1.0/external/pocketfft/pocketfft_hdronly.h +3853 -0
- samcore-0.1.0/include/samcore/array.hpp +153 -0
- samcore-0.1.0/include/samcore/io/fileio.hpp +73 -0
- samcore-0.1.0/include/samcore/preprocessing.hpp +48 -0
- samcore-0.1.0/include/samcore/sam_dataset.hpp +164 -0
- samcore-0.1.0/include/samcore/sam_header.hpp +58 -0
- samcore-0.1.0/include/samcore/sam_labels.hpp +114 -0
- samcore-0.1.0/include/samcore/sam_scan.hpp +215 -0
- samcore-0.1.0/include/samcore/samcore.hpp +12 -0
- samcore-0.1.0/include/samcore/signal/kernels.hpp +106 -0
- samcore-0.1.0/include/samcore/utils.hpp +44 -0
- samcore-0.1.0/pyproject.toml +70 -0
- samcore-0.1.0/shell.nix +33 -0
- samcore-0.1.0/src/bindings/common.hpp +172 -0
- samcore-0.1.0/src/bindings/dataset.cpp +361 -0
- samcore-0.1.0/src/bindings/header.cpp +207 -0
- samcore-0.1.0/src/bindings/labels.cpp +225 -0
- samcore-0.1.0/src/bindings/module.cpp +18 -0
- samcore-0.1.0/src/bindings/scan.cpp +546 -0
- samcore-0.1.0/src/bindings/submodules.cpp +310 -0
- samcore-0.1.0/src/io/fileio_h5sam.cpp +297 -0
- samcore-0.1.0/src/io/fileio_h5samd.cpp +349 -0
- samcore-0.1.0/src/io/h5_common.hpp +241 -0
- samcore-0.1.0/src/io/h5_lazy.hpp +38 -0
- samcore-0.1.0/src/preprocessing.cpp +350 -0
- samcore-0.1.0/src/sam_dataset.cpp +314 -0
- samcore-0.1.0/src/sam_header.cpp +82 -0
- samcore-0.1.0/src/sam_labels.cpp +399 -0
- samcore-0.1.0/src/sam_scan.cpp +720 -0
- samcore-0.1.0/src/samcore/__init__.py +36 -0
- samcore-0.1.0/src/samcore/_dataset.py +549 -0
- samcore-0.1.0/src/samcore/_io.py +104 -0
- samcore-0.1.0/src/samcore/_labels.py +152 -0
- samcore-0.1.0/src/samcore/_scan.py +348 -0
- samcore-0.1.0/src/signal/filters.cpp +517 -0
- samcore-0.1.0/src/signal/spectral.cpp +302 -0
- samcore-0.1.0/src/utils.cpp +160 -0
- samcore-0.1.0/tests/CMakeLists.txt +33 -0
- samcore-0.1.0/tests/data/testdata.h5sam +0 -0
- samcore-0.1.0/tests/data/testdata.h5samd +0 -0
- samcore-0.1.0/tests/python/conftest.py +46 -0
- samcore-0.1.0/tests/python/test_core.py +547 -0
- samcore-0.1.0/tests/python/test_dataset.py +1482 -0
- samcore-0.1.0/tests/python/test_labels.py +891 -0
- samcore-0.1.0/tests/python/test_parity.py +399 -0
- samcore-0.1.0/tests/python/test_rotate_mirror.py +274 -0
- samcore-0.1.0/tests/python/test_signal.py +159 -0
- samcore-0.1.0/tests/test_array.cpp +61 -0
- samcore-0.1.0/tests/test_header.cpp +66 -0
- samcore-0.1.0/tests/test_io_h5sam.cpp +175 -0
- samcore-0.1.0/tests/test_io_h5samd.cpp +205 -0
- samcore-0.1.0/tests/test_labels.cpp +208 -0
- samcore-0.1.0/tests/test_omp_parity.cpp +89 -0
- samcore-0.1.0/tests/test_preprocessing.cpp +138 -0
- samcore-0.1.0/tests/test_sam_scan.cpp +188 -0
- samcore-0.1.0/tests/test_sam_scan_ops.cpp +317 -0
- samcore-0.1.0/tests/test_signal.cpp +131 -0
- samcore-0.1.0/tests/test_utils.cpp +118 -0
- samcore-0.1.0/version.txt +1 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Set up Clang/conda toolchain (Windows & macOS)
|
|
2
|
+
description: >-
|
|
3
|
+
Install Clang, the LLVM OpenMP runtime and HDF5 from conda-forge for
|
|
4
|
+
building samcore on Windows and macOS.
|
|
5
|
+
|
|
6
|
+
# samcore's OpenMP loops use unsigned (size_t) loop indices, which MSVC's
|
|
7
|
+
# OpenMP 2.0 implementation rejects with error C3016. GCC/Clang accept them,
|
|
8
|
+
# so the README recommends LLVM Clang, and this mirrors it: conda-forge
|
|
9
|
+
# provides Clang, llvm-openmp and HDF5 in one ABI-consistent environment.
|
|
10
|
+
#
|
|
11
|
+
# Windows: conda-forge does NOT ship the Windows SDK import libraries
|
|
12
|
+
# (kernel32.lib, ...) - the `vc`/`ucrt` packages only contain the runtime.
|
|
13
|
+
# clang and lld-link trust %INCLUDE%/%LIB% when set and then skip
|
|
14
|
+
# auto-detecting the SDK, so the MSVC developer environment must be
|
|
15
|
+
# initialized as well and the conda prefix prepended to it.
|
|
16
|
+
#
|
|
17
|
+
# macOS: AppleClang has no OpenMP, hence the conda-forge clang + libomp.
|
|
18
|
+
#
|
|
19
|
+
# On Linux runners every step below is a no-op.
|
|
20
|
+
|
|
21
|
+
runs:
|
|
22
|
+
using: composite
|
|
23
|
+
steps:
|
|
24
|
+
# Initializes the MSVC + Windows SDK %INCLUDE%/%LIB%/%PATH% environment.
|
|
25
|
+
- name: Initialize MSVC environment (Windows)
|
|
26
|
+
if: runner.os == 'Windows'
|
|
27
|
+
uses: ilammy/msvc-dev-cmd@v1
|
|
28
|
+
with:
|
|
29
|
+
arch: x64
|
|
30
|
+
|
|
31
|
+
- name: Create conda-forge toolchain environment
|
|
32
|
+
if: runner.os != 'Linux'
|
|
33
|
+
id: conda
|
|
34
|
+
uses: mamba-org/setup-micromamba@v3
|
|
35
|
+
with:
|
|
36
|
+
environment-name: samcore-build
|
|
37
|
+
create-args: >-
|
|
38
|
+
clangxx
|
|
39
|
+
llvm-openmp
|
|
40
|
+
hdf5=1.14
|
|
41
|
+
cmake
|
|
42
|
+
ninja
|
|
43
|
+
init-shell: none
|
|
44
|
+
cache-environment: true
|
|
45
|
+
|
|
46
|
+
- name: Export toolchain env (Windows)
|
|
47
|
+
if: runner.os == 'Windows'
|
|
48
|
+
shell: pwsh
|
|
49
|
+
run: |
|
|
50
|
+
$prefix = "${{ steps.conda.outputs.environment-path }}"
|
|
51
|
+
if (-not $env:INCLUDE -or -not $env:LIB) {
|
|
52
|
+
throw "MSVC environment (INCLUDE/LIB) was not initialized by the previous step"
|
|
53
|
+
}
|
|
54
|
+
"$prefix\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
|
|
55
|
+
"$prefix\Library\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
|
|
56
|
+
# Prepend the conda prefix to the MSVC-provided INCLUDE/LIB so the
|
|
57
|
+
# build finds omp.h / libomp / HDF5 while the Windows SDK import libs
|
|
58
|
+
# stay on the search path. LTO is disabled because clang's -flto
|
|
59
|
+
# needs lld-link on Windows (link.exe cannot consume LLVM bitcode).
|
|
60
|
+
@(
|
|
61
|
+
"HDF5_ROOT=$prefix\Library"
|
|
62
|
+
"CMAKE_PREFIX_PATH=$prefix\Library"
|
|
63
|
+
"INCLUDE=$prefix\Library\include;$env:INCLUDE"
|
|
64
|
+
"LIB=$prefix\Library\lib;$env:LIB"
|
|
65
|
+
"CC=$prefix\Library\bin\clang.exe"
|
|
66
|
+
"CXX=$prefix\Library\bin\clang++.exe"
|
|
67
|
+
"CMAKE_ARGS=-G Ninja -DSAMCORE_ENABLE_LTO=OFF"
|
|
68
|
+
) | Out-File -FilePath $env:GITHUB_ENV -Append
|
|
69
|
+
Write-Host "Windows toolchain prefix: $prefix"
|
|
70
|
+
|
|
71
|
+
- name: Export toolchain env (macOS)
|
|
72
|
+
if: runner.os == 'macOS'
|
|
73
|
+
shell: bash
|
|
74
|
+
run: |
|
|
75
|
+
prefix="${{ steps.conda.outputs.environment-path }}"
|
|
76
|
+
if [ ! -x "$prefix/bin/clang++" ]; then
|
|
77
|
+
echo "clang++ not found in $prefix/bin" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
echo "$prefix/bin" >> "$GITHUB_PATH"
|
|
81
|
+
# CPATH/LIBRARY_PATH let the build find omp.h/libomp/HDF5 while
|
|
82
|
+
# CMAKE_PREFIX_PATH/HDF5_ROOT cover CMake's find_package.
|
|
83
|
+
{
|
|
84
|
+
echo "CC=$prefix/bin/clang"
|
|
85
|
+
echo "CXX=$prefix/bin/clang++"
|
|
86
|
+
echo "HDF5_ROOT=$prefix"
|
|
87
|
+
echo "CMAKE_PREFIX_PATH=$prefix"
|
|
88
|
+
echo "CPATH=$prefix/include"
|
|
89
|
+
echo "LIBRARY_PATH=$prefix/lib"
|
|
90
|
+
echo "SDKROOT=$(xcrun --show-sdk-path)"
|
|
91
|
+
echo "CMAKE_ARGS=-G Ninja"
|
|
92
|
+
} >> "$GITHUB_ENV"
|
|
93
|
+
echo "macOS toolchain prefix: $prefix"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
python-tests:
|
|
11
|
+
name: Python ${{ matrix.python }} on ${{ matrix.os }}
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
17
|
+
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
with:
|
|
21
|
+
submodules: recursive
|
|
22
|
+
|
|
23
|
+
- name: Install HDF5 (Linux)
|
|
24
|
+
if: runner.os == 'Linux'
|
|
25
|
+
run: sudo apt-get update && sudo apt-get -y install libhdf5-dev ninja-build
|
|
26
|
+
|
|
27
|
+
# Windows/macOS use the conda-forge LLVM toolchain (as the README
|
|
28
|
+
# recommends): MSVC's OpenMP 2.0 rejects the unsigned loop indices
|
|
29
|
+
# (error C3016) and AppleClang has no OpenMP. The action also installs
|
|
30
|
+
# HDF5 and llvm-openmp and exports the compiler env.
|
|
31
|
+
- uses: ./.github/actions/setup-conda-toolchain
|
|
32
|
+
|
|
33
|
+
- name: Set up Python
|
|
34
|
+
uses: actions/setup-python@v7
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python }}
|
|
37
|
+
|
|
38
|
+
- name: Build and install package
|
|
39
|
+
run: |
|
|
40
|
+
python -m pip install --upgrade pip
|
|
41
|
+
pip install .[test]
|
|
42
|
+
|
|
43
|
+
- name: Run Python tests
|
|
44
|
+
env:
|
|
45
|
+
SAMCORE_TEST_DATA_DIR: tests/data
|
|
46
|
+
run: pytest tests/python -v
|
|
47
|
+
|
|
48
|
+
cpp-tests:
|
|
49
|
+
name: C++ (sanitizers) on ${{ matrix.os }}
|
|
50
|
+
runs-on: ${{ matrix.os }}
|
|
51
|
+
strategy:
|
|
52
|
+
fail-fast: false
|
|
53
|
+
matrix:
|
|
54
|
+
os: [ubuntu-latest]
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v7
|
|
57
|
+
with:
|
|
58
|
+
submodules: recursive
|
|
59
|
+
|
|
60
|
+
- name: Install HDF5
|
|
61
|
+
run: sudo apt-get update && sudo apt-get -y install libhdf5-dev ninja-build
|
|
62
|
+
|
|
63
|
+
- name: Configure, build, test (debug + sanitizers)
|
|
64
|
+
run: |
|
|
65
|
+
cmake -B build-debug -G Ninja -DCMAKE_BUILD_TYPE=Debug
|
|
66
|
+
cmake --build build-debug
|
|
67
|
+
ASAN_OPTIONS=detect_leaks=1:halt_on_error=1 \
|
|
68
|
+
UBSAN_OPTIONS=halt_on_error=1 \
|
|
69
|
+
ctest --test-dir build-debug --output-on-failure
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
name: Build wheels & attach to release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
- "ci-test*"
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build-wheels:
|
|
15
|
+
name: Build wheel on ${{ matrix.os }}
|
|
16
|
+
runs-on: ${{ matrix.os }}
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
os:
|
|
21
|
+
- ubuntu-latest # manylinux/musllinux x86_64
|
|
22
|
+
- ubuntu-24.04-arm # manylinux/musllinux aarch64
|
|
23
|
+
- macos-latest
|
|
24
|
+
- windows-latest
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v7
|
|
28
|
+
with:
|
|
29
|
+
submodules: recursive
|
|
30
|
+
|
|
31
|
+
- uses: actions/setup-python@v7
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
|
|
35
|
+
- name: Install cibuildwheel
|
|
36
|
+
run: python -m pip install --upgrade pip cibuildwheel
|
|
37
|
+
|
|
38
|
+
# HDF5 is required (find_package(HDF5 COMPONENTS CXX REQUIRED)). On
|
|
39
|
+
# Linux it is installed inside the manylinux/musllinux container (see
|
|
40
|
+
# CIBW_BEFORE_ALL_LINUX below). On Windows and macOS the conda-forge
|
|
41
|
+
# toolchain action installs HDF5, Clang and llvm-openmp and exports the
|
|
42
|
+
# compiler env (Clang is required on Windows because MSVC's OpenMP 2.0
|
|
43
|
+
# rejects the unsigned loop indices, error C3016; on macOS because
|
|
44
|
+
# AppleClang has no OpenMP).
|
|
45
|
+
- uses: ./.github/actions/setup-conda-toolchain
|
|
46
|
+
|
|
47
|
+
- name: Build wheels
|
|
48
|
+
run: python -m cibuildwheel --output-dir wheelhouse
|
|
49
|
+
env:
|
|
50
|
+
CIBW_BUILD: cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*
|
|
51
|
+
CIBW_ARCHS_LINUX: auto
|
|
52
|
+
CIBW_ARCHS_MACOS: auto
|
|
53
|
+
# 64-bit only: the LLVM clang GNU driver cannot target 32-bit
|
|
54
|
+
# Windows against the win32 CPython the way MSVC's vcvars does.
|
|
55
|
+
CIBW_ARCHS_WINDOWS: AMD64
|
|
56
|
+
# HDF5 + ninja inside the Linux containers. CIBW_BEFORE_ALL_* runs
|
|
57
|
+
# once per container (before the whole CPython matrix), so the
|
|
58
|
+
# system packages are not re-installed for every Python version.
|
|
59
|
+
# manylinux is RHEL-based (yum on manylinux2014, dnf on
|
|
60
|
+
# manylinux_2_28+); musllinux is Alpine and uses apk, where HDF5's
|
|
61
|
+
# C++ headers ship in hdf5-dev (community repo).
|
|
62
|
+
CIBW_BEFORE_ALL_LINUX: |
|
|
63
|
+
if command -v apk >/dev/null 2>&1; then
|
|
64
|
+
apk add --no-cache --repository "https://dl-cdn.alpinelinux.org/alpine/v$(cut -d. -f1,2 /etc/alpine-release)/community" build-base cmake hdf5-dev ninja
|
|
65
|
+
elif command -v dnf >/dev/null 2>&1; then
|
|
66
|
+
dnf install -y hdf5-devel ninja-build
|
|
67
|
+
else
|
|
68
|
+
yum install -y hdf5-devel ninja-build
|
|
69
|
+
fi
|
|
70
|
+
# numpy is intentionally NOT installed here: pip's build isolation
|
|
71
|
+
# pulls it from [build-system].requires in pyproject.toml, and the
|
|
72
|
+
# wheel test env gets it as the package's declared dependency.
|
|
73
|
+
# Windows: bundle the HDF5/OpenMP runtime DLLs into the wheel
|
|
74
|
+
# (delvewheel is installed into the build env; HDF5_ROOT is exported
|
|
75
|
+
# by the toolchain action above). Commands on Windows run under
|
|
76
|
+
# cmd.exe, so use %VAR% syntax here, not PowerShell's $env:VAR.
|
|
77
|
+
CIBW_BEFORE_BUILD_WINDOWS: python -m pip install delvewheel
|
|
78
|
+
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >-
|
|
79
|
+
delvewheel repair --add-path "%HDF5_ROOT%\bin" -w {dest_dir} {wheel}
|
|
80
|
+
CIBW_TEST_REQUIRES: pytest
|
|
81
|
+
CIBW_TEST_COMMAND: >-
|
|
82
|
+
pytest {project}/tests/python -v -p no:cacheprovider
|
|
83
|
+
|
|
84
|
+
- name: Show artifacts
|
|
85
|
+
shell: bash
|
|
86
|
+
run: ls -lh wheelhouse
|
|
87
|
+
|
|
88
|
+
- uses: actions/upload-artifact@v7
|
|
89
|
+
with:
|
|
90
|
+
name: wheels-${{ matrix.os }}
|
|
91
|
+
path: wheelhouse/*.whl
|
|
92
|
+
|
|
93
|
+
build-sdist:
|
|
94
|
+
name: Build sdist
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/checkout@v7
|
|
98
|
+
with:
|
|
99
|
+
submodules: recursive
|
|
100
|
+
|
|
101
|
+
- uses: actions/setup-python@v7
|
|
102
|
+
with:
|
|
103
|
+
python-version: "3.12"
|
|
104
|
+
|
|
105
|
+
- name: Install HDF5
|
|
106
|
+
run: |
|
|
107
|
+
sudo apt-get update
|
|
108
|
+
sudo apt-get -y install libhdf5-dev ninja-build
|
|
109
|
+
|
|
110
|
+
- name: Build sdist
|
|
111
|
+
run: |
|
|
112
|
+
python -m pip install --upgrade pip build
|
|
113
|
+
python -m build --sdist --outdir dist
|
|
114
|
+
|
|
115
|
+
- uses: actions/upload-artifact@v7
|
|
116
|
+
with:
|
|
117
|
+
name: sdist
|
|
118
|
+
path: dist/*.tar.gz
|
|
119
|
+
|
|
120
|
+
publish:
|
|
121
|
+
name: Attach artifacts to GitHub Release
|
|
122
|
+
needs: [build-wheels, build-sdist]
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
# Run for every tag this workflow is triggered by ("v*" and "ci-test*").
|
|
125
|
+
# A tag-less run (workflow_dispatch on a branch) skips this job.
|
|
126
|
+
# Note: a job skipped via `if` cannot be re-run on its own, so the CI
|
|
127
|
+
# test tags must be allowed here too, otherwise publish stays skipped.
|
|
128
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
129
|
+
permissions:
|
|
130
|
+
contents: write
|
|
131
|
+
steps:
|
|
132
|
+
- uses: actions/download-artifact@v8
|
|
133
|
+
with:
|
|
134
|
+
pattern: wheels-*
|
|
135
|
+
path: dist
|
|
136
|
+
merge-multiple: true
|
|
137
|
+
|
|
138
|
+
- uses: actions/download-artifact@v8
|
|
139
|
+
with:
|
|
140
|
+
name: sdist
|
|
141
|
+
path: dist
|
|
142
|
+
|
|
143
|
+
- name: List artifacts
|
|
144
|
+
run: ls -lh dist
|
|
145
|
+
|
|
146
|
+
- name: Attach wheels to GitHub Release
|
|
147
|
+
uses: softprops/action-gh-release@v3
|
|
148
|
+
with:
|
|
149
|
+
files: dist/*
|
|
150
|
+
# ci-test* releases are marked prerelease so they never become
|
|
151
|
+
# the repository's "Latest" release.
|
|
152
|
+
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
|
153
|
+
|
|
154
|
+
publish-pypi:
|
|
155
|
+
name: Publish to PyPI
|
|
156
|
+
needs: [build-wheels, build-sdist]
|
|
157
|
+
runs-on: ubuntu-latest
|
|
158
|
+
# Real releases only - the ci-test* tags must never reach PyPI.
|
|
159
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
160
|
+
# Must match the "Environment name" configured for the trusted publisher
|
|
161
|
+
# on PyPI (see README). Add required reviewers here for a manual gate.
|
|
162
|
+
environment:
|
|
163
|
+
name: pypi
|
|
164
|
+
url: https://pypi.org/p/samcore
|
|
165
|
+
permissions:
|
|
166
|
+
id-token: write # OIDC trusted publishing (no API token/secret)
|
|
167
|
+
contents: read
|
|
168
|
+
steps:
|
|
169
|
+
- uses: actions/download-artifact@v8
|
|
170
|
+
with:
|
|
171
|
+
pattern: wheels-*
|
|
172
|
+
path: dist
|
|
173
|
+
merge-multiple: true
|
|
174
|
+
|
|
175
|
+
- uses: actions/download-artifact@v8
|
|
176
|
+
with:
|
|
177
|
+
name: sdist
|
|
178
|
+
path: dist
|
|
179
|
+
|
|
180
|
+
# Trusted Publishing: authenticates via the GitHub OIDC token. PEP 740
|
|
181
|
+
# attestations are enabled by default. `dist/` holds the wheels + sdist.
|
|
182
|
+
- name: Publish to PyPI
|
|
183
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
samcore-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# build artifacts
|
|
2
|
+
build/
|
|
3
|
+
build-*/
|
|
4
|
+
_skbuild/
|
|
5
|
+
dist/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
*.so
|
|
8
|
+
*.dylib
|
|
9
|
+
*.dll
|
|
10
|
+
*.whl
|
|
11
|
+
result
|
|
12
|
+
|
|
13
|
+
# python
|
|
14
|
+
__pycache__/
|
|
15
|
+
*.py[cod]
|
|
16
|
+
.venv/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
|
|
19
|
+
# nix
|
|
20
|
+
.nix-shell/
|
|
21
|
+
|
|
22
|
+
# test data: keep the sample testdata files, ignore everything else
|
|
23
|
+
tests/data/*
|
|
24
|
+
!tests/data/testdata.h5sam
|
|
25
|
+
!tests/data/testdata.h5samd
|
|
26
|
+
|
|
27
|
+
# All markdown/text except README.md and version.txt
|
|
28
|
+
*.md
|
|
29
|
+
*.txt
|
|
30
|
+
!version.txt
|
|
31
|
+
!README.md
|
|
32
|
+
|
|
33
|
+
# Notebooks
|
|
34
|
+
*.ipynb
|
|
35
|
+
|
|
36
|
+
# Images
|
|
37
|
+
*.tiff
|
|
38
|
+
*.tif
|
|
39
|
+
*.TIFF
|
|
40
|
+
*.png
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
[submodule "external/pocketfft"]
|
|
2
|
+
path = external/pocketfft
|
|
3
|
+
# The upstream host (gitlab.mpcdf.mpg.de) is often unreachable from CI,
|
|
4
|
+
# so use the original author's GitHub mirror of the same repository. The
|
|
5
|
+
# pinned commit is the tip of its `cpp` branch and is byte-identical.
|
|
6
|
+
url = https://github.com/mreineck/pocketfft.git
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.22)
|
|
2
|
+
|
|
3
|
+
# Read version from version.txt; register it as a configure dependency so
|
|
4
|
+
# the build re-runs automatically when the file changes.
|
|
5
|
+
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
|
6
|
+
"${CMAKE_SOURCE_DIR}/version.txt")
|
|
7
|
+
file(READ "${CMAKE_SOURCE_DIR}/version.txt" VERSION_STRING)
|
|
8
|
+
string(STRIP "${VERSION_STRING}" PROJECT_VERSION)
|
|
9
|
+
|
|
10
|
+
project(samcore VERSION ${PROJECT_VERSION} LANGUAGES CXX)
|
|
11
|
+
|
|
12
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
13
|
+
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|
14
|
+
set(CMAKE_CXX_EXTENSIONS Off)
|
|
15
|
+
|
|
16
|
+
if(NOT CMAKE_BUILD_TYPE)
|
|
17
|
+
set(CMAKE_BUILD_TYPE Release)
|
|
18
|
+
endif()
|
|
19
|
+
|
|
20
|
+
option(SAMCORE_BUILD_TESTS "Build the libsamcore test suite" ${PROJECT_IS_TOP_LEVEL})
|
|
21
|
+
option(SAMCORE_BUILD_EXECUTABLES "Build executables (bench, gen_data)" OFF)
|
|
22
|
+
option(SAMCORE_BUILD_PYTHON "Build the samcore Python bindings (nanobind)" OFF)
|
|
23
|
+
option(SAMCORE_ENABLE_OPENMP "Enable OpenMP parallelism" ON)
|
|
24
|
+
option(SAMCORE_NATIVE_ARCH "Compile with -march=native" OFF)
|
|
25
|
+
option(SAMCORE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
|
|
26
|
+
option(SAMCORE_ENABLE_SANITIZERS "Enable ASan+UBSan in Debug builds" ON)
|
|
27
|
+
option(SAMCORE_ENABLE_FAST_MATH "Enable -ffast-math in Release builds" ON)
|
|
28
|
+
option(SAMCORE_ENABLE_LTO "Enable link-time optimization in Release builds" ON)
|
|
29
|
+
|
|
30
|
+
# Alpine's fortify-headers (musl) define libc functions as always_inline
|
|
31
|
+
# wrappers that GCC cannot inline under -flto ("function body can be
|
|
32
|
+
# overwritten at link time"), which breaks the link. Disable LTO on musl
|
|
33
|
+
# targets (e.g. musllinux wheels); glibc and the other platforms are fine.
|
|
34
|
+
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
35
|
+
execute_process(COMMAND "${CMAKE_CXX_COMPILER}" -dumpmachine
|
|
36
|
+
OUTPUT_VARIABLE _samcore_target_triple
|
|
37
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
|
|
38
|
+
if(_samcore_target_triple MATCHES "musl"
|
|
39
|
+
OR CMAKE_CXX_LIBRARY_ARCHITECTURE MATCHES "musl")
|
|
40
|
+
if(SAMCORE_ENABLE_LTO)
|
|
41
|
+
message(STATUS "samcore: disabling LTO for musl target (fortify-headers)")
|
|
42
|
+
endif()
|
|
43
|
+
set(SAMCORE_ENABLE_LTO OFF)
|
|
44
|
+
endif()
|
|
45
|
+
endif()
|
|
46
|
+
|
|
47
|
+
if(SAMCORE_NATIVE_ARCH)
|
|
48
|
+
add_compile_options(-march=native)
|
|
49
|
+
endif()
|
|
50
|
+
|
|
51
|
+
include(cmake/dependencies.cmake)
|
|
52
|
+
|
|
53
|
+
add_library(samcore
|
|
54
|
+
src/sam_header.cpp
|
|
55
|
+
src/sam_labels.cpp
|
|
56
|
+
src/sam_scan.cpp
|
|
57
|
+
src/sam_dataset.cpp
|
|
58
|
+
src/io/fileio_h5sam.cpp
|
|
59
|
+
src/io/fileio_h5samd.cpp
|
|
60
|
+
src/signal/filters.cpp
|
|
61
|
+
src/signal/spectral.cpp
|
|
62
|
+
src/preprocessing.cpp
|
|
63
|
+
src/utils.cpp
|
|
64
|
+
)
|
|
65
|
+
add_library(samcore::samcore ALIAS samcore)
|
|
66
|
+
|
|
67
|
+
target_include_directories(samcore
|
|
68
|
+
PUBLIC
|
|
69
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
70
|
+
$<INSTALL_INTERFACE:include>
|
|
71
|
+
PRIVATE
|
|
72
|
+
# pocketfft is an internal dependency: no public header includes it, so
|
|
73
|
+
# it must not leak into the exported interface.
|
|
74
|
+
${SAMCORE_POCKETFFT_INCLUDE_DIR})
|
|
75
|
+
|
|
76
|
+
target_link_libraries(samcore
|
|
77
|
+
PUBLIC
|
|
78
|
+
HDF5::HDF5
|
|
79
|
+
PRIVATE
|
|
80
|
+
# OpenMP is an implementation detail: apply it only in the build tree
|
|
81
|
+
# so the exported package does not reference OpenMP::OpenMP_CXX.
|
|
82
|
+
$<$<BOOL:${SAMCORE_ENABLE_OPENMP}>:$<BUILD_INTERFACE:OpenMP::OpenMP_CXX>>)
|
|
83
|
+
|
|
84
|
+
target_compile_definitions(samcore PUBLIC SAMCORE_VERSION="${PROJECT_VERSION}")
|
|
85
|
+
|
|
86
|
+
if(SAMCORE_ENABLE_OPENMP)
|
|
87
|
+
target_compile_definitions(samcore PUBLIC SAMCORE_HAS_OPENMP=1)
|
|
88
|
+
endif()
|
|
89
|
+
|
|
90
|
+
target_compile_options(samcore PRIVATE
|
|
91
|
+
$<$<CXX_COMPILER_ID:MSVC>:/W4;/permissive->
|
|
92
|
+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall;-Wextra;-Wpedantic>
|
|
93
|
+
$<$<AND:$<NOT:$<CXX_COMPILER_ID:MSVC>>,$<BOOL:${SAMCORE_WARNINGS_AS_ERRORS}>>:-Werror>)
|
|
94
|
+
|
|
95
|
+
# ---- build modes ---------------------------------------------------------
|
|
96
|
+
# Build-interface targets so the flags reach every final link target
|
|
97
|
+
# (tests, executables, python module) without leaking into the installed
|
|
98
|
+
# package interface.
|
|
99
|
+
add_library(samcore_debug_flags INTERFACE)
|
|
100
|
+
target_compile_options(samcore_debug_flags INTERFACE
|
|
101
|
+
$<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Debug>,$<BOOL:${SAMCORE_ENABLE_SANITIZERS}>>:-fsanitize=address,undefined;-fno-omit-frame-pointer>)
|
|
102
|
+
target_link_options(samcore_debug_flags INTERFACE
|
|
103
|
+
$<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Debug>,$<BOOL:${SAMCORE_ENABLE_SANITIZERS}>>:-fsanitize=address,undefined>)
|
|
104
|
+
|
|
105
|
+
add_library(samcore_release_flags INTERFACE)
|
|
106
|
+
target_compile_options(samcore_release_flags INTERFACE
|
|
107
|
+
$<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>:$<$<CONFIG:Release>:-O2>>
|
|
108
|
+
$<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Release>,$<BOOL:${SAMCORE_ENABLE_FAST_MATH}>>:-ffast-math>
|
|
109
|
+
$<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Release>,$<BOOL:${SAMCORE_ENABLE_LTO}>>:-flto>)
|
|
110
|
+
target_link_options(samcore_release_flags INTERFACE
|
|
111
|
+
$<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Release>,$<BOOL:${SAMCORE_ENABLE_LTO}>>:-flto>)
|
|
112
|
+
|
|
113
|
+
target_link_libraries(samcore PUBLIC
|
|
114
|
+
$<BUILD_INTERFACE:samcore_debug_flags>
|
|
115
|
+
$<BUILD_INTERFACE:samcore_release_flags>)
|
|
116
|
+
|
|
117
|
+
set_target_properties(samcore PROPERTIES
|
|
118
|
+
POSITION_INDEPENDENT_CODE ON
|
|
119
|
+
CXX_VISIBILITY_PRESET hidden
|
|
120
|
+
VISIBILITY_INLINES_HIDDEN ON)
|
|
121
|
+
|
|
122
|
+
# ---- install / export ----------------------------------------------------
|
|
123
|
+
include(GNUInstallDirs)
|
|
124
|
+
include(CMakePackageConfigHelpers)
|
|
125
|
+
|
|
126
|
+
install(TARGETS samcore
|
|
127
|
+
EXPORT samcoreTargets
|
|
128
|
+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
129
|
+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
130
|
+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
131
|
+
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
132
|
+
|
|
133
|
+
install(EXPORT samcoreTargets
|
|
134
|
+
FILE samcoreTargets.cmake
|
|
135
|
+
NAMESPACE samcore::
|
|
136
|
+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/samcore)
|
|
137
|
+
|
|
138
|
+
configure_package_config_file(
|
|
139
|
+
cmake/samcoreConfig.cmake.in
|
|
140
|
+
"${CMAKE_CURRENT_BINARY_DIR}/samcoreConfig.cmake"
|
|
141
|
+
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/samcore)
|
|
142
|
+
write_basic_package_version_file(
|
|
143
|
+
"${CMAKE_CURRENT_BINARY_DIR}/samcoreConfigVersion.cmake"
|
|
144
|
+
VERSION ${PROJECT_VERSION}
|
|
145
|
+
COMPATIBILITY SameMajorVersion)
|
|
146
|
+
|
|
147
|
+
install(FILES
|
|
148
|
+
"${CMAKE_CURRENT_BINARY_DIR}/samcoreConfig.cmake"
|
|
149
|
+
"${CMAKE_CURRENT_BINARY_DIR}/samcoreConfigVersion.cmake"
|
|
150
|
+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/samcore)
|
|
151
|
+
|
|
152
|
+
# ---- tests & executables ---------------------------------------------------
|
|
153
|
+
if(SAMCORE_BUILD_TESTS)
|
|
154
|
+
enable_testing()
|
|
155
|
+
add_subdirectory(tests)
|
|
156
|
+
endif()
|
|
157
|
+
|
|
158
|
+
if(SAMCORE_BUILD_EXECUTABLES)
|
|
159
|
+
add_subdirectory(executables)
|
|
160
|
+
endif()
|
|
161
|
+
|
|
162
|
+
if(SAMCORE_BUILD_PYTHON)
|
|
163
|
+
# ---- nanobind extension (samcore python package) ------------------------
|
|
164
|
+
# Built via scikit-build-core (pip install .), which exposes nanobind to
|
|
165
|
+
# CMake automatically; the execute_process lookup is only a fallback for
|
|
166
|
+
# direct -DSAMCORE_BUILD_PYTHON=ON builds outside pip.
|
|
167
|
+
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
|
|
168
|
+
|
|
169
|
+
find_package(nanobind CONFIG QUIET)
|
|
170
|
+
if(NOT nanobind_FOUND)
|
|
171
|
+
execute_process(COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
|
|
172
|
+
OUTPUT_VARIABLE NB_CMAKE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
173
|
+
if(NB_CMAKE_DIR)
|
|
174
|
+
list(APPEND CMAKE_PREFIX_PATH "${NB_CMAKE_DIR}")
|
|
175
|
+
endif()
|
|
176
|
+
find_package(nanobind CONFIG REQUIRED)
|
|
177
|
+
endif()
|
|
178
|
+
|
|
179
|
+
# Per-version extension (no abi3): keeps wheel builds uniform across the
|
|
180
|
+
# whole CPython range. Bindings are split per type under src/bindings/.
|
|
181
|
+
nanobind_add_module(_samcore NB_DOMAIN numpy
|
|
182
|
+
src/bindings/module.cpp
|
|
183
|
+
src/bindings/header.cpp
|
|
184
|
+
src/bindings/labels.cpp
|
|
185
|
+
src/bindings/scan.cpp
|
|
186
|
+
src/bindings/dataset.cpp
|
|
187
|
+
src/bindings/submodules.cpp)
|
|
188
|
+
target_link_libraries(_samcore PRIVATE samcore)
|
|
189
|
+
|
|
190
|
+
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
191
|
+
target_compile_options(_samcore PRIVATE -w)
|
|
192
|
+
endif()
|
|
193
|
+
|
|
194
|
+
install(TARGETS _samcore LIBRARY DESTINATION samcore)
|
|
195
|
+
|
|
196
|
+
# package version, generated from version.txt (single source of truth)
|
|
197
|
+
configure_file("${CMAKE_SOURCE_DIR}/cmake/_version.py.in"
|
|
198
|
+
"${CMAKE_CURRENT_BINARY_DIR}/_version.py" @ONLY)
|
|
199
|
+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_version.py"
|
|
200
|
+
DESTINATION samcore)
|
|
201
|
+
|
|
202
|
+
# ---- typed stubs (nanobind stubgen) -------------------------------------
|
|
203
|
+
# Generate .pyi stubs + the py.typed marker at build time and ship them
|
|
204
|
+
# in the wheel. stubgen must be able to import the assembled samcore
|
|
205
|
+
# package (pure Python modules + generated _version.py + the compiled
|
|
206
|
+
# extension), so stage an importable copy of the package in the build
|
|
207
|
+
# tree, run stubgen against it, and install the results. Stub generation
|
|
208
|
+
# is optional tooling and never fails the build (see cmake/stubgen.cmake);
|
|
209
|
+
# the install below uses OPTIONAL so a skipped run ships no stubs.
|
|
210
|
+
execute_process(
|
|
211
|
+
COMMAND "${Python_EXECUTABLE}" -c "import nanobind.stubgen"
|
|
212
|
+
RESULT_VARIABLE SAMCORE_HAS_NB_STUBGEN OUTPUT_QUIET ERROR_QUIET)
|
|
213
|
+
if(SAMCORE_HAS_NB_STUBGEN EQUAL 0)
|
|
214
|
+
set(SAMCORE_STUB_DIR "${CMAKE_CURRENT_BINARY_DIR}/_stub")
|
|
215
|
+
set(SAMCORE_STUB_PKG "${SAMCORE_STUB_DIR}/samcore")
|
|
216
|
+
|
|
217
|
+
add_custom_command(
|
|
218
|
+
OUTPUT "${SAMCORE_STUB_PKG}/py.typed"
|
|
219
|
+
COMMAND ${CMAKE_COMMAND} -E rm -rf "${SAMCORE_STUB_PKG}"
|
|
220
|
+
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
221
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/src/samcore" "${SAMCORE_STUB_PKG}"
|
|
222
|
+
COMMAND ${CMAKE_COMMAND} -E rm -rf "${SAMCORE_STUB_PKG}/__pycache__"
|
|
223
|
+
COMMAND ${CMAKE_COMMAND} -E copy
|
|
224
|
+
"${CMAKE_CURRENT_BINARY_DIR}/_version.py"
|
|
225
|
+
"${SAMCORE_STUB_PKG}/_version.py"
|
|
226
|
+
COMMAND ${CMAKE_COMMAND} -E copy
|
|
227
|
+
"$<TARGET_FILE:_samcore>"
|
|
228
|
+
"${SAMCORE_STUB_PKG}/$<TARGET_FILE_NAME:_samcore>"
|
|
229
|
+
COMMAND ${CMAKE_COMMAND} -DSTUB_DIR=${SAMCORE_STUB_DIR}
|
|
230
|
+
-DSTUB_PKG=${SAMCORE_STUB_PKG}
|
|
231
|
+
-DPYTHON=${Python_EXECUTABLE}
|
|
232
|
+
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/stubgen.cmake"
|
|
233
|
+
DEPENDS _samcore "${CMAKE_CURRENT_BINARY_DIR}/_version.py"
|
|
234
|
+
COMMENT "Generating samcore .pyi stubs (nanobind stubgen)"
|
|
235
|
+
VERBATIM)
|
|
236
|
+
add_custom_target(samcore_stub ALL
|
|
237
|
+
DEPENDS "${SAMCORE_STUB_PKG}/py.typed")
|
|
238
|
+
|
|
239
|
+
install(FILES
|
|
240
|
+
"${SAMCORE_STUB_PKG}/__init__.pyi"
|
|
241
|
+
"${SAMCORE_STUB_PKG}/_samcore.pyi"
|
|
242
|
+
"${SAMCORE_STUB_PKG}/_scan.pyi"
|
|
243
|
+
"${SAMCORE_STUB_PKG}/_labels.pyi"
|
|
244
|
+
"${SAMCORE_STUB_PKG}/_dataset.pyi"
|
|
245
|
+
"${SAMCORE_STUB_PKG}/_io.pyi"
|
|
246
|
+
"${SAMCORE_STUB_PKG}/py.typed"
|
|
247
|
+
DESTINATION samcore
|
|
248
|
+
OPTIONAL)
|
|
249
|
+
else()
|
|
250
|
+
message(STATUS
|
|
251
|
+
"nanobind.stubgen not importable by ${Python_EXECUTABLE}; "
|
|
252
|
+
"skipping .pyi stub generation")
|
|
253
|
+
endif()
|
|
254
|
+
endif()
|
samcore-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Freiburg INATECH BDS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|