diff --git a/Dockerfile b/Dockerfile index 5758343..a7ac46c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,6 +39,7 @@ COPY wrappers/uasm-wrapper.sh $TOOLCHAIN_PREFIX/bin # Build everything that uses the llvm monorepo. We need to build the mingw runtime before the compiler-rt/libunwind/libcxxabi/libcxx runtimes. COPY build-llvm.sh strip-llvm.sh install-wrappers.sh build-mingw-w64.sh build-mingw-w64-tools.sh build-compiler-rt.sh build-mingw-w64-libraries.sh build-libcxx.sh build-openmp.sh ./ COPY wrappers/*.sh wrappers/*.c wrappers/*.h ./wrappers/ +COPY patches/llvm-project/*.patch ./patches/llvm-project/ RUN ./build-llvm.sh $TOOLCHAIN_PREFIX && \ ./strip-llvm.sh $TOOLCHAIN_PREFIX && \ ./install-wrappers.sh $TOOLCHAIN_PREFIX && \ diff --git a/Dockerfile.dev b/Dockerfile.dev index 9087e94..69a4a1c 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -26,6 +26,7 @@ ARG FULL_LLVM # Build LLVM COPY build-llvm.sh ./ +COPY patches/llvm-project/*.patch ./patches/llvm-project/ RUN ./build-llvm.sh $TOOLCHAIN_PREFIX # Strip the LLVM install output immediately. (This doesn't reduce the diff --git a/build-llvm.sh b/build-llvm.sh index 66372dc..423ae07 100755 --- a/build-llvm.sh +++ b/build-llvm.sh @@ -77,6 +77,11 @@ if [ -n "$SYNC" ] || [ -n "$CHECKOUT" ]; then cd llvm-project [ -z "$SYNC" ] || git fetch git checkout $LLVM_VERSION + if [ "$LLVM_VERSION" = "llvmorg-12.0.0" ]; then + # The bundled patches for std::filesystem apply on the 12.0.0 release, + # but don't try to apply them if a different version was requested. + git am -3 --keep-non-patch ../patches/llvm-project/*.patch + fi cd .. fi diff --git a/patches/llvm-project/0001-libcxx-Implement-the-stat-function-family-on-top-of-.patch b/patches/llvm-project/0001-libcxx-Implement-the-stat-function-family-on-top-of-.patch new file mode 100644 index 0000000..44ac256 --- /dev/null +++ b/patches/llvm-project/0001-libcxx-Implement-the-stat-function-family-on-top-of-.patch @@ -0,0 +1,332 @@ +From 4655075efa11ee17c28493d13760d51e360f7ddd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 16:59:07 +0200 +Subject: [PATCH 01/24] [libcxx] Implement the stat function family on top of + native windows APIs + +While the windows CRTs (the modern UCRT, and the legacy msvcrt.dll +that mingw still often defaults to) do provide stat functions, they're +a bit lacking - they only provide second precision on the modification +time, lack support for symlinks and a few other details. + +Instead reimplement them using a couple windows native functions, +getting exactly the info we need. (Technically, the implementation +within the CRT calls these functions anyway.) + +If we only need a few fields, we could also do with fewer calls, as a +later optimization. + +Differential Revision: https://reviews.llvm.org/D91141 + +(cherry picked from commit 2ff8662b5d16129ec6d1ee60dcec4f6ff8f717e2) +--- + libcxx/src/CMakeLists.txt | 1 + + libcxx/src/filesystem/filesystem_common.h | 32 ++++ + libcxx/src/filesystem/operations.cpp | 10 +- + libcxx/src/filesystem/posix_compat.h | 187 ++++++++++++++++++++++ + 4 files changed, 226 insertions(+), 4 deletions(-) + create mode 100644 libcxx/src/filesystem/posix_compat.h + +diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt +index 9965104cb5b2..0d95e766100d 100644 +--- a/libcxx/src/CMakeLists.txt ++++ b/libcxx/src/CMakeLists.txt +@@ -91,6 +91,7 @@ if (LIBCXX_ENABLE_FILESYSTEM) + filesystem/filesystem_common.h + filesystem/operations.cpp + filesystem/directory_iterator.cpp ++ filesystem/posix_compat.h + ) + # Filesystem uses __int128_t, which requires a definition of __muloi4 when + # compiled with UBSAN. This definition is not provided by libgcc_s, but is +diff --git a/libcxx/src/filesystem/filesystem_common.h b/libcxx/src/filesystem/filesystem_common.h +index e0fdbccf96b1..8204e9a2e5f8 100644 +--- a/libcxx/src/filesystem/filesystem_common.h ++++ b/libcxx/src/filesystem/filesystem_common.h +@@ -224,9 +224,41 @@ private: + using chrono::duration; + using chrono::duration_cast; + ++#if defined(_LIBCPP_WIN32API) ++// Various C runtime versions (UCRT, or the legacy msvcrt.dll used by ++// some mingw toolchains) provide different stat function implementations, ++// with a number of limitations with respect to what we want from the ++// stat function. Instead provide our own (in the anonymous detail namespace ++// in posix_compat.h) which does exactly what we want, along with our own ++// stat structure and flag macros. ++ ++struct TimeSpec { ++ int64_t tv_sec; ++ int64_t tv_nsec; ++}; ++struct StatT { ++ unsigned st_mode; ++ TimeSpec st_atim; ++ TimeSpec st_mtim; ++ uint64_t st_dev; // FILE_ID_INFO::VolumeSerialNumber ++ struct FileIdStruct { ++ unsigned char id[16]; // FILE_ID_INFO::FileId ++ bool operator==(const FileIdStruct &other) const { ++ for (int i = 0; i < 16; i++) ++ if (id[i] != other.id[i]) ++ return false; ++ return true; ++ } ++ } st_ino; ++ uint32_t st_nlink; ++ uintmax_t st_size; ++}; ++ ++#else + using TimeSpec = struct timespec; + using TimeVal = struct timeval; + using StatT = struct stat; ++#endif + + template ::value> +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 50a895dc2fae..548a0273ce71 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -17,6 +17,8 @@ + + #include "filesystem_common.h" + ++#include "posix_compat.h" ++ + #if defined(_LIBCPP_WIN32API) + # define WIN32_LEAN_AND_MEAN + # define NOMINMAX +@@ -495,7 +497,7 @@ file_status create_file_status(error_code& m_ec, path const& p, + + file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) { + error_code m_ec; +- if (::stat(p.c_str(), &path_stat) == -1) ++ if (detail::stat(p.c_str(), &path_stat) == -1) + m_ec = detail::capture_errno(); + return create_file_status(m_ec, p, path_stat, ec); + } +@@ -507,7 +509,7 @@ file_status posix_stat(path const& p, error_code* ec) { + + file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) { + error_code m_ec; +- if (::lstat(p.c_str(), &path_stat) == -1) ++ if (detail::lstat(p.c_str(), &path_stat) == -1) + m_ec = detail::capture_errno(); + return create_file_status(m_ec, p, path_stat, ec); + } +@@ -545,7 +547,7 @@ file_status FileDescriptor::refresh_status(error_code& ec) { + m_status = file_status{}; + m_stat = {}; + error_code m_ec; +- if (::fstat(fd, &m_stat) == -1) ++ if (detail::fstat(fd, &m_stat) == -1) + m_ec = capture_errno(); + m_status = create_file_status(m_ec, name, m_stat, &ec); + return m_status; +@@ -1197,7 +1199,7 @@ path __read_symlink(const path& p, error_code* ec) { + auto buff = std::unique_ptr(stack_buff); + #else + StatT sb; +- if (::lstat(p.c_str(), &sb) == -1) { ++ if (detail::lstat(p.c_str(), &sb) == -1) { + return err.report(capture_errno()); + } + const size_t size = sb.st_size + 1; +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +new file mode 100644 +index 000000000000..3eec4634e929 +--- /dev/null ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -0,0 +1,187 @@ ++//===----------------------------------------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++// ++// POSIX-like portability helper functions. ++// ++// These generally behave like the proper posix functions, with these ++// exceptions: ++// On Windows, they take paths in wchar_t* form, instead of char* form. ++// ++// These are provided within an anonymous namespace within the detail ++// namespace - callers need to include this header and call them as ++// detail::function(), regardless of platform. ++// ++ ++#ifndef POSIX_COMPAT_H ++#define POSIX_COMPAT_H ++ ++#include "filesystem" ++ ++#include "filesystem_common.h" ++ ++#if defined(_LIBCPP_WIN32API) ++# define WIN32_LEAN_AND_MEAN ++# define NOMINMAX ++# include ++# include ++#else ++# include ++# include ++# include ++#endif ++#include ++ ++_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM ++ ++namespace detail { ++namespace { ++ ++#if defined(_LIBCPP_WIN32API) ++ ++// Various C runtime header sets provide more or less of these. As we ++// provide our own implementation, undef all potential defines from the ++// C runtime headers and provide a complete set of macros of our own. ++ ++#undef _S_IFMT ++#undef _S_IFDIR ++#undef _S_IFCHR ++#undef _S_IFIFO ++#undef _S_IFREG ++#undef _S_IFBLK ++#undef _S_IFLNK ++#undef _S_IFSOCK ++ ++#define _S_IFMT 0xF000 ++#define _S_IFDIR 0x4000 ++#define _S_IFCHR 0x2000 ++#define _S_IFIFO 0x1000 ++#define _S_IFREG 0x8000 ++#define _S_IFBLK 0x6000 ++#define _S_IFLNK 0xA000 ++#define _S_IFSOCK 0xC000 ++ ++#undef S_ISDIR ++#undef S_ISFIFO ++#undef S_ISCHR ++#undef S_ISREG ++#undef S_ISLNK ++#undef S_ISBLK ++#undef S_ISSOCK ++ ++#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) ++#define S_ISCHR(m) (((m) & _S_IFMT) == _S_IFCHR) ++#define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO) ++#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) ++#define S_ISBLK(m) (((m) & _S_IFMT) == _S_IFBLK) ++#define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) ++#define S_ISSOCK(m) (((m) & _S_IFMT) == _S_IFSOCK) ++ ++ ++// There were 369 years and 89 leap days from the Windows epoch ++// (1601) to the Unix epoch (1970). ++#define FILE_TIME_OFFSET_SECS (uint64_t(369 * 365 + 89) * (24 * 60 * 60)) ++ ++TimeSpec filetime_to_timespec(LARGE_INTEGER li) { ++ TimeSpec ret; ++ ret.tv_sec = li.QuadPart / 10000000 - FILE_TIME_OFFSET_SECS; ++ ret.tv_nsec = (li.QuadPart % 10000000) * 100; ++ return ret; ++} ++ ++int set_errno(int e = GetLastError()) { ++ errno = static_cast(__win_err_to_errc(e)); ++ return -1; ++} ++ ++class WinHandle { ++public: ++ WinHandle(const wchar_t *p, DWORD access, DWORD flags) { ++ h = CreateFileW( ++ p, access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, ++ nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | flags, nullptr); ++ } ++ ~WinHandle() { ++ if (h != INVALID_HANDLE_VALUE) ++ CloseHandle(h); ++ } ++ operator HANDLE() const { return h; } ++ operator bool() const { return h != INVALID_HANDLE_VALUE; } ++ ++private: ++ HANDLE h; ++}; ++ ++int stat_handle(HANDLE h, StatT *buf) { ++ FILE_BASIC_INFO basic; ++ if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic))) ++ return set_errno(); ++ memset(buf, 0, sizeof(*buf)); ++ buf->st_mtim = filetime_to_timespec(basic.LastWriteTime); ++ buf->st_atim = filetime_to_timespec(basic.LastAccessTime); ++ buf->st_mode = 0555; // Read-only ++ if (!(basic.FileAttributes & FILE_ATTRIBUTE_READONLY)) ++ buf->st_mode |= 0222; // Write ++ if (basic.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { ++ buf->st_mode |= _S_IFDIR; ++ } else { ++ buf->st_mode |= _S_IFREG; ++ } ++ if (basic.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { ++ FILE_ATTRIBUTE_TAG_INFO tag; ++ if (!GetFileInformationByHandleEx(h, FileAttributeTagInfo, &tag, ++ sizeof(tag))) ++ return set_errno(); ++ if (tag.ReparseTag == IO_REPARSE_TAG_SYMLINK) ++ buf->st_mode = (buf->st_mode & ~_S_IFMT) | _S_IFLNK; ++ } ++ FILE_STANDARD_INFO standard; ++ if (!GetFileInformationByHandleEx(h, FileStandardInfo, &standard, ++ sizeof(standard))) ++ return set_errno(); ++ buf->st_nlink = standard.NumberOfLinks; ++ buf->st_size = standard.EndOfFile.QuadPart; ++ BY_HANDLE_FILE_INFORMATION info; ++ if (!GetFileInformationByHandle(h, &info)) ++ return set_errno(); ++ buf->st_dev = info.dwVolumeSerialNumber; ++ memcpy(&buf->st_ino.id[0], &info.nFileIndexHigh, 4); ++ memcpy(&buf->st_ino.id[4], &info.nFileIndexLow, 4); ++ return 0; ++} ++ ++int stat_file(const wchar_t *path, StatT *buf, DWORD flags) { ++ WinHandle h(path, FILE_READ_ATTRIBUTES, flags); ++ if (!h) ++ return set_errno(); ++ int ret = stat_handle(h, buf); ++ return ret; ++} ++ ++int stat(const wchar_t *path, StatT *buf) { return stat_file(path, buf, 0); } ++ ++int lstat(const wchar_t *path, StatT *buf) { ++ return stat_file(path, buf, FILE_FLAG_OPEN_REPARSE_POINT); ++} ++ ++int fstat(int fd, StatT *buf) { ++ HANDLE h = reinterpret_cast(_get_osfhandle(fd)); ++ return stat_handle(h, buf); ++} ++#else ++using ::fstat; ++using ::lstat; ++using ::stat; ++#endif ++ ++} // namespace ++} // end namespace detail ++ ++_LIBCPP_END_NAMESPACE_FILESYSTEM ++ ++#endif // POSIX_COMPAT_H +-- +2.25.1 + diff --git a/patches/llvm-project/0002-libcxx-Implement-_FilesystemClock-now-and-__last_wri.patch b/patches/llvm-project/0002-libcxx-Implement-_FilesystemClock-now-and-__last_wri.patch new file mode 100644 index 0000000..de98c21 --- /dev/null +++ b/patches/llvm-project/0002-libcxx-Implement-_FilesystemClock-now-and-__last_wri.patch @@ -0,0 +1,133 @@ +From ab432bcd28596265e97eb062a19cbbbdc3d426f5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 22:56:03 +0200 +Subject: [PATCH 02/24] [libcxx] Implement _FilesystemClock::now() and + __last_write_time for windows + +Differential Revision: https://reviews.llvm.org/D91142 + +(cherry picked from commit 592d62352933d34af62334d172c6fc665933e0de) +--- + libcxx/src/filesystem/filesystem_common.h | 6 ++++++ + libcxx/src/filesystem/operations.cpp | 23 +++++++++++++++++++++-- + libcxx/src/filesystem/posix_compat.h | 17 +++++++++++++++++ + 3 files changed, 44 insertions(+), 2 deletions(-) + +diff --git a/libcxx/src/filesystem/filesystem_common.h b/libcxx/src/filesystem/filesystem_common.h +index 8204e9a2e5f8..a4505171c3eb 100644 +--- a/libcxx/src/filesystem/filesystem_common.h ++++ b/libcxx/src/filesystem/filesystem_common.h +@@ -437,7 +437,11 @@ public: + } + }; + ++#if defined(_LIBCPP_WIN32API) ++using fs_time = time_util; ++#else + using fs_time = time_util; ++#endif + + #if defined(__APPLE__) + inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; } +@@ -456,6 +460,7 @@ inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; } + inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; } + #endif + ++#if !defined(_LIBCPP_WIN32API) + inline TimeVal make_timeval(TimeSpec const& ts) { + using namespace chrono; + auto Convert = [](long nsec) { +@@ -498,6 +503,7 @@ bool set_file_times(const path& p, std::array const& TS, + return posix_utimensat(p, TS, ec); + #endif + } ++#endif /* !_LIBCPP_WIN32API */ + + } // namespace + } // end namespace detail +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 548a0273ce71..40a863db69a1 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -42,7 +42,7 @@ + # define _LIBCPP_FILESYSTEM_USE_FSTREAM + #endif + +-#if !defined(CLOCK_REALTIME) ++#if !defined(CLOCK_REALTIME) && !defined(_LIBCPP_WIN32API) + # include // for gettimeofday and timeval + #endif + +@@ -567,7 +567,14 @@ const bool _FilesystemClock::is_steady; + + _FilesystemClock::time_point _FilesystemClock::now() noexcept { + typedef chrono::duration __secs; +-#if defined(CLOCK_REALTIME) ++#if defined(_LIBCPP_WIN32API) ++ typedef chrono::duration __nsecs; ++ FILETIME time; ++ GetSystemTimeAsFileTime(&time); ++ TimeSpec tp = detail::filetime_to_timespec(time); ++ return time_point(__secs(tp.tv_sec) + ++ chrono::duration_cast(__nsecs(tp.tv_nsec))); ++#elif defined(CLOCK_REALTIME) + typedef chrono::duration __nsecs; + struct timespec tp; + if (0 != clock_gettime(CLOCK_REALTIME, &tp)) +@@ -1121,6 +1128,17 @@ void __last_write_time(const path& p, file_time_type new_time, error_code* ec) { + using detail::fs_time; + ErrorHandler err("last_write_time", ec, &p); + ++#if defined(_LIBCPP_WIN32API) ++ TimeSpec ts; ++ if (!fs_time::convert_to_timespec(ts, new_time)) ++ return err.report(errc::value_too_large); ++ detail::WinHandle h(p.c_str(), FILE_WRITE_ATTRIBUTES, 0); ++ if (!h) ++ return err.report(detail::make_windows_error(GetLastError())); ++ FILETIME last_write = timespec_to_filetime(ts); ++ if (!SetFileTime(h, nullptr, nullptr, &last_write)) ++ return err.report(detail::make_windows_error(GetLastError())); ++#else + error_code m_ec; + array tbuf; + #if !defined(_LIBCPP_USE_UTIMENSAT) +@@ -1142,6 +1160,7 @@ void __last_write_time(const path& p, file_time_type new_time, error_code* ec) { + detail::set_file_times(p, tbuf, m_ec); + if (m_ec) + return err.report(m_ec); ++#endif + } + + void __permissions(const path& p, perms prms, perm_options opts, +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 3eec4634e929..7caf9d28b49d 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -94,6 +94,23 @@ TimeSpec filetime_to_timespec(LARGE_INTEGER li) { + return ret; + } + ++TimeSpec filetime_to_timespec(FILETIME ft) { ++ LARGE_INTEGER li; ++ li.LowPart = ft.dwLowDateTime; ++ li.HighPart = ft.dwHighDateTime; ++ return filetime_to_timespec(li); ++} ++ ++FILETIME timespec_to_filetime(TimeSpec ts) { ++ LARGE_INTEGER li; ++ li.QuadPart = ++ ts.tv_nsec / 100 + (ts.tv_sec + FILE_TIME_OFFSET_SECS) * 10000000; ++ FILETIME ft; ++ ft.dwLowDateTime = li.LowPart; ++ ft.dwHighDateTime = li.HighPart; ++ return ft; ++} ++ + int set_errno(int e = GetLastError()) { + errno = static_cast(__win_err_to_errc(e)); + return -1; +-- +2.25.1 + diff --git a/patches/llvm-project/0003-libcxx-Hook-up-a-number-of-operation-functions-to-th.patch b/patches/llvm-project/0003-libcxx-Hook-up-a-number-of-operation-functions-to-th.patch new file mode 100644 index 0000000..ac2ebcb --- /dev/null +++ b/patches/llvm-project/0003-libcxx-Hook-up-a-number-of-operation-functions-to-th.patch @@ -0,0 +1,295 @@ +From 13bc151ad49587f94a123cdb3e2ba770c1513d9d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Fri, 6 Nov 2020 11:16:30 +0200 +Subject: [PATCH 03/24] [libcxx] Hook up a number of operation functions to + their windows counterparts + +Use the corresponding wchar functions, named "_wfunc" instead of "func", +where feasible, or reimplement functions with native windows APIs. + +Differential Revision: https://reviews.llvm.org/D91143 + +(cherry picked from commit efec3cc6524bc536459b9cb6faca190b1e3804b6) +--- + libcxx/src/filesystem/operations.cpp | 41 ++++++----- + libcxx/src/filesystem/posix_compat.h | 102 +++++++++++++++++++++++++++ + 2 files changed, 124 insertions(+), 19 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 40a863db69a1..ddb4d7588e54 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -405,7 +405,7 @@ struct FileDescriptor { + static FileDescriptor create(const path* p, error_code& ec, Args... args) { + ec.clear(); + int fd; +- if ((fd = ::open(p->c_str(), args...)) == -1) { ++ if ((fd = detail::open(p->c_str(), args...)) == -1) { + ec = capture_errno(); + return FileDescriptor{p}; + } +@@ -431,7 +431,7 @@ struct FileDescriptor { + + void close() noexcept { + if (fd != -1) +- ::close(fd); ++ detail::close(fd); + fd = -1; + } + +@@ -521,7 +521,7 @@ file_status posix_lstat(path const& p, error_code* ec) { + + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html + bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) { +- if (::ftruncate(fd.fd, to_size) == -1) { ++ if (detail::ftruncate(fd.fd, to_size) == -1) { + ec = capture_errno(); + return true; + } +@@ -828,8 +828,8 @@ bool __copy_file(const path& from, const path& to, copy_options options, + ErrorHandler err("copy_file", ec, &to, &from); + + error_code m_ec; +- FileDescriptor from_fd = +- FileDescriptor::create_with_status(&from, m_ec, O_RDONLY | O_NONBLOCK); ++ FileDescriptor from_fd = FileDescriptor::create_with_status( ++ &from, m_ec, O_RDONLY | O_NONBLOCK | O_BINARY); + if (m_ec) + return err.report(m_ec); + +@@ -881,7 +881,7 @@ bool __copy_file(const path& from, const path& to, copy_options options, + + // Don't truncate right away. We may not be opening the file we originally + // looked at; we'll check this later. +- int to_open_flags = O_WRONLY; ++ int to_open_flags = O_WRONLY | O_BINARY; + if (!to_exists) + to_open_flags |= O_CREAT; + FileDescriptor to_fd = FileDescriptor::create_with_status( +@@ -917,10 +917,13 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink, + if (ec && *ec) { + return; + } +- // NOTE: proposal says you should detect if you should call +- // create_symlink or create_directory_symlink. I don't think this +- // is needed with POSIX +- __create_symlink(real_path, new_symlink, ec); ++#if defined(_LIBCPP_WIN32API) ++ error_code local_ec; ++ if (is_directory(real_path, local_ec)) ++ __create_directory_symlink(real_path, new_symlink, ec); ++ else ++#endif ++ __create_symlink(real_path, new_symlink, ec); + } + + bool __create_directories(const path& p, error_code* ec) { +@@ -953,7 +956,7 @@ bool __create_directories(const path& p, error_code* ec) { + bool __create_directory(const path& p, error_code* ec) { + ErrorHandler err("create_directory", ec, &p); + +- if (::mkdir(p.c_str(), static_cast(perms::all)) == 0) ++ if (detail::mkdir(p.c_str(), static_cast(perms::all)) == 0) + return true; + + if (errno == EEXIST) { +@@ -981,7 +984,7 @@ bool __create_directory(path const& p, path const& attributes, error_code* ec) { + return err.report(errc::not_a_directory, + "the specified attribute path is invalid"); + +- if (::mkdir(p.c_str(), attr_stat.st_mode) == 0) ++ if (detail::mkdir(p.c_str(), attr_stat.st_mode) == 0) + return true; + + if (errno == EEXIST) { +@@ -1000,19 +1003,19 @@ bool __create_directory(path const& p, path const& attributes, error_code* ec) { + void __create_directory_symlink(path const& from, path const& to, + error_code* ec) { + ErrorHandler err("create_directory_symlink", ec, &from, &to); +- if (::symlink(from.c_str(), to.c_str()) != 0) ++ if (detail::symlink_dir(from.c_str(), to.c_str()) == -1) + return err.report(capture_errno()); + } + + void __create_hard_link(const path& from, const path& to, error_code* ec) { + ErrorHandler err("create_hard_link", ec, &from, &to); +- if (::link(from.c_str(), to.c_str()) == -1) ++ if (detail::link(from.c_str(), to.c_str()) == -1) + return err.report(capture_errno()); + } + + void __create_symlink(path const& from, path const& to, error_code* ec) { + ErrorHandler err("create_symlink", ec, &from, &to); +- if (::symlink(from.c_str(), to.c_str()) == -1) ++ if (detail::symlink_file(from.c_str(), to.c_str()) == -1) + return err.report(capture_errno()); + } + +@@ -1032,7 +1035,7 @@ path __current_path(error_code* ec) { + + void __current_path(const path& p, error_code* ec) { + ErrorHandler err("current_path", ec, &p); +- if (::chdir(p.c_str()) == -1) ++ if (detail::chdir(p.c_str()) == -1) + err.report(capture_errno()); + } + +@@ -1236,7 +1239,7 @@ path __read_symlink(const path& p, error_code* ec) { + + bool __remove(const path& p, error_code* ec) { + ErrorHandler err("remove", ec, &p); +- if (::remove(p.c_str()) == -1) { ++ if (detail::remove(p.c_str()) == -1) { + if (errno != ENOENT) + err.report(capture_errno()); + return false; +@@ -1285,13 +1288,13 @@ uintmax_t __remove_all(const path& p, error_code* ec) { + + void __rename(const path& from, const path& to, error_code* ec) { + ErrorHandler err("rename", ec, &from, &to); +- if (::rename(from.c_str(), to.c_str()) == -1) ++ if (detail::rename(from.c_str(), to.c_str()) == -1) + err.report(capture_errno()); + } + + void __resize_file(const path& p, uintmax_t size, error_code* ec) { + ErrorHandler err("resize_file", ec, &p); +- if (::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1) ++ if (detail::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1) + return err.report(capture_errno()); + } + +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 7caf9d28b49d..1e70e12cb4d7 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -12,6 +12,8 @@ + // These generally behave like the proper posix functions, with these + // exceptions: + // On Windows, they take paths in wchar_t* form, instead of char* form. ++// The symlink() function is split into two frontends, symlink_file() ++// and symlink_dir(). + // + // These are provided within an anonymous namespace within the detail + // namespace - callers need to include this header and call them as +@@ -82,6 +84,8 @@ namespace { + #define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) + #define S_ISSOCK(m) (((m) & _S_IFMT) == _S_IFSOCK) + ++#define O_NONBLOCK 0 ++ + + // There were 369 years and 89 leap days from the Windows epoch + // (1601) to the Unix epoch (1970). +@@ -190,10 +194,108 @@ int fstat(int fd, StatT *buf) { + HANDLE h = reinterpret_cast(_get_osfhandle(fd)); + return stat_handle(h, buf); + } ++ ++int mkdir(const wchar_t *path, int permissions) { ++ (void)permissions; ++ return _wmkdir(path); ++} ++ ++int symlink_file_dir(const wchar_t *oldname, const wchar_t *newname, ++ bool is_dir) { ++ DWORD flags = is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; ++ if (CreateSymbolicLinkW(newname, oldname, ++ flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) ++ return 0; ++ int e = GetLastError(); ++ if (e != ERROR_INVALID_PARAMETER) ++ return set_errno(e); ++ if (CreateSymbolicLinkW(newname, oldname, flags)) ++ return 0; ++ return set_errno(); ++} ++ ++int symlink_file(const wchar_t *oldname, const wchar_t *newname) { ++ return symlink_file_dir(oldname, newname, false); ++} ++ ++int symlink_dir(const wchar_t *oldname, const wchar_t *newname) { ++ return symlink_file_dir(oldname, newname, true); ++} ++ ++int link(const wchar_t *oldname, const wchar_t *newname) { ++ if (CreateHardLinkW(newname, oldname, nullptr)) ++ return 0; ++ return set_errno(); ++} ++ ++int remove(const wchar_t *path) { ++ detail::WinHandle h(path, DELETE, FILE_FLAG_OPEN_REPARSE_POINT); ++ if (!h) ++ return set_errno(); ++ FILE_DISPOSITION_INFO info; ++ info.DeleteFile = TRUE; ++ if (!SetFileInformationByHandle(h, FileDispositionInfo, &info, sizeof(info))) ++ return set_errno(); ++ return 0; ++} ++ ++int truncate_handle(HANDLE h, off_t length) { ++ LARGE_INTEGER size_param; ++ size_param.QuadPart = length; ++ if (!SetFilePointerEx(h, size_param, 0, FILE_BEGIN)) ++ return set_errno(); ++ if (!SetEndOfFile(h)) ++ return set_errno(); ++ return 0; ++} ++ ++int ftruncate(int fd, off_t length) { ++ HANDLE h = reinterpret_cast(_get_osfhandle(fd)); ++ return truncate_handle(h, length); ++} ++ ++int truncate(const wchar_t *path, off_t length) { ++ detail::WinHandle h(path, GENERIC_WRITE, 0); ++ if (!h) ++ return set_errno(); ++ return truncate_handle(h, length); ++} ++ ++int rename(const wchar_t *from, const wchar_t *to) { ++ if (!(MoveFileExW(from, to, ++ MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | ++ MOVEFILE_WRITE_THROUGH))) ++ return set_errno(); ++ return 0; ++} ++ ++template int open(const wchar_t *filename, Args... args) { ++ return _wopen(filename, args...); ++} ++int close(int fd) { return _close(fd); } ++int chdir(const wchar_t *path) { return _wchdir(path); } + #else ++int symlink_file(const char *oldname, const char *newname) { ++ return ::symlink(oldname, newname); ++} ++int symlink_dir(const char *oldname, const char *newname) { ++ return ::symlink(oldname, newname); ++} ++using ::chdir; ++using ::close; + using ::fstat; ++using ::ftruncate; ++using ::link; + using ::lstat; ++using ::mkdir; ++using ::open; ++using ::remove; ++using ::rename; + using ::stat; ++using ::truncate; ++ ++#define O_BINARY 0 ++ + #endif + + } // namespace +-- +2.25.1 + diff --git a/patches/llvm-project/0004-libcxx-Sanitize-paths-before-creating-symlinks-on-wi.patch b/patches/llvm-project/0004-libcxx-Sanitize-paths-before-creating-symlinks-on-wi.patch new file mode 100644 index 0000000..d742921 --- /dev/null +++ b/patches/llvm-project/0004-libcxx-Sanitize-paths-before-creating-symlinks-on-wi.patch @@ -0,0 +1,65 @@ +From 21ffff20ef2e05f8c99bd120d7b1212d75f14dcd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Mon, 9 Nov 2020 15:20:18 +0200 +Subject: [PATCH 04/24] [libcxx] Sanitize paths before creating symlinks on + windows + +The MS STL does even more cleanup (corresponding to lexically_normal +I think), but this seems to be the very minimum needed for making the +symlinks work when the target path contains non-native paths. + +Differential Revision: https://reviews.llvm.org/D91145 + +(cherry picked from commit f65ba25cf37a57dc87db7af389c9dc637ca7dd8c) +--- + libcxx/src/filesystem/posix_compat.h | 3 +++ + .../create_symlink.pass.cpp | 20 +++++++++++++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 1e70e12cb4d7..0dbae1235a00 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -202,6 +202,9 @@ int mkdir(const wchar_t *path, int permissions) { + + int symlink_file_dir(const wchar_t *oldname, const wchar_t *newname, + bool is_dir) { ++ path dest(oldname); ++ dest.make_preferred(); ++ oldname = dest.c_str(); + DWORD flags = is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; + if (CreateSymbolicLinkW(newname, oldname, + flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) +diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_symlink/create_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_symlink/create_symlink.pass.cpp +index 47ec916a169b..3b54cff309b7 100644 +--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_symlink/create_symlink.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_symlink/create_symlink.pass.cpp +@@ -71,5 +71,25 @@ TEST_CASE(create_symlink_basic) + } + } + ++TEST_CASE(create_symlink_dest_cleanup) ++{ ++ scoped_test_env env; ++ const path dir = env.create_dir("dir"); ++ const path file = env.create_file("file", 42); ++ const path sym = dir / "link"; ++ // The target path has to be normalized to backslashes before creating ++ // the link on windows, otherwise the link isn't dereferencable. ++ const path sym_target = "../file"; ++ path sym_target_normalized = sym_target; ++ sym_target_normalized.make_preferred(); ++ std::error_code ec; ++ fs::create_symlink(sym_target, sym, ec); ++ TEST_REQUIRE(!ec); ++ TEST_CHECK(equivalent(sym, file, ec)); ++ const path ret = fs::read_symlink(sym, ec); ++ TEST_CHECK(!ec); ++ TEST_CHECK(ret.native() == sym_target_normalized.native()); ++} ++ + + TEST_SUITE_END() +-- +2.25.1 + diff --git a/patches/llvm-project/0005-libcxx-Implement-the-space-function-for-windows.patch b/patches/llvm-project/0005-libcxx-Implement-the-space-function-for-windows.patch new file mode 100644 index 0000000..4c5d471 --- /dev/null +++ b/patches/llvm-project/0005-libcxx-Implement-the-space-function-for-windows.patch @@ -0,0 +1,90 @@ +From 51a0f3a5af65c8e8e1618dc5621705471aba2151 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 23:32:13 +0200 +Subject: [PATCH 05/24] [libcxx] Implement the space function for windows + +Differential Revision: https://reviews.llvm.org/D91168 + +(cherry picked from commit a3cc99658d52f79faad26beeea06691b3a50bc95) +--- + libcxx/src/filesystem/operations.cpp | 4 +-- + libcxx/src/filesystem/posix_compat.h | 37 ++++++++++++++++++++++++++++ + 2 files changed, 39 insertions(+), 2 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index ddb4d7588e54..fcb5c2def23c 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1301,8 +1301,8 @@ void __resize_file(const path& p, uintmax_t size, error_code* ec) { + space_info __space(const path& p, error_code* ec) { + ErrorHandler err("space", ec, &p); + space_info si; +- struct statvfs m_svfs = {}; +- if (::statvfs(p.c_str(), &m_svfs) == -1) { ++ detail::StatVFS m_svfs = {}; ++ if (detail::statvfs(p.c_str(), &m_svfs) == -1) { + err.report(capture_errno()); + si.capacity = si.free = si.available = static_cast(-1); + return si; +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 0dbae1235a00..5f868a090693 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -277,6 +277,40 @@ template int open(const wchar_t *filename, Args... args) { + } + int close(int fd) { return _close(fd); } + int chdir(const wchar_t *path) { return _wchdir(path); } ++ ++struct StatVFS { ++ uint64_t f_frsize; ++ uint64_t f_blocks; ++ uint64_t f_bfree; ++ uint64_t f_bavail; ++}; ++ ++int statvfs(const wchar_t *p, StatVFS *buf) { ++ path dir = p; ++ while (true) { ++ error_code local_ec; ++ const file_status st = status(dir, local_ec); ++ if (!exists(st) || is_directory(st)) ++ break; ++ path parent = dir.parent_path(); ++ if (parent == dir) { ++ errno = ENOENT; ++ return -1; ++ } ++ dir = parent; ++ } ++ ULARGE_INTEGER free_bytes_available_to_caller, total_number_of_bytes, ++ total_number_of_free_bytes; ++ if (!GetDiskFreeSpaceExW(dir.c_str(), &free_bytes_available_to_caller, ++ &total_number_of_bytes, &total_number_of_free_bytes)) ++ return set_errno(); ++ buf->f_frsize = 1; ++ buf->f_blocks = total_number_of_bytes.QuadPart; ++ buf->f_bfree = total_number_of_free_bytes.QuadPart; ++ buf->f_bavail = free_bytes_available_to_caller.QuadPart; ++ return 0; ++} ++ + #else + int symlink_file(const char *oldname, const char *newname) { + return ::symlink(oldname, newname); +@@ -295,10 +329,13 @@ using ::open; + using ::remove; + using ::rename; + using ::stat; ++using ::statvfs; + using ::truncate; + + #define O_BINARY 0 + ++using StatVFS = struct statvfs; ++ + #endif + + } // namespace +-- +2.25.1 + diff --git a/patches/llvm-project/0006-libcxx-Implement-the-current_path-function-for-windo.patch b/patches/llvm-project/0006-libcxx-Implement-the-current_path-function-for-windo.patch new file mode 100644 index 0000000..e6d8c77 --- /dev/null +++ b/patches/llvm-project/0006-libcxx-Implement-the-current_path-function-for-windo.patch @@ -0,0 +1,78 @@ +From 0bdb2f73e8ba72de291d1ca95363835ab538bdab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 23:46:12 +0200 +Subject: [PATCH 06/24] [libcxx] Implement the current_path function for + windows + +Differential Revision: https://reviews.llvm.org/D91169 + +(cherry picked from commit 0c71c914faa371ba502a2e1835f763104837cb9f) +--- + libcxx/src/filesystem/operations.cpp | 25 +++++++++++++++++++++---- + libcxx/src/filesystem/posix_compat.h | 2 ++ + 2 files changed, 23 insertions(+), 4 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index fcb5c2def23c..429a58501a49 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1022,15 +1022,32 @@ void __create_symlink(path const& from, path const& to, error_code* ec) { + path __current_path(error_code* ec) { + ErrorHandler err("current_path", ec); + ++#if defined(_LIBCPP_WIN32API) ++ // Common extension outside of POSIX getcwd() spec, without needing to ++ // preallocate a buffer. Also supported by a number of other POSIX libcs. ++ int size = 0; ++ path::value_type* ptr = nullptr; ++ typedef decltype(&::free) Deleter; ++ Deleter deleter = &::free; ++#else + auto size = ::pathconf(".", _PC_PATH_MAX); + _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size"); + +- auto buff = unique_ptr(new char[size + 1]); +- char* ret; +- if ((ret = ::getcwd(buff.get(), static_cast(size))) == nullptr) ++ auto buff = unique_ptr(new path::value_type[size + 1]); ++ path::value_type* ptr = buff.get(); ++ ++ // Preallocated buffer, don't free the buffer in the second unique_ptr ++ // below. ++ struct Deleter { void operator()(void*) const {} }; ++ Deleter deleter; ++#endif ++ ++ unique_ptr hold(detail::getcwd(ptr, size), ++ deleter); ++ if (hold.get() == nullptr) + return err.report(capture_errno(), "call to getcwd failed"); + +- return {buff.get()}; ++ return {hold.get()}; + } + + void __current_path(const path& p, error_code* ec) { +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 5f868a090693..13753fdbb760 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -311,6 +311,7 @@ int statvfs(const wchar_t *p, StatVFS *buf) { + return 0; + } + ++wchar_t *getcwd(wchar_t *buff, size_t size) { return _wgetcwd(buff, size); } + #else + int symlink_file(const char *oldname, const char *newname) { + return ::symlink(oldname, newname); +@@ -322,6 +323,7 @@ using ::chdir; + using ::close; + using ::fstat; + using ::ftruncate; ++using ::getcwd; + using ::link; + using ::lstat; + using ::mkdir; +-- +2.25.1 + diff --git a/patches/llvm-project/0007-libcxx-Implement-the-canonical-function-for-windows.patch b/patches/llvm-project/0007-libcxx-Implement-the-canonical-function-for-windows.patch new file mode 100644 index 0000000..9163941 --- /dev/null +++ b/patches/llvm-project/0007-libcxx-Implement-the-canonical-function-for-windows.patch @@ -0,0 +1,104 @@ +From b6ef948856bfde6326ebc2d1fddc24890b4182f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 23:51:12 +0200 +Subject: [PATCH 07/24] [libcxx] Implement the canonical function for windows + +Differential Revision: https://reviews.llvm.org/D91170 + +(cherry picked from commit 83d705adb2e043e576c9cbaf9709795bc69fe5cf) +--- + libcxx/src/filesystem/operations.cpp | 14 +++++----- + libcxx/src/filesystem/posix_compat.h | 38 ++++++++++++++++++++++++++++ + 2 files changed, 45 insertions(+), 7 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 429a58501a49..fc18de9e5d80 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -636,20 +636,20 @@ path __canonical(path const& orig_p, error_code* ec) { + ErrorHandler err("canonical", ec, &orig_p, &cwd); + + path p = __do_absolute(orig_p, &cwd, ec); +-#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112 +- std::unique_ptr +- hold(::realpath(p.c_str(), nullptr), &::free); ++#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API) ++ std::unique_ptr ++ hold(detail::realpath(p.c_str(), nullptr), &::free); + if (hold.get() == nullptr) + return err.report(capture_errno()); + return {hold.get()}; + #else + #if defined(__MVS__) && !defined(PATH_MAX) +- char buff[ _XOPEN_PATH_MAX + 1 ]; ++ path::value_type buff[ _XOPEN_PATH_MAX + 1 ]; + #else +- char buff[PATH_MAX + 1]; ++ path::value_type buff[PATH_MAX + 1]; + #endif +- char* ret; +- if ((ret = ::realpath(p.c_str(), buff)) == nullptr) ++ path::value_type* ret; ++ if ((ret = detail::realpath(p.c_str(), buff)) == nullptr) + return err.report(capture_errno()); + return {ret}; + #endif +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 13753fdbb760..1adce3a779c2 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -312,6 +312,43 @@ int statvfs(const wchar_t *p, StatVFS *buf) { + } + + wchar_t *getcwd(wchar_t *buff, size_t size) { return _wgetcwd(buff, size); } ++ ++wchar_t *realpath(const wchar_t *path, wchar_t *resolved_name) { ++ // Only expected to be used with us allocating the buffer. ++ _LIBCPP_ASSERT(resolved_name == nullptr, ++ "Windows realpath() assumes a null resolved_name"); ++ ++ WinHandle h(path, FILE_READ_ATTRIBUTES, 0); ++ if (!h) { ++ set_errno(); ++ return nullptr; ++ } ++ size_t buff_size = MAX_PATH + 10; ++ std::unique_ptr buff( ++ static_cast(malloc(buff_size * sizeof(wchar_t))), &::free); ++ DWORD retval = GetFinalPathNameByHandleW( ++ h, buff.get(), buff_size, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); ++ if (retval > buff_size) { ++ buff_size = retval; ++ buff.reset(static_cast(malloc(buff_size * sizeof(wchar_t)))); ++ retval = GetFinalPathNameByHandleW(h, buff.get(), buff_size, ++ FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); ++ } ++ if (!retval) { ++ set_errno(); ++ return nullptr; ++ } ++ wchar_t *ptr = buff.get(); ++ if (!wcsncmp(ptr, L"\\\\?\\", 4)) { ++ if (ptr[5] == ':') { // \\?\X: -> X: ++ memmove(&ptr[0], &ptr[4], (wcslen(&ptr[4]) + 1) * sizeof(wchar_t)); ++ } else if (!wcsncmp(&ptr[4], L"UNC\\", 4)) { // \\?\UNC\server -> \\server ++ wcscpy(&ptr[0], L"\\\\"); ++ memmove(&ptr[2], &ptr[8], (wcslen(&ptr[8]) + 1) * sizeof(wchar_t)); ++ } ++ } ++ return buff.release(); ++} + #else + int symlink_file(const char *oldname, const char *newname) { + return ::symlink(oldname, newname); +@@ -328,6 +365,7 @@ using ::link; + using ::lstat; + using ::mkdir; + using ::open; ++using ::realpath; + using ::remove; + using ::rename; + using ::stat; +-- +2.25.1 + diff --git a/patches/llvm-project/0008-libcxx-Implement-the-permissions-function-for-window.patch b/patches/llvm-project/0008-libcxx-Implement-the-permissions-function-for-window.patch new file mode 100644 index 0000000..e158618 --- /dev/null +++ b/patches/llvm-project/0008-libcxx-Implement-the-permissions-function-for-window.patch @@ -0,0 +1,133 @@ +From 96748da73f57e6b080643d03e41e8a690ee571a9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 23:55:10 +0200 +Subject: [PATCH 08/24] [libcxx] Implement the permissions function for windows + +Differential Revision: https://reviews.llvm.org/D91171 + +(cherry picked from commit 40117b700f723a27b90989a429ceb66c0873b161) +--- + libcxx/src/filesystem/operations.cpp | 10 ++---- + libcxx/src/filesystem/posix_compat.h | 54 ++++++++++++++++++++++++++++ + 2 files changed, 57 insertions(+), 7 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index fc18de9e5d80..47cd5e23c092 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -455,10 +455,6 @@ perms posix_get_perms(const StatT& st) noexcept { + return static_cast(st.st_mode) & perms::mask; + } + +-::mode_t posix_convert_perms(perms prms) { +- return static_cast< ::mode_t>(prms & perms::mask); +-} +- + file_status create_file_status(error_code& m_ec, path const& p, + const StatT& path_stat, error_code* ec) { + if (ec) +@@ -530,7 +526,7 @@ bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) { + } + + bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) { +- if (::fchmod(fd.fd, st.st_mode) == -1) { ++ if (detail::fchmod(fd.fd, st.st_mode) == -1) { + ec = capture_errno(); + return true; + } +@@ -1212,11 +1208,11 @@ void __permissions(const path& p, perms prms, perm_options opts, + else if (remove_perms) + prms = st.permissions() & ~prms; + } +- const auto real_perms = detail::posix_convert_perms(prms); ++ const auto real_perms = static_cast(prms & perms::mask); + + #if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD) + const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0; +- if (::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) { ++ if (detail::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) { + return err.report(capture_errno()); + } + #else +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 1adce3a779c2..af4a3691db33 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -349,6 +349,57 @@ wchar_t *realpath(const wchar_t *path, wchar_t *resolved_name) { + } + return buff.release(); + } ++ ++#define AT_FDCWD -1 ++#define AT_SYMLINK_NOFOLLOW 1 ++using ModeT = int; ++ ++int fchmod_handle(HANDLE h, int perms) { ++ FILE_BASIC_INFO basic; ++ if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic))) ++ return set_errno(); ++ DWORD orig_attributes = basic.FileAttributes; ++ basic.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; ++ if ((perms & 0222) == 0) ++ basic.FileAttributes |= FILE_ATTRIBUTE_READONLY; ++ if (basic.FileAttributes != orig_attributes && ++ !SetFileInformationByHandle(h, FileBasicInfo, &basic, sizeof(basic))) ++ return set_errno(); ++ return 0; ++} ++ ++int fchmodat(int fd, const wchar_t *path, int perms, int flag) { ++ DWORD attributes = GetFileAttributesW(path); ++ if (attributes == INVALID_FILE_ATTRIBUTES) ++ return set_errno(); ++ if (attributes & FILE_ATTRIBUTE_REPARSE_POINT && ++ !(flag & AT_SYMLINK_NOFOLLOW)) { ++ // If the file is a symlink, and we are supposed to operate on the target ++ // of the symlink, we need to open a handle to it, without the ++ // FILE_FLAG_OPEN_REPARSE_POINT flag, to open the destination of the ++ // symlink, and operate on it via the handle. ++ detail::WinHandle h(path, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 0); ++ if (!h) ++ return set_errno(); ++ return fchmod_handle(h, perms); ++ } else { ++ // For a non-symlink, or if operating on the symlink itself instead of ++ // its target, we can use SetFileAttributesW, saving a few calls. ++ DWORD orig_attributes = attributes; ++ attributes &= ~FILE_ATTRIBUTE_READONLY; ++ if ((perms & 0222) == 0) ++ attributes |= FILE_ATTRIBUTE_READONLY; ++ if (attributes != orig_attributes && !SetFileAttributesW(path, attributes)) ++ return set_errno(); ++ } ++ return 0; ++} ++ ++int fchmod(int fd, int perms) { ++ HANDLE h = reinterpret_cast(_get_osfhandle(fd)); ++ return fchmod_handle(h, perms); ++} ++ + #else + int symlink_file(const char *oldname, const char *newname) { + return ::symlink(oldname, newname); +@@ -358,6 +409,8 @@ int symlink_dir(const char *oldname, const char *newname) { + } + using ::chdir; + using ::close; ++using ::fchmod; ++using ::fchmodat; + using ::fstat; + using ::ftruncate; + using ::getcwd; +@@ -375,6 +428,7 @@ using ::truncate; + #define O_BINARY 0 + + using StatVFS = struct statvfs; ++using ModeT = ::mode_t; + + #endif + +-- +2.25.1 + diff --git a/patches/llvm-project/0009-libcxx-Implement-the-read_symlink-function-for-windo.patch b/patches/llvm-project/0009-libcxx-Implement-the-read_symlink-function-for-windo.patch new file mode 100644 index 0000000..562002c --- /dev/null +++ b/patches/llvm-project/0009-libcxx-Implement-the-read_symlink-function-for-windo.patch @@ -0,0 +1,171 @@ +From f28542330202fae54a6fee1bf02a311feef3f60a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 23:51:18 +0200 +Subject: [PATCH 09/24] [libcxx] Implement the read_symlink function for + windows + +Differential Revision: https://reviews.llvm.org/D91172 + +(cherry picked from commit cdc60a3b9aa523b49329a7a5e4c1774d3b9e3db9) +--- + libcxx/src/filesystem/operations.cpp | 16 +++--- + libcxx/src/filesystem/posix_compat.h | 79 ++++++++++++++++++++++++++++ + 2 files changed, 89 insertions(+), 6 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 47cd5e23c092..a5463e48f4d7 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1227,21 +1227,25 @@ void __permissions(const path& p, perms prms, perm_options opts, + path __read_symlink(const path& p, error_code* ec) { + ErrorHandler err("read_symlink", ec, &p); + +-#ifdef PATH_MAX ++#if defined(PATH_MAX) || defined(MAX_SYMLINK_SIZE) + struct NullDeleter { void operator()(void*) const {} }; ++#ifdef MAX_SYMLINK_SIZE ++ const size_t size = MAX_SYMLINK_SIZE + 1; ++#else + const size_t size = PATH_MAX + 1; +- char stack_buff[size]; +- auto buff = std::unique_ptr(stack_buff); ++#endif ++ path::value_type stack_buff[size]; ++ auto buff = std::unique_ptr(stack_buff); + #else + StatT sb; + if (detail::lstat(p.c_str(), &sb) == -1) { + return err.report(capture_errno()); + } + const size_t size = sb.st_size + 1; +- auto buff = unique_ptr(new char[size]); ++ auto buff = unique_ptr(new path::value_type[size]); + #endif +- ::ssize_t ret; +- if ((ret = ::readlink(p.c_str(), buff.get(), size)) == -1) ++ detail::SSizeT ret; ++ if ((ret = detail::readlink(p.c_str(), buff.get(), size)) == -1) + return err.report(capture_errno()); + _LIBCPP_ASSERT(ret > 0, "TODO"); + if (static_cast(ret) >= size) +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index af4a3691db33..8062bd65ce00 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -32,6 +32,7 @@ + # define NOMINMAX + # include + # include ++# include + #else + # include + # include +@@ -39,6 +40,36 @@ + #endif + #include + ++#if defined(_LIBCPP_WIN32API) ++// This struct isn't defined in the normal Windows SDK, but only in the ++// Windows Driver Kit. ++struct LIBCPP_REPARSE_DATA_BUFFER { ++ unsigned long ReparseTag; ++ unsigned short ReparseDataLength; ++ unsigned short Reserved; ++ union { ++ struct { ++ unsigned short SubstituteNameOffset; ++ unsigned short SubstituteNameLength; ++ unsigned short PrintNameOffset; ++ unsigned short PrintNameLength; ++ unsigned long Flags; ++ wchar_t PathBuffer[1]; ++ } SymbolicLinkReparseBuffer; ++ struct { ++ unsigned short SubstituteNameOffset; ++ unsigned short SubstituteNameLength; ++ unsigned short PrintNameOffset; ++ unsigned short PrintNameLength; ++ wchar_t PathBuffer[1]; ++ } MountPointReparseBuffer; ++ struct { ++ unsigned char DataBuffer[1]; ++ } GenericReparseBuffer; ++ }; ++}; ++#endif ++ + _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM + + namespace detail { +@@ -400,6 +431,52 @@ int fchmod(int fd, int perms) { + return fchmod_handle(h, perms); + } + ++#define MAX_SYMLINK_SIZE MAXIMUM_REPARSE_DATA_BUFFER_SIZE ++using SSizeT = ::int64_t; ++ ++SSizeT readlink(const wchar_t *path, wchar_t *ret_buf, size_t bufsize) { ++ uint8_t buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; ++ detail::WinHandle h(path, FILE_READ_ATTRIBUTES, FILE_FLAG_OPEN_REPARSE_POINT); ++ if (!h) ++ return set_errno(); ++ DWORD out; ++ if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf, sizeof(buf), ++ &out, 0)) ++ return set_errno(); ++ const auto *reparse = reinterpret_cast(buf); ++ size_t path_buf_offset = offsetof(LIBCPP_REPARSE_DATA_BUFFER, ++ SymbolicLinkReparseBuffer.PathBuffer[0]); ++ if (out < path_buf_offset) { ++ errno = EINVAL; ++ return -1; ++ } ++ if (reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) { ++ errno = EINVAL; ++ return -1; ++ } ++ const auto &symlink = reparse->SymbolicLinkReparseBuffer; ++ unsigned short name_offset, name_length; ++ if (symlink.PrintNameLength == 0) { ++ name_offset = symlink.SubstituteNameOffset; ++ name_length = symlink.SubstituteNameLength; ++ } else { ++ name_offset = symlink.PrintNameOffset; ++ name_length = symlink.PrintNameLength; ++ } ++ // name_offset/length are expressed in bytes, not in wchar_t ++ if (path_buf_offset + name_offset + name_length > out) { ++ errno = EINVAL; ++ return -1; ++ } ++ if (name_length / sizeof(wchar_t) > bufsize) { ++ errno = ENOMEM; ++ return -1; ++ } ++ memcpy(ret_buf, &symlink.PathBuffer[name_offset / sizeof(wchar_t)], ++ name_length); ++ return name_length / sizeof(wchar_t); ++} ++ + #else + int symlink_file(const char *oldname, const char *newname) { + return ::symlink(oldname, newname); +@@ -418,6 +495,7 @@ using ::link; + using ::lstat; + using ::mkdir; + using ::open; ++using ::readlink; + using ::realpath; + using ::remove; + using ::rename; +@@ -429,6 +507,7 @@ using ::truncate; + + using StatVFS = struct statvfs; + using ModeT = ::mode_t; ++using SSizeT = ::ssize_t; + + #endif + +-- +2.25.1 + diff --git a/patches/llvm-project/0010-libcxx-Use-the-posix-code-for-directory_entry-__do_r.patch b/patches/llvm-project/0010-libcxx-Use-the-posix-code-for-directory_entry-__do_r.patch new file mode 100644 index 0000000..ff18039 --- /dev/null +++ b/patches/llvm-project/0010-libcxx-Use-the-posix-code-for-directory_entry-__do_r.patch @@ -0,0 +1,79 @@ +From 6f225c9fca50bda8911a4909b3cbf0eb28733384 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Fri, 30 Oct 2020 22:24:25 +0200 +Subject: [PATCH 10/24] [libcxx] Use the posix code for + directory_entry::__do_refresh + +This works just fine for windows, as all the functions it calls +are implemented and wrapped for windows. + +Differential Revision: https://reviews.llvm.org/D91173 + +(cherry picked from commit 4d292d531bea6f7a6021f212e59b3826bc7cd913) +--- + libcxx/src/filesystem/operations.cpp | 43 ---------------------------- + 1 file changed, 43 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index a5463e48f4d7..dccec9378531 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1847,7 +1847,6 @@ size_t __char_to_wide(const string &str, wchar_t *out, size_t outlen) { + // directory entry definitions + /////////////////////////////////////////////////////////////////////////////// + +-#ifndef _LIBCPP_WIN32API + error_code directory_entry::__do_refresh() noexcept { + __data_.__reset(); + error_code failure_ec; +@@ -1901,47 +1900,5 @@ error_code directory_entry::__do_refresh() noexcept { + + return failure_ec; + } +-#else +-error_code directory_entry::__do_refresh() noexcept { +- __data_.__reset(); +- error_code failure_ec; +- +- file_status st = _VSTD_FS::symlink_status(__p_, failure_ec); +- if (!status_known(st)) { +- __data_.__reset(); +- return failure_ec; +- } +- +- if (!_VSTD_FS::exists(st) || !_VSTD_FS::is_symlink(st)) { +- __data_.__cache_type_ = directory_entry::_RefreshNonSymlink; +- __data_.__type_ = st.type(); +- __data_.__non_sym_perms_ = st.permissions(); +- } else { // we have a symlink +- __data_.__sym_perms_ = st.permissions(); +- // Get the information about the linked entity. +- // Ignore errors from stat, since we don't want errors regarding symlink +- // resolution to be reported to the user. +- error_code ignored_ec; +- st = _VSTD_FS::status(__p_, ignored_ec); +- +- __data_.__type_ = st.type(); +- __data_.__non_sym_perms_ = st.permissions(); +- +- // If we failed to resolve the link, then only partially populate the +- // cache. +- if (!status_known(st)) { +- __data_.__cache_type_ = directory_entry::_RefreshSymlinkUnresolved; +- return error_code{}; +- } +- __data_.__cache_type_ = directory_entry::_RefreshSymlink; +- } +- +- // FIXME: This is currently broken, and the implementation only a placeholder. +- // We need to cache last_write_time, file_size, and hard_link_count here before +- // the implementation actually works. +- +- return failure_ec; +-} +-#endif + + _LIBCPP_END_NAMESPACE_FILESYSTEM +-- +2.25.1 + diff --git a/patches/llvm-project/0011-libcxx-Implement-temp_directory_path-using-GetTempPa.patch b/patches/llvm-project/0011-libcxx-Implement-temp_directory_path-using-GetTempPa.patch new file mode 100644 index 0000000..3e7c2f7 --- /dev/null +++ b/patches/llvm-project/0011-libcxx-Implement-temp_directory_path-using-GetTempPa.patch @@ -0,0 +1,53 @@ +From 3474a416fef709f218ff5897842819d379495cad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Thu, 29 Oct 2020 12:10:26 +0200 +Subject: [PATCH 11/24] [libcxx] Implement temp_directory_path using + GetTempPath on windows + +This does roughly the same as the manual implementation, but checks +a slightly different set of environment variables and has a more +appropriate fallback if no environment variables are available +(/tmp isn't a very useful fallback on windows). + +Differential Revision: https://reviews.llvm.org/D91175 + +(cherry picked from commit d4f4e723d0b4f09d72880f1679c02d586bf8abfa) +--- + libcxx/src/filesystem/operations.cpp | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index dccec9378531..674f19154e6f 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1347,6 +1347,19 @@ file_status __symlink_status(const path& p, error_code* ec) { + path __temp_directory_path(error_code* ec) { + ErrorHandler err("temp_directory_path", ec); + ++#if defined(_LIBCPP_WIN32API) ++ wchar_t buf[MAX_PATH]; ++ DWORD retval = GetTempPathW(MAX_PATH, buf); ++ if (!retval) ++ return err.report(detail::make_windows_error(GetLastError())); ++ if (retval > MAX_PATH) ++ return err.report(errc::filename_too_long); ++ // GetTempPathW returns a path with a trailing slash, which we ++ // shouldn't include for consistency. ++ if (buf[retval-1] == L'\\') ++ buf[retval-1] = L'\0'; ++ path p(buf); ++#else + const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; + const char* ret = nullptr; + +@@ -1357,6 +1370,7 @@ path __temp_directory_path(error_code* ec) { + ret = "/tmp"; + + path p(ret); ++#endif + error_code m_ec; + file_status st = detail::posix_stat(p, &m_ec); + if (!status_known(st)) +-- +2.25.1 + diff --git a/patches/llvm-project/0012-libcxx-Implement-parsing-of-root_name-for-paths-on-w.patch b/patches/llvm-project/0012-libcxx-Implement-parsing-of-root_name-for-paths-on-w.patch new file mode 100644 index 0000000..9200e17 --- /dev/null +++ b/patches/llvm-project/0012-libcxx-Implement-parsing-of-root_name-for-paths-on-w.patch @@ -0,0 +1,173 @@ +From 3f12aeb4ae3fd43d98f0c698952713d9fef7bef3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Tue, 3 Nov 2020 23:52:32 +0200 +Subject: [PATCH 12/24] [libcxx] Implement parsing of root_name for paths on + windows + +Differential Revision: https://reviews.llvm.org/D91176 + +(cherry picked from commit 929f0bcc24e246ea02ab57df8009a6fd5751d45c) +--- + libcxx/src/filesystem/operations.cpp | 92 +++++++++++++++++++++++++--- + 1 file changed, 85 insertions(+), 7 deletions(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 674f19154e6f..88a039f85021 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -64,6 +64,10 @@ bool isSeparator(path::value_type C) { + return false; + } + ++bool isDriveLetter(path::value_type C) { ++ return (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z'); ++} ++ + namespace parser { + + using string_view_t = path::__string_view; +@@ -120,6 +124,12 @@ public: + + switch (State) { + case PS_BeforeBegin: { ++ PosPtr TkEnd = consumeRootName(Start, End); ++ if (TkEnd) ++ return makeState(PS_InRootName, Start, TkEnd); ++ } ++ _LIBCPP_FALLTHROUGH(); ++ case PS_InRootName: { + PosPtr TkEnd = consumeSeparator(Start, End); + if (TkEnd) + return makeState(PS_InRootDir, Start, TkEnd); +@@ -142,7 +152,6 @@ public: + case PS_InTrailingSep: + return makeState(PS_AtEnd); + +- case PS_InRootName: + case PS_AtEnd: + _LIBCPP_UNREACHABLE(); + } +@@ -160,9 +169,15 @@ public: + if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) { + if (SepEnd == REnd) + return makeState(PS_InRootDir, Path.data(), RStart + 1); ++ PosPtr TkStart = consumeRootName(SepEnd, REnd); ++ if (TkStart == REnd) ++ return makeState(PS_InRootDir, RStart, RStart + 1); + return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1); + } else { +- PosPtr TkStart = consumeName(RStart, REnd); ++ PosPtr TkStart = consumeRootName(RStart, REnd); ++ if (TkStart == REnd) ++ return makeState(PS_InRootName, TkStart + 1, RStart + 1); ++ TkStart = consumeName(RStart, REnd); + return makeState(PS_InFilenames, TkStart + 1, RStart + 1); + } + } +@@ -173,11 +188,17 @@ public: + PosPtr SepEnd = consumeSeparator(RStart, REnd); + if (SepEnd == REnd) + return makeState(PS_InRootDir, Path.data(), RStart + 1); +- PosPtr TkEnd = consumeName(SepEnd, REnd); +- return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1); ++ PosPtr TkStart = consumeRootName(SepEnd ? SepEnd : RStart, REnd); ++ if (TkStart == REnd) { ++ if (SepEnd) ++ return makeState(PS_InRootDir, SepEnd + 1, RStart + 1); ++ return makeState(PS_InRootName, TkStart + 1, RStart + 1); ++ } ++ TkStart = consumeName(SepEnd, REnd); ++ return makeState(PS_InFilenames, TkStart + 1, SepEnd + 1); + } + case PS_InRootDir: +- // return makeState(PS_InRootName, Path.data(), RStart + 1); ++ return makeState(PS_InRootName, Path.data(), RStart + 1); + case PS_InRootName: + case PS_BeforeBegin: + _LIBCPP_UNREACHABLE(); +@@ -284,7 +305,7 @@ private: + } + + PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept { +- if (P == End || !isSeparator(*P)) ++ if (P == nullptr || P == End || !isSeparator(*P)) + return nullptr; + const int Inc = P < End ? 1 : -1; + P += Inc; +@@ -293,15 +314,72 @@ private: + return P; + } + ++ // Consume exactly N separators, or return nullptr. ++ PosPtr consumeNSeparators(PosPtr P, PosPtr End, int N) const noexcept { ++ PosPtr Ret = consumeSeparator(P, End); ++ if (Ret == nullptr) ++ return nullptr; ++ if (P < End) { ++ if (Ret == P + N) ++ return Ret; ++ } else { ++ if (Ret == P - N) ++ return Ret; ++ } ++ return nullptr; ++ } ++ + PosPtr consumeName(PosPtr P, PosPtr End) const noexcept { +- if (P == End || isSeparator(*P)) ++ PosPtr Start = P; ++ if (P == nullptr || P == End || isSeparator(*P)) + return nullptr; + const int Inc = P < End ? 1 : -1; + P += Inc; + while (P != End && !isSeparator(*P)) + P += Inc; ++ if (P == End && Inc < 0) { ++ // Iterating backwards and consumed all the rest of the input. ++ // Check if the start of the string would have been considered ++ // a root name. ++ PosPtr RootEnd = consumeRootName(End + 1, Start); ++ if (RootEnd) ++ return RootEnd - 1; ++ } + return P; + } ++ ++ PosPtr consumeDriveLetter(PosPtr P, PosPtr End) const noexcept { ++ if (P == End) ++ return nullptr; ++ if (P < End) { ++ if (P + 1 == End || !isDriveLetter(P[0]) || P[1] != ':') ++ return nullptr; ++ return P + 2; ++ } else { ++ if (P - 1 == End || !isDriveLetter(P[-1]) || P[0] != ':') ++ return nullptr; ++ return P - 2; ++ } ++ } ++ ++ PosPtr consumeNetworkRoot(PosPtr P, PosPtr End) const noexcept { ++ if (P == End) ++ return nullptr; ++ if (P < End) ++ return consumeName(consumeNSeparators(P, End, 2), End); ++ else ++ return consumeNSeparators(consumeName(P, End), End, 2); ++ } ++ ++ PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept { ++#if defined(_LIBCPP_WIN32API) ++ if (PosPtr Ret = consumeDriveLetter(P, End)) ++ return Ret; ++ if (PosPtr Ret = consumeNetworkRoot(P, End)) ++ return Ret; ++#endif ++ return nullptr; ++ } + }; + + string_view_pair separate_filename(string_view_t const& s) { +-- +2.25.1 + diff --git a/patches/llvm-project/0013-libcxx-Implement-is_absolute-properly-for-windows.patch b/patches/llvm-project/0013-libcxx-Implement-is_absolute-properly-for-windows.patch new file mode 100644 index 0000000..6c164f6 --- /dev/null +++ b/patches/llvm-project/0013-libcxx-Implement-is_absolute-properly-for-windows.patch @@ -0,0 +1,48 @@ +From bd50990c64deae4ce6027767659c75a22822e2f5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Sun, 1 Nov 2020 23:39:03 +0200 +Subject: [PATCH 13/24] [libcxx] Implement is_absolute properly for windows + +Differential Revision: https://reviews.llvm.org/D91177 + +(cherry picked from commit 8a783e68452f646360d9902d2c2bc0e115d7bfa9) +--- + libcxx/include/filesystem | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem +index 92e37e183def..eecf416e851c 100644 +--- a/libcxx/include/filesystem ++++ b/libcxx/include/filesystem +@@ -1341,7 +1341,28 @@ public: + } + + _LIBCPP_INLINE_VISIBILITY bool is_absolute() const { ++#if defined(_LIBCPP_WIN32API) ++ __string_view __root_name_str = __root_name(); ++ __string_view __root_dir = __root_directory(); ++ if (__root_name_str.size() == 2 && __root_name_str[1] == ':') { ++ // A drive letter with no root directory is relative, e.g. x:example. ++ return !__root_dir.empty(); ++ } ++ // If no root name, it's relative, e.g. \example is relative to the current drive ++ if (__root_name_str.empty()) ++ return false; ++ if (__root_name_str.size() < 3) ++ return false; ++ // A server root name, like \\server, is always absolute ++ if (__root_name_str[0] != '/' && __root_name_str[0] != '\\') ++ return false; ++ if (__root_name_str[1] != '/' && __root_name_str[1] != '\\') ++ return false; ++ // Seems to be a server root name ++ return true; ++#else + return has_root_directory(); ++#endif + } + _LIBCPP_INLINE_VISIBILITY bool is_relative() const { return !is_absolute(); } + +-- +2.25.1 + diff --git a/patches/llvm-project/0014-libcxx-Implement-append-and-operator-properly-for-wi.patch b/patches/llvm-project/0014-libcxx-Implement-append-and-operator-properly-for-wi.patch new file mode 100644 index 0000000..99e768a --- /dev/null +++ b/patches/llvm-project/0014-libcxx-Implement-append-and-operator-properly-for-wi.patch @@ -0,0 +1,174 @@ +From bd884f520faaa41618d6e901e1631ff02fab3044 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Wed, 4 Nov 2020 15:59:56 +0200 +Subject: [PATCH 14/24] [libcxx] Implement append and operator/ properly for + windows + +The root_path function has to be changed to return the parsed bit +as-is; otherwise a path like "//net" gets a root path of "//net/", as +the root name, "//net", gets the root directory (an empty string) appended, +forming "//net/". (The same doesn't happen for the root dir "c:" though.) + +Differential Revision: https://reviews.llvm.org/D91178 + +(cherry picked from commit 78d693faecf98718dadfa6e39f291e5999f380c7) +--- + libcxx/include/filesystem | 52 ++++++++++++++++--- + .../path.member/path.append.pass.cpp | 48 +++++++++++++++++ + 2 files changed, 92 insertions(+), 8 deletions(-) + +diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem +index eecf416e851c..201cecc0e8b2 100644 +--- a/libcxx/include/filesystem ++++ b/libcxx/include/filesystem +@@ -1006,14 +1006,44 @@ public: + return *this; + } + +-private: +- template +- static bool __source_is_absolute(_ECharT __first_or_null) { +- return __is_separator(__first_or_null); +- } +- + public: + // appends ++#if defined(_LIBCPP_WIN32API) ++ path& operator/=(const path& __p) { ++ auto __p_root_name = __p.__root_name(); ++ auto __p_root_name_size = __p_root_name.size(); ++ if (__p.is_absolute() || ++ (!__p_root_name.empty() && __p_root_name != root_name())) { ++ __pn_ = __p.__pn_; ++ return *this; ++ } ++ if (__p.has_root_directory()) { ++ path __root_name_str = root_name(); ++ __pn_ = __root_name_str.native(); ++ __pn_ += __p.__pn_.substr(__p_root_name_size); ++ return *this; ++ } ++ if (has_filename() || (!has_root_directory() && is_absolute())) ++ __pn_ += preferred_separator; ++ __pn_ += __p.__pn_.substr(__p_root_name_size); ++ return *this; ++ } ++ template ++ _LIBCPP_INLINE_VISIBILITY _EnableIfPathable<_Source> ++ operator/=(const _Source& __src) { ++ return operator/=(path(__src)); ++ } ++ ++ template ++ _EnableIfPathable<_Source> append(const _Source& __src) { ++ return operator/=(path(__src)); ++ } ++ ++ template ++ path& append(_InputIt __first, _InputIt __last) { ++ return operator/=(path(__first, __last)); ++ } ++#else + path& operator/=(const path& __p) { + if (__p.is_absolute()) { + __pn_ = __p.__pn_; +@@ -1038,7 +1068,8 @@ public: + _EnableIfPathable<_Source> append(const _Source& __src) { + using _Traits = __is_pathable<_Source>; + using _CVT = _PathCVT<_SourceChar<_Source> >; +- if (__source_is_absolute(_Traits::__first_or_null(__src))) ++ bool __source_is_absolute = __is_separator(_Traits::__first_or_null(__src)); ++ if (__source_is_absolute) + __pn_.clear(); + else if (has_filename()) + __pn_ += preferred_separator; +@@ -1051,13 +1082,14 @@ public: + typedef typename iterator_traits<_InputIt>::value_type _ItVal; + static_assert(__can_convert_char<_ItVal>::value, "Must convertible"); + using _CVT = _PathCVT<_ItVal>; +- if (__first != __last && __source_is_absolute(*__first)) ++ if (__first != __last && __is_separator(*__first)) + __pn_.clear(); + else if (has_filename()) + __pn_ += preferred_separator; + _CVT::__append_range(__pn_, __first, __last); + return *this; + } ++#endif + + // concatenation + _LIBCPP_INLINE_VISIBILITY +@@ -1295,7 +1327,11 @@ public: + return string_type(__root_directory()); + } + _LIBCPP_INLINE_VISIBILITY path root_path() const { ++#if defined(_LIBCPP_WIN32API) ++ return string_type(__root_path_raw()); ++#else + return root_name().append(string_type(__root_directory())); ++#endif + } + _LIBCPP_INLINE_VISIBILITY path relative_path() const { + return string_type(__relative_path()); +diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp +index eabd6f92da3c..ad9d06eb9849 100644 +--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp +@@ -63,6 +63,54 @@ const AppendOperatorTestcase Cases[] = + , {S("/p1"), S("/p2/"), S("/p2/")} + , {S("p1"), S(""), S("p1/")} + , {S("p1/"), S(""), S("p1/")} ++ ++ , {S("//host"), S("foo"), S("//host/foo")} ++ , {S("//host/"), S("foo"), S("//host/foo")} ++ , {S("//host"), S(""), S("//host/")} ++ ++#ifdef _WIN32 ++ , {S("foo"), S("C:/bar"), S("C:/bar")} ++ , {S("foo"), S("C:"), S("C:")} ++ ++ , {S("C:"), S(""), S("C:")} ++ , {S("C:foo"), S("/bar"), S("C:/bar")} ++ , {S("C:foo"), S("bar"), S("C:foo/bar")} ++ , {S("C:/foo"), S("bar"), S("C:/foo/bar")} ++ , {S("C:/foo"), S("/bar"), S("C:/bar")} ++ ++ , {S("C:foo"), S("C:/bar"), S("C:/bar")} ++ , {S("C:foo"), S("C:bar"), S("C:foo/bar")} ++ , {S("C:/foo"), S("C:/bar"), S("C:/bar")} ++ , {S("C:/foo"), S("C:bar"), S("C:/foo/bar")} ++ ++ , {S("C:foo"), S("c:/bar"), S("c:/bar")} ++ , {S("C:foo"), S("c:bar"), S("c:bar")} ++ , {S("C:/foo"), S("c:/bar"), S("c:/bar")} ++ , {S("C:/foo"), S("c:bar"), S("c:bar")} ++ ++ , {S("C:/foo"), S("D:bar"), S("D:bar")} ++#else ++ , {S("foo"), S("C:/bar"), S("foo/C:/bar")} ++ , {S("foo"), S("C:"), S("foo/C:")} ++ ++ , {S("C:"), S(""), S("C:/")} ++ , {S("C:foo"), S("/bar"), S("/bar")} ++ , {S("C:foo"), S("bar"), S("C:foo/bar")} ++ , {S("C:/foo"), S("bar"), S("C:/foo/bar")} ++ , {S("C:/foo"), S("/bar"), S("/bar")} ++ ++ , {S("C:foo"), S("C:/bar"), S("C:foo/C:/bar")} ++ , {S("C:foo"), S("C:bar"), S("C:foo/C:bar")} ++ , {S("C:/foo"), S("C:/bar"), S("C:/foo/C:/bar")} ++ , {S("C:/foo"), S("C:bar"), S("C:/foo/C:bar")} ++ ++ , {S("C:foo"), S("c:/bar"), S("C:foo/c:/bar")} ++ , {S("C:foo"), S("c:bar"), S("C:foo/c:bar")} ++ , {S("C:/foo"), S("c:/bar"), S("C:/foo/c:/bar")} ++ , {S("C:/foo"), S("c:bar"), S("C:/foo/c:bar")} ++ ++ , {S("C:/foo"), S("D:bar"), S("C:/foo/D:bar")} ++#endif + }; + + +-- +2.25.1 + diff --git a/patches/llvm-project/0015-libcxx-Have-lexically_normal-return-the-path-with-pr.patch b/patches/llvm-project/0015-libcxx-Have-lexically_normal-return-the-path-with-pr.patch new file mode 100644 index 0000000..eb83ae3 --- /dev/null +++ b/patches/llvm-project/0015-libcxx-Have-lexically_normal-return-the-path-with-pr.patch @@ -0,0 +1,28 @@ +From 6131abb23fc2990f9a869ef3d9c63bb0fd5ff464 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Thu, 5 Nov 2020 23:09:15 +0200 +Subject: [PATCH 15/24] [libcxx] Have lexically_normal return the path with + preferred separators + +Differential Revision: https://reviews.llvm.org/D91179 + +(cherry picked from commit 513463fd266f059864ce3c0236494cced5de0f56) +--- + libcxx/src/filesystem/operations.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 88a039f85021..a3b93b594a07 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1719,6 +1719,7 @@ path path::lexically_normal() const { + if (NeedTrailingSep) + Result /= PS(""); + ++ Result.make_preferred(); + return Result; + } + +-- +2.25.1 + diff --git a/patches/llvm-project/0016-libcxx-Make-generic_-string-return-paths-with-forwar.patch b/patches/llvm-project/0016-libcxx-Make-generic_-string-return-paths-with-forwar.patch new file mode 100644 index 0000000..c75bfdf --- /dev/null +++ b/patches/llvm-project/0016-libcxx-Make-generic_-string-return-paths-with-forwar.patch @@ -0,0 +1,98 @@ +From 415ea201cb19775f253dfb0cf8dc561891cff02d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Mon, 9 Nov 2020 11:45:13 +0200 +Subject: [PATCH 16/24] [libcxx] Make generic_*string return paths with forward + slashes on windows + +This matches what MS STL returns; in std::filesystem, forward slashes +are considered generic dir separators that are valid on all platforms. + +Differential Revision: https://reviews.llvm.org/D91181 + +(cherry picked from commit f4f5fb915104887fefa602cfcbd1d4fc8447d46b) +--- + libcxx/include/filesystem | 22 ++++++++++++++++--- + .../path.generic.obs/named_overloads.pass.cpp | 15 +++++++++---- + 2 files changed, 30 insertions(+), 7 deletions(-) + +diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem +index 201cecc0e8b2..19efca2d8fc2 100644 +--- a/libcxx/include/filesystem ++++ b/libcxx/include/filesystem +@@ -1193,7 +1193,12 @@ public: + #if defined(_LIBCPP_WIN32API) + _LIBCPP_INLINE_VISIBILITY _VSTD::wstring wstring() const { return __pn_; } + +- _VSTD::wstring generic_wstring() const { return __pn_; } ++ _VSTD::wstring generic_wstring() const { ++ _VSTD::wstring __s; ++ __s.resize(__pn_.size()); ++ _VSTD::replace_copy(__pn_.begin(), __pn_.end(), __s.begin(), '\\', '/'); ++ return __s; ++ } + + #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) + template , +@@ -1230,13 +1235,24 @@ public: + class _Allocator = allocator<_ECharT> > + basic_string<_ECharT, _Traits, _Allocator> + generic_string(const _Allocator& __a = _Allocator()) const { +- return string<_ECharT, _Traits, _Allocator>(__a); ++ using _Str = basic_string<_ECharT, _Traits, _Allocator>; ++ _Str __s = string<_ECharT, _Traits, _Allocator>(__a); ++ // Note: This (and generic_u8string below) is slightly suboptimal as ++ // it iterates twice over the string; once to convert it to the right ++ // character type, and once to replace path delimiters. ++ _VSTD::replace(__s.begin(), __s.end(), ++ static_cast<_ECharT>('\\'), static_cast<_ECharT>('/')); ++ return __s; + } + + _VSTD::string generic_string() const { return generic_string(); } + _VSTD::u16string generic_u16string() const { return generic_string(); } + _VSTD::u32string generic_u32string() const { return generic_string(); } +- __u8_string generic_u8string() const { return u8string(); } ++ __u8_string generic_u8string() const { ++ __u8_string __s = u8string(); ++ _VSTD::replace(__s.begin(), __s.end(), '\\', '/'); ++ return __s; ++ } + #endif /* !_LIBCPP_HAS_NO_LOCALIZATION */ + #else /* _LIBCPP_WIN32API */ + +diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/named_overloads.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/named_overloads.pass.cpp +index 58c07e2feb70..d8991592efcf 100644 +--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/named_overloads.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/named_overloads.pass.cpp +@@ -32,17 +32,24 @@ + #include "min_allocator.h" + #include "filesystem_test_helper.h" + +-MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); ++MultiStringType input = MKSTR("c:\\foo\\bar"); ++#ifdef _WIN32 ++// On windows, the generic_* accessors return a path with forward slashes ++MultiStringType ref = MKSTR("c:/foo/bar"); ++#else ++// On posix, the input string is returned as-is ++MultiStringType ref = MKSTR("c:\\foo\\bar"); ++#endif + + int main(int, char**) + { + using namespace fs; +- auto const& MS = longString; +- const char* value = longString; ++ auto const& MS = ref; ++ const char* value = input; + const path p(value); + { + std::string s = p.generic_string(); +- assert(s == value); ++ assert(s == (const char*)MS); + } + { + #if TEST_STD_VER > 17 && defined(__cpp_char8_t) +-- +2.25.1 + diff --git a/patches/llvm-project/0017-SystemZ-ZOS-Guard-using-declaration-for-fchmodat.patch b/patches/llvm-project/0017-SystemZ-ZOS-Guard-using-declaration-for-fchmodat.patch new file mode 100644 index 0000000..cd3f429 --- /dev/null +++ b/patches/llvm-project/0017-SystemZ-ZOS-Guard-using-declaration-for-fchmodat.patch @@ -0,0 +1,33 @@ +From c8c08547b2d1e87391213846c7e17f5426a3dd48 Mon Sep 17 00:00:00 2001 +From: Zbigniew Sarbinowski +Date: Thu, 18 Feb 2021 14:49:46 +0000 +Subject: [PATCH 17/24] [SystemZ][ZOS] Guard using declaration for ::fchmodat + +The use of fchmodat() is beeing guarded but its using declaration is not. Let's use the same guard in both places to avoid compiler errors on platforms where `fchmodat` does not exist. + +Reviewed By: #libc, ldionne + +Differential Revision: https://reviews.llvm.org/D96303 + +(cherry picked from commit 25aa0d12445eed6e278489546d18fcb7a33cfaa6) +--- + libcxx/src/filesystem/posix_compat.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h +index 8062bd65ce00..f5c8ec39df2b 100644 +--- a/libcxx/src/filesystem/posix_compat.h ++++ b/libcxx/src/filesystem/posix_compat.h +@@ -487,7 +487,9 @@ int symlink_dir(const char *oldname, const char *newname) { + using ::chdir; + using ::close; + using ::fchmod; ++#if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD) + using ::fchmodat; ++#endif + using ::fstat; + using ::ftruncate; + using ::getcwd; +-- +2.25.1 + diff --git a/patches/llvm-project/0018-libcxx-Enable-filesystem-by-default-for-mingw-target.patch b/patches/llvm-project/0018-libcxx-Enable-filesystem-by-default-for-mingw-target.patch new file mode 100644 index 0000000..5096b88 --- /dev/null +++ b/patches/llvm-project/0018-libcxx-Enable-filesystem-by-default-for-mingw-target.patch @@ -0,0 +1,38 @@ +From ff2e16fe051405232fbd9a09c406767b229d27ec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Thu, 5 Nov 2020 00:13:22 +0200 +Subject: [PATCH 18/24] [libcxx] Enable filesystem by default for mingw targets + +This feature can be built successfully for windows now. However, +the helper functions for __int128_t aren't available in MSVC +configurations, so don't enable it by default there yet. (See +https://reviews.llvm.org/D91139 for discussion on how to proceed +with things in MSVC environments.) + +Differential Revision: https://reviews.llvm.org/D97075 + +(cherry picked from commit 99fc4a65847a7020ae328e42a67e80cc29c1e762) +--- + libcxx/CMakeLists.txt | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt +index 9bf1a02f0908..b8716a34a325 100644 +--- a/libcxx/CMakeLists.txt ++++ b/libcxx/CMakeLists.txt +@@ -90,7 +90,11 @@ option(LIBCXX_ENABLE_SHARED "Build libc++ as a shared library." ON) + option(LIBCXX_ENABLE_STATIC "Build libc++ as a static library." ON) + option(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY "Build libc++experimental.a" ON) + set(ENABLE_FILESYSTEM_DEFAULT ON) +-if (WIN32) ++if (WIN32 AND NOT MINGW) ++ # Filesystem is buildable for windows, but it requires __int128 helper ++ # functions, that currently are provided by libgcc or compiler_rt builtins. ++ # These are available in MinGW environments, but not currently in MSVC ++ # environments. + set(ENABLE_FILESYSTEM_DEFAULT OFF) + endif() + option(LIBCXX_ENABLE_FILESYSTEM "Build filesystem as part of the main libc++ library" +-- +2.25.1 + diff --git a/patches/llvm-project/0019-libcxx-Explicitly-return-the-expected-error-code-in-.patch b/patches/llvm-project/0019-libcxx-Explicitly-return-the-expected-error-code-in-.patch new file mode 100644 index 0000000..8868a54 --- /dev/null +++ b/patches/llvm-project/0019-libcxx-Explicitly-return-the-expected-error-code-in-.patch @@ -0,0 +1,39 @@ +From 69856f3e59b210249020504329e016c691f138e9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Fri, 18 Dec 2020 13:34:35 +0200 +Subject: [PATCH 19/24] [libcxx] Explicitly return the expected error code in + create_directories if the parent isn't a directory + +On windows, going ahead and actually trying to create the directory +doesn't return an error code that maps to +std::errc::not_a_directory in this case. + +This fixes two cases of + TEST_CHECK(ErrorIs(ec, std::errc::not_a_directory)) +in filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp +for windows (in testcases added in 59c72a70121567f7aee347e96b4ac8f3cfe9f4b2). + +Differential Revision: https://reviews.llvm.org/D97090 + +(cherry picked from commit c5e8f024dca9ddf6d14253fe2fcc5c4956de2d3c) +--- + libcxx/src/filesystem/operations.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index a3b93b594a07..bfc6c44d4ce4 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1022,7 +1022,8 @@ bool __create_directories(const path& p, error_code* ec) { + if (ec && *ec) { + return false; + } +- } ++ } else if (not is_directory(parent_st)) ++ return err.report(errc::not_a_directory); + } + return __create_directory(p, ec); + } +-- +2.25.1 + diff --git a/patches/llvm-project/0020-libcxx-Avoid-infinite-recursion-in-create_directorie.patch b/patches/llvm-project/0020-libcxx-Avoid-infinite-recursion-in-create_directorie.patch new file mode 100644 index 0000000..f144778 --- /dev/null +++ b/patches/llvm-project/0020-libcxx-Avoid-infinite-recursion-in-create_directorie.patch @@ -0,0 +1,53 @@ +From 6c968da9f1e9281055d31f66c292f81de204924d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Sat, 27 Feb 2021 19:12:25 +0200 +Subject: [PATCH 20/24] [libcxx] Avoid infinite recursion in + create_directories, if the root directory doesn't exist + +Differential Revision: https://reviews.llvm.org/D97618 + +(cherry picked from commit 99c7b532946508efcf6cd978d86ee24b2a66d096) +--- + libcxx/src/filesystem/operations.cpp | 2 ++ + .../create_directories.pass.cpp | 14 ++++++++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index bfc6c44d4ce4..7c77660538b5 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -1018,6 +1018,8 @@ bool __create_directories(const path& p, error_code* ec) { + if (not status_known(parent_st)) + return err.report(m_ec); + if (not exists(parent_st)) { ++ if (parent == p) ++ return err.report(errc::invalid_argument); + __create_directories(parent, ec); + if (ec && *ec) { + return false; +diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp +index 9ce450a0e48d..54820cad0f55 100644 +--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp +@@ -138,4 +138,18 @@ TEST_CASE(dest_final_part_is_file) + TEST_CHECK(!exists(dir)); + } + ++#ifdef _WIN32 ++TEST_CASE(nonexistent_root) ++{ ++ std::error_code ec = GetTestEC(); ++ // If Q:\ doesn't exist, create_directories would try to recurse upwards ++ // to parent_path() until it finds a directory that does exist. As the ++ // whole path is the root name, parent_path() returns itself, and it ++ // would recurse indefinitely, unless the recursion is broken. ++ if (!exists("Q:\\")) ++ TEST_CHECK(fs::create_directories("Q:\\", ec) == false); ++ TEST_CHECK(fs::create_directories("\\\\nonexistentserver", ec) == false); ++} ++#endif ++ + TEST_SUITE_END() +-- +2.25.1 + diff --git a/patches/llvm-project/0021-libcxx-Map-ERROR_BAD_PATHNAME-to-errc-no_such_file_o.patch b/patches/llvm-project/0021-libcxx-Map-ERROR_BAD_PATHNAME-to-errc-no_such_file_o.patch new file mode 100644 index 0000000..f4127fd --- /dev/null +++ b/patches/llvm-project/0021-libcxx-Map-ERROR_BAD_PATHNAME-to-errc-no_such_file_o.patch @@ -0,0 +1,46 @@ +From cd87c3f6b36fd6cd991b8e3a3ac85dc208d55ea1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Sat, 27 Feb 2021 16:09:49 +0200 +Subject: [PATCH 21/24] [libcxx] Map ERROR_BAD_PATHNAME to + errc::no_such_file_or_directory on windows + +Opening a path like \\server (without a trailing share name and +path) produces this error, while opening e.g. \\server\share +(for a nonexistent server/share) produces ERROR_BAD_NETPATH (which +already is mapped). + +This happens in some testcases (in fs.op.proximate); as proximate() +calls weakly_canonical() on the inputs, weakly_canonical() checks +whether the path exists or not. When the error code wasn't recognized +(it mapped to errc::invalid_argument), the stat operation wasn't +conclusive and weakly_canonical() errored out. With the proper error +code mapping, this isn't considered an error, just a nonexistent +path, and weakly_canonical() can proceed. + +This roughly matches what MS STL does - it doesn't have +ERROR_BAD_PATHNAME in its error code mapping table, but it +checks for this error code specifically in the return of their +correspondence of the stat function. + +Differential Revision: https://reviews.llvm.org/D97619 + +(cherry picked from commit 29012ce986fcb24175f19317b4e2d119cd8cdbb2) +--- + libcxx/src/filesystem/operations.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 7c77660538b5..360643d4c241 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -411,6 +411,7 @@ errc __win_err_to_errc(int err) { + {ERROR_ACCESS_DENIED, errc::permission_denied}, + {ERROR_ALREADY_EXISTS, errc::file_exists}, + {ERROR_BAD_NETPATH, errc::no_such_file_or_directory}, ++ {ERROR_BAD_PATHNAME, errc::no_such_file_or_directory}, + {ERROR_BAD_UNIT, errc::no_such_device}, + {ERROR_BROKEN_PIPE, errc::broken_pipe}, + {ERROR_BUFFER_OVERFLOW, errc::filename_too_long}, +-- +2.25.1 + diff --git a/patches/llvm-project/0022-libcxx-Avoid-intermediate-string-objects-for-substri.patch b/patches/llvm-project/0022-libcxx-Avoid-intermediate-string-objects-for-substri.patch new file mode 100644 index 0000000..3e834bb --- /dev/null +++ b/patches/llvm-project/0022-libcxx-Avoid-intermediate-string-objects-for-substri.patch @@ -0,0 +1,63 @@ +From 497842a9ebb0ca1a6f871a07c0f4b4f1c700a798 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Thu, 11 Mar 2021 13:06:08 +0200 +Subject: [PATCH 22/24] [libcxx] Avoid intermediate string objects for + substrings in windows operator/= + +Check that appends with a path object doesn't do allocations, even +on windows. + +Suggested by Marek in D98398. The patch might apply without D98398 +(depending on how much of the diff context has to match), but doesn't +make much sense until after that patch has landed. + +Differential Revision: https://reviews.llvm.org/D98412 + +(cherry picked from commit 49173ca4db21e4d1576c2440b79ebff48c6c4156) +--- + libcxx/include/filesystem | 4 ++-- + .../class.path/path.member/path.append.pass.cpp | 9 +++++++++ + 2 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem +index 19efca2d8fc2..2fc110715559 100644 +--- a/libcxx/include/filesystem ++++ b/libcxx/include/filesystem +@@ -1020,12 +1020,12 @@ public: + if (__p.has_root_directory()) { + path __root_name_str = root_name(); + __pn_ = __root_name_str.native(); +- __pn_ += __p.__pn_.substr(__p_root_name_size); ++ __pn_ += __string_view(__p.__pn_).substr(__p_root_name_size); + return *this; + } + if (has_filename() || (!has_root_directory() && is_absolute())) + __pn_ += preferred_separator; +- __pn_ += __p.__pn_.substr(__p_root_name_size); ++ __pn_ += __string_view(__p.__pn_).substr(__p_root_name_size); + return *this; + } + template +diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp +index ad9d06eb9849..834d5e999abb 100644 +--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp +@@ -189,6 +189,15 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) + } + assert(PathEq(LHS, E)); + } ++ { ++ path LHS(L); PathReserve(LHS, ReserveSize); ++ path RHS(R); ++ { ++ DisableAllocationGuard g; ++ LHS /= RHS; ++ } ++ assert(PathEq(LHS, E)); ++ } + // input iterator - For non-native char types, appends needs to copy the + // iterator range into a contiguous block of memory before it can perform the + // code_cvt conversions. +-- +2.25.1 + diff --git a/patches/llvm-project/0023-libcxx-Test-accessing-a-directory-on-windows-that-gi.patch b/patches/llvm-project/0023-libcxx-Test-accessing-a-directory-on-windows-that-gi.patch new file mode 100644 index 0000000..44bb113 --- /dev/null +++ b/patches/llvm-project/0023-libcxx-Test-accessing-a-directory-on-windows-that-gi.patch @@ -0,0 +1,197 @@ +From 9a1bb266e38b31c78de03abc2d41cdd3245595d0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Mon, 8 Mar 2021 12:03:30 +0200 +Subject: [PATCH 23/24] [libcxx] Test accessing a directory on windows that + gives "access denied" errors + +Fix handling of skip_permission_denied on windows; after converting +the return value of GetLastError() to a standard error_code, ec.value() +is in the standard errc range, not a native windows error code. This +was missed in 156180727d6c347eda3ba749730707acb8a48093. + +The directory "C:\System Volume Information" does seem to exist and +have these properties on most relevant contempory setups. + +Differential Revision: https://reviews.llvm.org/D98166 + +(cherry picked from commit e69c65d5c45557e140b13237448581d28bbd5846) +--- + libcxx/src/filesystem/directory_iterator.cpp | 3 ++- + .../directory_entry.cons/path.pass.cpp | 18 +++++++++++++ + .../directory_iterator.members/ctor.pass.cpp | 8 ++++++ + .../rec.dir.itr.members/ctor.pass.cpp | 8 ++++++ + .../fs.op.funcs/fs.op.exists/exists.pass.cpp | 14 ++++++++--- + libcxx/test/support/filesystem_test_helper.h | 25 +++++++++++++++++++ + 6 files changed, 72 insertions(+), 4 deletions(-) + +diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp +index 2721dea5c98f..bb3653076bfc 100644 +--- a/libcxx/src/filesystem/directory_iterator.cpp ++++ b/libcxx/src/filesystem/directory_iterator.cpp +@@ -124,7 +124,8 @@ public: + ec = detail::make_windows_error(GetLastError()); + const bool ignore_permission_denied = + bool(opts & directory_options::skip_permission_denied); +- if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED) ++ if (ignore_permission_denied && ++ ec.value() == static_cast(errc::permission_denied)) + ec.clear(); + return; + } +diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp +index e3759e22b0ba..a118fae4d54b 100644 +--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp +@@ -148,6 +148,23 @@ TEST_CASE(path_ctor_dne) { + + TEST_CASE(path_ctor_cannot_resolve) { + using namespace fs; ++#ifdef _WIN32 ++ // Windows doesn't support setting perms::none to trigger failures ++ // reading directories; test using a special inaccessible directory ++ // instead. ++ const path dir = GetWindowsInaccessibleDir(); ++ TEST_REQUIRE(!dir.empty()); ++ const path file = dir / "file"; ++ { ++ std::error_code ec = GetTestEC(); ++ directory_entry ent(file, ec); ++ TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); ++ TEST_CHECK(ent.path() == file); ++ } ++ { ++ TEST_CHECK_NO_THROW(directory_entry(file)); ++ } ++#else + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); +@@ -179,6 +196,7 @@ TEST_CASE(path_ctor_cannot_resolve) { + TEST_CHECK_NO_THROW(directory_entry(sym_in_dir)); + TEST_CHECK_NO_THROW(directory_entry(sym_out_of_dir)); + } ++#endif + } + + TEST_SUITE_END() +diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp +index 48cf72a77b05..fae696613c75 100644 +--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp +@@ -87,6 +87,13 @@ TEST_CASE(test_construction_from_bad_path) + TEST_CASE(access_denied_test_case) + { + using namespace fs; ++#ifdef _WIN32 ++ // Windows doesn't support setting perms::none to trigger failures ++ // reading directories; test using a special inaccessible directory ++ // instead. ++ const path testDir = GetWindowsInaccessibleDir(); ++ TEST_REQUIRE(!testDir.empty()); ++#else + scoped_test_env env; + path const testDir = env.make_env_path("dir1"); + path const testFile = testDir / "testFile"; +@@ -100,6 +107,7 @@ TEST_CASE(access_denied_test_case) + } + // Change the permissions so we can no longer iterate + permissions(testDir, perms::none); ++#endif + + // Check that the construction fails when skip_permissions_denied is + // not given. +diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp +index ddffaca39335..53ca5737ba31 100644 +--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp +@@ -88,6 +88,13 @@ TEST_CASE(test_construction_from_bad_path) + TEST_CASE(access_denied_test_case) + { + using namespace fs; ++#ifdef _WIN32 ++ // Windows doesn't support setting perms::none to trigger failures ++ // reading directories; test using a special inaccessible directory ++ // instead. ++ const path testDir = GetWindowsInaccessibleDir(); ++ TEST_REQUIRE(!testDir.empty()); ++#else + scoped_test_env env; + path const testDir = env.make_env_path("dir1"); + path const testFile = testDir / "testFile"; +@@ -102,6 +109,7 @@ TEST_CASE(access_denied_test_case) + + // Change the permissions so we can no longer iterate + permissions(testDir, perms::none); ++#endif + + // Check that the construction fails when skip_permissions_denied is + // not given. +diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp +index 581d9fe041a9..1b7131bbadff 100644 +--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp ++++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp +@@ -73,16 +73,24 @@ TEST_CASE(test_exist_not_found) + + TEST_CASE(test_exists_fails) + { ++#ifdef _WIN32 ++ // Windows doesn't support setting perms::none to trigger failures ++ // reading directories; test using a special inaccessible directory ++ // instead. ++ const path p = GetWindowsInaccessibleDir(); ++ TEST_REQUIRE(!p.empty()); ++#else + scoped_test_env env; + const path dir = env.create_dir("dir"); +- const path file = env.create_file("dir/file", 42); ++ const path p = env.create_file("dir/file", 42); + permissions(dir, perms::none); ++#endif + + std::error_code ec; +- TEST_CHECK(exists(file, ec) == false); ++ TEST_CHECK(exists(p, ec) == false); + TEST_CHECK(ec); + +- TEST_CHECK_THROW(filesystem_error, exists(file)); ++ TEST_CHECK_THROW(filesystem_error, exists(p)); + } + + TEST_CASE(test_name_too_long) { +diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h +index eeee3e935b8a..92c017f610fe 100644 +--- a/libcxx/test/support/filesystem_test_helper.h ++++ b/libcxx/test/support/filesystem_test_helper.h +@@ -690,4 +690,29 @@ struct ExceptionChecker { + + }; + ++inline fs::path GetWindowsInaccessibleDir() { ++ // Only makes sense on windows, but the code can be compiled for ++ // any platform. ++ const fs::path dir("C:\\System Volume Information"); ++ std::error_code ec; ++ const fs::path root("C:\\"); ++ fs::directory_iterator it(root, ec); ++ if (ec) ++ return fs::path(); ++ const fs::directory_iterator endIt{}; ++ while (it != endIt) { ++ const fs::directory_entry &ent = *it; ++ if (ent == dir) { ++ // Basic sanity checks on the directory_entry ++ if (!ent.exists()) ++ return fs::path(); ++ if (!ent.is_directory()) ++ return fs::path(); ++ return ent; ++ } ++ ++it; ++ } ++ return fs::path(); ++} ++ + #endif /* FILESYSTEM_TEST_HELPER_HPP */ +-- +2.25.1 + diff --git a/patches/llvm-project/0024-libc-Improve-src-filesystem-s-formatting-of-paths.patch b/patches/llvm-project/0024-libc-Improve-src-filesystem-s-formatting-of-paths.patch new file mode 100644 index 0000000..3a8db35 --- /dev/null +++ b/patches/llvm-project/0024-libc-Improve-src-filesystem-s-formatting-of-paths.patch @@ -0,0 +1,337 @@ +From 7a94808f488301270f4a55ff7a17a8ebff3d388c Mon Sep 17 00:00:00 2001 +From: Arthur O'Dwyer +Date: Fri, 5 Mar 2021 20:13:35 -0500 +Subject: [PATCH 24/24] [libc++] Improve src/filesystem's formatting of paths. + +This is my attempt to merge D98077 (bugfix the format strings for +Windows paths, which use wchar_t not char) +and D96986 (replace C++ variadic templates with C-style varargs so that +`__attribute__((format(printf)))` can be applied, for better safety) +and D98065 (remove an unused function overload). + +The one intentional functional change here is in `__create_what`. +It now prints path1 and path2 in square-brackets _and_ double-quotes, +rather than just square-brackets. Prior to this patch, it would +print either path double-quoted if-and-only-if it was the empty +string. Now the double-quotes are always present. I doubt anybody's +code is relying on the current format, right? + +Differential Revision: https://reviews.llvm.org/D98097 +--- + libcxx/include/__config | 7 + + libcxx/src/filesystem/directory_iterator.cpp | 7 +- + libcxx/src/filesystem/filesystem_common.h | 146 ++++++++++--------- + libcxx/src/filesystem/operations.cpp | 25 ++-- + libcxx/test/support/filesystem_test_helper.h | 4 +- + 5 files changed, 101 insertions(+), 88 deletions(-) + +diff --git a/libcxx/include/__config b/libcxx/include/__config +index a3838c89e8e1..89838ca76c8c 100644 +--- a/libcxx/include/__config ++++ b/libcxx/include/__config +@@ -1445,6 +1445,13 @@ extern "C" _LIBCPP_FUNC_VIS void __sanitizer_annotate_contiguous_container( + # define _LIBCPP_INIT_PRIORITY_MAX + #endif + ++#if defined(__GNUC__) || defined(__clang__) ++#define _LIBCPP_FORMAT_PRINTF(a, b) \ ++ __attribute__((__format__(__printf__, a, b))) ++#else ++#define _LIBCPP_FORMAT_PRINTF(a, b) ++#endif ++ + #endif // __cplusplus + + #endif // _LIBCPP_CONFIG +diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp +index bb3653076bfc..7b83ba9ff123 100644 +--- a/libcxx/src/filesystem/directory_iterator.cpp ++++ b/libcxx/src/filesystem/directory_iterator.cpp +@@ -273,7 +273,7 @@ directory_iterator& directory_iterator::__increment(error_code* ec) { + path root = move(__imp_->__root_); + __imp_.reset(); + if (m_ec) +- err.report(m_ec, "at root \"%s\"", root); ++ err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str()); + } + return *this; + } +@@ -360,7 +360,7 @@ void recursive_directory_iterator::__advance(error_code* ec) { + if (m_ec) { + path root = move(stack.top().__root_); + __imp_.reset(); +- err.report(m_ec, "at root \"%s\"", root); ++ err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str()); + } else { + __imp_.reset(); + } +@@ -405,7 +405,8 @@ bool recursive_directory_iterator::__try_recursion(error_code* ec) { + } else { + path at_ent = move(curr_it.__entry_.__p_); + __imp_.reset(); +- err.report(m_ec, "attempting recursion into \"%s\"", at_ent); ++ err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT, ++ at_ent.c_str()); + } + } + return false; +diff --git a/libcxx/src/filesystem/filesystem_common.h b/libcxx/src/filesystem/filesystem_common.h +index a4505171c3eb..df50a7de1059 100644 +--- a/libcxx/src/filesystem/filesystem_common.h ++++ b/libcxx/src/filesystem/filesystem_common.h +@@ -42,8 +42,10 @@ + + #if defined(_LIBCPP_WIN32API) + #define PS(x) (L##x) ++#define PATH_CSTR_FMT "\"%ls\"" + #else + #define PS(x) (x) ++#define PATH_CSTR_FMT "\"%s\"" + #endif + + _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM +@@ -57,68 +59,47 @@ errc __win_err_to_errc(int err); + + namespace { + +-static string format_string_imp(const char* msg, ...) { +- // we might need a second shot at this, so pre-emptivly make a copy +- struct GuardVAList { +- va_list& target; +- bool active = true; +- GuardVAList(va_list& tgt) : target(tgt), active(true) {} +- void clear() { +- if (active) +- va_end(target); +- active = false; +- } +- ~GuardVAList() { +- if (active) +- va_end(target); +- } +- }; +- va_list args; +- va_start(args, msg); +- GuardVAList args_guard(args); +- +- va_list args_cp; +- va_copy(args_cp, args); +- GuardVAList args_copy_guard(args_cp); +- +- std::string result; +- +- array local_buff; +- size_t size_with_null = local_buff.size(); +- auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp); +- +- args_copy_guard.clear(); +- +- // handle empty expansion +- if (ret == 0) +- return result; +- if (static_cast(ret) < size_with_null) { +- result.assign(local_buff.data(), static_cast(ret)); +- return result; ++static _LIBCPP_FORMAT_PRINTF(1, 0) string ++format_string_impl(const char* msg, va_list ap) { ++ array buf; ++ ++ va_list apcopy; ++ va_copy(apcopy, ap); ++ int ret = ::vsnprintf(buf.data(), buf.size(), msg, apcopy); ++ va_end(apcopy); ++ ++ string result; ++ if (static_cast(ret) < buf.size()) { ++ result.assign(buf.data(), static_cast(ret)); ++ } else { ++ // we did not provide a long enough buffer on our first attempt. The ++ // return value is the number of bytes (excluding the null byte) that are ++ // needed for formatting. ++ size_t size_with_null = static_cast(ret) + 1; ++ result.__resize_default_init(size_with_null - 1); ++ ret = ::vsnprintf(&result[0], size_with_null, msg, ap); ++ _LIBCPP_ASSERT(static_cast(ret) == (size_with_null - 1), "TODO"); + } +- +- // we did not provide a long enough buffer on our first attempt. The +- // return value is the number of bytes (excluding the null byte) that are +- // needed for formatting. +- size_with_null = static_cast(ret) + 1; +- result.__resize_default_init(size_with_null - 1); +- ret = ::vsnprintf(&result[0], size_with_null, msg, args); +- _LIBCPP_ASSERT(static_cast(ret) == (size_with_null - 1), "TODO"); +- + return result; + } + +-const path::value_type* unwrap(path::string_type const& s) { return s.c_str(); } +-const path::value_type* unwrap(path const& p) { return p.native().c_str(); } +-template +-Arg const& unwrap(Arg const& a) { +- static_assert(!is_class::value, "cannot pass class here"); +- return a; +-} +- +-template +-string format_string(const char* fmt, Args const&... args) { +- return format_string_imp(fmt, unwrap(args)...); ++static _LIBCPP_FORMAT_PRINTF(1, 2) string ++format_string(const char* msg, ...) { ++ string ret; ++ va_list ap; ++ va_start(ap, msg); ++#ifndef _LIBCPP_NO_EXCEPTIONS ++ try { ++#endif // _LIBCPP_NO_EXCEPTIONS ++ ret = format_string_impl(msg, ap); ++#ifndef _LIBCPP_NO_EXCEPTIONS ++ } catch (...) { ++ va_end(ap); ++ throw; ++ } ++#endif // _LIBCPP_NO_EXCEPTIONS ++ va_end(ap); ++ return ret; + } + + error_code capture_errno() { +@@ -190,14 +171,14 @@ struct ErrorHandler { + _LIBCPP_UNREACHABLE(); + } + +- template +- T report(const error_code& ec, const char* msg, Args const&... args) const { ++ _LIBCPP_FORMAT_PRINTF(3, 0) ++ void report_impl(const error_code& ec, const char* msg, va_list ap) const { + if (ec_) { + *ec_ = ec; +- return error_value(); ++ return; + } + string what = +- string("in ") + func_name_ + ": " + format_string(msg, args...); ++ string("in ") + func_name_ + ": " + format_string_impl(msg, ap); + switch (bool(p1_) + bool(p2_)) { + case 0: + __throw_filesystem_error(what, ec); +@@ -209,11 +190,44 @@ struct ErrorHandler { + _LIBCPP_UNREACHABLE(); + } + +- T report(errc const& err) const { return report(make_error_code(err)); } ++ _LIBCPP_FORMAT_PRINTF(3, 4) ++ T report(const error_code& ec, const char* msg, ...) const { ++ va_list ap; ++ va_start(ap, msg); ++#ifndef _LIBCPP_NO_EXCEPTIONS ++ try { ++#endif // _LIBCPP_NO_EXCEPTIONS ++ report_impl(ec, msg, ap); ++#ifndef _LIBCPP_NO_EXCEPTIONS ++ } catch (...) { ++ va_end(ap); ++ throw; ++ } ++#endif // _LIBCPP_NO_EXCEPTIONS ++ va_end(ap); ++ return error_value(); ++ } ++ ++ T report(errc const& err) const { ++ return report(make_error_code(err)); ++ } + +- template +- T report(errc const& err, const char* msg, Args const&... args) const { +- return report(make_error_code(err), msg, args...); ++ _LIBCPP_FORMAT_PRINTF(3, 4) ++ T report(errc const& err, const char* msg, ...) const { ++ va_list ap; ++ va_start(ap, msg); ++#ifndef _LIBCPP_NO_EXCEPTIONS ++ try { ++#endif // _LIBCPP_NO_EXCEPTIONS ++ report_impl(make_error_code(err), msg, ap); ++#ifndef _LIBCPP_NO_EXCEPTIONS ++ } catch (...) { ++ va_end(ap); ++ throw; ++ } ++#endif // _LIBCPP_NO_EXCEPTIONS ++ va_end(ap); ++ return error_value(); + } + + private: +diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp +index 360643d4c241..0cc4bad34c72 100644 +--- a/libcxx/src/filesystem/operations.cpp ++++ b/libcxx/src/filesystem/operations.cpp +@@ -666,27 +666,20 @@ _FilesystemClock::time_point _FilesystemClock::now() noexcept { + + filesystem_error::~filesystem_error() {} + +-#if defined(_LIBCPP_WIN32API) +-#define PS_FMT "%ls" +-#else +-#define PS_FMT "%s" +-#endif +- + void filesystem_error::__create_what(int __num_paths) { + const char* derived_what = system_error::what(); + __storage_->__what_ = [&]() -> string { +- const path::value_type* p1 = path1().native().empty() ? PS("\"\"") : path1().c_str(); +- const path::value_type* p2 = path2().native().empty() ? PS("\"\"") : path2().c_str(); + switch (__num_paths) { +- default: ++ case 0: + return detail::format_string("filesystem error: %s", derived_what); + case 1: +- return detail::format_string("filesystem error: %s [" PS_FMT "]", derived_what, +- p1); ++ return detail::format_string("filesystem error: %s [" PATH_CSTR_FMT "]", ++ derived_what, path1().c_str()); + case 2: +- return detail::format_string("filesystem error: %s [" PS_FMT "] [" PS_FMT "]", +- derived_what, p1, p2); ++ return detail::format_string("filesystem error: %s [" PATH_CSTR_FMT "] [" PATH_CSTR_FMT "]", ++ derived_what, path1().c_str(), path2().c_str()); + } ++ _LIBCPP_UNREACHABLE(); + }(); + } + +@@ -1456,11 +1449,11 @@ path __temp_directory_path(error_code* ec) { + error_code m_ec; + file_status st = detail::posix_stat(p, &m_ec); + if (!status_known(st)) +- return err.report(m_ec, "cannot access path \"" PS_FMT "\"", p); ++ return err.report(m_ec, "cannot access path " PATH_CSTR_FMT, p.c_str()); + + if (!exists(st) || !is_directory(st)) +- return err.report(errc::not_a_directory, "path \"" PS_FMT "\" is not a directory", +- p); ++ return err.report(errc::not_a_directory, ++ "path " PATH_CSTR_FMT " is not a directory", p.c_str()); + + return p; + } +diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h +index 92c017f610fe..9d4062a987eb 100644 +--- a/libcxx/test/support/filesystem_test_helper.h ++++ b/libcxx/test/support/filesystem_test_helper.h +@@ -653,9 +653,7 @@ struct ExceptionChecker { + additional_msg = opt_message + ": "; + } + auto transform_path = [](const fs::path& p) { +- if (p.native().empty()) +- return std::string("\"\""); +- return p.string(); ++ return "\"" + p.string() + "\""; + }; + std::string format = [&]() -> std::string { + switch (num_paths) { +-- +2.25.1 +