Index: llvm/include/llvm/Support/FileSystem.h =================================================================== --- llvm/include/llvm/Support/FileSystem.h +++ llvm/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. Index: llvm/lib/Support/Path.cpp =================================================================== --- llvm/lib/Support/Path.cpp +++ llvm/lib/Support/Path.cpp @@ -911,6 +911,44 @@ return create_directory(P, IgnoreExisting, Perms); } +template +static std::error_code remove_directories_impl(const T &Entry, + bool IgnoreErrors) { + std::error_code EC; + directory_iterator Begin(Entry, EC); + 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 = remove(Item.path()); + 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; + remove(path); + return std::error_code(); +} + std::error_code copy_file(const Twine &From, const Twine &To) { int ReadFD, WriteFD; if (std::error_code EC = openFileForRead(From, ReadFD)) Index: llvm/unittests/Support/Path.cpp =================================================================== --- llvm/unittests/Support/Path.cpp +++ llvm/unittests/Support/Path.cpp @@ -768,6 +768,37 @@ 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(BaseDir)); + ASSERT_FALSE(fs::exists(BaseDir)); +} + const char archive[] = "!\x0A"; const char bitcode[] = "\xde\xc0\x17\x0b"; const char coff_object[] = "\x00\x00......";