diff options
-rw-r--r-- | src/buildtool/file_system/file_system_manager.hpp | 41 | ||||
-rw-r--r-- | test/buildtool/file_system/file_system_manager.test.cpp | 41 |
2 files changed, 81 insertions, 1 deletions
diff --git a/src/buildtool/file_system/file_system_manager.hpp b/src/buildtool/file_system/file_system_manager.hpp index 5d86a4ff..868acbcc 100644 --- a/src/buildtool/file_system/file_system_manager.hpp +++ b/src/buildtool/file_system/file_system_manager.hpp @@ -23,6 +23,7 @@ #include <filesystem> #include <fstream> #include <optional> +#include <unordered_set> #include <fcntl.h> @@ -56,6 +57,9 @@ class FileSystemManager { using ReadDirEntryFunc = std::function<bool(std::filesystem::path const&, ObjectType type)>; + using UseDirEntryFunc = + std::function<bool(std::filesystem::path const&, bool /*is_tree*/)>; + class DirectoryAnchor { friend class FileSystemManager; @@ -724,6 +728,43 @@ class FileSystemManager { return true; } + /// \brief Read all entries recursively in a filesystem directory tree. + /// \param dir root directory to traverse + /// \param use_entry callback to call with found valid entries + /// \param ignored_subdirs directory names to be ignored wherever found in + /// the directory tree of dir. + [[nodiscard]] static auto ReadDirectoryEntriesRecursive( + std::filesystem::path const& dir, + UseDirEntryFunc const& use_entry, + std::unordered_set<std::string> const& ignored_subdirs = {}) noexcept + -> bool { + try { + // constructor of this iterator points to end by default; + for (auto it = std::filesystem::recursive_directory_iterator(dir); + it != std::filesystem::recursive_directory_iterator(); + ++it) { + // check for ignored subdirs + if (std::filesystem::is_directory(it->symlink_status()) and + ignored_subdirs.contains(*--it->path().end())) { + it.disable_recursion_pending(); + continue; + } + // use the entry + if (not use_entry( + it->path().lexically_relative(dir), + std::filesystem::is_directory(it->symlink_status()))) { + return false; + } + } + } catch (std::exception const& ex) { + Logger::Log(LogLevel::Error, + "reading directory {} recursively failed", + dir.string()); + return false; + } + return true; + } + /// \brief Read the content of a symlink. [[nodiscard]] static auto ReadSymlink(std::filesystem::path const& link) -> std::optional<std::string> { diff --git a/test/buildtool/file_system/file_system_manager.test.cpp b/test/buildtool/file_system/file_system_manager.test.cpp index b4a22035..0c2ad292 100644 --- a/test/buildtool/file_system/file_system_manager.test.cpp +++ b/test/buildtool/file_system/file_system_manager.test.cpp @@ -83,7 +83,10 @@ class SymlinkTestsFixture { using filetree_t = std::unordered_map<std::string, ObjectType>; filetree_t const kExpected = {{"foo", ObjectType::File}, {"baz", ObjectType::Tree}, - {"baz/foo", ObjectType::File}}; + {"baz/foo", ObjectType::File}, + {"bazz", ObjectType::Tree}, + {"bazz/baz", ObjectType::Tree}, + {"bazz/baz/foo", ObjectType::File}}; struct LinkInfo { std::string to; @@ -117,6 +120,11 @@ class SymlinkTestsFixture { .resolvesToExisting = false, .isNonUpwards = false}}; + // distinct dir entries + size_t const num_entries_{12U}; + // distinct dir entries after removing all subdirs named "baz" + size_t const num_root_file_entries_{5U}; + void create_files() { for (auto const& [path, type] : kExpected) { switch (type) { @@ -778,6 +786,11 @@ TEST_CASE_METHOD(CopyFileFixture, "CreateFileHardlinkAs", "[file_system]") { } TEST_CASE_METHOD(SymlinkTestsFixture, "Symlinks", "[file_system]") { + CHECK(std::filesystem::is_directory(root_dir_ / "baz")); + CHECK(std::filesystem::is_symlink(root_dir_ / "baz_l")); + CHECK_FALSE(std::filesystem::is_directory( + std::filesystem::symlink_status(root_dir_ / "baz_l"))); + auto i = GENERATE(range(0U, 6U /* kSymExpected.size() */)); SECTION(fmt::format("Non-upwards symlinks - entry {}", i)) { @@ -793,3 +806,29 @@ TEST_CASE_METHOD(SymlinkTestsFixture, "Symlinks", "[file_system]") { kSymExpected[i].resolvesToExisting); } } + +TEST_CASE_METHOD(SymlinkTestsFixture, + "ReadDirectoryEntriesRecursive", + "[file_system]") { + size_t count{}; + auto use_entry = [&count](std::filesystem::path const& /*name*/, + bool /*is_tree*/) { + ++count; + return true; + }; + + SECTION("Check directory is complete") { + REQUIRE(FileSystemManager::ReadDirectoryEntriesRecursive(root_dir_, + use_entry)); + CHECK(count == num_entries_); + } + + SECTION("Check directory with missing paths") { + REQUIRE(FileSystemManager::ReadDirectoryEntriesRecursive( + root_dir_, + use_entry, + /*ignored_subdirs=*/ + {"baz"})); + CHECK(count == num_root_file_entries_); + } +} |