Skip to content

Commit f094912

Browse files
author
steven varga
committed
Merge branch '278-cdash-off-ci-submissions' into staging
2 parents 22800d6 + 6ecfbe3 commit f094912

4 files changed

Lines changed: 373 additions & 0 deletions

File tree

CTestCustom.cmake

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,20 @@ set(CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE 1048576)
22
set(CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE 1048576)
33
set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS 100)
44
set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS 100)
5+
6+
# Coverage scope — restrict the CDash Coverage step to the h5cpp library
7+
# headers only. Without these excludes CTest folds every instrumented
8+
# .gcda it can find into the dashboard, so vendored thirdparty libraries,
9+
# the test harness, and the example translation units inflate the LOC
10+
# denominator and bury the real library figure (observed: 9276 LOC counted
11+
# vs. 2453 library lines). These patterns are POSIX regexes matched against
12+
# the absolute source path and mirror the lcov --remove list in the CI
13+
# coverage job (ci.yml) so CDash and Codecov report the same number.
14+
set(CTEST_CUSTOM_COVERAGE_EXCLUDE
15+
${CTEST_CUSTOM_COVERAGE_EXCLUDE}
16+
"/thirdparty/"
17+
"/test/"
18+
"/examples/"
19+
"/usr/"
20+
"/CMakeFiles/"
21+
)

scripts/CTestDashboard.cmake

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# Off-CI CDash submission script for h5cpp.
2+
# Invoked via: ctest -S scripts/CTestDashboard.cmake [options] -VV
3+
# or via the wrapper: scripts/cdash [KEY=value ...]
4+
#
5+
# Overridable variables (pass as -DKEY=value or KEY=value to the wrapper):
6+
# BUILD_TYPE Release|Debug|RelWithDebInfo (default: Debug when COVERAGE=ON, else Release)
7+
# TRACK Experimental|Nightly (default: Experimental)
8+
# JOBS N (default: SLURM CPU allocation if set, else logical CPU count)
9+
# HDF5_ROOT /path/to/hdf5 (default: cmake auto-detect)
10+
# HDF5_DIR /path/to/hdf5/cmake (default: cmake auto-detect)
11+
# CTEST_BUILD_NAME label (default: <os>-<arch>-<compiler>-<BUILD_TYPE>[-<node>] )
12+
# CTEST_SITE label (default: SLURM_CLUSTER_NAME if set, else hostname)
13+
# SUBMIT ON|OFF (default: ON)
14+
# COVERAGE ON|OFF (default: ON; forces Debug + gcov)
15+
# BUILD_EXAMPLES ON|OFF (default: ON)
16+
#
17+
# Runs unattended on desktops and under SLURM (sbatch/srun) alike. Under SLURM,
18+
# JOBS honours the job's CPU allocation (SLURM_CPUS_PER_TASK / SLURM_CPUS_ON_NODE)
19+
# so the build never oversubscribes a shared node, the dashboard site defaults to
20+
# the cluster name (SLURM_CLUSTER_NAME) so all nodes group together, and the build
21+
# name is suffixed with the compute-node hostname to keep per-node runs distinct.
22+
# Note: compute nodes often lack direct outbound HTTPS — run on a login/interactive
23+
# node, set HTTPS_PROXY, or pass SUBMIT=OFF on the node and resubmit from a login node.
24+
25+
cmake_minimum_required(VERSION 3.17)
26+
27+
# ── paths ──────────────────────────────────────────────────────────────────
28+
get_filename_component(CTEST_SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
29+
set(CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}/build-cdash")
30+
31+
# ── defaults ───────────────────────────────────────────────────────────────
32+
if(NOT DEFINED BUILD_TYPE)
33+
set(BUILD_TYPE "Release")
34+
endif()
35+
if(NOT DEFINED TRACK)
36+
set(TRACK "Experimental")
37+
endif()
38+
if(NOT DEFINED SUBMIT)
39+
set(SUBMIT ON)
40+
endif()
41+
if(NOT DEFINED COVERAGE)
42+
set(COVERAGE ON)
43+
endif()
44+
if(COVERAGE)
45+
# gcov line counts are only meaningful against an unoptimised, instrumented
46+
# build, so coverage runs force Debug regardless of any BUILD_TYPE passed.
47+
set(BUILD_TYPE "Debug")
48+
endif()
49+
if(NOT DEFINED JOBS)
50+
# Under SLURM, honour the job's CPU allocation so the build does not
51+
# oversubscribe a shared node; fall back to the machine's logical core
52+
# count on a desktop / login node.
53+
if(DEFINED ENV{SLURM_CPUS_PER_TASK})
54+
set(JOBS "$ENV{SLURM_CPUS_PER_TASK}")
55+
elseif(DEFINED ENV{SLURM_CPUS_ON_NODE})
56+
set(JOBS "$ENV{SLURM_CPUS_ON_NODE}")
57+
else()
58+
cmake_host_system_information(RESULT JOBS QUERY NUMBER_OF_LOGICAL_CORES)
59+
endif()
60+
endif()
61+
62+
# ── site: cluster name under SLURM, else hostname (overridable) ─────────────
63+
if(NOT DEFINED CTEST_SITE)
64+
if(DEFINED ENV{SLURM_CLUSTER_NAME})
65+
set(CTEST_SITE "$ENV{SLURM_CLUSTER_NAME}")
66+
else()
67+
cmake_host_system_information(RESULT CTEST_SITE QUERY HOSTNAME)
68+
endif()
69+
endif()
70+
71+
# ── build name: os-arch-compiler-type (auto, overridable) ─────────────────
72+
if(NOT DEFINED CTEST_BUILD_NAME)
73+
cmake_host_system_information(RESULT _os QUERY OS_NAME)
74+
cmake_host_system_information(RESULT _arch QUERY OS_PLATFORM)
75+
76+
if(DEFINED ENV{CXX})
77+
get_filename_component(_compiler "$ENV{CXX}" NAME)
78+
else()
79+
set(_compiler "c++")
80+
endif()
81+
82+
set(CTEST_BUILD_NAME "${_os}-${_arch}-${_compiler}-${BUILD_TYPE}")
83+
84+
# When running under SLURM the site is the (shared) cluster name, so append
85+
# the compute-node hostname to keep concurrent per-node submissions distinct.
86+
if(DEFINED ENV{SLURM_JOB_ID})
87+
cmake_host_system_information(RESULT _node QUERY HOSTNAME)
88+
set(CTEST_BUILD_NAME "${CTEST_BUILD_NAME}-${_node}")
89+
endif()
90+
endif()
91+
92+
# ── generator: prefer Ninja ────────────────────────────────────────────────
93+
find_program(_ninja ninja)
94+
if(_ninja)
95+
set(CTEST_CMAKE_GENERATOR "Ninja")
96+
else()
97+
set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
98+
endif()
99+
set(CTEST_BUILD_CONFIGURATION "${BUILD_TYPE}")
100+
set(CTEST_BUILD_FLAGS "-j${JOBS}")
101+
102+
# ── cmake configure options ────────────────────────────────────────────────
103+
if(NOT DEFINED BUILD_EXAMPLES)
104+
set(BUILD_EXAMPLES ON)
105+
endif()
106+
107+
set(_options
108+
-DCMAKE_BUILD_TYPE=${BUILD_TYPE}
109+
-DCMAKE_CXX_STANDARD=17
110+
-DH5CPP_BUILD_TESTS=ON
111+
-DH5CPP_BUILD_EXAMPLES=${BUILD_EXAMPLES}
112+
)
113+
if(DEFINED ENV{CC})
114+
list(APPEND _options "-DCMAKE_C_COMPILER=$ENV{CC}")
115+
endif()
116+
if(DEFINED ENV{CXX})
117+
list(APPEND _options "-DCMAKE_CXX_COMPILER=$ENV{CXX}")
118+
endif()
119+
if(DEFINED HDF5_ROOT)
120+
list(APPEND _options "-DHDF5_ROOT=${HDF5_ROOT}")
121+
endif()
122+
# HDF5_DIR (the package config dir) is the reliable discovery knob when an
123+
# h5cc on PATH would otherwise shadow the intended install.
124+
if(DEFINED HDF5_DIR)
125+
list(APPEND _options "-DHDF5_DIR=${HDF5_DIR}")
126+
endif()
127+
128+
# ── coverage instrumentation (optional) ──────────────────────────────────────
129+
if(COVERAGE)
130+
list(APPEND _options
131+
"-DCMAKE_C_FLAGS=--coverage -fprofile-update=atomic -O0 -g"
132+
"-DCMAKE_CXX_FLAGS=--coverage -fprofile-update=atomic -O0 -g"
133+
"-DCMAKE_EXE_LINKER_FLAGS=--coverage")
134+
135+
# gcov tool — honour $GCOV (e.g. gcov-14 to match g++-14), else first on PATH.
136+
if(DEFINED ENV{GCOV})
137+
set(CTEST_COVERAGE_COMMAND "$ENV{GCOV}")
138+
else()
139+
find_program(CTEST_COVERAGE_COMMAND NAMES gcov)
140+
endif()
141+
142+
# Scope the Coverage step to library headers only. CTestCustom.cmake already
143+
# carries these excludes for the build tree; we set them here too so the
144+
# off-CI -S run is self-contained. H5Zpipeline_pool.hpp is dead through the
145+
# public API (see #286) and excluded until activation is fixed.
146+
list(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE
147+
"/thirdparty/" "/test/" "/examples/" "/usr/" "/CMakeFiles/"
148+
"/H5Zpipeline_pool.hpp")
149+
endif()
150+
151+
# ── announce ───────────────────────────────────────────────────────────────
152+
message(STATUS "────────────────────────────────────────")
153+
message(STATUS "h5cpp CDash submission")
154+
message(STATUS " site: ${CTEST_SITE}")
155+
message(STATUS " build name: ${CTEST_BUILD_NAME}")
156+
message(STATUS " track: ${TRACK}")
157+
message(STATUS " build dir: ${CTEST_BINARY_DIRECTORY}")
158+
message(STATUS " jobs: ${JOBS}")
159+
message(STATUS " examples: ${BUILD_EXAMPLES}")
160+
message(STATUS " coverage: ${COVERAGE}")
161+
message(STATUS " submit: ${SUBMIT}")
162+
message(STATUS "────────────────────────────────────────")
163+
164+
# ── pipeline ───────────────────────────────────────────────────────────────
165+
ctest_start("${TRACK}")
166+
167+
ctest_configure(
168+
BUILD "${CTEST_BINARY_DIRECTORY}"
169+
OPTIONS "${_options}"
170+
RETURN_VALUE _rv_configure
171+
)
172+
173+
ctest_build(
174+
BUILD "${CTEST_BINARY_DIRECTORY}"
175+
RETURN_VALUE _rv_build
176+
)
177+
178+
ctest_test(
179+
BUILD "${CTEST_BINARY_DIRECTORY}"
180+
PARALLEL_LEVEL "${JOBS}"
181+
RETURN_VALUE _rv_test
182+
)
183+
184+
if(COVERAGE)
185+
ctest_coverage(
186+
BUILD "${CTEST_BINARY_DIRECTORY}"
187+
RETURN_VALUE _rv_coverage
188+
)
189+
if(_rv_coverage)
190+
message(WARNING "CDash coverage step returned ${_rv_coverage}")
191+
endif()
192+
endif()
193+
194+
if(SUBMIT)
195+
ctest_submit(RETURN_VALUE _rv_submit)
196+
if(_rv_submit)
197+
message(WARNING "CDash submit returned ${_rv_submit}")
198+
endif()
199+
else()
200+
message(STATUS "SUBMIT=OFF — skipping upload")
201+
endif()

scripts/README.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# `scripts/`
2+
3+
Developer tooling for h5cpp. Each script is self-contained and documented in
4+
its own header; this file is the quick reference.
5+
6+
---
7+
8+
## `cdash` — off-CI CDash test + coverage submission
9+
10+
Builds h5cpp, runs the test suite, collects coverage, and uploads the results
11+
to [my.cdash.org](https://my.cdash.org/index.php?project=h5cpp) from any
12+
machine — desktop, laptop, or an HPC cluster under SLURM. It is a thin wrapper
13+
over `ctest -S CTestDashboard.cmake` (the CTest dashboard script that does the
14+
actual configure → build → test → coverage → submit pipeline).
15+
16+
### Quick start
17+
18+
```bash
19+
git submodule update --init --recursive # vendored thirdparty libs must be present
20+
CXX=g++-14 GCOV=gcov-14 scripts/cdash # coverage + examples, then submit
21+
```
22+
23+
The result appears on CDash under the **Experimental** group, tagged with the
24+
host's site name and an auto build name like `Linux-x86_64-g++-14-Debug`.
25+
26+
### Defaults
27+
28+
| Option | Default | Notes |
29+
|--------|---------|-------|
30+
| `COVERAGE` | `ON` | forces a `Debug -O0 -g --coverage` build + gcov collection |
31+
| `BUILD_EXAMPLES` | `ON` | also builds the `examples/` tree |
32+
| `SUBMIT` | `ON` | upload to CDash; set `OFF` for a local dry run |
33+
| `TRACK` | `Experimental` | CDash group — `Nightly` for scheduled cron runs |
34+
| `BUILD_TYPE` | `Debug` (when `COVERAGE=ON`) | otherwise `Release` |
35+
| `JOBS` | SLURM allocation, else logical cores | see SLURM section |
36+
37+
> **Track vs build type.** `Experimental`/`Nightly`/`Continuous` is the CDash
38+
> *group* (when/why a build ran). It is **not** an alternative to
39+
> `Release`/`Debug`, which is the *build type* (how it was compiled). Coverage
40+
> requires `Debug` instrumentation, so coverage runs are always `Debug`.
41+
42+
### Common invocations
43+
44+
```bash
45+
scripts/cdash # coverage + examples, submit (defaults)
46+
scripts/cdash SUBMIT=OFF # dry run: build + test + coverage, no upload
47+
scripts/cdash COVERAGE=OFF BUILD_TYPE=Release # plain Release build, no coverage
48+
scripts/cdash HDF5_DIR=/opt/hdf5/cmake # pin HDF5 (module installs / discovery trap)
49+
CXX=g++-14 GCOV=gcov-14 scripts/cdash # match gcov to the compiler
50+
CXX=clang++-18 GCOV='llvm-cov gcov' scripts/cdash
51+
```
52+
53+
All `KEY=value` arguments are forwarded to `CTestDashboard.cmake` as `-DKEY=value`.
54+
Full option list: `HDF5_ROOT`, `HDF5_DIR`, `CTEST_SITE`, `CTEST_BUILD_NAME`,
55+
`JOBS`, `TRACK`, `SUBMIT`, `COVERAGE`, `BUILD_EXAMPLES`, `BUILD_TYPE`.
56+
57+
### Prerequisites
58+
59+
- `cmake` ≥ 3.17, `ctest`, and `ninja` (preferred) or `make`
60+
- A compiler with `--coverage` support **and a matching `gcov`** — e.g. `g++-14`
61+
needs `gcov-14`, not the default `gcov`. Set both via `CXX=` and `GCOV=`.
62+
(`lcov` is **not** needed — CTest drives `gcov` directly.)
63+
- HDF5 ≥ 1.12.3 — auto-detected, or pinned with `HDF5_DIR=.../cmake`
64+
- thirdparty submodules initialised (the build won't configure without them)
65+
- outbound HTTPS to `my.cdash.org` (open submission, no credentials)
66+
67+
### Running under SLURM
68+
69+
No special flags are required — the script adapts to the job automatically:
70+
71+
- **`JOBS`** is derived from `SLURM_CPUS_PER_TASK``SLURM_CPUS_ON_NODE`
72+
logical core count, so the build never oversubscribes a shared node.
73+
- **Site** defaults to `SLURM_CLUSTER_NAME` (so every node groups under one
74+
cluster on the dashboard); override with `CTEST_SITE`.
75+
- **Build name** is suffixed with the compute-node hostname so concurrent
76+
per-node submissions stay distinct.
77+
78+
```bash
79+
#!/bin/bash
80+
#SBATCH -N1 -c16 -t00:30:00
81+
module load hdf5 gcc # site-specific
82+
srun scripts/cdash HDF5_DIR="$HDF5_DIR/cmake"
83+
```
84+
85+
> **Compute-node networking.** Many HPC sites block direct outbound HTTPS from
86+
> compute nodes, so `SUBMIT=ON` may fail at the upload step. Options: run on a
87+
> login/interactive node, export `HTTPS_PROXY`, or use `SUBMIT=OFF` on the
88+
> compute node and re-run the submit from a login node.
89+
90+
### Troubleshooting
91+
92+
| Symptom | Cause / fix |
93+
|---------|-------------|
94+
| `gcov` version-mismatch / zeroed counts | `gcov` doesn't match the compiler → set `GCOV=gcov-NN` for `g++-NN` |
95+
| HDF5 not found / wrong version | pass `HDF5_DIR=.../cmake` (an `h5cc` on `PATH` can shadow the intended install) |
96+
| upload fails on a cluster | compute node has no outbound HTTPS — see the SLURM note above |
97+
| stale build | safe to `rm -rf build-cdash` between runs |
98+
99+
---
100+
101+
## `amalgamate.py` — single-header generator
102+
103+
Concatenates the modular h5cpp headers into one self-contained `h5cpp.hpp`,
104+
stripping `#pragma once` and internal `#include "H5*.hpp"` / `compat.hpp`
105+
directives so the result compiles as a single translation unit. Useful for
106+
drop-in distribution or quick experimentation without the full include tree.
107+
108+
### Usage
109+
110+
```bash
111+
python3 scripts/amalgamate.py <input-dir> <output-file>
112+
113+
# example: amalgamate the in-tree headers into a build artifact
114+
python3 scripts/amalgamate.py h5cpp build/h5cpp.hpp
115+
```
116+
117+
- `<input-dir>` — the `h5cpp/` header directory (must contain the `core` and
118+
`io` umbrella files; their pre-`#ifndef` preambles are preserved).
119+
- `<output-file>` — destination path for the generated single header.
120+
121+
Requires Python 3; no third-party packages.

scripts/cdash

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
# Submit h5cpp test + coverage results to CDash from any machine.
3+
#
4+
# Defaults: COVERAGE=ON (forces Debug + gcov), BUILD_EXAMPLES=ON, SUBMIT=ON.
5+
# Runs on desktops and under SLURM unchanged — JOBS, site, and build name adapt
6+
# to the job's CPU allocation and cluster automatically (see CTestDashboard.cmake).
7+
#
8+
# Usage: scripts/cdash [KEY=value ...]
9+
#
10+
# Examples:
11+
# scripts/cdash # coverage + examples, submit
12+
# scripts/cdash SUBMIT=OFF # dry run: build+test+coverage, no upload
13+
# scripts/cdash COVERAGE=OFF BUILD_TYPE=Release # plain Release build, no coverage
14+
# scripts/cdash HDF5_DIR=/opt/hdf5/cmake # pin HDF5 (module installs, discovery trap)
15+
# CXX=g++-14 GCOV=gcov-14 scripts/cdash # match gcov to the compiler
16+
# CXX=clang++-18 GCOV='llvm-cov gcov' scripts/cdash
17+
#
18+
# SLURM (sbatch) — JOBS auto-derives from the allocation:
19+
# #!/bin/bash
20+
# #SBATCH -N1 -c16 -t00:30:00
21+
# module load hdf5 gcc # site-specific
22+
# srun scripts/cdash HDF5_DIR="$HDF5_DIR/cmake" # or run on a login node if compute nodes have no outbound HTTPS
23+
#
24+
# All KEY=value pairs are forwarded to CTestDashboard.cmake as -DKEY=value.
25+
set -euo pipefail
26+
27+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28+
29+
d_args=()
30+
for arg in "$@"; do
31+
d_args+=("-D${arg}")
32+
done
33+
34+
exec ctest -S "${SCRIPT_DIR}/CTestDashboard.cmake" "${d_args[@]}" -VV

0 commit comments

Comments
 (0)