From 63e6d57b9c1866ba0ac8b3e7b134d0652c2be44c Mon Sep 17 00:00:00 2001 From: William Roy Date: Sun, 1 Nov 2026 13:28:16 -0500 Subject: [PATCH] fix: Keep search paths in the specified order (#1069) * fix: Keep search paths in the specified order * chore: remove namespaces from tests * fix: alphabetical order in linker default paths for backwards compatibility * chore: extract AddPathsStringIfDistinct function * chore: make sure linker default source search path stays backwards compatible --------- Co-authored-by: Jan Laupetin --- src/Linking/LinkerArgs.h | 11 +++++------ src/Linking/LinkerPaths.cpp | 2 +- src/Unlinking/UnlinkerArgs.h | 3 +-- src/Utils/Utils/FileUtils.cpp | 16 +++++++++++++--- src/Utils/Utils/FileUtils.h | 6 +++--- test/CommonTests/Utils/FileUtilsTests.cpp | 21 +++++++++++++++++++++ 6 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 test/CommonTests/Utils/FileUtilsTests.cpp diff --git a/src/Linking/LinkerArgs.h b/src/Linking/LinkerArgs.h index e9c93c15c..07c0c49a0 100644 --- a/src/Linking/LinkerArgs.h +++ b/src/Linking/LinkerArgs.h @@ -1,7 +1,6 @@ #pragma once #include "Utils/Arguments/ArgumentParser.h" -#include #include class LinkerArgs @@ -10,9 +9,9 @@ public: static constexpr auto DEFAULT_BASE_FOLDER = "."; static constexpr auto DEFAULT_CACHE_FOLDER = "?base?/.oat/cache/?project?"; static constexpr auto DEFAULT_OUTPUT_FOLDER = "?base?/zone_out/?project?"; - static constexpr auto DEFAULT_ASSET_SEARCH_PATH = "?bin?/raw/?game?;?base?/raw;?base?/raw/?game?;?base?/zone_raw/?project?"; + static constexpr auto DEFAULT_ASSET_SEARCH_PATH = "?base?/raw;?base?/raw/?game?;?base?/zone_raw/?project?;?bin?/raw/?game?"; static constexpr auto DEFAULT_GDT_SEARCH_PATH = "?base?/source_data;?base?/zone_raw/?project?/source_data"; - static constexpr auto DEFAULT_SOURCE_SEARCH_PATH = "?base?/zone_source;?base?/zone_raw/?project?;?base?/zone_raw/?project?/zone_source"; + static constexpr auto DEFAULT_SOURCE_SEARCH_PATH = "?base?/zone_raw/?project?;?base?/zone_raw/?project?/zone_source;?base?/zone_source"; LinkerArgs(); bool ParseArgs(int argc, const char** argv, bool& shouldContinue); @@ -24,9 +23,9 @@ public: std::string m_base_folder; std::string m_out_folder; - std::set m_asset_search_paths; - std::set m_gdt_search_paths; - std::set m_source_search_paths; + std::vector m_asset_search_paths; + std::vector m_gdt_search_paths; + std::vector m_source_search_paths; private: /** diff --git a/src/Linking/LinkerPaths.cpp b/src/Linking/LinkerPaths.cpp index 30fea2659..49a6e0c42 100644 --- a/src/Linking/LinkerPaths.cpp +++ b/src/Linking/LinkerPaths.cpp @@ -152,7 +152,7 @@ namespace { } - void BuildFromArgs(const std::set& templates) + void BuildFromArgs(const std::vector& templates) { m_templates.reserve(templates.size()); for (const auto& templateString : templates) diff --git a/src/Unlinking/UnlinkerArgs.h b/src/Unlinking/UnlinkerArgs.h index c0a6aa36e..ec42b5618 100644 --- a/src/Unlinking/UnlinkerArgs.h +++ b/src/Unlinking/UnlinkerArgs.h @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -46,7 +45,7 @@ public: std::vector m_zones_to_load; std::vector m_zones_to_unlink; - std::set m_user_search_paths; + std::vector m_user_search_paths; ProcessingTask m_task; std::string m_output_folder; diff --git a/src/Utils/Utils/FileUtils.cpp b/src/Utils/Utils/FileUtils.cpp index f37030197..9bf0429df 100644 --- a/src/Utils/Utils/FileUtils.cpp +++ b/src/Utils/Utils/FileUtils.cpp @@ -1,13 +1,23 @@ #include "FileUtils.h" +#include #include #include namespace fs = std::filesystem; +namespace +{ + void AddPathsStringIfDistinct(std::vector& output, std::string path) + { + if (std::ranges::find(output, path) == output.end()) + output.emplace_back(std::move(path)); + }; +} // namespace + namespace utils { - bool ParsePathsString(const std::string& pathsString, std::set& output) + bool ParsePathsString(const std::string& pathsString, std::vector& output) { std::ostringstream currentPath; auto pathStart = true; @@ -39,7 +49,7 @@ namespace utils auto path = currentPath.str(); if (!path.empty()) { - output.insert(path); + AddPathsStringIfDistinct(output, std::move(path)); currentPath = std::ostringstream(); } @@ -61,7 +71,7 @@ namespace utils if (currentPath.tellp() > 0) { - output.insert(currentPath.str()); + AddPathsStringIfDistinct(output, currentPath.str()); } return true; diff --git a/src/Utils/Utils/FileUtils.h b/src/Utils/Utils/FileUtils.h index faa196fbc..548d801ca 100644 --- a/src/Utils/Utils/FileUtils.h +++ b/src/Utils/Utils/FileUtils.h @@ -3,9 +3,9 @@ #include #include #include -#include #include #include +#include namespace utils { @@ -17,10 +17,10 @@ namespace utils /** * \brief Splits a path string as user input into a list of paths. * \param pathsString The path string that was taken as user input. - * \param output A set for strings to save the output to. + * \param output A list to append the paths to in the order they are specified, skipping paths it already contains. * \return \c true if the user input was valid and could be processed successfully, otherwise \c false. */ - bool ParsePathsString(const std::string& pathsString, std::set& output); + bool ParsePathsString(const std::string& pathsString, std::vector& output); enum class TextFileCheckDirtyResult : std::uint8_t { diff --git a/test/CommonTests/Utils/FileUtilsTests.cpp b/test/CommonTests/Utils/FileUtilsTests.cpp new file mode 100644 index 000000000..87cf48ed1 --- /dev/null +++ b/test/CommonTests/Utils/FileUtilsTests.cpp @@ -0,0 +1,21 @@ +#include "Utils/FileUtils.h" + +#include +#include +#include + +TEST_CASE("FileUtils: Parses paths string in specified order", "[fileutils]") +{ + std::vector paths; + + REQUIRE(utils::ParsePathsString("zone_raw/b;zone_raw/a;\"raw;c\"", paths)); + REQUIRE(paths == std::vector{"zone_raw/b", "zone_raw/a", "raw;c"}); +} + +TEST_CASE("FileUtils: Parses paths string keeping first of duplicate paths", "[fileutils]") +{ + std::vector paths{"raw"}; + + REQUIRE(utils::ParsePathsString("zone_raw;raw;zone_raw;lang", paths)); + REQUIRE(paths == std::vector{"raw", "zone_raw", "lang"}); +}