Files
Jungfraujoch/CMakeLists.txt
T
leonarski_f 749db470ca
Build Packages / build:rpm (rocky9) (push) Successful in 19m56s
Build Packages / Unit tests (push) Skipped
Build Packages / build:windows:nocuda (push) Successful in 16m57s
Build Packages / build:windows:cuda (push) Successful in 19m18s
Build Packages / build:viewer-tgz:cpu (push) Successful in 14m48s
Build Packages / build:viewer-tgz:cuda (push) Successful in 16m18s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 14m19s
Build Packages / build:rugnux:windows (push) Successful in 10m34s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 8m49s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 20m55s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 17m4s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 20m48s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 19m15s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 24m26s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 20m32s
Build Packages / build:rpm (rocky8) (push) Successful in 23m39s
Build Packages / Generate python client (push) Successful in 46s
Build Packages / Build documentation (push) Successful in 1m45s
Build Packages / Create release (push) Skipped
Build Packages / XDS test (durin plugin) (push) Successful in 11m3s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 11m30s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 20m10s
Build Packages / XDS test (neggia plugin) (push) Successful in 10m17s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 23m12s
Build Packages / DIALS test (push) Successful in 20m12s
v1.0.0-rc.164 (#74)
* rugnux now tells you whether a crystal diffracts anisotropically and how far it reaches in each direction, without a second program: a new `9. DIFFRACTION ANISOTROPY` section in `<prefix>_report.txt` and matching `_reflns.pdbx_aniso_B_tensor_*` / `_reflns.jfjoch_aniso_*` items in the merged mmCIF report the anisotropic deltaB, the diffraction limit along each principal direction, and a `NOT DETECTED` / `DETECTED` / `CANNOT DETERMINE` verdict measured against the data set's own systematic error. It is a description only - no intensity is corrected, no reflection is removed, and the merged data do not depend on direction.
* rugnux can hand its integrated observations to another scaling program: `--export-unmerged` writes `<prefix>_unmerged.mtz`, an unmerged MTZ readable by aimless, pointless, careless and `iotbx.merging_statistics`, in `--mode mx` and `--mode scale` alike. Each rotation reflection's partials are summed into one full; `--export-unmerged-partials` writes one row per image instead. Intensities carry the Lorentz-polarization factor and nothing else, since those programs scale the data themselves. Lattice-centring absences are not written; screw and glide absences are.
* rugnux integrates crystals with broad spots better - where it changes anything, per-shell mean I/sigma improves by up to 31% and R_meas by up to 24% - because on rotation data the integration signal radius is now taken from the crystal's own measured spot width instead of a fixed 4 px. `--adaptive-integration-radius=off` restores the fixed radius and an explicit `--integration-radius` still overrides both. The widened radius applies to the final integration pass only, and a pattern too dense for it is re-integrated at 4 px with a note in the log.
* rugnux discards fewer stills reflections for want of a background ring, improving per-shell R_meas over most of the signal-bearing range: the stills background ring now runs to 14 px instead of 12. The gain reverses in shells below a mean I/sigma of about 4.
* rugnux determines the space group with thresholds that mean the same thing on a weak crystal as on a strong one: symmetry operators are scored on resolution-normalised intensities (E squared) instead of raw merged intensities, and a reflection counts as genuinely present on its counting significance instead of on the merged I/sigma, which saturates at the merge's own ISa. The search resolution cut is no longer able to move the answer, and the twin-law H bound moves from 1.70 to 1.85, which stops one class of correct high-symmetry assignment being refused as twinning.
* rugnux says what the space-group search tested and what it could not: the twin-law disagreement H is printed for every operator together with the adopted point group's H ratio and its bound; alternatives that are not on the reported lattice are named with how their cell differs; and a lattice centring the data could not test - the crystal having been integrated on the primitive sub-cell, so the reflections it extinguishes were never measured - is marked `UNTESTED` and warned about where it is adopted, as coming from the lattice metric rather than from the intensities.
* rugnux `--mode scale` re-merges a `_process.h5` in the right symmetry without being told it: the file now records the space group on every run - a two-pass rotation run wrote none before, so re-merging defaulted to P1 - together with the change of basis under `/entry/MX/reindexMatrix` where the lattice was re-seated, and `--mode scale` also reports the Wilson B-factor estimate instead of `WILSON_B= nan`. A file written before this stops with a message naming the two cells and the override to use, instead of failing inside the merge. A third-party reader of a `_process.h5` must apply `reindexMatrix` where it is present.
* rugnux installs on its own, as a package called `rugnux` - `dnf install rugnux` or `apt install rugnux` - instead of arriving inside `jfjoch-viewer`. It pulls in none of the acquisition stack, so a machine that only processes data no longer has to carry the broker, the detector libraries or Qt to get it. Installing it over a `jfjoch-viewer` from rc.163 or earlier, which still owns `/usr/bin/rugnux`, upgrades cleanly rather than failing on the duplicate file.
* rugnux is also a standalone download, built for arm64 as well as x86_64: `rugnux-<version>-linux-{x86_64|aarch64}-cuda<major>.tgz` and `rugnux-<version>-win64-cuda<major>.zip` on the release page, for machines that are not managed by a package manager. The aarch64 build targets GH200 and DGX Spark, and is untested on hardware.
* Every portable Linux binary is now a single self-contained file: cuFFT is linked statically instead of being shipped beside the executable and found through an rpath, so `rugnux` and `jfjoch_viewer` need nothing but an NVIDIA driver, and only to use the GPU. The `.rpm`/`.deb` continue to take cuFFT from the distribution. The developer utilities `jfjoch_extract_hkl` and `jfjoch_recompress` are no longer packaged anywhere.
* Jungfraujoch needs six fewer shared libraries on the machine - libopenblas and libmetis, and libgfortran, libquadmath, libgomp and libz behind them - because the Ceres LAPACK, METIS and SuiteSparse back-ends are no longer built. Nothing in the code ever selected them, and results are unchanged.
* The PCIe driver DKMS package builds for the kernel it is being installed for instead of the running one, so a module built while a kernel update is being applied loads after the reboot.
* The PCIe driver builds on RHEL 9.5 and later, and on their CentOS Stream, Rocky and AlmaLinux equivalents, where the `vm_flags` kernel interface was backported into the 5.14 kernel.
* A data collection started with `async_start` that fails to start - a writer refusing to overwrite an existing file, for instance - is reported as an error by `/wait_until_running` and `/wait_till_done` instead of as a timeout and a successful collection respectively. The error message is the one the writer gave.
* A calibration that is cancelled or that fails to collect its pedestals is no longer reported as a successful one. The broker goes to `Inactive` with an error message and has to be initialized again, instead of sitting in `Idle` looking ready to measure while holding partial pedestals - data collected in that state was silently mis-converted.
* A failed `/initialize` is reported to `/wait_until_running` and `/wait_till_done` as soon as it happens, instead of when their timeout expires.
* `space_group_number` accepts space groups up to 230 in the API schema, so cubic space groups can be recorded. The broker always accepted them; the generated clients rejected them before the request was sent.
* The results report's `REPORT_VERSION` is 3, two sections having been added. Existing key names and table columns are unchanged.
* The merged statistics table has **9** resolution shells instead of 10, which is what XDS reports. The bins were already XDS's - equal steps in 1/d^2 between the lowest- and the highest-resolution reflection the merge kept - so at the same resolution limits the two tables now have the same shell boundaries and can be read row for row. `--resolution-shells` sets a different count.
* `rugnux --model` now settles the frame the merged reflections are written in, not only the frame the R-factors and the maps are computed in: the `.mtz`/`.cif`/`.hkl` come out in the model's indexing, and where the data were merged in the model's enantiomorph they take the model's hand and space group - which on anomalous data puts I(+) and I(-) the right way round. The indexing choice is logged with the winning R-free and the runner-up, so a decision made within noise is visible.
* `rugnux --model` can resolve the indexing ambiguity of a **serial stills** run, which a model could not do before: structure factors computed from the model become the per-image reference, the same role a reference MTZ plays. It needs the cell and space group up front (`-C` / `-S`). Without one or the other, a merohedral serial run still merges both hands together and says so.
* The rugnux documentation opens with a quick start - the default run, and runs with a reference MTZ, with a model, or with the space group and cell pinned - and explains the indexing ambiguity: what it costs on rotation and on serial data, and which of `-z` / `--model` resolves it in each case. The long reference pages now carry a table of contents.

Reviewed-on: #74
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
2026-08-26 22:47:00 +02:00

726 lines
36 KiB
CMake

CMAKE_MINIMUM_REQUIRED(VERSION 3.26)
# The VERSION file is the single source of truth for the version, and JFJOCH_VERSION carries it
# verbatim - including a pre-release suffix such as "-rc.161" - into the package names, the DKMS
# install path and the version every binary reports. PROJECT(VERSION) cannot hold that string: it
# only accepts numeric major.minor.patch, so the numeric part is cut out of the same file rather
# than written out a second time, and PROJECT_VERSION can never drift from VERSION.
FILE(STRINGS VERSION JFJOCH_VERSION)
STRING(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" JFJOCH_VERSION_NUMERIC "${JFJOCH_VERSION}")
PROJECT(jfjoch VERSION ${JFJOCH_VERSION_NUMERIC} LANGUAGES C CXX)
SET(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
SET(CMAKE_CXX_STANDARD 20)
SET(CMAKE_CXX_STANDARD_REQUIRED True)
SET(JFJOCH_WRITER_ONLY OFF CACHE BOOL "Compile HDF5 writer only")
SET(JFJOCH_INSTALL_DRIVER_SOURCE OFF CACHE BOOL "Install kernel driver source (ignored if building writer only; necessary for RPM building)")
SET(JFJOCH_USE_CUDA ON CACHE BOOL "Compile Jungfraujoch with CUDA")
SET(JFJOCH_VIEWER_BUILD OFF CACHE BOOL "Compile Jungfraujoch viewer")
# The two shippable products are built separately, one per configure, and each produces exactly one
# artifact holding exactly one program:
#
# JFJOCH_VIEWER_ONLY jfjoch_viewer .tar.gz (Linux) / installer (Windows) / .dmg (macOS)
# JFJOCH_RUGNUX_ONLY rugnux .tar.gz (Linux, incl. cross-built aarch64) / .zip (Windows)
#
# They used to be one package: the viewer tarball also carried rugnux and two CLI tools, which made
# the download confusing (a GUI package containing a batch processor) and meant a rugnux user had to
# take Qt with it. Splitting them is possible because cuFFT is now linked statically, so neither
# artifact has to ship a runtime library beside its executable.
SET(JFJOCH_RUGNUX_ONLY OFF CACHE BOOL "Compile only rugnux, the offline analysis CLI (no Qt)")
# Only the viewer (and its portable dependency tree) is supported on Windows and macOS -- the
# broker/receiver/FPGA/writer server stack is Linux-only. Force viewer-only on those platforms so a
# plain configure builds the right subset; on Linux it remains a user-togglable option. A rugnux-only
# build is portable too (it is the same subtree minus viewer/), so asking for it explicitly wins.
# TRUE for either portable product. Both build the same subtree of analysis libraries and neither
# wants any server-side target -- the broker service, the writer executables, the FPGA/detector
# tools or the frontend -- so the guards that exclude those key on this rather than on one product's
# option. Set below, once JFJOCH_VIEWER_ONLY has been resolved.
IF ((WIN32 OR APPLE) AND NOT JFJOCH_RUGNUX_ONLY)
SET(JFJOCH_VIEWER_ONLY ON CACHE BOOL "Compile only jfjoch_viewer and its dependencies" FORCE)
ELSEIF (JFJOCH_RUGNUX_ONLY)
SET(JFJOCH_VIEWER_ONLY OFF CACHE BOOL "Compile only jfjoch_viewer and its dependencies" FORCE)
ELSE()
SET(JFJOCH_VIEWER_ONLY OFF CACHE BOOL "Compile only jfjoch_viewer and its dependencies")
ENDIF()
IF (JFJOCH_VIEWER_ONLY OR JFJOCH_RUGNUX_ONLY)
SET(JFJOCH_PORTABLE_ONLY ON)
ELSE()
SET(JFJOCH_PORTABLE_ONLY OFF)
ENDIF()
# GPU architectures to generate device code for. Overridable so a build that targets one machine
# can pin its own set instead of paying for the full fat binary -- e.g. a cross build for
# GB10 (DGX Spark, sm_121) and GH200 (Grace Hopper, sm_90) passes -DJFJOCH_CUDA_ARCHITECTURES="90;121".
SET(JFJOCH_CUDA_ARCHITECTURES "" CACHE STRING "Override the CUDA architecture list (empty = the default set below)")
SET (ZLIB_USE_STATIC_LIBS TRUE)
FIND_PACKAGE(ZLIB REQUIRED)
OPTION(SLS9 "Build with sls_detector_package v9.2.0" OFF)
SET(BUILD_SHARED_LIBS OFF)
SET(BUILD_TESTING OFF)
SET(ZSTD_LEGACY_SUPPORT OFF)
SET(ZSTD_MULTITHREAD_SUPPORT OFF)
SET(ZSTD_BUILD_PROGRAMS OFF)
SET(ZSTD_BUILD_SHARED OFF)
SET(SLS_USE_RECEIVER OFF)
SET(SLS_USE_RECEIVER_BINARIES OFF)
SET(SLS_BUILD_SHARED_LIBRARIES OFF)
SET(BUILD_FAST_INDEXER OFF)
SET(BUILD_FAST_INDEXER_STATIC ON)
INCLUDE(CheckLanguage)
INCLUDE(CheckIncludeFile)
# Locate nvcc ourselves when it isn't already pinned (-DCMAKE_CUDA_COMPILER / $CUDACXX). CHECK_LANGUAGE
# below only searches PATH, which is missed routinely on Windows and intermittently on Linux; the
# standard CUDA install locations (CUDA_PATH on Windows, /usr/local/cuda on Linux) cover both, so the
# compiler need not be passed by hand. Only set it when actually found, leaving CHECK_LANGUAGE to run
# its normal detection otherwise.
IF (JFJOCH_USE_CUDA AND NOT CMAKE_CUDA_COMPILER AND NOT DEFINED ENV{CUDACXX})
FIND_PROGRAM(_jfjoch_nvcc nvcc
HINTS ENV CUDA_PATH ENV CUDA_HOME ENV CUDA_ROOT /usr/local/cuda /opt/cuda
PATH_SUFFIXES bin)
IF (_jfjoch_nvcc)
SET(CMAKE_CUDA_COMPILER "${_jfjoch_nvcc}")
ENDIF()
ENDIF()
CHECK_LANGUAGE(CUDA)
# GPU architectures to generate device code for: Turing (T4), Ampere (A100; RTX A4000), Ada (L4),
# Hopper, and Blackwell in both its data-centre and consumer forms. A bare entry emits SASS *and*
# PTX for that architecture, so the newest one here also covers any future GPU: the driver
# JIT-compiles its PTX on first launch. Volta (V100) is added below, once the nvcc version is known.
IF (JFJOCH_CUDA_ARCHITECTURES)
SET(CMAKE_CUDA_ARCHITECTURES ${JFJOCH_CUDA_ARCHITECTURES})
ELSE()
SET(CMAKE_CUDA_ARCHITECTURES 75 80 86 89 90 100 120)
ENDIF()
SET(CMAKE_CUDA_STANDARD 20)
SET(CMAKE_CUDA_STANDARD_REQUIRED True)
SET(CMAKE_CUDA_FLAGS_RELEASE "-O3 -lineinfo")
SET(CMAKE_CUDA_RUNTIME_LIBRARY Static)
SET(JFJOCH_CUDA_AVAILABLE OFF)
IF (CMAKE_CUDA_COMPILER)
IF (JFJOCH_USE_CUDA)
ENABLE_LANGUAGE(CUDA)
MESSAGE(STATUS "CUDA VERSION: ${CMAKE_CUDA_COMPILER_VERSION}")
IF (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "12.8")
FIND_PACKAGE(CUDAToolkit REQUIRED)
ADD_COMPILE_DEFINITIONS(JFJOCH_USE_CUDA)
SET(JFJOCH_CUDA_AVAILABLE ON)
# Blackwell GB10 (DGX Spark) is sm_121, only known to nvcc >= 12.9; add it there so the
# binary runs natively on Spark instead of having the driver JIT sm_120 PTX for it.
IF (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "12.9" AND NOT JFJOCH_CUDA_ARCHITECTURES)
LIST(APPEND CMAKE_CUDA_ARCHITECTURES 121)
ENDIF()
# Volta (V100) is sm_70, and PTX only ever JITs *forwards* - without an sm_70 entry a
# V100 has no runnable code in the fatbin at all and every launch fails with "no kernel
# image is available for execution on the device". CUDA 13 removed offline compilation
# for Volta, so only a CUDA 12 toolkit can emit it; 12.8/12.9 still do, but warn on
# every .cu, hence -Wno-deprecated-gpu-targets. Volta is the floor - older GPUs are
# not in use here.
IF (CMAKE_CUDA_COMPILER_VERSION VERSION_LESS "13.0" AND NOT JFJOCH_CUDA_ARCHITECTURES)
LIST(APPEND CMAKE_CUDA_ARCHITECTURES 70)
SET(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
ENDIF()
ELSE()
MESSAGE(WARNING "CUDA older than 12.8 not supported")
ENDIF()
ELSE()
MESSAGE(WARNING "CUDA Available, but disabled by user")
ENDIF()
ENDIF()
INCLUDE_DIRECTORIES(include)
include(FetchContent)
SET(HDF5_ENABLE_SZIP_SUPPORT OFF CACHE BOOL "" FORCE)
SET(HDF5_ENABLE_SZIP_ENCODING OFF CACHE BOOL "" FORCE)
SET(HDF5_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
SET(HDF5_BUILD_CPP_LIB OFF CACHE BOOL "" FORCE)
SET(HDF5_ENABLE_ZLIB_SUPPORT ON CACHE BOOL "" FORCE)
SET(HDF5_EXTERNALLY_CONFIGURED 1 CACHE BOOL "" FORCE)
SET(SPDLOG_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
SET(SPDLOG_BUILD_TESTS OFF CACHE BOOL "" FORCE)
SET(SPDLOG_BUILD_BENCH OFF CACHE BOOL "" FORCE)
set(SPDLOG_BUILD_SHARED OFF CACHE BOOL "" FORCE)
SET(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
SET(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO OFF CACHE BOOL "" FORCE)
SET(HTTPLIB_REQUIRE_ZLIB ON CACHE BOOL "" FORCE)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.17.0
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
zstd
GIT_REPOSITORY https://github.com/facebook/zstd
GIT_TAG 794ea1b0afca0f020f4e57b6732332231fb23c70
SOURCE_SUBDIR build/cmake
EXCLUDE_FROM_ALL
)
FetchContent_Declare(hdf5
GIT_REPOSITORY https://github.com/HDFGroup/hdf5/
GIT_TAG hdf5_2.1.0
GIT_SHALLOW 1
EXCLUDE_FROM_ALL)
SET(SLS_DETECTOR_GIT_TAG "8.0.2")
IF(SLS9)
SET(SLS_DETECTOR_GIT_TAG "9.2.0")
ENDIF()
FetchContent_Declare(
sls_detector_package
GIT_REPOSITORY https://github.com/slsdetectorgroup/slsDetectorPackage
GIT_TAG ${SLS_DETECTOR_GIT_TAG}
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2
GIT_TAG v3.13.0
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
httplib
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
GIT_TAG v0.39.0
EXCLUDE_FROM_ALL
)
# httplib enables Brotli content-encoding whenever it finds system Brotli; we don't use it
# (gzip/zlib is enough for the broker), so disable it to avoid linking libbrotli.
SET(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF CACHE BOOL "" FORCE)
# httplib auto-enables HTTPS by linking system OpenSSL whenever it finds it. The broker serves
# plain HTTP only (TLS, if ever wanted, is terminated by a front reverse proxy), so disable it --
# otherwise jfjoch_broker would pull in libssl/libcrypto for no reason.
SET(HTTPLIB_USE_OPENSSL_IF_AVAILABLE OFF CACHE BOOL "" FORCE)
# ZeroMQ (libzmq): fetch it here, at the top level, so that THIS project - not
# slsDetectorPackage - controls the version. slsDetectorPackage bundles its own libzmq
# archive, but only populates it behind `if(NOT libzmq_POPULATED)`; by making libzmq
# available before sls below, sls reuses this copy and a single libzmq-static target is
# built (no duplicate target / double-symbol clash). A future viewer-only / Windows build
# (which does not build sls) fetches the same libzmq standalone.
SET(BUILD_SHARED OFF CACHE BOOL "" FORCE) # libzmq: static only (matches sls)
SET(BUILD_TESTS OFF CACHE BOOL "" FORCE) # libzmq: no test build
SET(WITH_PERF_TOOL OFF CACHE BOOL "" FORCE) # libzmq: no perf tools
SET(WITH_DOCS OFF CACHE BOOL "" FORCE) # libzmq: no docs
SET(ENABLE_CPACK OFF CACHE BOOL "" FORCE) # libzmq: no CPack injection
FetchContent_Declare(
libzmq
GIT_REPOSITORY https://github.com/zeromq/libzmq.git
GIT_TAG v4.3.5
EXCLUDE_FROM_ALL
)
# CMake >= 4.0 (e.g. the 4.x bundled with Visual Studio 2026) makes any
# cmake_minimum_required(VERSION < 3.5) a fatal error. Some fetched dependencies still
# declare such old floors (libzmq: 3.0.2), so raise the policy-version floor for the
# FetchContent subprojects. Ignored (harmless) on CMake < 3.30, which lacks this variable.
SET(CMAKE_POLICY_VERSION_MINIMUM 3.5)
# libzmq must be made available BEFORE sls_detector_package for the override above to take effect.
FetchContent_MakeAvailable(libzmq)
IF (JFJOCH_PORTABLE_ONLY)
# A portable build still needs zstd/hdf5/spdlog/httplib (JFJochReader uses httplib).
# Only sls_detector_package (detector) and catch2 (tests) are not built here -- and sls
# in particular does not configure under MSVC -- so skip just those two.
FetchContent_MakeAvailable(zstd hdf5 spdlog httplib)
ELSE()
FetchContent_MakeAvailable(zstd sls_detector_package catch2 hdf5 spdlog httplib)
ENDIF()
# libtiff (used by JFJochPreview in every build mode): build it ourselves. Its C++ binding
# (tiffxx) is packaged inconsistently across distros (missing on Rocky 9) and absent on
# Windows. Library only; the SET()s below are libtiff's own codec/tool switches.
# We only need DEFLATE (zlib) + the internal LZW codec, matching what Python tifffile writes
# by default. The remaining codecs default to ON whenever their library is found on the build
# host, so each must be explicitly turned OFF to keep the dependency set minimal -- otherwise
# webp pulls in libwebp + libsharpyuv and lerc pulls in libLerc.
SET(jbig OFF)
SET(zstd OFF)
SET(lzma OFF)
SET(jpeg OFF)
SET(old-jpeg OFF)
SET(webp OFF)
SET(lerc OFF)
SET(tiff-tools OFF)
SET(tiff-tests OFF)
FetchContent_Declare(tiff
GIT_REPOSITORY https://gitlab.com/libtiff/libtiff.git
GIT_TAG v4.7.1
EXCLUDE_FROM_ALL)
FetchContent_MakeAvailable(tiff)
# FFTW single precision (target fftw3f): the CPU fallback indexer, enables JFJOCH_USE_FFTW.
# Built from the release tarball -- the git repo ships no pre-generated codelets (needs the
# OCaml genfft).
SET(ENABLE_FLOAT ON CACHE BOOL "" FORCE)
SET(BUILD_TESTS OFF CACHE BOOL "" FORCE)
# SIMD codelets are runtime-dispatched (cpuid), so enabling AVX2 does NOT require an AVX2 CPU
# -- FFTW falls back at runtime. The codelet sets are arch-specific, so guard by processor:
# x86 gets SSE2/AVX/AVX2, aarch64 (e.g. Grace on DGX Spark) gets NEON; other arches build scalar.
IF (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64")
SET(ENABLE_SSE2 ON CACHE BOOL "" FORCE)
SET(ENABLE_AVX ON CACHE BOOL "" FORCE)
SET(ENABLE_AVX2 ON CACHE BOOL "" FORCE)
ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
SET(ENABLE_NEON ON CACHE BOOL "" FORCE)
ENDIF()
FetchContent_Declare(fftw
URL https://www.fftw.org/fftw-3.3.10.tar.gz
URL_HASH SHA256=56c932549852cddcfafdab3820b0200c7742675be92179e59e6215b340e26467
EXCLUDE_FROM_ALL)
FetchContent_MakeAvailable(fftw)
# FFTW exposes fftw3.h only via $<INSTALL_INTERFACE:include>, so consuming fftw3f straight from
# the build tree (FetchContent) leaves the header unreachable. On Linux a system-installed
# fftw3.h masks this; on Windows it does not. Add the source-tree api/ dir for build-tree use.
TARGET_INCLUDE_DIRECTORIES(fftw3f INTERFACE $<BUILD_INTERFACE:${fftw_SOURCE_DIR}/api>)
ADD_COMPILE_DEFINITIONS(JFJOCH_USE_FFTW)
# Eigen is an EXTERNAL dependency (like ZLIB), resolved by find_package(Eigen3) in the subdirectories
# that need it (image_analysis, and transitively Ceres and ffbidx). It is deliberately NOT vendored
# via FetchContent: declaring Eigen with OVERRIDE_FIND_PACKAGE makes the CMake bundled with Visual
# Studio (cmake 4.x "-msvc") intermittently SEGFAULT during configure. The fault is in CMake's own
# FetchContent variable-stack cleanup, reached when Ceres' find_package(Eigen3) resolves the override
# through a nested FetchContent_MakeAvailable -- ~1 in 3 fresh configures, and 100% with Ceres CUDA on.
# Stock Kitware CMake runs the identical scripts fine, so it is a bug in the VS-bundled cmake binary;
# providing Eigen externally avoids that code path entirely and is stable (verified, both Ceres CUDA
# on and off). Provide it via the system package on Linux (eigen3-devel / libeigen3-dev) or a build
# prefix on Windows (point CMAKE_PREFIX_PATH / Eigen3_DIR at it), exactly as for ZLIB.
# getopt/getopt_long shim for Windows: the MSVC CRT has no <getopt.h>. Vendored OpenBSD/NetBSD
# implementation (BSD-licensed). Built only on Windows and defined here, before the subdirectories,
# so the portable CLI tools in tools/ can link it. A no-op on Linux/macOS, where getopt lives in
# libc (macOS uses the system getopt even though it is also a forced viewer-only platform).
IF (WIN32)
ADD_LIBRARY(wingetopt STATIC tools/wingetopt/getopt.c tools/wingetopt/getopt.h)
TARGET_INCLUDE_DIRECTORIES(wingetopt PUBLIC tools/wingetopt)
ENDIF()
# libcurl: the jfjoch_viewer HTTP client (viewer/JFJochHttpReader). Fetched and STATICALLY linked
# ONLY for viewer builds, so the broker/writer never pull in a TLS/Kerberos stack. Backends are
# OS-native per platform to keep the Windows build self-contained: Schannel (TLS) + SSPI
# (Negotiate/Kerberos) on Windows -- no external OpenSSL/krb5 -- and system OpenSSL + GSSAPI (krb5)
# on Linux. Trimmed to HTTP(S) only, mirroring the libtiff codec pruning above.
IF (JFJOCH_VIEWER_BUILD OR JFJOCH_VIEWER_ONLY)
SET(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
SET(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) # static libcurl only, never a shared lib
SET(BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE)
SET(BUILD_TESTING OFF CACHE BOOL "" FORCE)
SET(CURL_DISABLE_INSTALL ON CACHE BOOL "" FORCE)
SET(HTTP_ONLY ON CACHE BOOL "" FORCE) # HTTP/HTTPS only; drop FTP/LDAP/SMTP/...
SET(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE) # avoid the libpsl dependency
SET(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE)
SET(CURL_ZLIB ON CACHE BOOL "" FORCE) # reuse the project's ZLIB
IF (WIN32)
SET(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE) # native Windows TLS (no OpenSSL)
SET(CURL_USE_SSPI ON CACHE BOOL "" FORCE) # native Negotiate/Kerberos (no krb5)
ELSE()
SET(CURL_USE_OPENSSL ON CACHE BOOL "" FORCE)
SET(CURL_USE_GSSAPI ON CACHE BOOL "" FORCE) # Kerberos via system krb5 (needs krb5-dev)
ENDIF()
FetchContent_Declare(curl
GIT_REPOSITORY https://github.com/curl/curl.git
GIT_TAG curl-8_11_1
EXCLUDE_FROM_ALL)
FetchContent_MakeAvailable(curl)
# The viewer links libcurl_static (curl's concrete static-lib target) directly rather than the
# CURL::libcurl alias: the alias can be shadowed by a system libcurl (find_package / dependency
# provider / a distro CURL config), silently linking the OS curl with the wrong TLS/Kerberos
# backend. libcurl_static is unambiguous; guard that FetchContent actually produced it.
IF (NOT TARGET libcurl_static)
MESSAGE(FATAL_ERROR "libcurl_static missing after FetchContent_MakeAvailable(curl) -- "
"the vendored static libcurl was not built.")
ENDIF()
ENDIF()
IF (JFJOCH_RUGNUX_ONLY)
# The viewer-only subtree minus viewer/ and tools/: just rugnux and what it links. broker/ is
# here for JFJochAPI alone (JFJochReader links it); its service targets are gated out. preview/
# arrives through JFJochWriter -> JFJochPreview, so libjpeg-turbo and libtiff are built too.
ADD_SUBDIRECTORY(jungfrau)
ADD_SUBDIRECTORY(compression)
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(gemmi_gph)
ADD_SUBDIRECTORY(frame_serialize)
ADD_SUBDIRECTORY(preview)
ADD_SUBDIRECTORY(writer)
ADD_SUBDIRECTORY(image_analysis)
ADD_SUBDIRECTORY(broker)
ADD_SUBDIRECTORY(reader)
ADD_SUBDIRECTORY(rugnux)
ELSEIF (JFJOCH_VIEWER_ONLY)
# Minimal subtree: jfjoch_viewer and only the libraries it transitively links.
# (broker here provides JFJochAPI only; its service targets are gated out.)
ADD_SUBDIRECTORY(jungfrau)
ADD_SUBDIRECTORY(compression)
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(gemmi_gph)
ADD_SUBDIRECTORY(frame_serialize)
ADD_SUBDIRECTORY(preview)
ADD_SUBDIRECTORY(writer)
ADD_SUBDIRECTORY(image_analysis)
ADD_SUBDIRECTORY(broker)
ADD_SUBDIRECTORY(reader)
ADD_SUBDIRECTORY(rugnux)
ADD_SUBDIRECTORY(viewer)
ADD_SUBDIRECTORY(tools) # builds only the portable analysis tools (rugnux/extract_hkl)
ELSE()
ADD_SUBDIRECTORY(jungfrau)
ADD_SUBDIRECTORY(compression)
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(writer)
ADD_SUBDIRECTORY(frame_serialize)
ADD_SUBDIRECTORY(reader)
ADD_SUBDIRECTORY(detector_control)
ADD_SUBDIRECTORY(image_puller)
ADD_SUBDIRECTORY(preview)
ADD_SUBDIRECTORY(gemmi_gph)
ADD_SUBDIRECTORY(xds-plugin)
IF (JFJOCH_WRITER_ONLY)
MESSAGE(STATUS "Compiling HDF5 writer only")
ELSE()
ADD_SUBDIRECTORY(image_pusher)
ADD_SUBDIRECTORY(broker)
ADD_SUBDIRECTORY(fpga)
ADD_SUBDIRECTORY(acquisition_device)
ADD_SUBDIRECTORY(receiver)
ADD_SUBDIRECTORY(image_analysis)
ADD_SUBDIRECTORY(rugnux)
ADD_SUBDIRECTORY(tests)
ADD_SUBDIRECTORY(tools)
ENDIF()
IF (JFJOCH_VIEWER_BUILD)
ADD_SUBDIRECTORY(viewer)
ENDIF()
ENDIF()
IF (NOT JFJOCH_WRITER_ONLY AND NOT JFJOCH_PORTABLE_ONLY)
ADD_CUSTOM_COMMAND(OUTPUT frontend/dist/index.html
COMMAND npm ci
COMMAND npm run build
COMMAND npm run licenses # write dist/THIRD_PARTY_LICENSES.txt (npm deps attribution)
COMMAND npm run redocly
COMMAND npm run docs # bundle Sphinx docs into dist/docs (served at /frontend/docs)
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/frontend)
ADD_CUSTOM_TARGET(frontend DEPENDS frontend/dist/index.html)
ADD_CUSTOM_TARGET(update_version
COMMAND bash update_version.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
IF (JFJOCH_INSTALL_DRIVER_SOURCE)
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/
DESTINATION /usr/src/jfjoch-${JFJOCH_VERSION}
COMPONENT driver-dkms
FILES_MATCHING PATTERN "dkms.conf")
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/
DESTINATION /usr/src/jfjoch-${JFJOCH_VERSION}/src
COMPONENT driver-dkms
FILES_MATCHING PATTERN "*.c" PATTERN "*.h" PATTERN "Makefile")
ENDIF()
FILE(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/frontend/dist/)
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/frontend/dist/ DESTINATION share/jfjoch/frontend COMPONENT jfjoch )
ENDIF()
# Windows has no static cuFFT (the toolkit ships no cufft_static.lib), so there the indexer links
# the import library and the self-contained products must carry the DLL beside the executable --
# the same idea the Linux builds no longer need, since they link libcufft_static.a. CUDA 13 keeps
# the redistributable DLLs in bin/x64, earlier toolkits in bin; glob both.
IF (WIN32 AND JFJOCH_CUDA_AVAILABLE AND JFJOCH_PORTABLE_ONLY AND NOT TARGET CUDA::cufft_static)
FILE(GLOB _cufft_dll
"${CUDAToolkit_BIN_DIR}/x64/cufft64_*.dll"
"${CUDAToolkit_BIN_DIR}/cufft64_*.dll")
IF (NOT _cufft_dll)
MESSAGE(FATAL_ERROR "cuFFT runtime DLL not found under ${CUDAToolkit_BIN_DIR}")
ENDIF()
IF (JFJOCH_RUGNUX_ONLY)
INSTALL(FILES ${_cufft_dll} DESTINATION bin COMPONENT rugnux)
ELSE()
INSTALL(FILES ${_cufft_dll} DESTINATION bin COMPONENT viewer)
ENDIF()
ENDIF()
IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
SET(CMAKE_INSTALL_PREFIX /opt/jfjoch CACHE PATH "Default directory" FORCE)
ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
# Set Package Name
SET(CPACK_PACKAGE_NAME "jfjoch")
# Select the components to package based on build mode
if (JFJOCH_RUGNUX_ONLY)
set(CPACK_COMPONENTS_ALL rugnux)
elseif (JFJOCH_VIEWER_ONLY)
set(CPACK_COMPONENTS_ALL viewer)
else()
# rugnux is packaged separately from everything else, as "rugnux" rather than "jfjoch-rugnux"
# (see the DEB/RPM branches below). It is the offline analysis CLI: it links none of the
# hardware libraries and talks to no service, so a machine that only processes data can install
# it alone. Up to 1.0.0-rc.163 it rode inside the jfjoch-viewer package, which is why both
# branches below declare the file move.
set(CPACK_COMPONENTS_ALL jfjoch writer rugnux)
if (JFJOCH_INSTALL_DRIVER_SOURCE)
list(APPEND CPACK_COMPONENTS_ALL driver-dkms)
endif()
if (JFJOCH_VIEWER_BUILD)
list(APPEND CPACK_COMPONENTS_ALL viewer)
endif()
endif()
# Common metadata
set(CPACK_PACKAGE_CONTACT "Filip Leonarski <filip.leonarski@psi.ch>")
set(CPACK_PACKAGE_VENDOR "Paul Scherrer Institut")
set(CPACK_PACKAGE_VERSION ${JFJOCH_VERSION})
# OS-aware packaging: DragNDrop (.dmg) on macOS, NSIS installer on Windows, DEB on Debian/Ubuntu,
# RPM on RHEL/Rocky. macOS/Windows are checked first because the /etc/* probes below are Linux-only.
if (APPLE AND NOT JFJOCH_RUGNUX_ONLY)
# .dmg containing jfjoch_viewer.app (Qt runtime already deployed into the bundle).
set(CPACK_GENERATOR "DragNDrop")
elseif (WIN32 AND NOT JFJOCH_RUGNUX_ONLY)
# NSIS installer .exe (Qt runtime deployed next to the binary by windeployqt).
set(CPACK_GENERATOR "NSIS")
# GPLv3 text shown as the click-through license page of the installer.
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
# Branding is split across three CPack knobs so the CUDA/CPU variant surfaces exactly where we
# want it and nowhere else:
# - Install folder + Start Menu group come from CPACK_PACKAGE_INSTALL_DIRECTORY and
# CPACK_NSIS_PACKAGE_NAME (the latter feeds $(^Name), the Start Menu group's default folder).
# Both stay plain "Jungfraujoch", so the Start Menu group carries no variant tag and the two
# builds install to the same place (CUDA is a strict superset -- they replace, not coexist).
# - CPACK_NSIS_DISPLAY_NAME is the Add/Remove Programs entry -- tagged "(CUDA)"/"(CPU)".
# - CPACK_PACKAGE_FILE_NAME is the installer .exe filename -- tagged "-cuda<major>"/"-cpu", so
# the (much larger) CUDA download is self-identifying, with the CUDA major version baked in.
# The tag follows JFJOCH_CUDA_AVAILABLE automatically; CUDAToolkit_VERSION_MAJOR is set whenever
# it is ON (find_package(CUDAToolkit) ran in the same guard above).
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Jungfraujoch")
set(CPACK_NSIS_PACKAGE_NAME "Jungfraujoch")
if (JFJOCH_CUDA_AVAILABLE)
set(CPACK_NSIS_DISPLAY_NAME "Jungfraujoch (CUDA)")
set(CPACK_PACKAGE_FILE_NAME "jfjoch-viewer-${JFJOCH_VERSION}-win64-cuda${CUDAToolkit_VERSION_MAJOR}")
else()
set(CPACK_NSIS_DISPLAY_NAME "Jungfraujoch (CPU)")
set(CPACK_PACKAGE_FILE_NAME "jfjoch-viewer-${JFJOCH_VERSION}-win64-cpu")
endif()
# Start Menu shortcut for the viewer ("<exe basename>;<label>"). The shortcut and the
# Add/Remove Programs entry both use the .exe's embedded icon.
set(CPACK_PACKAGE_EXECUTABLES "jfjoch_viewer;Jungfraujoch Viewer")
# Forward slash on purpose: CPack writes this value verbatim into CPackConfig.cmake, and a
# backslash there ("bin\jfjoch...") is an invalid escape that cmake 4.x (CMP0010 strict, e.g. the
# VS-bundled cmake) rejects when cpack re-parses it. Windows accepts the forward slash at runtime.
set(CPACK_NSIS_INSTALLED_ICON_NAME "bin/jfjoch_viewer.exe")
elseif (WIN32)
# rugnux on Windows is a single self-contained rugnux.exe (cuFFT, cudart and the fast-feedback
# indexer are all static, so nothing rides alongside it). An NSIS installer would be ceremony
# around one file that a user drops wherever they like, so ship a .zip: the .exe plus the
# licence notices the component installs.
set(CPACK_GENERATOR "ZIP")
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
if (JFJOCH_CUDA_AVAILABLE)
set(CPACK_PACKAGE_FILE_NAME "rugnux-${JFJOCH_VERSION}-win64-cuda${CUDAToolkit_VERSION_MAJOR}")
else()
set(CPACK_PACKAGE_FILE_NAME "rugnux-${JFJOCH_VERSION}-win64-cpu")
endif()
elseif (JFJOCH_VIEWER_ONLY OR JFJOCH_RUGNUX_ONLY)
# Linux portable viewer: a single self-contained .tar.gz of jfjoch_viewer (Qt is linked
# statically here, so there is nothing external to ship alongside the binary). Built on the
# oldest supported distro (RHEL 8) for a low glibc floor, so the archive runs on any newer
# Linux -- unlike the per-distro .rpm/.deb this is meant to replace in the release. Named to
# mirror the Windows/macOS artifacts, with the same -cuda<major>/-cpu tag so the (larger) GPU
# build is self-identifying. CI renames the .tar.gz to .tgz before upload.
set(CPACK_GENERATOR "TGZ")
# Package only the components in CPACK_COMPONENTS_ALL (here: viewer). Without this the archive
# generator falls back to a monolithic install and walks every install() rule in the tree --
# including the fetched HDF5 command-line tools, abseil's EXCLUDE_FROM_ALL sub-libraries and the
# non-viewer components -- none of which the viewer-only build produces, so cpack aborts with
# "cannot find <artifact>". ALL_COMPONENTS_IN_ONE keeps the output a single tarball named
# CPACK_PACKAGE_FILE_NAME (no per-component suffix), matching the CI upload glob. This mirrors
# CPACK_DEB_COMPONENT_INSTALL / CPACK_RPM_COMPONENT_INSTALL in the DEB/RPM branches below.
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
# The rugnux archive always carries its architecture, because it is built for more than one:
# x86_64 natively and aarch64 through the cross toolchain. CMAKE_SYSTEM_PROCESSOR is the TARGET
# architecture (the toolchain file declares it), never the build host's, so a cross-built tarball
# cannot be mistaken for a native one. The viewer is x86_64-only and keeps its existing name.
if (JFJOCH_RUGNUX_ONLY)
set(_jfjoch_tgz_name "rugnux")
set(_jfjoch_tgz_arch "-${CMAKE_SYSTEM_PROCESSOR}")
else()
set(_jfjoch_tgz_name "jfjoch_viewer")
set(_jfjoch_tgz_arch "")
endif()
if (JFJOCH_CUDA_AVAILABLE)
set(CPACK_PACKAGE_FILE_NAME "${_jfjoch_tgz_name}-${JFJOCH_VERSION}-linux${_jfjoch_tgz_arch}-cuda${CUDAToolkit_VERSION_MAJOR}")
else()
set(CPACK_PACKAGE_FILE_NAME "${_jfjoch_tgz_name}-${JFJOCH_VERSION}-linux${_jfjoch_tgz_arch}-cpu")
endif()
elseif (EXISTS "/etc/debian_version")
set(CPACK_PACKAGE_LICENSE "GPL-3.0-only")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
# Debian/Ubuntu: .deb with components
set(CPACK_GENERATOR "DEB")
set(CPACK_DEB_COMPONENT_INSTALL ON)
# Every component package is named "<CPACK_PACKAGE_NAME>-<component>" unless the component
# overrides it, so the broker component came out as "jfjoch-jfjoch". Name it plain "jfjoch", to
# match what the RPM generator produces for CPACK_RPM_MAIN_COMPONENT below. (There is no
# CPACK_DEBIAN_MAIN_COMPONENT: only CPackRPM.cmake has that variable, and the DEB spelling this
# used to set was silently ignored.)
set(CPACK_DEBIAN_JFJOCH_PACKAGE_NAME "jfjoch")
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
# Up to 1.0.0-rc.160 that package was "jfjoch-jfjoch". It owns the same files as "jfjoch" does
# now, so dpkg would refuse to unpack over it; declare the rename so an existing installation
# upgrades instead of conflicting.
set(CPACK_DEBIAN_JFJOCH_PACKAGE_REPLACES "jfjoch-jfjoch")
set(CPACK_DEBIAN_JFJOCH_PACKAGE_CONFLICTS "jfjoch-jfjoch")
# rugnux ships as plain "rugnux", not "jfjoch-rugnux": it is a separately named product (see
# docs/NAMING.md), the released tarball is called rugnux-<version>-..., and it is what a user
# types. Overriding the name is the only way to drop the CPACK_PACKAGE_NAME prefix.
set(CPACK_DEBIAN_RUGNUX_PACKAGE_NAME "rugnux")
# /usr/bin/rugnux moved out of jfjoch-viewer into this package. Breaks+Replaces, NOT Conflicts:
# the two packages are meant to be co-installable from 1.0.0-rc.164 on, and this only has to
# stop an OLD viewer (which still owns the file) from being unpacked alongside. Without it dpkg
# refuses with "trying to overwrite '/usr/bin/rugnux', which is also in package jfjoch-viewer".
set(CPACK_DEBIAN_RUGNUX_PACKAGE_BREAKS "jfjoch-viewer (<< ${JFJOCH_VERSION})")
set(CPACK_DEBIAN_RUGNUX_PACKAGE_REPLACES "jfjoch-viewer (<< ${JFJOCH_VERSION})")
# Enable automatic shlib dependency discovery
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
# Optional DEB metadata (tune as needed)
set(CPACK_DEBIAN_PACKAGE_SECTION "science")
# set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://example.org/jfjoch")
# DKMS component (maps from RPM settings)
if (JFJOCH_INSTALL_DRIVER_SOURCE)
# Dependencies and arch for the DKMS component
# Note: component name "driver-dkms" maps to variable suffix "DRIVER_DKMS"
set(CPACK_DEBIAN_DRIVER_DKMS_PACKAGE_DEPENDS "dkms, gcc, bash, sed")
set(CPACK_DEBIAN_DRIVER_DKMS_PACKAGE_ARCHITECTURE "all")
# Provide Debian control scripts (postinst/prerm).
# We rename existing scripts to Debian control names at configure time.
set(_dkms_ctrl_dir "${CMAKE_CURRENT_BINARY_DIR}/cpack-debian-driver-dkms")
file(MAKE_DIRECTORY "${_dkms_ctrl_dir}")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/postinstall.sh
${_dkms_ctrl_dir}/postinst COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/preuninstall.sh
${_dkms_ctrl_dir}/prerm COPYONLY)
set(CPACK_DEBIAN_DRIVER_DKMS_PACKAGE_CONTROL_EXTRA
"${_dkms_ctrl_dir}/postinst;${_dkms_ctrl_dir}/prerm")
endif()
elseif (EXISTS "/etc/redhat-release")
# RHEL/Rocky: .rpm with components (original behavior)
set(CPACK_GENERATOR "RPM")
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_RPM_MAIN_COMPONENT jfjoch)
set(CPACK_RPM_PACKAGE_RELEASE_DIST ON)
set(CPACK_RPM_FILE_NAME "RPM-DEFAULT")
set(CPACK_RPM_PACKAGE_VERSION ${JFJOCH_VERSION})
set(CPACK_RPM_PACKAGE_RELEASE 1)
set(CPACK_RPM_PACKAGE_SUMMARY "Jungfraujoch data acquisition system")
set(CPACK_RPM_PACKAGE_DESCRIPTION "Jungfraujoch")
set(CPACK_RPM_PACKAGE_LICENSE "GPLv3")
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
/usr/src /usr/share /usr/share/dbus-1 /usr/share/dbus-1/services /usr/share/pixmaps /usr/share/applications)
# rugnux ships as plain "rugnux", not "jfjoch-rugnux" -- see the DEB branch above for why.
set(CPACK_RPM_RUGNUX_PACKAGE_NAME "rugnux")
set(CPACK_RPM_RUGNUX_PACKAGE_SUMMARY "Offline crystallographic data analysis for Jungfraujoch")
set(CPACK_RPM_RUGNUX_PACKAGE_DESCRIPTION
"rugnux processes a stored Jungfraujoch HDF5 dataset: spot finding, indexing, geometry
refinement, Bragg integration, scaling and merging. It needs none of the acquisition stack -- no
broker, no detector, no FPGA card -- so it can be installed on a machine that only processes data.")
# /usr/bin/rugnux moved out of the jfjoch-viewer package into this one. Conflicts against the
# OLD viewer only, and deliberately NOT Obsoletes: obsoleting jfjoch-viewer would uninstall the
# viewer, which still exists and is still wanted. rpm turns "-" into "_" in a package version
# (an installed release reads 1.0.0_rc.160), so the bound has to be spelled the same way or the
# comparison silently never matches.
string(REPLACE "-" "_" _jfjoch_rpm_version "${JFJOCH_VERSION}")
set(CPACK_RPM_RUGNUX_PACKAGE_CONFLICTS "jfjoch-viewer < ${_jfjoch_rpm_version}")
# DKMS component scripts and requirements for RPM
if (JFJOCH_INSTALL_DRIVER_SOURCE)
set(CPACK_RPM_DRIVER-DKMS_PACKAGE_REQUIRES "dkms, gcc, bash, sed")
set(CPACK_RPM_DRIVER-DKMS_PACKAGE_ARCHITECTURE "noarch")
set(CPACK_RPM_DRIVER-DKMS_POST_INSTALL_SCRIPT_FILE
${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/postinstall.sh)
set(CPACK_RPM_DRIVER-DKMS_PRE_UNINSTALL_SCRIPT_FILE
${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/preuninstall.sh)
endif()
else()
# Fallback if OS is unknown
set(CPACK_GENERATOR "TGZ")
endif()
# Ship the license notices (top-level LICENSE, the third-party manifest, and the verbatim
# license texts) with every package variant. Installed per component so the files land in
# whichever component package(s) the current build mode produces.
#
# Each component gets a directory of its own, named after the package's principal binary:
# share/doc/jfjoch_broker, jfjoch_writer, jfjoch_viewer, jfjoch_driver_dkms. No two components
# may write the same path, and no component writes the plain share/doc/jfjoch these all used to
# share. Deployment updates the packages one at a time (jfjoch first, then jfjoch-writer), so a
# path claimed by two packages blocks the first step as soon as a release changes its content -
# and leaving the main package on the old shared path would keep blocking it for anyone
# upgrading from a release that still had the collision. These notices were the only files any
# two components had in common.
SET(JFJOCH_NOTICE_FILES
${CMAKE_SOURCE_DIR}/LICENSE
${CMAKE_SOURCE_DIR}/THIRD_PARTY_NOTICES.md)
FOREACH(_component IN LISTS CPACK_COMPONENTS_ALL)
IF (_component STREQUAL "jfjoch")
SET(_suffix broker)
ELSE()
STRING(REPLACE "-" "_" _suffix ${_component})
ENDIF()
INSTALL(FILES ${JFJOCH_NOTICE_FILES}
DESTINATION share/doc/jfjoch_${_suffix} COMPONENT ${_component})
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/licenses/
DESTINATION share/doc/jfjoch_${_suffix}/licenses COMPONENT ${_component}
PATTERN "COLLECT.sh" EXCLUDE)
ENDFOREACH()
INCLUDE(CPack)