From 4500184b48efa9fdc670a07fe613cae079e70b18 Mon Sep 17 00:00:00 2001 From: Robin Moeller Date: Fri, 23 Oct 2026 14:19:25 +0200 Subject: [PATCH] Build: the plugin tests build and run on Linux dingosdk_plugin_tests covers the plugin sandbox and every reskate.* API, but it only built on Windows, so contributors without Windows could not run it. Now it builds wherever DINGOSDK_BUILD_PLUGIN_TESTS is on. On Linux the test target adds plugin_discovery.cpp and mod_list.cpp itself, because the Linux plugins library leaves out finding plugins among the mods. The other tests in that group stay Windows only. mod_list.cpp replaced mods.json and the exclusion list with MoveFileExW. That code is unchanged under _WIN32. Elsewhere it uses std::filesystem::rename, which replaces the file in one step on POSIX, and the error message comes from the error_code. Ran on Linux (Debian trixie, linux-x64 preset with the plugin and multiplayer tests on): dingosdk_plugin_tests, dingosdk_server and dingosdk_server_plugin_tests build, and ctest passes plugins ("plugin tests passed (15 skipped)", the gitignored contrib/plugins) and server_plugins. The Windows build was not run. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Hs7xxJDf6SDcQUaXnzmtL5 --- CMakeLists.txt | 11 +++++++++-- Engine/Vfs/mod_list.cpp | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0479c577..693fe94a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,16 +442,23 @@ if(DINGOSDK_BUILD_CAPTURE_TESTS AND TARGET dingosdk_capture) add_test(NAME capture COMMAND dingosdk_capture_tests "${PROJECT_SOURCE_DIR}/External/fonts") endif() option(DINGOSDK_BUILD_PLUGIN_TESTS "Build plugin host regression tests" OFF) -if(DINGOSDK_BUILD_PLUGIN_TESTS AND WIN32) +if(DINGOSDK_BUILD_PLUGIN_TESTS) enable_testing() # The plugin host apart from the game: manifests, the reskate API, storage, modules, hot reload, - # and the sandbox against plugins written to break it. + # and the sandbox against plugins written to break it. These build on Linux too, so they can be + # run without Windows. add_executable(dingosdk_plugin_tests Extension/Plugins/Test/plugin_tests.cpp) target_include_directories(dingosdk_plugin_tests PRIVATE "${PROJECT_SOURCE_DIR}") target_link_libraries(dingosdk_plugin_tests PRIVATE dingosdk_plugins) + if(NOT WIN32) + # The Linux plugins library leaves out finding plugins among the mods; the tests need it. + target_sources(dingosdk_plugin_tests PRIVATE Extension/Plugins/plugin_discovery.cpp Engine/Vfs/mod_list.cpp) + endif() # It also runs the test plugins in contrib/plugins. target_compile_definitions(dingosdk_plugin_tests PRIVATE "RESKATE_SOURCE_DIR=\"${PROJECT_SOURCE_DIR}\"") add_test(NAME plugins COMMAND dingosdk_plugin_tests) +endif() +if(DINGOSDK_BUILD_PLUGIN_TESTS AND WIN32) # Tricks read from the score HUD's values (Extension/Skater/trick_watch.h), what plugins hear. add_executable(dingosdk_trick_watch_tests Extension/Skater/Test/trick_watch_tests.cpp Extension/Skater/trick_watch.cpp) target_include_directories(dingosdk_trick_watch_tests PRIVATE "${PROJECT_SOURCE_DIR}") diff --git a/Engine/Vfs/mod_list.cpp b/Engine/Vfs/mod_list.cpp index 57135953..fed3442b 100644 --- a/Engine/Vfs/mod_list.cpp +++ b/Engine/Vfs/mod_list.cpp @@ -4,7 +4,9 @@ #include "Engine/Core/Json/json.h" #include "Engine/Core/Platform/path_text.h" +#ifdef _WIN32 #include +#endif #include #include @@ -429,7 +431,13 @@ void write_exclusions(const std::filesystem::path& mods_root, output.write(text.data(), static_cast(text.size())); if (!output.flush()) return; } +#ifdef _WIN32 MoveFileExW(temporary.c_str(), path.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); +#else + // rename replaces the old file in one step on POSIX, as MoveFileExW does on Windows. + std::error_code ignored; + fs::rename(temporary, path, ignored); +#endif } catch (...) {} } @@ -615,12 +623,23 @@ void save_mod_order(const std::filesystem::path& mods_root, const std::vector(text.size())) || !output.flush()) throw std::runtime_error("Cannot write " + path_utf8(temporary)); } +#ifdef _WIN32 if (!MoveFileExW(temporary.c_str(), target.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { std::error_code ignored; fs::remove(temporary, ignored); throw std::runtime_error("Cannot replace " + path_utf8(target) + " (Windows error " + std::to_string(GetLastError()) + ")"); } +#else + // Elsewhere (the plugin tests on Linux) rename replaces the old file in one step. + std::error_code renamed; + fs::rename(temporary, target, renamed); + if (renamed) { + std::error_code ignored; + fs::remove(temporary, ignored); + throw std::runtime_error("Cannot replace " + path_utf8(target) + " (" + renamed.message() + ")"); + } +#endif } } // namespace dingosdk::mods