Build Packages / Create release (push) Successful in 21s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 9m40s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 9m49s
Build Packages / build:viewer-tgz:cpu (push) Successful in 11m37s
Build Packages / build:viewer-tgz:cuda (push) Successful in 12m40s
Build Packages / build:windows:nocuda (push) Successful in 17m44s
Build Packages / build:windows:cuda (push) Successful in 20m13s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 14m41s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 25m59s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 15m5s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 14m35s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 15m53s
Build Packages / build:rugnux:windows (push) Successful in 11m29s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 18m51s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 18m43s
Build Packages / Generate python client (push) Successful in 51s
Build Packages / build:rpm (rocky8) (push) Successful in 18m51s
Build Packages / Build documentation (push) Successful in 1m21s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 18m38s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 18m24s
Build Packages / build:rpm (rocky9) (push) Successful in 19m19s
Build Packages / Unit tests (push) Successful in 1h37m15s
* Building Jungfraujoch no longer needs zlib or Eigen installed on the machine, and the dependencies the build fetches are pinned and updated to current releases. * rugnux: improvements in indexing, lattice selection and geometry post-refinement, which index crystals that previously returned no lattice and keep the better of the two geometries a run measures. * rugnux: improvements in beam-centre measurement, beam-stop detection and space-group determination. * rugnux: the unit cell reported with a determined space group now obeys that group - a cell whose symmetry was confirmed from the intensities is re-refined under it, and a cell the group cannot describe is reported with a warning rather than as it stands. * rugnux drops the stretches of a rotation sweep whose removal measurably improves the merged intensities and reports what became of every frame, and decides the resolution cut on the crystal's own diffraction rather than on its ice rings. * The rugnux results report is machine-readable - every line that is not `KEY= value` data starts with `#` - and states the build it was written by, its authorship and its terms of use (`REPORT_VERSION= 8`). * `jfjoch_viewer`: improvements in the file manager (CBF frames beside HDF5 datasets, a remembered root), the dataset plots, the inspector and the image statistics, plus a settable font size, a view of the rugnux results report, usable performance over a remote display (`ssh -X`) and a reset of all settings to defaults; the reciprocal-space window is removed. * Broker fixes around DECTRIS collections and dark-mask calibration: re-initialising after a run that never started no longer freezes the broker, a cancelled calibration is abandoned instead of reported as done, and a collection whose start message never arrives ends by itself. Reviewed-on: #79 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
875 lines
46 KiB
CMake
875 lines
46 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)
|
|
INCLUDE(GNUInstallDirs)
|
|
|
|
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)")
|
|
|
|
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 -DNDEBUG -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)
|
|
|
|
# Dependencies are fetched as PINNED TARBALLS, not git clones. GitHub throttles unauthenticated
|
|
# git-upload-pack and picks its targets by TLS fingerprint, so a clone answers 200 to the initial
|
|
# ref advertisement and then 401 to the fetch itself; git asks for a password, finds no terminal,
|
|
# and the configure stops dead with no error. Which images it hits follows their OpenSSL version,
|
|
# which is why it looks like a broken build image rather than a rate limit. The archive endpoint is
|
|
# not part of that campaign, and a tarball is a fraction of the transfer (hdf5: 40 MB against a
|
|
# 271 MB shallow clone), so it is both the fix and the cheaper fetch.
|
|
#
|
|
# Every URL is pinned by tag or commit and its bytes by URL_HASH, so a silently changed archive
|
|
# fails the configure instead of entering the build. To bump one: change the version in the URL and
|
|
# replace the hash with `sha256sum` of the new tarball.
|
|
#
|
|
# Ceres needs one extra step for its abseil submodule; see image_analysis/CMakeLists.txt.
|
|
|
|
# zlib, via zlib-ng in its zlib-compatible mode: same zlib.h, same API and symbol names, faster
|
|
# deflate. It used to be the one compression library the project asked the host for; providing it
|
|
# here is what makes a build need no zlib-devel.
|
|
#
|
|
# It is built HERE, during the configure, into a small install prefix in the build tree, and
|
|
# ZLIB_ROOT then points every find_package(ZLIB) at it -- ours and those of HDF5, libtiff,
|
|
# cpp-httplib and libcurl. Two alternatives were rejected:
|
|
#
|
|
# * FetchContent OVERRIDE_FIND_PACKAGE is the mechanism banned by the Eigen note further down
|
|
# (it segfaults the CMake bundled with Visual Studio), and a subproject's find_package(ZLIB)
|
|
# resolving an override through a nested FetchContent_MakeAvailable is exactly the faulting
|
|
# pattern.
|
|
# * Adding zlib-ng with add_subdirectory and aliasing its target to ZLIB::ZLIB looks simpler, but
|
|
# ZLIB::ZLIB is then an alias of a target built in this project, and libcurl puts ZLIB::ZLIB in
|
|
# CMAKE_REQUIRED_LIBRARIES for its OpenSSL feature checks. try_compile only accepts IMPORTED
|
|
# targets, so that is a hard configure error, not a degraded check (verified).
|
|
#
|
|
# Building it first and finding it as an ordinary installed library avoids both: every consumer
|
|
# runs the stock FindZLIB module against a real libz.a and gets a genuine imported target.
|
|
#
|
|
# Passing -DZLIB_ROOT=<prefix> skips the build below and uses the zlib found there instead.
|
|
IF (NOT ZLIB_ROOT)
|
|
SET(ZLIB_ROOT "${CMAKE_BINARY_DIR}/zlib-ng-install")
|
|
IF (NOT EXISTS "${ZLIB_ROOT}/include/zlib.h")
|
|
FetchContent_Declare(zlib
|
|
URL https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.3.3.tar.gz
|
|
URL_HASH SHA256=f9c65aa9c852eb8255b636fd9f07ce1c406f061ec19a2e7d508b318ca0c907d1
|
|
SOURCE_SUBDIR cmake) # no CMakeLists.txt there: populate only, do not add_subdirectory
|
|
FetchContent_MakeAvailable(zlib)
|
|
|
|
# The nested configure inherits the toolchain, not the environment: pass the compiler, the
|
|
# flags and the platform selection explicitly, or a cross build (or an MSVC build with a
|
|
# non-default platform/toolset/runtime) would silently produce a library for the wrong one.
|
|
SET(JFJOCH_ZLIB_CMAKE_ARGS
|
|
-DCMAKE_INSTALL_PREFIX=${ZLIB_ROOT}
|
|
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
|
|
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
|
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON # xds-plugin is a shared library
|
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
|
-DBUILD_SHARED_LIBS=OFF
|
|
-DBUILD_TESTING=OFF
|
|
-DINSTALL_UTILS=OFF
|
|
-DZLIB_COMPAT=ON # provide zlib.h and the zlib API, not zlib-ng.h
|
|
-DWITH_GZFILEOP=ON # gzopen/gzread: GEMMI reads gzipped MTZ and CIF
|
|
-DWITH_NATIVE_INSTRUCTIONS=OFF) # portable binary; SIMD is chosen at run time
|
|
SET(JFJOCH_ZLIB_FORWARD CMAKE_TOOLCHAIN_FILE CMAKE_MSVC_RUNTIME_LIBRARY
|
|
CMAKE_OSX_ARCHITECTURES CMAKE_OSX_SYSROOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
|
# CMAKE_SYSTEM_NAME only when actually cross-compiling: setting it is what PUTS a build into
|
|
# cross mode, so forwarding it unconditionally would make even a native zlib-ng think it is
|
|
# being cross-compiled.
|
|
IF (CMAKE_CROSSCOMPILING)
|
|
LIST(APPEND JFJOCH_ZLIB_FORWARD CMAKE_SYSTEM_NAME CMAKE_SYSTEM_PROCESSOR)
|
|
ENDIF()
|
|
FOREACH (_var IN LISTS JFJOCH_ZLIB_FORWARD)
|
|
IF (${_var})
|
|
LIST(APPEND JFJOCH_ZLIB_CMAKE_ARGS "-D${_var}=${${_var}}")
|
|
ENDIF()
|
|
ENDFOREACH()
|
|
IF (CMAKE_GENERATOR_PLATFORM)
|
|
LIST(APPEND JFJOCH_ZLIB_CMAKE_ARGS -A "${CMAKE_GENERATOR_PLATFORM}")
|
|
ENDIF()
|
|
IF (CMAKE_GENERATOR_TOOLSET)
|
|
LIST(APPEND JFJOCH_ZLIB_CMAKE_ARGS -T "${CMAKE_GENERATOR_TOOLSET}")
|
|
ENDIF()
|
|
# Single-config generators pick the configuration at configure time, multi-config ones at
|
|
# build time; give each the one it reads and keep both on this build's configuration.
|
|
IF (CMAKE_CONFIGURATION_TYPES OR NOT CMAKE_BUILD_TYPE)
|
|
SET(JFJOCH_ZLIB_CONFIG Release)
|
|
ELSE()
|
|
SET(JFJOCH_ZLIB_CONFIG ${CMAKE_BUILD_TYPE})
|
|
ENDIF()
|
|
IF (NOT CMAKE_CONFIGURATION_TYPES)
|
|
LIST(APPEND JFJOCH_ZLIB_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${JFJOCH_ZLIB_CONFIG})
|
|
ENDIF()
|
|
|
|
MESSAGE(STATUS "Building zlib-ng (zlib-compatible) into ${ZLIB_ROOT}")
|
|
EXECUTE_PROCESS(
|
|
COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" ${JFJOCH_ZLIB_CMAKE_ARGS}
|
|
-S "${zlib_SOURCE_DIR}" -B "${zlib_BINARY_DIR}"
|
|
RESULT_VARIABLE JFJOCH_ZLIB_RESULT
|
|
OUTPUT_VARIABLE JFJOCH_ZLIB_LOG ERROR_VARIABLE JFJOCH_ZLIB_LOG)
|
|
IF (JFJOCH_ZLIB_RESULT EQUAL 0)
|
|
EXECUTE_PROCESS(
|
|
COMMAND ${CMAKE_COMMAND} --build "${zlib_BINARY_DIR}" --config ${JFJOCH_ZLIB_CONFIG}
|
|
--target install
|
|
RESULT_VARIABLE JFJOCH_ZLIB_RESULT
|
|
OUTPUT_VARIABLE JFJOCH_ZLIB_LOG ERROR_VARIABLE JFJOCH_ZLIB_LOG)
|
|
ENDIF()
|
|
IF (NOT JFJOCH_ZLIB_RESULT EQUAL 0)
|
|
MESSAGE(FATAL_ERROR "Building the bundled zlib-ng failed:\n${JFJOCH_ZLIB_LOG}\n"
|
|
"Re-run the configure with -DZLIB_ROOT=<prefix> to use a zlib "
|
|
"already on this machine.")
|
|
ENDIF()
|
|
ENDIF()
|
|
ENDIF()
|
|
|
|
SET(ZLIB_USE_STATIC_LIBS TRUE)
|
|
FIND_PACKAGE(ZLIB REQUIRED)
|
|
|
|
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
|
|
URL https://github.com/gabime/spdlog/archive/refs/tags/v1.17.0.tar.gz
|
|
URL_HASH SHA256=d8862955c6d74e5846b3f580b1605d2428b11d97a410d86e2fb13e857cd3a744
|
|
EXCLUDE_FROM_ALL
|
|
)
|
|
|
|
FetchContent_Declare(
|
|
zstd
|
|
URL https://github.com/facebook/zstd/archive/refs/tags/v1.5.7.tar.gz
|
|
URL_HASH SHA256=37d7284556b20954e56e1ca85b80226768902e2edabd3b649e9e72c0c9012ee3
|
|
SOURCE_SUBDIR build/cmake
|
|
EXCLUDE_FROM_ALL
|
|
)
|
|
|
|
FetchContent_Declare(hdf5
|
|
URL https://github.com/HDFGroup/hdf5/archive/refs/tags/2.2.0.tar.gz
|
|
URL_HASH SHA256=5b8d75125ae7b4fef55d3b39e8d3dbfd238cdf63b6518afd67fa69ac80f06542
|
|
EXCLUDE_FROM_ALL)
|
|
|
|
SET(SLS_DETECTOR_VERSION "8.0.2")
|
|
SET(SLS_DETECTOR_SHA256 "783b557ebff281a537ec70a20a775dd412dd345bfcefff85988ecfafe703222a")
|
|
IF(SLS9)
|
|
SET(SLS_DETECTOR_VERSION "9.2.0")
|
|
SET(SLS_DETECTOR_SHA256 "020a7791934e5a9d034cfa4c1c69df7109488ab2f80a4e7fca07b3e51978933f")
|
|
ENDIF()
|
|
|
|
FetchContent_Declare(
|
|
sls_detector_package
|
|
URL https://github.com/slsdetectorgroup/slsDetectorPackage/archive/refs/tags/${SLS_DETECTOR_VERSION}.tar.gz
|
|
URL_HASH SHA256=${SLS_DETECTOR_SHA256}
|
|
EXCLUDE_FROM_ALL
|
|
)
|
|
|
|
FetchContent_Declare(
|
|
catch2
|
|
URL https://github.com/catchorg/Catch2/archive/refs/tags/v3.16.0.tar.gz
|
|
URL_HASH SHA256=0957cae5821b17ce07f0833aaa52b5137643a8382203221f363a8303c109af34
|
|
EXCLUDE_FROM_ALL
|
|
)
|
|
|
|
FetchContent_Declare(
|
|
httplib
|
|
URL https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.56.0.tar.gz
|
|
URL_HASH SHA256=41f214d844e3b9117b734a8cc305b6141c5b47d63009681944e03bc31d92b9ca
|
|
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
|
|
URL https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.5.tar.gz
|
|
URL_HASH SHA256=6c972d1e6a91a0ecd79c3236f04cf0126f2f4dfbbad407d72b4606a7ba93f9c6
|
|
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
|
|
URL https://gitlab.com/libtiff/libtiff/-/archive/v4.7.2/libtiff-v4.7.2.tar.gz
|
|
URL_HASH SHA256=62b15f44c10106065211efa22a2dc77937bd97ee1f6e3afd2c9659010de112eb
|
|
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 (header-only), consumed by the analysis libraries directly and by Ceres and ffbidx through
|
|
# their own find_package(Eigen3). The headers are fetched here and a two-file config package for
|
|
# them is written into the build tree; Eigen3_DIR then points at it, so every find_package(Eigen3)
|
|
# in the build -- ours, Ceres', ffbidx' -- resolves through the ordinary config path and gets this
|
|
# one copy. Nothing needs Eigen's own CMake, so the source is populated but never added.
|
|
#
|
|
# The obvious route, FetchContent_Declare(Eigen3 ... OVERRIDE_FIND_PACKAGE), IS BANNED and must not
|
|
# be reintroduced: it makes the CMake bundled with Visual Studio (cmake 4.x "-msvc") intermittently
|
|
# SEGFAULT during configure -- ~1 in 3 fresh configures, and 100% with Ceres CUDA on. 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. Stock Kitware CMake runs the
|
|
# identical scripts fine, so it is a bug in the VS-bundled cmake binary. A generated config package
|
|
# never enters that machinery: find_package finds an ordinary Eigen3Config.cmake on disk, exactly as
|
|
# it would for an Eigen installed on the system.
|
|
#
|
|
# Passing -DEigen3_DIR=<dir> skips the fetch and uses the Eigen whose config package lives there.
|
|
SET(JFJOCH_EIGEN_VERSION 3.4.1)
|
|
IF (Eigen3_DIR)
|
|
MESSAGE(STATUS "Eigen3_DIR is set (${Eigen3_DIR}) -- using that Eigen instead of the bundled one")
|
|
ELSE()
|
|
FetchContent_Declare(eigen3
|
|
URL https://gitlab.com/libeigen/eigen/-/archive/${JFJOCH_EIGEN_VERSION}/eigen-${JFJOCH_EIGEN_VERSION}.tar.gz
|
|
URL_HASH SHA256=b93c667d1b69265cdb4d9f30ec21f8facbbe8b307cf34c0b9942834c6d4fdbe2
|
|
SOURCE_SUBDIR cmake) # no CMakeLists.txt there: populate only, do not add_subdirectory
|
|
FetchContent_MakeAvailable(eigen3)
|
|
|
|
SET(JFJOCH_EIGEN_CONFIG_DIR "${CMAKE_BINARY_DIR}/eigen3-config")
|
|
FILE(WRITE "${JFJOCH_EIGEN_CONFIG_DIR}/Eigen3Config.cmake"
|
|
"# Generated by the Jungfraujoch build -- do not edit.\n"
|
|
"if (NOT TARGET Eigen3::Eigen)\n"
|
|
" add_library(Eigen3::Eigen INTERFACE IMPORTED GLOBAL)\n"
|
|
" set_target_properties(Eigen3::Eigen PROPERTIES\n"
|
|
" INTERFACE_INCLUDE_DIRECTORIES \"${eigen3_SOURCE_DIR}\")\n"
|
|
"endif()\n"
|
|
"set(EIGEN3_INCLUDE_DIR \"${eigen3_SOURCE_DIR}\")\n"
|
|
"set(EIGEN3_INCLUDE_DIRS \"${eigen3_SOURCE_DIR}\")\n"
|
|
"set(EIGEN3_VERSION_STRING \"${JFJOCH_EIGEN_VERSION}\")\n"
|
|
"set(Eigen3_FOUND TRUE)\n")
|
|
# AnyNewerVersion, not Eigen's own SameMajorVersion rule: Ceres asks for a version RANGE,
|
|
# find_package(Eigen3 3.3.4...5 NO_MODULE), and a same-major file declines any range whose two
|
|
# endpoints differ in major version. Eigen's own config file does exactly that, which is how a
|
|
# build could end up compiling Ceres against one Eigen and everything else against another.
|
|
INCLUDE(CMakePackageConfigHelpers)
|
|
WRITE_BASIC_PACKAGE_VERSION_FILE("${JFJOCH_EIGEN_CONFIG_DIR}/Eigen3ConfigVersion.cmake"
|
|
VERSION ${JFJOCH_EIGEN_VERSION} COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT)
|
|
SET(Eigen3_DIR "${JFJOCH_EIGEN_CONFIG_DIR}")
|
|
ENDIF()
|
|
|
|
# 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
|
|
URL https://github.com/curl/curl/archive/refs/tags/curl-8_22_0.tar.gz
|
|
URL_HASH SHA256=222c6b5c1f368ac63aed59bce2774eb5def9e8e67e46e800be182e684d2845a3
|
|
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 notices (top-level LICENSE, the third-party manifest, the verbatim license texts, and
|
|
# the acknowledgements the programs refer a user to) 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
|
|
${CMAKE_SOURCE_DIR}/docs/ACKNOWLEDGEMENT.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)
|