Skip to content

Commit 77d3a07

Browse files
committed
Fix std::filesystem
This change makes a second pass, at fixing the errno issue with libcxx's filesystem code. Previously, 89.01% of LLVM's test suite was passing and now 98.59% of their tests pass. Best of all, it's now possible for Clang to be built as a working APE binary that can to compile the Cosmopolitan repository. Please note it has only been vetted so far for some objects, and more work would obviously need to be done in cosmo, to fix warnings.
1 parent 0d7c272 commit 77d3a07

File tree

14 files changed

+408
-69
lines changed

14 files changed

+408
-69
lines changed

test/libcxx/errc_test.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <cerrno>
2+
#include <system_error>
3+
4+
bool test_errc_mapping(int posix_error, std::errc expected_errc) {
5+
std::error_code ec(posix_error, std::generic_category());
6+
return ec.value() == posix_error && //
7+
std::error_condition(expected_errc) == ec;
8+
}
9+
10+
int main() {
11+
if (!test_errc_mapping(EACCES, std::errc::permission_denied))
12+
return 1;
13+
if (!test_errc_mapping(ENOENT, std::errc::no_such_file_or_directory))
14+
return 2;
15+
if (!test_errc_mapping(EEXIST, std::errc::file_exists))
16+
return 3;
17+
if (!test_errc_mapping(EINVAL, std::errc::invalid_argument))
18+
return 4;
19+
if (!test_errc_mapping(ENOSPC, std::errc::no_space_on_device))
20+
return 5;
21+
return 0;
22+
}

test/libcxx/filesystem_test.cc

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
#include <chrono>
2+
#include <cstdio>
3+
#include <filesystem>
4+
#include <fstream>
5+
#include <random>
6+
#include <string>
7+
#include <thread>
8+
9+
#define ASSERT(condition) \
10+
if (!(condition)) { \
11+
fprintf(stderr, "%s:%d: test failed: %s\n", __FILE__, __LINE__, \
12+
#condition); \
13+
return 1; \
14+
}
15+
16+
namespace fs = std::filesystem;
17+
18+
fs::path g_temp_path;
19+
fs::path g_orig_path;
20+
std::string g_tmpdir;
21+
22+
void setup() {
23+
g_orig_path = fs::current_path();
24+
fs::path temp_path = fs::temp_directory_path();
25+
auto now = std::chrono::system_clock::now();
26+
auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
27+
auto value = now_ms.time_since_epoch();
28+
long duration = value.count();
29+
std::random_device rd;
30+
std::mt19937 gen(rd());
31+
std::uniform_int_distribution<> dis(0, 999999);
32+
int random_number = dis(gen);
33+
std::string dir_name =
34+
"temp_" + std::to_string(duration) + "_" + std::to_string(random_number);
35+
g_temp_path = temp_path / dir_name;
36+
fs::create_directory(g_temp_path);
37+
fs::current_path(g_temp_path);
38+
fs::create_directory("tmp");
39+
g_tmpdir = fs::absolute("tmp");
40+
setenv("TMPDIR", g_tmpdir.c_str(), true);
41+
}
42+
43+
void teardown() {
44+
fs::current_path(g_orig_path);
45+
fs::remove_all(g_temp_path);
46+
}
47+
48+
int test_create_directory() {
49+
fs::path dir = "test_dir";
50+
ASSERT(fs::create_directory(dir));
51+
ASSERT(fs::is_directory(dir));
52+
ASSERT(!fs::create_directory(dir));
53+
fs::remove(dir);
54+
return 0;
55+
}
56+
57+
int test_create_directories() {
58+
fs::path dirs = "test_dir/nested/deep";
59+
ASSERT(fs::create_directories(dirs));
60+
ASSERT(fs::is_directory(dirs));
61+
ASSERT(!fs::create_directories(dirs));
62+
fs::remove_all("test_dir");
63+
return 0;
64+
}
65+
66+
int test_remove() {
67+
fs::path file = "test_file.txt";
68+
std::ofstream(file).put('a');
69+
ASSERT(fs::remove(file));
70+
ASSERT(!fs::remove(file));
71+
return 0;
72+
}
73+
74+
int test_remove_all() {
75+
fs::path dir = "test_dir/nested/deep";
76+
fs::create_directories(dir);
77+
ASSERT(fs::remove_all("test_dir") > 0);
78+
ASSERT(fs::remove_all("test_dir") == 0);
79+
return 0;
80+
}
81+
82+
int test_rename() {
83+
fs::path old_name = "old.txt";
84+
fs::path new_name = "new.txt";
85+
std::ofstream(old_name).put('a');
86+
fs::rename(old_name, new_name);
87+
ASSERT(!fs::exists(old_name));
88+
ASSERT(fs::exists(new_name));
89+
fs::remove(new_name);
90+
return 0;
91+
}
92+
93+
int test_copy() {
94+
fs::path src = "src.txt";
95+
fs::path dst = "dst.txt";
96+
std::ofstream(src) << "test";
97+
fs::copy(src, dst);
98+
ASSERT(fs::exists(dst));
99+
ASSERT(fs::file_size(src) == fs::file_size(dst));
100+
fs::remove(src);
101+
fs::remove(dst);
102+
return 0;
103+
}
104+
105+
int test_copy_file() {
106+
fs::path src = "src.txt";
107+
fs::path dst = "dst.txt";
108+
std::ofstream(src) << "test";
109+
ASSERT(fs::copy_file(src, dst));
110+
ASSERT(!fs::copy_file(src, dst, fs::copy_options::skip_existing));
111+
fs::remove(src);
112+
fs::remove(dst);
113+
return 0;
114+
}
115+
116+
int test_exists() {
117+
fs::path file = "test.txt";
118+
ASSERT(!fs::exists(file));
119+
std::ofstream(file).put('a');
120+
ASSERT(fs::exists(file));
121+
fs::remove(file);
122+
return 0;
123+
}
124+
125+
int test_is_regular_file() {
126+
fs::path file = "test.txt";
127+
fs::path dir = "test_dir";
128+
std::ofstream(file).put('a');
129+
fs::create_directory(dir);
130+
ASSERT(fs::is_regular_file(file));
131+
ASSERT(!fs::is_regular_file(dir));
132+
fs::remove(file);
133+
fs::remove(dir);
134+
return 0;
135+
}
136+
137+
int test_is_directory() {
138+
fs::path file = "test.txt";
139+
fs::path dir = "test_dir";
140+
std::ofstream(file).put('a');
141+
fs::create_directory(dir);
142+
ASSERT(!fs::is_directory(file));
143+
ASSERT(fs::is_directory(dir));
144+
fs::remove(file);
145+
fs::remove(dir);
146+
return 0;
147+
}
148+
149+
int test_is_symlink() {
150+
fs::path file = "test.txt";
151+
fs::path link = "test_link";
152+
std::ofstream(file).put('a');
153+
fs::create_symlink(file, link);
154+
ASSERT(!fs::is_symlink(file));
155+
ASSERT(fs::is_symlink(link));
156+
fs::remove(file);
157+
fs::remove(link);
158+
return 0;
159+
}
160+
161+
int test_file_size() {
162+
fs::path file = "test.txt";
163+
std::ofstream(file) << "test";
164+
ASSERT(fs::file_size(file) == 4);
165+
fs::remove(file);
166+
return 0;
167+
}
168+
169+
int test_last_write_time() {
170+
fs::path file = "test.txt";
171+
auto now = fs::file_time_type::clock::now();
172+
std::ofstream(file).put('a');
173+
fs::last_write_time(file, now);
174+
ASSERT(fs::last_write_time(file) == now);
175+
fs::remove(file);
176+
return 0;
177+
}
178+
179+
int test_permissions() {
180+
fs::path file = "test.txt";
181+
std::ofstream(file).put('a');
182+
fs::permissions(file, fs::perms::owner_read | fs::perms::owner_write);
183+
auto perms = fs::status(file).permissions();
184+
ASSERT((perms & fs::perms::owner_read) != fs::perms::none);
185+
ASSERT((perms & fs::perms::owner_write) != fs::perms::none);
186+
ASSERT((perms & fs::perms::owner_exec) == fs::perms::none);
187+
fs::remove(file);
188+
return 0;
189+
}
190+
191+
int test_current_path() {
192+
auto original_path = fs::current_path();
193+
fs::path new_path = fs::temp_directory_path();
194+
fs::current_path(new_path);
195+
ASSERT(fs::current_path() == new_path);
196+
fs::current_path(original_path);
197+
return 0;
198+
}
199+
200+
int test_absolute() {
201+
fs::path relative = "test.txt";
202+
auto abs_path = fs::absolute(relative);
203+
ASSERT(abs_path.is_absolute());
204+
return 0;
205+
}
206+
207+
int test_canonical() {
208+
fs::path dir = "test_dir";
209+
fs::path file = dir / "test.txt";
210+
fs::create_directories(dir);
211+
std::ofstream(file).put('a');
212+
auto can_path = fs::canonical(file);
213+
ASSERT(can_path.is_absolute());
214+
ASSERT(!can_path.lexically_normal().string().empty());
215+
fs::remove_all(dir);
216+
return 0;
217+
}
218+
219+
int test_read_symlink() {
220+
fs::path file = "test.txt";
221+
fs::path link = "test_link";
222+
std::ofstream(file).put('a');
223+
fs::create_symlink(file, link);
224+
ASSERT(fs::read_symlink(link) == file);
225+
fs::remove(file);
226+
fs::remove(link);
227+
return 0;
228+
}
229+
230+
int test_create_symlink_and_hard_link() {
231+
fs::path file = "test.txt";
232+
fs::path symlink = "test_symlink";
233+
fs::path hardlink = "test_hardlink";
234+
std::ofstream(file).put('a');
235+
fs::create_symlink(file, symlink);
236+
fs::create_hard_link(file, hardlink);
237+
ASSERT(fs::exists(symlink));
238+
ASSERT(fs::exists(hardlink));
239+
ASSERT(fs::is_symlink(symlink));
240+
ASSERT(!fs::is_symlink(hardlink));
241+
fs::remove(file);
242+
fs::remove(symlink);
243+
fs::remove(hardlink);
244+
return 0;
245+
}
246+
247+
int test_space() {
248+
auto space_info = fs::space(".");
249+
ASSERT(space_info.capacity > 0);
250+
ASSERT(space_info.free > 0);
251+
ASSERT(space_info.available > 0);
252+
return 0;
253+
}
254+
255+
int test_equivalent() {
256+
fs::path file1 = "test1.txt";
257+
fs::path file2 = "test2.txt";
258+
std::ofstream(file1).put('a');
259+
fs::create_hard_link(file1, file2);
260+
ASSERT(fs::equivalent(file1, file2));
261+
fs::remove(file1);
262+
fs::remove(file2);
263+
return 0;
264+
}
265+
266+
int test_resize_file() {
267+
fs::path file = "test.txt";
268+
std::ofstream(file) << "test";
269+
fs::resize_file(file, 10);
270+
ASSERT(fs::file_size(file) == 10);
271+
fs::remove(file);
272+
return 0;
273+
}
274+
275+
int test_status() {
276+
fs::path file = "test.txt";
277+
std::ofstream(file).put('a');
278+
auto status = fs::status(file);
279+
ASSERT(status.type() == fs::file_type::regular);
280+
fs::remove(file);
281+
return 0;
282+
}
283+
284+
int test_copy_enoent() {
285+
fs::path src = "non_existent_file.txt";
286+
fs::path dst = "destination.txt";
287+
try {
288+
fs::copy(src, dst);
289+
ASSERT(false);
290+
} catch (const fs::filesystem_error& e) {
291+
if (e.code() == std::errc::no_such_file_or_directory) {
292+
return 0;
293+
} else {
294+
ASSERT(false);
295+
}
296+
} catch (const std::exception& e) {
297+
ASSERT(false);
298+
}
299+
}
300+
301+
#define RUN(func) \
302+
result = func(); \
303+
if (result) \
304+
return result
305+
306+
int test() {
307+
int result = 0;
308+
RUN(test_copy_enoent);
309+
RUN(test_create_directory);
310+
RUN(test_create_directories);
311+
RUN(test_remove);
312+
RUN(test_remove_all);
313+
RUN(test_rename);
314+
RUN(test_copy);
315+
RUN(test_copy_file);
316+
RUN(test_exists);
317+
RUN(test_is_regular_file);
318+
RUN(test_is_directory);
319+
RUN(test_is_symlink);
320+
RUN(test_file_size);
321+
RUN(test_last_write_time);
322+
RUN(test_permissions);
323+
RUN(test_current_path);
324+
RUN(test_absolute);
325+
RUN(test_canonical);
326+
RUN(test_read_symlink);
327+
RUN(test_create_symlink_and_hard_link);
328+
RUN(test_space);
329+
RUN(test_equivalent);
330+
RUN(test_resize_file);
331+
RUN(test_status);
332+
return 0;
333+
}
334+
335+
int main() {
336+
int rc;
337+
setup();
338+
rc = test();
339+
teardown();
340+
return rc;
341+
}

third_party/libcxx/BUILD.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,11 +1094,11 @@ third_party/libcxx/fs/filesystem_clock.cpp \
10941094
third_party/libcxx/fs/filesystem_error.cpp \
10951095
third_party/libcxx/fs/int128_builtins.cpp \
10961096
third_party/libcxx/fs/operations.cpp \
1097-
third_party/libcxx/fs/cosmo.cpp \
10981097
third_party/libcxx/fs/path.cpp \
10991098
third_party/libcxx/ryu/d2fixed.cpp \
11001099
third_party/libcxx/ryu/d2s.cpp \
11011100
third_party/libcxx/ryu/f2s.cpp \
1101+
third_party/libcxx/errc.cpp \
11021102

11031103
THIRD_PARTY_LIBCXX_A_HDRS_CHECKEM = \
11041104
third_party/libcxx/__assertion_handler \

third_party/libcxx/__system_error/errc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
141141
// This leads to the odd pushing and popping of the deprecated
142142
// diagnostic.
143143
_LIBCPP_DECLARE_STRONG_ENUM(errc){
144+
success = 0,
144145
address_family_not_supported = 65536, // = EAFNOSUPPORT,
145146
address_in_use, // = EADDRINUSE,
146147
address_not_available, // = EADDRNOTAVAIL,
@@ -221,6 +222,9 @@ _LIBCPP_DECLARE_STRONG_ENUM(errc){
221222
wrong_protocol_type};
222223
_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(errc)
223224

225+
errc __err_to_errc(int) noexcept;
226+
int __errc_to_err(errc) noexcept;
227+
224228
_LIBCPP_END_NAMESPACE_STD
225229

226230
#endif // _LIBCPP___ERRC

0 commit comments

Comments
 (0)