refactor: replace QProcess/QObject with boost::process#8939
refactor: replace QProcess/QObject with boost::process#8939timosachsenberg merged 19 commits intodevelopfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces Qt-based process and string handling with Boost.Process/Boost.Asio and OpenMS String/std::vector across core system, TOPP, GUI, wrappers, and adapters; adds idle_callback for event-loop pumping and migrates async IO to Boost-based implementations. Changes
Sequence Diagram(s)sequenceDiagram
participant GUI as "GUI / ExternalProcessMBox"
participant TOPP as "TOPPBase / Caller"
participant EP as "ExternalProcess (Boost.Process)"
participant Child as "Child process (R/Java/Python/Tool)"
GUI->>TOPP: user triggers external run
TOPP->>EP: run(exe: String, args: vector<String>, env, idle_callback)
EP->>Child: launch via boost::process (start_dir, env)
Child-->>EP: stdout / stderr (async pipes)
EP->>EP: io_context poll loop (invoke idle_callback periodically)
EP-->>TOPP: return EXIT / CRASH / NONZERO_EXIT + captured output
TOPP-->>GUI: display results / error (only if error_msg non-empty)
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/openms/source/SYSTEM/ExternalProcess.cpp (1)
171-197:⚠️ Potential issue | 🟠 MajorNormalize POSIX exit classification across both branches.
native_exit_code()is the raw/uninterpreted status, whileexit_code()is a separate value and is documented as meaningless once the child was terminated. The comparison at Line 179 therefore turns ordinary POSIX non-zero exits intoCRASH, and the no-I/O path never consults the native status so signal terminations are only reported asNONZERO_EXIT. Please derive the return state from the native status once and reuse that logic in both branches. (codebrowser.dev)Also applies to: 234-243
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openms/source/SYSTEM/ExternalProcess.cpp` around lines 171 - 197, The POSIX branch incorrectly uses exit_code() instead of native_exit_code(), causing normal non-zero exits to be classified as CRASH; update ExternalProcess.cpp to compute a single status from child.native_exit_code() (e.g., store native = child.native_exit_code()), then decide between RETURNSTATE::CRASH and RETURNSTATE::NONZERO_EXIT based on that native value and reuse that logic in both the _WIN32 and non-WIN32 flows (use native for POSIX signal/exit classification while preserving the existing error_msg, exe, verbose and callbackStdErr_ handling), and apply the same normalization to the later similar block around the 234-243 region.
🧹 Nitpick comments (1)
src/openms/source/SYSTEM/JavaInfo.cpp (1)
47-50: Use\ninstead ofstd::endlin these log messages.These paths do not need an explicit flush, and
std::endlmakes every error report pay for one. As per coding guidelines "Use OpenMS logging macros andOpenMS::LogStream; avoidstd::cout/errdirectly; avoidstd::endlfor performance (use\n)".Also applies to: 83-95
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openms/source/SYSTEM/JavaInfo.cpp` around lines 47 - 50, Replace uses of std::endl in the Java-Check logging statements with "\n" to avoid unnecessary flushes; specifically update the OPENMS_LOG_ERROR output lines in JavaInfo.cpp (the Java-Check messages around the block that references java_executable and the later block at lines 83-95) so they end with "\n" instead of std::endl, preserving the same message text and using the existing OpenMS logging macros/LogStream.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/openms/source/SYSTEM/JavaInfo.cpp`:
- Around line 33-38: The bp::child invocation should resolve relative executable
names via PATH: replace the direct use of java_executable in the bp::child
constructor with bp::search_path(static_cast<std::string>(java_executable)) so
Boost.Process will find "java" on PATH while preserving absolute paths (update
the bp::child call that currently uses java_executable and
bp::args/pipe_out/pipe_err accordingly). Also change the logging statements that
use std::endl (the occurrences around the JavaInfo logging) to use "\n" instead
for performance and to match project guidelines.
In `@src/openms/source/SYSTEM/PythonInfo.cpp`:
- Around line 82-83: In canRun() ensure error_msg is set when the child process
exits non‑zero: before returning false on (child.exit_code() != 0) assign
error_msg from the accumulated output (e.g. ss.str()) and include the exit code
/ context if helpful; update the branch that currently returns
(child.exit_code() == 0) to explicitly set error_msg on the failure path so
callers see the error instead of an empty message.
---
Duplicate comments:
In `@src/openms/source/SYSTEM/ExternalProcess.cpp`:
- Around line 171-197: The POSIX branch incorrectly uses exit_code() instead of
native_exit_code(), causing normal non-zero exits to be classified as CRASH;
update ExternalProcess.cpp to compute a single status from
child.native_exit_code() (e.g., store native = child.native_exit_code()), then
decide between RETURNSTATE::CRASH and RETURNSTATE::NONZERO_EXIT based on that
native value and reuse that logic in both the _WIN32 and non-WIN32 flows (use
native for POSIX signal/exit classification while preserving the existing
error_msg, exe, verbose and callbackStdErr_ handling), and apply the same
normalization to the later similar block around the 234-243 region.
---
Nitpick comments:
In `@src/openms/source/SYSTEM/JavaInfo.cpp`:
- Around line 47-50: Replace uses of std::endl in the Java-Check logging
statements with "\n" to avoid unnecessary flushes; specifically update the
OPENMS_LOG_ERROR output lines in JavaInfo.cpp (the Java-Check messages around
the block that references java_executable and the later block at lines 83-95) so
they end with "\n" instead of std::endl, preserving the same message text and
using the existing OpenMS logging macros/LogStream.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 35c67225-bf36-4374-ad32-d3be1671f620
📒 Files selected for processing (3)
src/openms/source/SYSTEM/ExternalProcess.cppsrc/openms/source/SYSTEM/JavaInfo.cppsrc/openms/source/SYSTEM/PythonInfo.cpp
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/openms_gui/source/VISUAL/DIALOGS/FLASHDeconvTabWidget.cpp`:
- Around line 297-300: The call to exit(1) inside FLASHDeconvTabWidget when
ep_.run(...) for getFLASHDeconvExe() fails should be replaced with a graceful
tab-level failure: catch the non-success return from ep_.run, log or display an
error to the user (e.g., via QMessageBox::critical or the widget's status area),
disable or mark this tab as unavailable (use the parent QTabWidget or
this->setEnabled(false)), and return from the method instead of terminating the
process; update the block around ep_.run(..., {"-write_ini", tmp_file}, ...) to
perform those steps and ensure tmp_file is cleaned up if needed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: db2dead5-06f5-4ca9-bab8-f16bf2afbd0c
📒 Files selected for processing (3)
src/openms_gui/include/OpenMS/VISUAL/MISC/ExternalProcessMBox.hsrc/openms_gui/source/VISUAL/DIALOGS/FLASHDeconvTabWidget.cppsrc/openms_gui/source/VISUAL/DIALOGS/SwathTabWidget.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
- src/openms_gui/source/VISUAL/DIALOGS/SwathTabWidget.cpp
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/openms/include/OpenMS/ANALYSIS/TOPDOWN/FLASHHelperClasses.h (1)
13-15: Remove non-OpenMSuintalias and useUIntfromOpenMS/CONCEPT/Types.hLines 13–15 introduce a non-standard global type alias in a public header, violating the guideline to use OpenMS primitive types. This creates unnecessary coupling to bare
uintacross the module. Replace the alias with proper imports and migrateuintusages (index,ms_level) toUInt.Suggested refactor
Replace:
-#ifdef _MSC_VER -using uint = unsigned int; // POSIX uint not available on MSVC; was provided transitively via Qt -#endif +#include <OpenMS/CONCEPT/Types.h>Then update member declarations:
- uint index; + UInt index; - uint ms_level; + UInt ms_level;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openms/include/OpenMS/ANALYSIS/TOPDOWN/FLASHHelperClasses.h` around lines 13 - 15, Remove the non-standard global alias "using uint = unsigned int;" from FLASHHelperClasses.h, add an `#include` for OpenMS/CONCEPT/Types.h, and change all uses of the bare `uint` in this header (e.g., member variables named index, ms_level and any function parameters/returns) to the OpenMS typedef `UInt`; ensure all forward declarations and public APIs in FLASHHelperClasses (class/struct names in this file) use `UInt` so the public header no longer introduces a global `uint` alias.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/openms/include/OpenMS/ANALYSIS/TOPDOWN/FLASHHelperClasses.h`:
- Around line 13-15: Remove the non-standard global alias "using uint = unsigned
int;" from FLASHHelperClasses.h, add an `#include` for OpenMS/CONCEPT/Types.h, and
change all uses of the bare `uint` in this header (e.g., member variables named
index, ms_level and any function parameters/returns) to the OpenMS typedef
`UInt`; ensure all forward declarations and public APIs in FLASHHelperClasses
(class/struct names in this file) use `UInt` so the public header no longer
introduces a global `uint` alias.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: abce75cc-8965-4c98-8dc1-74e0c9fc91eb
📒 Files selected for processing (1)
src/openms/include/OpenMS/ANALYSIS/TOPDOWN/FLASHHelperClasses.h
77cd6ce to
572c0d0
Compare
Remove QObject inheritance and Q_OBJECT macro from ExternalProcess. Rewrite ExternalProcess::run() using boost::process::child with boost::asio::io_context + async_pipe for streaming stdout/stderr. Add idle_callback parameter to allow GUI callers to pump their event loop (used by ExternalProcessMBox to call QCoreApplication::processEvents). Replace QProcess in PythonInfo, JavaInfo, and RWrapper with boost::process::child using synchronous pipes and wait_for(30s) timeout. Update all public APIs from QString/QStringList to String/vector<String>: - ExternalProcess::run() - TOPPBase::runExternalProcess_() - RWrapper::findR(), runScript() - ExternalProcessMBox::run() - WizardHelper Command struct Update all callers in TOPP adapters, InternalCalibration, DBSuitability, and GUI widgets (SwathTabWidget, FLASHDeconvTabWidget). Replace QLockFile in MSGFPlusAdapter with boost::interprocess::file_lock. Replace QDir in SpectraSTSearchAdapter with std::filesystem::path. Replace QFileInfo in NovorAdapter with std::filesystem::canonical. Replace QFile::copy in PercolatorAdapter with File::copy. Add boost-process to vcpkg.json (header-only, no cmake COMPONENTS needed). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ignment Clang rejects 'bp::env = proc_env' (no viable overloaded '='). Pass the environment object as a positional argument to bp::child. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add forward declaration of QWidget in ExternalProcessMBox.h - Convert getCurrentOutDir_() (QString) to String for vector<String> args in FLASHDeconvTabWidget and SwathTabWidget - Add .toQString() for QFile::remove/copy calls with OpenMS::String args - Replace .count() with .size() on std::vector<String> (was QStringList) - Add .toQString() for QStringList << with String operand - Add missing #include <QFile> in SwathTabWidget.cpp Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FLASHHelperClasses.h and PeakGroup.h use bare 'uint' type which was provided transitively by Qt headers on MSVC. Add MSVC-only typedef since POSIX systems provide it via sys/types.h. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed QStringList to std::vector<String> and removed toQString() calls to match the updated ExternalProcess signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On develop, copyDirRecursively still takes const QString&. This PR (standalone on develop) must use toQString(). Will be cleaned up when PR 3 (filesystem) merges and changes the signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DataValue can be constructed directly from String; no need to go through QString. The transitive Qt include from TOPPBase.h was removed in this PR, breaking the incomplete type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…endency boost::process v1 internally uses boost::filesystem on Windows, causing unresolved symbols. BOOST_PROCESS_USE_STD_FS switches to std::filesystem which is already available (C++20 target). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TOPPDocumenter.cpp uses qputenv, toQString, QStringList directly. These were previously available transitively via TOPPBase.h/ExternalProcess.h which no longer include Qt headers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed from QString/QStringList to String/std::vector<String> to match the updated ExternalProcess signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test file still used QString/QStringList for test variables and section descriptions. Updated to match new ExternalProcess API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test wrapper and test cases still used QString/QStringList for runExternalProcess_ calls. Updated to match new signatures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
boost::process bp::child with a plain string does NOT search PATH (unlike QProcess::start which did). Use bp::search_path() to resolve executables before passing to bp::child. Fixes ExternalProcess_test, TOPPBase_test, and DatabaseSuitability test failures on all Linux platforms. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QProcess::exitStatus() returns CrashExit only when the process was killed by a signal (WIFSIGNALED). A normal non-zero exit (like ls -0 returning exit code 2) is NormalExit with non-zero exitCode. The previous logic compared native_exit_code() != exit_code() which is always true on POSIX (native encodes as status<<8), incorrectly reporting all non-zero exits as CRASH. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add version-conditional includes (#if BOOST_VERSION >= 108800) for Boost.Process v1 headers in ExternalProcess, JavaInfo, PythonInfo, and RWrapper. Boost 1.88+ removed the v1 compatibility shims; headers now live under boost/process/v1/. - Upgrade contrib submodule to Boost 1.87 (fixes MSVC C++20 typename bug in boost::process that existed in Boost 1.78/1.81) - Extend Boost_ADDITIONAL_VERSIONS in CMake to cover 1.79-1.87 - Bump CI contrib cache key to pick up new Boost 1.87 archives Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Boost 1.88+ moved v1 API to boost::process::v1 namespace. The v1/ headers don't re-export into boost::process. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Store native_exit_code() in variable before passing to WIFSIGNALED macro (macro takes address, but v1 returns rvalue) - Use bp:: alias for process_error catch (namespace-aware) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d7f9e20 to
ac28e9b
Compare
- Fix copy-paste bug in PercolatorAdapter: use decoy file as source for decoy protein output (was incorrectly copying target file) - Fix pipe deadlock in RWrapper: drain stdout/stderr concurrently before child.wait() (QProcess buffered internally; boost::process does not) - Fix NovorAdapter: wrap std::filesystem::canonical in try-catch (replaces QFileInfo::canonicalFilePath which returned empty on failure) - Fix MSFraggerAdapter: resolve exe/input/output to absolute paths before changing working directory to temp dir - Fix MSGFPlusAdapter: verify index file exists after BuildSA reports success - Fix SpectraSTSearchAdapter: validate file existence and stream state before copying output files - Fix SageAdapter: check regex match before dereferencing iterator in getVersionNumber_ to prevent crash on unexpected output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use single-thread pattern (stderr on thread, stdout on main) to avoid exception safety issue with two raw std::thread objects - Restore stderr capture in findR() matching Qt MergedChannels behavior (was incorrectly sending stderr to bp::null, losing R diagnostics) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add changelog entries for three Qt-removal refactors merged since the last sync (2026-03-19): - #8937: QDateTime/QDate/QSysInfo/QLocale replaced with std::chrono (BREAKING: QDate constructor removed from Date class) - #8938: QDir/QFile/QFileInfo replaced with std::filesystem (BREAKING: copyDirRecursively/removeDir signatures, ToolHandler return type) - #8939: QProcess/QObject replaced with boost::process (BREAKING: ExternalProcess/TOPPBase/RWrapper APIs; boost-process added) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Document the incremental Qt removal from libOpenMS core library: - QDateTime/QDate/QSysInfo/QLocale replaced by std::chrono (#8937) - QDir/QFile/QFileInfo replaced by std::filesystem (#8938) - QProcess/QObject replaced by boost::process (#8939) - QJsonDocument/QJsonObject replaced by nlohmann/json (#8936) These changes landed after the 2026-03-19 changelog sync and represent significant steps toward making Qt6 optional for the core library. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add changelog entries for three Qt-removal refactors merged since the last sync (2026-03-19): - #8937: QDateTime/QDate/QSysInfo/QLocale replaced with std::chrono (BREAKING: QDate constructor removed from Date class) - #8938: QDir/QFile/QFileInfo replaced with std::filesystem (BREAKING: copyDirRecursively/removeDir signatures, ToolHandler return type) - #8939: QProcess/QObject replaced with boost::process (BREAKING: ExternalProcess/TOPPBase/RWrapper APIs; boost-process added) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Document the incremental Qt removal from libOpenMS core library: - QDateTime/QDate/QSysInfo/QLocale replaced by std::chrono (#8937) - QDir/QFile/QFileInfo replaced by std::filesystem (#8938) - QProcess/QObject replaced by boost::process (#8939) - QJsonDocument/QJsonObject replaced by nlohmann/json (#8936) These changes landed after the 2026-03-19 changelog sync and represent significant steps toward making Qt6 optional for the core library. Co-authored-by: GitHub Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Timo Sachsenberg <timo.sachsenberg@uni-tuebingen.de>
Summary
idle_callbackparameter so GUI can pump Qt event loop without core depending on QtrunExternalProcess_signatures changed from QString/QStringList to String/vectorTest plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Refactor
Chores