From ba43cb43195833ecbb17b2c19d00d7c254966a54 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sat, 26 Sep 2026 16:50:14 +0200 Subject: [PATCH] Fix macOS build: rename Rugnux library, move in_worker TLS into a function The Rugnux library and the rugnux executable differ only in case, so on a case-insensitive filesystem (macOS, Windows) their CMakeFiles/.dir directories collide and the executable's build.make overwrites the library's ("No rule to make target rugnux/CMakeFiles/Rugnux.dir/depend"). The library is renamed JFJochRugnux, in line with the other libraries. Apple's linker rejects the TLS wrapper clang emits for the inline thread_local static member WorkerPool::in_worker as a duplicate symbol once it is included from several libraries. It is now a function-local thread_local. Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build_and_test.yml | 2 +- CLAUDE.md | 2 +- common/ParallelFor.h | 14 +++++++++----- rugnux/CMakeLists.txt | 6 +++--- tests/CMakeLists.txt | 2 +- viewer/CMakeLists.txt | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/build_and_test.yml b/.gitea/workflows/build_and_test.yml index c848c85ce..279b297a4 100644 --- a/.gitea/workflows/build_and_test.yml +++ b/.gitea/workflows/build_and_test.yml @@ -169,7 +169,7 @@ jobs: cd build # The "viewer" component is now jfjoch_viewer alone -- rugnux ships as its own artifact # (build:rugnux-tgz) and the two CLI tools are no longer packaged at all. The full tree is - # still built because the viewer links the Rugnux library for its in-process jobs. + # still built because the viewer links the JFJochRugnux library for its in-process jobs. ninja -j16 cpack - name: Upload viewer tgz to release diff --git a/CLAUDE.md b/CLAUDE.md index 73f61737a..112165816 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -339,7 +339,7 @@ generated API model and internal types. `JFJochImageAnalysis`): - `jfjoch_broker` — online, real-time (FPGA + GPU). - `jfjoch_viewer` — interactive Qt desktop (`viewer/`), results not persisted. -- `rugnux` (`rugnux/rugnux_cli.cpp`, built on the `Rugnux` library in the same directory) — offline +- `rugnux` (`rugnux/rugnux_cli.cpp`, built on the `JFJochRugnux` library in the same directory) — offline batch over a stored HDF5, invoked as `rugnux {} ` (it has **no `--help`**; run it with no arguments to print the usage, which is the authority on its flags). Rotation vs stills is auto-detected from the goniometer axis. Merging is on by default (`--no-merge` to disable); diff --git a/common/ParallelFor.h b/common/ParallelFor.h index 498e01cf3..a06a3a930 100644 --- a/common/ParallelFor.h +++ b/common/ParallelFor.h @@ -41,7 +41,7 @@ namespace parallel_detail { // True on a thread the pool owns. A parallel pass reached from inside one runs inline instead // of queueing: the workers are already occupied by the outer pass, so waiting for one of them // to pick up the inner work could wait forever. - static bool InWorker() { return in_worker; } + static bool InWorker() { return InWorkerFlag(); } size_t WorkerCount() const { return workers.size(); } @@ -58,7 +58,7 @@ namespace parallel_detail { const unsigned hw = std::max(1u, std::thread::hardware_concurrency()); workers.reserve(hw - 1); for (unsigned i = 0; i + 1 < hw; i++) // the submitting thread takes a share too - workers.emplace_back([this] { in_worker = true; Loop(); }); + workers.emplace_back([this] { InWorkerFlag() = true; Loop(); }); } ~WorkerPool() { @@ -89,10 +89,14 @@ namespace parallel_detail { std::deque > queue; std::vector workers; bool stop = false; - static thread_local bool in_worker; - }; - inline thread_local bool WorkerPool::in_worker = false; + // A function-local thread_local rather than an inline static member: Apple's linker rejects the + // TLS wrapper clang emits for the latter as a duplicate symbol once two libraries include this. + static bool &InWorkerFlag() { + static thread_local bool in_worker = false; + return in_worker; + } + }; // What the tasks of one pass share: the body to call, how many of them are still outstanding, and // the first exception any of them threw. diff --git a/rugnux/CMakeLists.txt b/rugnux/CMakeLists.txt index 569fc7d16..bdc15301c 100644 --- a/rugnux/CMakeLists.txt +++ b/rugnux/CMakeLists.txt @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute # SPDX-License-Identifier: GPL-3.0-only -ADD_LIBRARY(Rugnux STATIC +ADD_LIBRARY(JFJochRugnux STATIC Rugnux.cpp RugnuxDefaults.cpp RugnuxDefaults.h @@ -35,12 +35,12 @@ ADD_LIBRARY(Rugnux STATIC WriteModel.h ) -TARGET_LINK_LIBRARIES(Rugnux JFJochReader JFJochImageAnalysis JFJochWriter JFJochPreview gemmi fftw3f) +TARGET_LINK_LIBRARIES(JFJochRugnux JFJochReader JFJochImageAnalysis JFJochWriter JFJochPreview gemmi fftw3f) # rugnux is the single offline analysis CLI; --mode picks what it does: mx (the full pipeline, the # default), azint, scale (re-scale/merge stored reflections) or calibration (powder-ring geometry). ADD_EXECUTABLE(rugnux rugnux_cli.cpp) -TARGET_LINK_LIBRARIES(rugnux Rugnux JFJochReader JFJochImageAnalysis JFJochWriter) +TARGET_LINK_LIBRARIES(rugnux JFJochRugnux JFJochReader JFJochImageAnalysis JFJochWriter) INSTALL(TARGETS rugnux RUNTIME COMPONENT rugnux) # libcufft_static.a carries a relocatable-device-code object (separate_callback.o), so an executable diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 78ee3427f..5990ac1f8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -126,7 +126,7 @@ ADD_EXECUTABLE(jfjoch_test ) target_link_libraries(jfjoch_test Catch2WithMain JFJochBroker JFJochReceiver JFJochReader JFJochStreamWriter - Rugnux JFJochImageAnalysis JFJochCommon JFJochHLSSimulation JFJochPreview + JFJochRugnux JFJochImageAnalysis JFJochCommon JFJochHLSSimulation JFJochPreview jfjoch_xds_plugin ZLIB::ZLIB) target_include_directories(jfjoch_test PRIVATE .) diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 8affac6ea..eaf9d1ddb 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -119,7 +119,7 @@ ADD_EXECUTABLE(jfjoch_viewer jfjoch_viewer.cpp JFJochViewerWindow.cpp JFJochView ) TARGET_LINK_LIBRARIES(jfjoch_viewer Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Charts Qt6::Concurrent - JFJochReader JFJochLogger JFJochCommon JFJochWriter JFJochImageAnalysis Rugnux + JFJochReader JFJochLogger JFJochCommon JFJochWriter JFJochImageAnalysis JFJochRugnux fftw3f libcurl_static) # vendored static libcurl by concrete name, not the CURL::libcurl # alias, which a system libcurl can shadow (see top-level CMakeLists)