Index: llvm/trunk/include/llvm/Support/FileSystem.h =================================================================== --- llvm/trunk/include/llvm/Support/FileSystem.h +++ llvm/trunk/include/llvm/Support/FileSystem.h @@ -361,6 +361,13 @@ /// returns error if the file didn't exist. std::error_code remove(const Twine &path, bool IgnoreNonExisting = true); +/// @brief Recursively delete a directory. +/// +/// @param path Input path. +/// @returns errc::success if path has been removed or didn't exist, otherwise a +/// platform-specific error code. +std::error_code remove_directories(const Twine &path, bool IgnoreErrors = true); + /// @brief Rename \a from to \a to. Files are renamed as if by POSIX rename(). /// /// @param from The path to rename from. @@ -792,12 +799,13 @@ /// called. class directory_entry { std::string Path; + bool FollowSymlinks; mutable file_status Status; public: - explicit directory_entry(const Twine &path, file_status st = file_status()) - : Path(path.str()) - , Status(st) {} + explicit directory_entry(const Twine &path, bool follow_symlinks = true, + file_status st = file_status()) + : Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {} directory_entry() = default; @@ -823,7 +831,7 @@ struct DirIterState; - std::error_code directory_iterator_construct(DirIterState &, StringRef); + std::error_code directory_iterator_construct(DirIterState &, StringRef, bool); std::error_code directory_iterator_increment(DirIterState &); std::error_code directory_iterator_destruct(DirIterState &); @@ -844,18 +852,24 @@ /// it call report_fatal_error on error. class directory_iterator { std::shared_ptr State; + bool FollowSymlinks = true; public: - explicit directory_iterator(const Twine &path, std::error_code &ec) { + explicit directory_iterator(const Twine &path, std::error_code &ec, + bool follow_symlinks = true) + : FollowSymlinks(follow_symlinks) { State = std::make_shared(); SmallString<128> path_storage; - ec = detail::directory_iterator_construct(*State, - path.toStringRef(path_storage)); + ec = detail::directory_iterator_construct( + *State, path.toStringRef(path_storage), FollowSymlinks); } - explicit directory_iterator(const directory_entry &de, std::error_code &ec) { + explicit directory_iterator(const directory_entry &de, std::error_code &ec, + bool follow_symlinks = true) + : FollowSymlinks(follow_symlinks) { State = std::make_shared(); - ec = detail::directory_iterator_construct(*State, de.path()); + ec = + detail::directory_iterator_construct(*State, de.path(), FollowSymlinks); } /// Construct end iterator. @@ -902,12 +916,15 @@ /// recurses down into child directories. class recursive_directory_iterator { std::shared_ptr State; + bool Follow; public: recursive_directory_iterator() = default; - explicit recursive_directory_iterator(const Twine &path, std::error_code &ec) - : State(std::make_shared()) { - State->Stack.push(directory_iterator(path, ec)); + explicit recursive_directory_iterator(const Twine &path, std::error_code &ec, + bool follow_symlinks = true) + : State(std::make_shared()), + Follow(follow_symlinks) { + State->Stack.push(directory_iterator(path, ec, Follow)); if (State->Stack.top() == directory_iterator()) State.reset(); } @@ -922,7 +939,7 @@ file_status st; if ((ec = State->Stack.top()->status(st))) return *this; if (is_directory(st)) { - State->Stack.push(directory_iterator(*State->Stack.top(), ec)); + State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow)); if (ec) return *this; if (State->Stack.top() != end_itr) { ++State->Level; Index: llvm/trunk/lib/Support/Path.cpp =================================================================== --- llvm/trunk/lib/Support/Path.cpp +++ llvm/trunk/lib/Support/Path.cpp @@ -1189,7 +1189,7 @@ } std::error_code directory_entry::status(file_status &result) const { - return fs::status(Path, result); + return fs::status(Path, result, FollowSymlinks); } } // end namespace fs Index: llvm/trunk/lib/Support/Unix/Path.inc =================================================================== --- llvm/trunk/lib/Support/Unix/Path.inc +++ llvm/trunk/lib/Support/Unix/Path.inc @@ -618,7 +618,8 @@ } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path){ + StringRef path, + bool follow_symlinks) { SmallString<128> path_null(path); DIR *directory = ::opendir(path_null.c_str()); if (!directory) @@ -627,7 +628,7 @@ it.IterationHandle = reinterpret_cast(directory); // Add something for replace_filename to replace. path::append(path_null, "."); - it.CurrentEntry = directory_entry(path_null.str()); + it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks); return directory_iterator_increment(it); } @@ -795,6 +796,46 @@ return std::error_code(); } +template +static std::error_code remove_directories_impl(const T &Entry, + bool IgnoreErrors) { + std::error_code EC; + directory_iterator Begin(Entry, EC, false); + directory_iterator End; + while (Begin != End) { + auto &Item = *Begin; + file_status st; + EC = Item.status(st); + if (EC && !IgnoreErrors) + return EC; + + if (is_directory(st)) { + EC = remove_directories_impl(Item, IgnoreErrors); + if (EC && !IgnoreErrors) + return EC; + } + + EC = fs::remove(Item.path(), true); + if (EC && !IgnoreErrors) + return EC; + + Begin.increment(EC); + if (EC && !IgnoreErrors) + return EC; + } + return std::error_code(); +} + +std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { + auto EC = remove_directories_impl(path, IgnoreErrors); + if (EC && !IgnoreErrors) + return EC; + EC = fs::remove(path, true); + if (EC && !IgnoreErrors) + return EC; + return std::error_code(); +} + } // end namespace fs namespace path { Index: llvm/trunk/lib/Support/Windows/Path.inc =================================================================== --- llvm/trunk/lib/Support/Windows/Path.inc +++ llvm/trunk/lib/Support/Windows/Path.inc @@ -26,6 +26,7 @@ // These two headers must be included last, and make sure shlobj is required // after Windows.h to make sure it picks up our definition of _WIN32_WINNT #include "WindowsSupport.h" +#include #include #undef max @@ -693,7 +694,8 @@ } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path){ + StringRef path, + bool follow_symlinks) { SmallVector path_utf16; if (std::error_code ec = widenPath(path, path_utf16)) @@ -738,7 +740,7 @@ it.IterationHandle = intptr_t(FindHandle.take()); SmallString<128> directory_entry_path(path); path::append(directory_entry_path, directory_entry_name_utf8); - it.CurrentEntry = directory_entry(directory_entry_path); + it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks); return std::error_code(); } @@ -920,6 +922,33 @@ return windows::UTF16ToUTF8(TempPath.data(), CharCount, ResultPath); } + +std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { + // Convert to utf-16. + SmallVector Path16; + std::error_code EC = widenPath(path, Path16); + if (EC && !IgnoreErrors) + return EC; + + // SHFileOperation() accepts a list of paths, and so must be double null- + // terminated to indicate the end of the list. The buffer is already null + // terminated, but since that null character is not considered part of the + // vector's size, pushing another one will just consume that byte. So we + // need to push 2 null terminators. + Path16.push_back(0); + Path16.push_back(0); + + SHFILEOPSTRUCTW shfos = {}; + shfos.wFunc = FO_DELETE; + shfos.pFrom = Path16.data(); + shfos.fFlags = FOF_NO_UI; + + int result = ::SHFileOperationW(&shfos); + if (result != 0 && !IgnoreErrors) + return mapWindowsError(result); + return std::error_code(); +} + } // end namespace fs namespace path { Index: llvm/trunk/unittests/Support/Path.cpp =================================================================== --- llvm/trunk/unittests/Support/Path.cpp +++ llvm/trunk/unittests/Support/Path.cpp @@ -743,6 +743,39 @@ ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel")); } +TEST_F(FileSystemTest, Remove) { + SmallString<64> BaseDir; + SmallString<64> Paths[4]; + int fds[4]; + ASSERT_NO_ERROR(fs::createUniqueDirectory("fs_remove", BaseDir)); + + ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/baz")); + ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/buzz")); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[0], Paths[0])); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[1], Paths[1])); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[2], Paths[2])); + ASSERT_NO_ERROR(fs::createUniqueFile( + Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[3], Paths[3])); + + for (int fd : fds) + ::close(fd); + + EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/baz")); + EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/buzz")); + EXPECT_TRUE(fs::exists(Paths[0])); + EXPECT_TRUE(fs::exists(Paths[1])); + EXPECT_TRUE(fs::exists(Paths[2])); + EXPECT_TRUE(fs::exists(Paths[3])); + + ASSERT_NO_ERROR(fs::remove_directories("D:/footest")); + + ASSERT_NO_ERROR(fs::remove_directories(BaseDir)); + ASSERT_FALSE(fs::exists(BaseDir)); +} + const char archive[] = "!\x0A"; const char bitcode[] = "\xde\xc0\x17\x0b"; const char coff_object[] = "\x00\x00......";