Skip to content

Commit

Permalink
Add Path::removeNonEmpty() to remove non-empty dir
Browse files Browse the repository at this point in the history
We've implemented a function in slang-record-replay unit test
to remove the non-empty directory, now move this function into
slang `Path` namespace to make this function as an utility.

Close issue #4916
  • Loading branch information
kaizhangNV committed Sep 3, 2024
1 parent 45e0eee commit a3f010a
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 54 deletions.
57 changes: 57 additions & 0 deletions source/core/slang-io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifdef _WIN32
# include <direct.h>
# include <windows.h>
# include <shellapi.h>
#endif

#if defined(__linux__) || defined(__CYGWIN__) || SLANG_APPLE_FAMILY
Expand All @@ -27,6 +28,7 @@
# include <dirent.h>
# include <sys/stat.h>
# include <sys/file.h>
# include <ftw.h> // for nftw
#endif

#if SLANG_APPLE_FAMILY
Expand Down Expand Up @@ -777,6 +779,61 @@ namespace Slang
#endif
}

/* static */SlangResult Path::removeNonEmpty(const String& path)
{
if (File::exists(path) == false)
{
return SLANG_OK;
}

StringBuilder msgBuilder;
// Path::remove() doesn't support remove a non-empty directory, so we need to implement
// a simple function to remove the directory recursively.
#ifdef _WIN32
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa
// Note: the fromPath requires a double-null-terminated string.
String newPath = path;
newPath.append('\0');
SHFILEOPSTRUCTA file_op = {
NULL,
FO_DELETE,
newPath.begin(),
"",
FOF_NOCONFIRMATION |
FOF_NOERRORUI |
FOF_SILENT,
false,
0,
"" };
int ret = SHFileOperationA(&file_op);
if (ret)
{
return SLANG_FAIL;
}
#else
auto unlink_cb = [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int
{
SLANG_UNUSED(sb)
SLANG_UNUSED(typeflag)
SLANG_UNUSED(ftwbuf)
int rv = ::remove(fpath);
if (rv)
{
perror(fpath);
}
return rv;
};
// https://linux.die.net/man/3/nftw
int ret = ::nftw(path.begin(), unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
if (ret)
{
return SLANG_FAIL;
}
#endif

return SLANG_OK;
}

#if defined(_WIN32)
/* static */SlangResult Path::find(const String& directoryPath, const char* pattern, Visitor* visitor)
{
Expand Down
5 changes: 5 additions & 0 deletions source/core/slang-io.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ namespace Slang
/// @return SLANG_OK if file or directory is removed
static SlangResult remove(const String& path);

/// Remove a file or directory at specified path. The directory can be non-empty.
/// @param path
/// @return SLANG_OK if file or directory is removed
static SlangResult removeNonEmpty(const String& path);

static bool equals(String path1, String path2);

/// Turn `path` into a relative path from base.
Expand Down
59 changes: 5 additions & 54 deletions tools/slang-unit-test/unit-test-record-replay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@

#include "tools/unit-test/slang-unit-test.h"

#ifdef _WIN32
#include <windows.h>
#include <shellapi.h>
#else
#include <ftw.h>
#endif

#include <chrono>
#include <thread>

Expand Down Expand Up @@ -355,55 +348,13 @@ static SlangResult resultCompare(List<entryHashInfo> const& expectHashes, List<e

static SlangResult cleanupRecordFiles()
{
if (File::exists("slang-record") == false)
{
return SLANG_OK;
}

StringBuilder msgBuilder;
// Path::remove() doesn't support remove a non-empty directory, so we need to implement
// a simple function to remove the directory recursively.
#ifdef _WIN32
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa
SHFILEOPSTRUCTA file_op = {
NULL,
FO_DELETE,
"slang-record",
"",
FOF_NOCONFIRMATION |
FOF_NOERRORUI |
FOF_SILENT,
false,
0,
"" };
int ret = SHFileOperationA(&file_op);
if (ret)
{
msgBuilder << "fail to remove 'slang-record' dir, error: " << ret << "\n";
getTestReporter()->message(TestMessageType::TestFailure, msgBuilder.toString().getBuffer());
return SLANG_FAIL;
}
#else
auto unlink_cb = [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int
{
int rv = ::remove(fpath);
if (rv)
{
perror(fpath);
}
return rv;
};
// https://linux.die.net/man/3/nftw
int ret = nftw("slang-record", unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
if (ret)
SlangResult res = Path::removeNonEmpty("slang-record");
if (SLANG_FAILED(res))
{
msgBuilder << "fail to remove 'slang-record' dir, error: " << ret << ", " << strerror(errno) << "\n";
getTestReporter()->message(TestMessageType::TestFailure, msgBuilder.toString().getBuffer());
return SLANG_FAIL;
getTestReporter()->message(TestMessageType::TestFailure, "Failed to remove 'slang-record' directory\n");
}
#endif

return SLANG_OK;
return res;
}

static SlangResult runTest(UnitTestContext* context, const char* testName)
Expand All @@ -427,7 +378,7 @@ static SlangResult runTest(UnitTestContext* context, const char* testName)
}

error:
cleanupRecordFiles();
res = cleanupRecordFiles();
return res;
}

Expand Down

0 comments on commit a3f010a

Please sign in to comment.