From 5afda194d012821bb1eecaef0b00b63d15c17d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Silas=20=C3=98rting?= Date: Thu, 8 Jan 2026 14:23:49 +0100 Subject: [PATCH 1/8] Fix typos on linux part of Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5980fe705..8fe95e73e 100644 --- a/Makefile +++ b/Makefile @@ -67,14 +67,14 @@ linux-coverage-build: ## Linux: Debian package .PHONY: debian debian: linux-build - cd ${linux_build_dir} && pack -G DEB + cd ${linux_build_dir} && cpack -G DEB ## Linux: Flatpak package .PHONY: flatpak flatpak: cd flatpak && \ flatpak-builder --force-clean --install-deps-from=flathub --repo=repo build dk.ku.daisy.yml && \ - flatpak build-bundle repo daisy-$(version).flatpak dk.ku.daisy + flatpak build-bundle repo daisy-$(daisy_version).flatpak dk.ku.daisy ## Linux test using standard build From 767bbdde6c16b36f0bb96df972b5111ea5e7308d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Silas=20=C3=98rting?= Date: Thu, 8 Jan 2026 14:24:53 +0100 Subject: [PATCH 2/8] Handle different boost versions when building deb and flatpak Current debian of Boost is 1.83, where system is not header-only When building flatpak (and and macos + windows) we use version 1.90 where system is header only. --- CMakeLists.txt | 6 +++--- flatpak/dk.ku.daisy.yml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f762003b..2c7ee5e6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,10 +51,10 @@ if (${OS} STREQUAL "macos") message("Using HOMEBREW_PREFIX = ${HOMEBREW_PREFIX}") endif() -if (${OS} STREQUAL "macos") - find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem) -else() +if (${OS} STREQUAL "linux" AND NOT DEFINED DAISY_FLATPAK) find_package(Boost 1.83 REQUIRED CONFIG COMPONENTS filesystem system) +else() + find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem) endif() message(STATUS "Boost version: ${Boost_VERSION}") diff --git a/flatpak/dk.ku.daisy.yml b/flatpak/dk.ku.daisy.yml index bd60f2fa7..e0115a7cf 100644 --- a/flatpak/dk.ku.daisy.yml +++ b/flatpak/dk.ku.daisy.yml @@ -23,7 +23,7 @@ modules: - name: Boost buildsystem: simple build-commands: - - ./bootstrap.sh --prefix=/app --with-libraries=filesystem,system + - ./bootstrap.sh --prefix=/app --with-libraries=filesystem - ./b2 install link=static runtime-link=static cxxflags="-fPIC" -j $FLATPAK_BUILDER_N_JOBS cleanup: - '*' @@ -67,6 +67,7 @@ modules: config-opts: - --preset linux-gcc-portable - -DBoost_USE_STATIC_RUNTIME=ON + - -DDAISY_FLATPAK=ON sources: - type: git url: https://github.com/daisy-model/daisy.git From 3b01e424df3a1a2893d1f37cc29cd558d95e0ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Silas=20=C3=98rting?= Date: Thu, 8 Jan 2026 14:36:32 +0100 Subject: [PATCH 3/8] Rewrite spawn program to use boost::process::v2 + boost::asio --- include/util/run_cmd.h | 9 --- include/windows/windows_util.h | 3 +- src/programs/program_spawn.C | 106 +++++++++++++++++++-------------- src/util/CMakeLists.txt | 1 - src/util/run_cmd.C | 54 ----------------- 5 files changed, 61 insertions(+), 112 deletions(-) delete mode 100644 include/util/run_cmd.h delete mode 100644 src/util/run_cmd.C diff --git a/include/util/run_cmd.h b/include/util/run_cmd.h deleted file mode 100644 index 9a02754e8..000000000 --- a/include/util/run_cmd.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef DAISY_UTIL_RUN_CMD_H -#define DAISY_UTIL_RUN_CMD_H -#include -#include - -std::string quote_if_unquoted(const std::string &); -std::pair run_cmd(const std::string & exe, const std::string & args, const std::string & name); - -#endif diff --git a/include/windows/windows_util.h b/include/windows/windows_util.h index dcc329176..bab979187 100644 --- a/include/windows/windows_util.h +++ b/include/windows/windows_util.h @@ -35,6 +35,5 @@ std::basic_string GetStringFromWindowsApi( TStringGetterFunc stringGetter std::basic_string get_exe_path(); std::basic_string get_daisy_home_from_exe_path(); -//std::basic_string run_cmd(std::basic_string exe, std::basic_string args, std::basic_string name); #endif -#endif \ No newline at end of file +#endif diff --git a/src/programs/program_spawn.C b/src/programs/program_spawn.C index 9f074d6eb..b994185c2 100644 --- a/src/programs/program_spawn.C +++ b/src/programs/program_spawn.C @@ -30,6 +30,8 @@ #include #include +#include +#include #include #include @@ -41,8 +43,6 @@ #include #include -#include "util/run_cmd.h" - namespace { void handle_success(const std::string &name, Treelog &msg) { std::ostringstream tmp; @@ -64,6 +64,12 @@ namespace { file << "Exit code " << status; msg.message(tmp.str()); } + + struct Job { + std::string name; + std::vector args; + }; + } struct ProgramSpawn : public Program { @@ -77,8 +83,7 @@ struct ProgramSpawn : public Program { const int length; // State. - std::vector cmds; - std::vector names; + std::list jobs; void prepare_cmds(Treelog& msg) { for (int i = 0; i < length; ++i) { @@ -117,55 +122,65 @@ struct ProgramSpawn : public Program { msg.message (tmp.str ()); return; } - std::string cmd = " -d " + quote_if_unquoted(directory_one.name()) - + " -D " + quote_if_unquoted(input_directory.name()) - + " -q " + quote_if_unquoted(file_one.name()); + std::vector args{ + "-d", directory_one.name(), + "-D", input_directory.name(), + "-q", + file_one.name()}; if (program_one != Attribute::None()) { - cmd += " -p " + quote_if_unquoted(program_one.name()); + args.push_back("-p"); + args.push_back(program_one.name()); } - cmds.push_back(cmd); - names.push_back(directory_one.name()); + jobs.push_back({directory_one.name(), args}); } void run_cmds(Treelog& msg) { using namespace std::chrono_literals; + namespace bp = boost::process::v2; + namespace ba = boost::asio; + + ba::thread_pool pool; + auto exec = pool.get_executor(); + ba::strand strand(exec); + int running = 0; - std::list>> progs; - for (int idx = 0; idx < cmds.size(); ++idx) { - // If parallel > 0, then we only start that many tasks simultaneously - while (parallel > 0 && running >= parallel) { - for (auto it = progs.begin(); it != progs.end();) { - if (it->wait_for(0.1s) == std::future_status::ready) { - auto [status, name] = it->get(); - if (status == 0) { - handle_success(name, msg); - } - else { - handle_failure(status, name, msg); - } - it = progs.erase(it); + std::function start_next; + + // Function that recursively starts processes + start_next = [&]() { + ba::dispatch(strand, [&]() { + if ((parallel > 0 && running >= parallel) || jobs.empty()) { + // We are at the limit or done + return; + } + + auto job = jobs.front(); + jobs.pop_front(); + auto name = job.name; + msg.message("Running " + name); + ++running; + + auto proc = std::make_shared(exec, exe.name(), job.args); + + // Capturing proc by value keeps the pointer alive until we are done + proc->async_wait([&, proc, name](boost::system::error_code ec, int exit_code) { + ba::dispatch(strand, [&]() { --running; - } else { - ++it; + // Try to start a new one + start_next(); + }); + if (!ec) { + handle_success(name, msg); } - } - } - msg.message("Running " + names[idx]); - progs.push_back(std::async(std::launch::async, run_cmd, exe.name(), cmds[idx], names[idx])); - ++running; - } - // Wait for all tasks to finish - for (auto it = progs.begin(); it != progs.end();) { - it->wait(); - auto [status, name] = it->get(); - if (status == 0) { - handle_success(name, msg); - } - else { - handle_failure(status, name, msg); - } - it = progs.erase(it); - } + else { + handle_failure(exit_code, name, msg); + } + }); + start_next(); // Recursively start more until we hit the limit or are done + }); + }; + start_next(); + pool.join(); // Wait until everything is done. } // Use. @@ -229,8 +244,7 @@ struct ProgramSpawn : public Program { length (std::max ({ program.size (), directory.size (), file.size ()})), - cmds(), - names() + jobs() { } ~ProgramSpawn () { } diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 463a027ab..b6040bd5c 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -18,7 +18,6 @@ target_sources(${DAISY_CORE_NAME} PRIVATE nrutil.C path.C point.C - run_cmd.C scope.C scope_exchange.C scope_id.C diff --git a/src/util/run_cmd.C b/src/util/run_cmd.C deleted file mode 100644 index b3bf7d3a2..000000000 --- a/src/util/run_cmd.C +++ /dev/null @@ -1,54 +0,0 @@ -#include "util/run_cmd.h" -#include -#include - -std::string quote_if_unquoted(const std::string& str) { - if (str.size() < 2 || str.front() != '"' || str.back() != '"') { - return '"' + str + '"'; - } - return str; -} - -#if defined(_WIN32) || defined(__CYGWIN__32__) -#include -#include -std::pair run_cmd(const std::string & exe, const std::string & args, const std::string & name) { - STARTUPINFO si; - PROCESS_INFORMATION pi; - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - ZeroMemory( &pi, sizeof(pi) ); - int status = 0; - if (CreateProcessA(exe.c_str(), const_cast(args.c_str()), NULL, NULL, false, 0, NULL, NULL, &si, &pi)) { - // WaitForSingleObject returns WAIT_OBJECT_0 on success - // https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject - if (WaitForSingleObject(pi.hProcess, INFINITE) == 0) { - // Check the exit code. Is zero if there was an error - // https://learn.microsoft.com/en-gb/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess - DWORD exit_code; - if (GetExitCodeProcess(pi.hProcess, &exit_code)) { - status = exit_code; - } - else { - status = -3; - } - } - else { - status = -2; - } - } - else { - status = -1; - } - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - return std::make_pair(status, name); - -} -#else -#include -std::pair run_cmd(const std::string & exe, const std::string & args, const std::string & name) { - int status = std::system((quote_if_unquoted(exe) + " " + args).c_str()); - return std::make_pair(status, name); -} -#endif From a2435f8d9fdda48529e90ac090d8a4746505dca2 Mon Sep 17 00:00:00 2001 From: "U-GAIA\\silas" Date: Thu, 8 Jan 2026 15:56:39 +0100 Subject: [PATCH 4/8] Link against boost:process on windows --- CMakeLists.txt | 2 ++ cmake/MinGW.cmake | 1 + 2 files changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c7ee5e6e..d48cbb237 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,8 @@ endif() if (${OS} STREQUAL "linux" AND NOT DEFINED DAISY_FLATPAK) find_package(Boost 1.83 REQUIRED CONFIG COMPONENTS filesystem system) +elseif(${OS} STREQUAL "mingw") + find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem process) else() find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem) endif() diff --git a/cmake/MinGW.cmake b/cmake/MinGW.cmake index 584647c3c..6297b26ea 100644 --- a/cmake/MinGW.cmake +++ b/cmake/MinGW.cmake @@ -12,6 +12,7 @@ target_include_directories(${DAISY_CORE_NAME} PUBLIC include) target_link_libraries(${DAISY_CORE_NAME} PUBLIC cxsparse Boost::filesystem + Boost::process ) target_link_options(${DAISY_CORE_NAME} PRIVATE ${LINKER_OPTIONS}) From 4142b48648d79e89a0b312e500a967e1831e6e77 Mon Sep 17 00:00:00 2001 From: "U-GAIA\\silas" Date: Thu, 8 Jan 2026 16:05:33 +0100 Subject: [PATCH 5/8] Also check spawns exit code when handling success/failure --- src/programs/program_spawn.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/programs/program_spawn.C b/src/programs/program_spawn.C index b994185c2..6c00ae8e2 100644 --- a/src/programs/program_spawn.C +++ b/src/programs/program_spawn.C @@ -169,7 +169,7 @@ struct ProgramSpawn : public Program { // Try to start a new one start_next(); }); - if (!ec) { + if (!ec && exit_code == 0) { handle_success(name, msg); } else { From 2af09074b77e184d4560f2dbb1596a7bfc016ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Silas=20=C3=98rting?= Date: Thu, 8 Jan 2026 16:22:13 +0100 Subject: [PATCH 6/8] Always link against boost::process --- CMakeLists.txt | 6 ++---- cmake/Linux.cmake | 1 + cmake/MacOS.cmake | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d48cbb237..2de9470af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,11 +52,9 @@ if (${OS} STREQUAL "macos") endif() if (${OS} STREQUAL "linux" AND NOT DEFINED DAISY_FLATPAK) - find_package(Boost 1.83 REQUIRED CONFIG COMPONENTS filesystem system) -elseif(${OS} STREQUAL "mingw") - find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem process) + find_package(Boost 1.83 REQUIRED CONFIG COMPONENTS filesystem system process) else() - find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem) + find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem process) endif() message(STATUS "Boost version: ${Boost_VERSION}") diff --git a/cmake/Linux.cmake b/cmake/Linux.cmake index 0d5ee80ec..5491577c4 100644 --- a/cmake/Linux.cmake +++ b/cmake/Linux.cmake @@ -9,6 +9,7 @@ target_link_options(${DAISY_BIN_NAME} PRIVATE ${LINKER_OPTIONS}) target_link_libraries(${DAISY_BIN_NAME} PUBLIC cxsparse Boost::filesystem + Boost::process ) install(TARGETS ${DAISY_BIN_NAME} RUNTIME DESTINATION bin) diff --git a/cmake/MacOS.cmake b/cmake/MacOS.cmake index ba1ec8180..8cb8bbe71 100644 --- a/cmake/MacOS.cmake +++ b/cmake/MacOS.cmake @@ -9,6 +9,7 @@ target_link_options(${DAISY_BIN_NAME} PRIVATE ${LINKER_OPTIONS}) target_link_libraries(${DAISY_BIN_NAME} PUBLIC cxsparse Boost::filesystem + Boost::process ) target_link_directories(${DAISY_BIN_NAME} PRIVATE ${EXTRA_SYSTEM_INCLUDE_DIRECTORIES}) From 9a2ad8862a8496765cac16e1ef1c0efdd535fa50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Silas=20=C3=98rting?= Date: Thu, 8 Jan 2026 16:31:33 +0100 Subject: [PATCH 7/8] Don't link boost::process on non-flatpak linux + build boost::process on flatpak --- CMakeLists.txt | 2 +- cmake/Linux.cmake | 2 +- flatpak/dk.ku.daisy.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2de9470af..24b970e63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ if (${OS} STREQUAL "macos") endif() if (${OS} STREQUAL "linux" AND NOT DEFINED DAISY_FLATPAK) - find_package(Boost 1.83 REQUIRED CONFIG COMPONENTS filesystem system process) + find_package(Boost 1.83 REQUIRED CONFIG COMPONENTS filesystem system) else() find_package(Boost 1.90 REQUIRED CONFIG COMPONENTS filesystem process) endif() diff --git a/cmake/Linux.cmake b/cmake/Linux.cmake index 5491577c4..4c7ea11df 100644 --- a/cmake/Linux.cmake +++ b/cmake/Linux.cmake @@ -9,7 +9,7 @@ target_link_options(${DAISY_BIN_NAME} PRIVATE ${LINKER_OPTIONS}) target_link_libraries(${DAISY_BIN_NAME} PUBLIC cxsparse Boost::filesystem - Boost::process + $<$:Boost::process> ) install(TARGETS ${DAISY_BIN_NAME} RUNTIME DESTINATION bin) diff --git a/flatpak/dk.ku.daisy.yml b/flatpak/dk.ku.daisy.yml index e0115a7cf..da8bb611b 100644 --- a/flatpak/dk.ku.daisy.yml +++ b/flatpak/dk.ku.daisy.yml @@ -23,7 +23,7 @@ modules: - name: Boost buildsystem: simple build-commands: - - ./bootstrap.sh --prefix=/app --with-libraries=filesystem + - ./bootstrap.sh --prefix=/app --with-libraries=filesystem,process - ./b2 install link=static runtime-link=static cxxflags="-fPIC" -j $FLATPAK_BUILDER_N_JOBS cleanup: - '*' From e72513bdccad6a9a866293e7a30d9d1880bcf29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Silas=20=C3=98rting?= Date: Thu, 8 Jan 2026 17:17:17 +0100 Subject: [PATCH 8/8] Run tests sequentially (except for coverage) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8fe95e73e..a47a938b0 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ linux-test: $(linux_build_dir)/daisy cd $(linux_build_dir) && \ uv venv --allow-existing && \ uv pip install git+https://github.com/daisy-model/daisypy-test && \ - ctest -j 20 + ctest --output-on-failure ## Linux test using coverage build $(linux_coverage_build_dir)/daisy: linux-coverage-build