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