Skip to content

Commit 5c39eeb

Browse files
committed
[libcxx] [test] Fix filesystem_test_helper.h to compile for windows
Use .string() instead of .native() in places where we want to combine paths with std::string. Convert some methods to take a fs::path as parameter instead of std::string, for cases where they are called with paths as parameters (which can't be implicitly converted to std::string if the path's string_type is wstring). Differential Revision: https://reviews.llvm.org/D89530
1 parent afe40b3 commit 5c39eeb

File tree

2 files changed

+81
-25
lines changed

2 files changed

+81
-25
lines changed

libcxx/test/support/filesystem_test_helper.h

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
#include "filesystem_include.h"
55

66
#include <sys/stat.h> // for stat, mkdir, mkfifo
7+
#ifndef _WIN32
78
#include <unistd.h> // for ftruncate, link, symlink, getcwd, chdir
9+
#else
10+
#include <io.h>
11+
#include <direct.h>
12+
#include <windows.h> // for CreateSymbolicLink, CreateHardLink
13+
#endif
814

915
#include <cassert>
1016
#include <cstdio> // for printf
@@ -24,6 +30,28 @@
2430
#endif
2531

2632
namespace utils {
33+
#ifdef _WIN32
34+
inline int mkdir(const char* path, int mode) { (void)mode; return ::_mkdir(path); }
35+
inline int ftruncate(int fd, off_t length) { return ::_chsize(fd, length); }
36+
inline int symlink(const char* oldname, const char* newname, bool is_dir) {
37+
DWORD flags = is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
38+
if (CreateSymbolicLinkA(newname, oldname,
39+
flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
40+
return 0;
41+
if (GetLastError() != ERROR_INVALID_PARAMETER)
42+
return 1;
43+
return !CreateSymbolicLinkA(newname, oldname, flags);
44+
}
45+
inline int link(const char *oldname, const char* newname) {
46+
return !CreateHardLinkA(newname, oldname, NULL);
47+
}
48+
#else
49+
using ::mkdir;
50+
using ::ftruncate;
51+
inline int symlink(const char* oldname, const char* newname, bool is_dir) { (void)is_dir; return ::symlink(oldname, newname); }
52+
using ::link;
53+
#endif
54+
2755
inline std::string getcwd() {
2856
// Assume that path lengths are not greater than this.
2957
// This should be fine for testing purposes.
@@ -42,7 +70,13 @@ namespace utils {
4270
struct scoped_test_env
4371
{
4472
scoped_test_env() : test_root(available_cwd_path()) {
45-
std::string cmd = "mkdir -p " + test_root.native();
73+
#ifdef _WIN32
74+
// Windows mkdir can create multiple recursive directories
75+
// if needed.
76+
std::string cmd = "mkdir " + test_root.string();
77+
#else
78+
std::string cmd = "mkdir -p " + test_root.string();
79+
#endif
4680
int ret = std::system(cmd.c_str());
4781
assert(ret == 0);
4882

@@ -54,11 +88,16 @@ struct scoped_test_env
5488
}
5589

5690
~scoped_test_env() {
57-
std::string cmd = "chmod -R 777 " + test_root.native();
91+
#ifdef _WIN32
92+
std::string cmd = "rmdir /s /q " + test_root.string();
93+
int ret;
94+
#else
95+
std::string cmd = "chmod -R 777 " + test_root.string();
5896
int ret = std::system(cmd.c_str());
5997
assert(ret == 0);
6098

61-
cmd = "rm -r " + test_root.native();
99+
cmd = "rm -r " + test_root.string();
100+
#endif
62101
ret = std::system(cmd.c_str());
63102
assert(ret == 0);
64103
}
@@ -70,12 +109,12 @@ struct scoped_test_env
70109

71110
std::string sanitize_path(std::string raw) {
72111
assert(raw.find("..") == std::string::npos);
73-
std::string const& root = test_root.native();
112+
std::string root = test_root.string();
74113
if (root.compare(0, root.size(), raw, 0, root.size()) != 0) {
75114
assert(raw.front() != '\\');
76115
fs::path tmp(test_root);
77116
tmp /= raw;
78-
return std::move(const_cast<std::string&>(tmp.native()));
117+
return tmp.string();
79118
}
80119
return raw;
81120
}
@@ -85,10 +124,11 @@ struct scoped_test_env
85124
// but the caller is not (std::filesystem also uses uintmax_t rather than
86125
// off_t). On a 32-bit system this allows us to create a file larger than
87126
// 2GB.
88-
std::string create_file(std::string filename, uintmax_t size = 0) {
89-
#if defined(__LP64__)
127+
std::string create_file(fs::path filename_path, uintmax_t size = 0) {
128+
std::string filename = filename_path.string();
129+
#if defined(__LP64__) || defined(_WIN32)
90130
auto large_file_fopen = fopen;
91-
auto large_file_ftruncate = ftruncate;
131+
auto large_file_ftruncate = utils::ftruncate;
92132
using large_file_offset_t = off_t;
93133
#else
94134
auto large_file_fopen = fopen64;
@@ -104,7 +144,12 @@ struct scoped_test_env
104144
abort();
105145
}
106146

107-
FILE* file = large_file_fopen(filename.c_str(), "we");
147+
#ifndef _WIN32
148+
#define FOPEN_CLOEXEC_FLAG "e"
149+
#else
150+
#define FOPEN_CLOEXEC_FLAG ""
151+
#endif
152+
FILE* file = large_file_fopen(filename.c_str(), "w" FOPEN_CLOEXEC_FLAG);
108153
if (file == nullptr) {
109154
fprintf(stderr, "fopen %s failed: %s\n", filename.c_str(),
110155
strerror(errno));
@@ -123,38 +168,46 @@ struct scoped_test_env
123168
return filename;
124169
}
125170

126-
std::string create_dir(std::string filename) {
171+
std::string create_dir(fs::path filename_path) {
172+
std::string filename = filename_path.string();
127173
filename = sanitize_path(std::move(filename));
128-
int ret = ::mkdir(filename.c_str(), 0777); // rwxrwxrwx mode
174+
int ret = utils::mkdir(filename.c_str(), 0777); // rwxrwxrwx mode
129175
assert(ret == 0);
130176
return filename;
131177
}
132178

133-
std::string create_symlink(std::string source,
134-
std::string to,
135-
bool sanitize_source = true) {
179+
std::string create_symlink(fs::path source_path,
180+
fs::path to_path,
181+
bool sanitize_source = true,
182+
bool is_dir = false) {
183+
std::string source = source_path.string();
184+
std::string to = to_path.string();
136185
if (sanitize_source)
137186
source = sanitize_path(std::move(source));
138187
to = sanitize_path(std::move(to));
139-
int ret = ::symlink(source.c_str(), to.c_str());
188+
int ret = utils::symlink(source.c_str(), to.c_str(), is_dir);
140189
assert(ret == 0);
141190
return to;
142191
}
143192

144-
std::string create_hardlink(std::string source, std::string to) {
193+
std::string create_hardlink(fs::path source_path, fs::path to_path) {
194+
std::string source = source_path.string();
195+
std::string to = to_path.string();
145196
source = sanitize_path(std::move(source));
146197
to = sanitize_path(std::move(to));
147-
int ret = ::link(source.c_str(), to.c_str());
198+
int ret = utils::link(source.c_str(), to.c_str());
148199
assert(ret == 0);
149200
return to;
150201
}
151202

203+
#ifndef _WIN32
152204
std::string create_fifo(std::string file) {
153205
file = sanitize_path(std::move(file));
154206
int ret = ::mkfifo(file.c_str(), 0666); // rw-rw-rw- mode
155207
assert(ret == 0);
156208
return file;
157209
}
210+
#endif
158211

159212
// Some platforms doesn't support socket files so we shouldn't even
160213
// allow tests to call this unguarded.
@@ -186,7 +239,7 @@ struct scoped_test_env
186239
fs::path const base = tmp / cwd.filename();
187240
int i = 0;
188241
fs::path p = base / ("static_env." + std::to_string(i));
189-
while (utils::exists(p)) {
242+
while (utils::exists(p.string())) {
190243
p = fs::path(base) / ("static_env." + std::to_string(++i));
191244
}
192245
return p;
@@ -222,7 +275,7 @@ class static_test_env {
222275
env_.create_dir("dir1/dir2/dir3");
223276
env_.create_file("dir1/dir2/dir3/file5");
224277
env_.create_file("dir1/dir2/file4");
225-
env_.create_symlink("dir3", "dir1/dir2/symlink_to_dir3", false);
278+
env_.create_symlink("dir3", "dir1/dir2/symlink_to_dir3", false, true);
226279
env_.create_file("dir1/file1");
227280
env_.create_file("dir1/file2", 42);
228281
env_.create_file("empty_file");
@@ -398,7 +451,7 @@ std::size_t StrLen(CharT const* P) {
398451
// This hack forces path to allocate enough memory.
399452
inline void PathReserve(fs::path& p, std::size_t N) {
400453
auto const& native_ref = p.native();
401-
const_cast<std::string&>(native_ref).reserve(N);
454+
const_cast<fs::path::string_type&>(native_ref).reserve(N);
402455
}
403456

404457
template <class Iter1, class Iter2>
@@ -526,8 +579,8 @@ struct ExceptionChecker {
526579
}
527580
auto transform_path = [](const fs::path& p) {
528581
if (p.native().empty())
529-
return "\"\"";
530-
return p.c_str();
582+
return std::string("\"\"");
583+
return p.string();
531584
};
532585
std::string format = [&]() -> std::string {
533586
switch (num_paths) {
@@ -537,12 +590,12 @@ struct ExceptionChecker {
537590
case 1:
538591
return format_string("filesystem error: in %s: %s%s [%s]", func_name,
539592
additional_msg, message,
540-
transform_path(expected_path1));
593+
transform_path(expected_path1).c_str());
541594
case 2:
542595
return format_string("filesystem error: in %s: %s%s [%s] [%s]",
543596
func_name, additional_msg, message,
544-
transform_path(expected_path1),
545-
transform_path(expected_path2));
597+
transform_path(expected_path1).c_str(),
598+
transform_path(expected_path2).c_str());
546599
default:
547600
TEST_CHECK(false && "unexpected case");
548601
return "";

libcxx/utils/libcxx/test/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ def configure_compile_flags(self):
274274
if self.target_info.is_windows():
275275
# FIXME: Can we remove this?
276276
self.cxx.compile_flags += ['-D_CRT_SECURE_NO_WARNINGS']
277+
# Don't warn about using common but nonstandard unprefixed functions
278+
# like chdir, fileno.
279+
self.cxx.compile_flags += ['-D_CRT_NONSTDC_NO_WARNINGS']
277280
# Required so that tests using min/max don't fail on Windows,
278281
# and so that those tests don't have to be changed to tolerate
279282
# this insanity.

0 commit comments

Comments
 (0)