summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buildtool/file_system/file_system_manager.hpp41
-rw-r--r--test/buildtool/file_system/file_system_manager.test.cpp41
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_);
+ }
+}