Skip to content

Commit 1673cf9

Browse files
authored
Add Path::removeNonEmpty() to remove non-empty dir (shader-slang#4984)
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 shader-slang#4916
1 parent 45e0eee commit 1673cf9

File tree

3 files changed

+67
-54
lines changed

3 files changed

+67
-54
lines changed

source/core/slang-io.cpp

+57
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifdef _WIN32
1818
# include <direct.h>
1919
# include <windows.h>
20+
# include <shellapi.h>
2021
#endif
2122

2223
#if defined(__linux__) || defined(__CYGWIN__) || SLANG_APPLE_FAMILY
@@ -27,6 +28,7 @@
2728
# include <dirent.h>
2829
# include <sys/stat.h>
2930
# include <sys/file.h>
31+
# include <ftw.h> // for nftw
3032
#endif
3133

3234
#if SLANG_APPLE_FAMILY
@@ -777,6 +779,61 @@ namespace Slang
777779
#endif
778780
}
779781

782+
/* static */SlangResult Path::removeNonEmpty(const String& path)
783+
{
784+
if (File::exists(path) == false)
785+
{
786+
return SLANG_OK;
787+
}
788+
789+
StringBuilder msgBuilder;
790+
// Path::remove() doesn't support remove a non-empty directory, so we need to implement
791+
// a simple function to remove the directory recursively.
792+
#ifdef _WIN32
793+
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa
794+
// Note: the fromPath requires a double-null-terminated string.
795+
String newPath = path;
796+
newPath.append('\0');
797+
SHFILEOPSTRUCTA file_op = {
798+
NULL,
799+
FO_DELETE,
800+
newPath.begin(),
801+
nullptr,
802+
FOF_NOCONFIRMATION |
803+
FOF_NOERRORUI |
804+
FOF_SILENT,
805+
false,
806+
0,
807+
nullptr };
808+
int ret = SHFileOperationA(&file_op);
809+
if (ret)
810+
{
811+
return SLANG_FAIL;
812+
}
813+
#else
814+
auto unlink_cb = [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int
815+
{
816+
SLANG_UNUSED(sb)
817+
SLANG_UNUSED(typeflag)
818+
SLANG_UNUSED(ftwbuf)
819+
int rv = ::remove(fpath);
820+
if (rv)
821+
{
822+
perror(fpath);
823+
}
824+
return rv;
825+
};
826+
// https://linux.die.net/man/3/nftw
827+
int ret = ::nftw(path.begin(), unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
828+
if (ret)
829+
{
830+
return SLANG_FAIL;
831+
}
832+
#endif
833+
834+
return SLANG_OK;
835+
}
836+
780837
#if defined(_WIN32)
781838
/* static */SlangResult Path::find(const String& directoryPath, const char* pattern, Visitor* visitor)
782839
{

source/core/slang-io.h

+5
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ namespace Slang
217217
/// @return SLANG_OK if file or directory is removed
218218
static SlangResult remove(const String& path);
219219

220+
/// Remove a file or directory at specified path. The directory can be non-empty.
221+
/// @param path
222+
/// @return SLANG_OK if file or directory is removed
223+
static SlangResult removeNonEmpty(const String& path);
224+
220225
static bool equals(String path1, String path2);
221226

222227
/// Turn `path` into a relative path from base.

tools/slang-unit-test/unit-test-record-replay.cpp

+5-54
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@
99

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

12-
#ifdef _WIN32
13-
#include <windows.h>
14-
#include <shellapi.h>
15-
#else
16-
#include <ftw.h>
17-
#endif
18-
1912
#include <chrono>
2013
#include <thread>
2114

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

356349
static SlangResult cleanupRecordFiles()
357350
{
358-
if (File::exists("slang-record") == false)
359-
{
360-
return SLANG_OK;
361-
}
362-
363-
StringBuilder msgBuilder;
364-
// Path::remove() doesn't support remove a non-empty directory, so we need to implement
365-
// a simple function to remove the directory recursively.
366-
#ifdef _WIN32
367-
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa
368-
SHFILEOPSTRUCTA file_op = {
369-
NULL,
370-
FO_DELETE,
371-
"slang-record",
372-
"",
373-
FOF_NOCONFIRMATION |
374-
FOF_NOERRORUI |
375-
FOF_SILENT,
376-
false,
377-
0,
378-
"" };
379-
int ret = SHFileOperationA(&file_op);
380-
if (ret)
381-
{
382-
msgBuilder << "fail to remove 'slang-record' dir, error: " << ret << "\n";
383-
getTestReporter()->message(TestMessageType::TestFailure, msgBuilder.toString().getBuffer());
384-
return SLANG_FAIL;
385-
}
386-
#else
387-
auto unlink_cb = [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int
388-
{
389-
int rv = ::remove(fpath);
390-
if (rv)
391-
{
392-
perror(fpath);
393-
}
394-
return rv;
395-
};
396-
// https://linux.die.net/man/3/nftw
397-
int ret = nftw("slang-record", unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
398-
if (ret)
351+
SlangResult res = Path::removeNonEmpty("slang-record");
352+
if (SLANG_FAILED(res))
399353
{
400-
msgBuilder << "fail to remove 'slang-record' dir, error: " << ret << ", " << strerror(errno) << "\n";
401-
getTestReporter()->message(TestMessageType::TestFailure, msgBuilder.toString().getBuffer());
402-
return SLANG_FAIL;
354+
getTestReporter()->message(TestMessageType::TestFailure, "Failed to remove 'slang-record' directory\n");
403355
}
404-
#endif
405356

406-
return SLANG_OK;
357+
return res;
407358
}
408359

409360
static SlangResult runTest(UnitTestContext* context, const char* testName)
@@ -427,7 +378,7 @@ static SlangResult runTest(UnitTestContext* context, const char* testName)
427378
}
428379

429380
error:
430-
cleanupRecordFiles();
381+
res = cleanupRecordFiles();
431382
return res;
432383
}
433384

0 commit comments

Comments
 (0)