diff --git a/README.md b/README.md index 104b2481..01214a49 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ C library. ```toml [dependencies] -openkal-llvm-runtime = "0.19.1" +openkal-llvm-runtime = "0.19.2" ``` > **Engine floor (mcpp 2026.11.2.1):** 0.17.0 is built over openkal-musl 0.26.0, @@ -330,6 +330,18 @@ grants against raw file truncation. The runtime implementation is unchanged. ## OpenKal 0.24 dependency group +Runtime 0.19.2 keeps the volume where a name is rebuilt part by part. `X:` is a +root name only with the separator after it, so `lexically_normal` appended `D:` as +an ordinary name and the root directory replaced it (`D:/a/./b` became `/a/b`), and +so did `weakly_canonical`, which ends in it, and `path("D:/a") / "/b"`. On Windows a +program over the hosted arrangement then named every file beneath no volume: a +launcher could not start its runner (mcpp-community/mcpp#889). `lexically_normal` +now begins its result with the volume and its root directory as written, and +appending a rooted name beneath a volume keeps the volume (`D:/b`), as +[fs.path.append] states it for a system with root names. Both probes state the +three results; without the change two fail on Linux and under the Windows hosted +arrangement. + Runtime 0.19.1 reads a name beneath a volume, `X:/...`, as the C library beneath does: `X:` is the root name and `/` the root directory, so the working directory on a system with volumes is absolute and `absolute`, `canonical` and `relative` keep diff --git a/examples/cxx/src/main.cpp b/examples/cxx/src/main.cpp index c95e13c5..8c113fb2 100644 --- a/examples/cxx/src/main.cpp +++ b/examples/cxx/src/main.cpp @@ -153,6 +153,15 @@ int main() { check(fs::absolute(cwd, cwd_ec) == cwd && !cwd_ec, "absolute() of it is itself"); const fs::path can = fs::canonical(cwd, cwd_ec); check(!cwd_ec && can.is_absolute(), "canonical() of it succeeds"); + // 0.19.2: a builder that appends element by element keeps the volume. + check((fs::path("D:/a") / "/b") == "D:/b" && (fs::path("D:/a") / fs::path("/b")) == "D:/b" + && fs::path("D:/a").append(std::string("/b")) == "D:/b", "a rooted name appended beneath a volume keeps it"); + check(fs::path("D:/a/./b/../c").lexically_normal() == "D:/a/c" && fs::path("D:/").lexically_normal() == "D:/", + "lexically_normal() keeps the volume"); + check((fs::path("/a") / "/b") == "/b" && fs::path("/a/../b").lexically_normal() == "/b", "and `/' is unchanged"); + const fs::path missing = cwd / "openkal-no-such-name" / "." / "x"; + const fs::path weak = fs::weakly_canonical(missing, cwd_ec); + check(!cwd_ec && weak.is_absolute() && weak.root_name() == cwd.root_name(), "weakly_canonical() keeps the working directory's volume"); } // --- std::filesystem, which is the C++ face of openkal.fs --------------- diff --git a/examples/image-cxx/src/main.cpp b/examples/image-cxx/src/main.cpp index 3250b646..21b91bd7 100644 --- a/examples/image-cxx/src/main.cpp +++ b/examples/image-cxx/src/main.cpp @@ -63,6 +63,15 @@ int main(int argc, char** argv) { check(fs::absolute(cwd, cwd_ec) == cwd && !cwd_ec, "absolute() of it is itself"); const fs::path can = fs::canonical(cwd, cwd_ec); check(!cwd_ec && can.is_absolute(), "canonical() of it succeeds"); + // 0.19.2: a builder that appends element by element keeps the volume. + check((fs::path("D:/a") / "/b") == "D:/b" && (fs::path("D:/a") / fs::path("/b")) == "D:/b" + && fs::path("D:/a").append(std::string("/b")) == "D:/b", "a rooted name appended beneath a volume keeps it"); + check(fs::path("D:/a/./b/../c").lexically_normal() == "D:/a/c" && fs::path("D:/").lexically_normal() == "D:/", + "lexically_normal() keeps the volume"); + check((fs::path("/a") / "/b") == "/b" && fs::path("/a/../b").lexically_normal() == "/b", "and `/' is unchanged"); + const fs::path missing = cwd / "openkal-no-such-name" / "." / "x"; + const fs::path weak = fs::weakly_canonical(missing, cwd_ec); + check(!cwd_ec && weak.is_absolute() && weak.root_name() == cwd.root_name(), "weakly_canonical() keeps the working directory's volume"); } std::println("-- failures: {} --", failures); diff --git a/llvm/PATCHES.md b/llvm/PATCHES.md index 37a3a31a..b64b81eb 100644 --- a/llvm/PATCHES.md +++ b/llvm/PATCHES.md @@ -29,10 +29,10 @@ grep -rn "openkal ─── BEGIN" llvm/ ## 已替换 - 六处,五个文件,一个判据 —— 每一处都能填进上面那句话。 + 七处,六个文件,一个判据 —— 每一处都能填进上面那句话。 ```sh -grep -rn "openkal ─── BEGIN" llvm/ # 30 处 BEGIN 标记 +grep -rn "openkal ─── BEGIN" llvm/ # 31 处 BEGIN 标记 ``` | 文件 | 上游按什么分派 | 平台面 | openkal 的答案 | @@ -43,6 +43,7 @@ grep -rn "openkal ─── BEGIN" llvm/ # 30 处 BEGIN 标记 | `libunwind/src/UnwindCursor.hpp` | `_WIN32` | (无) | 整段没被用到,只是被 include | | `compiler-rt/…/emutls.c` | `_WIN32` | 互斥 + 对齐分配 | pthread + `posix_memalign` | | `libcxx/src/filesystem/path_parser.h` | `_LIBCPP_WIN32API` | 名字的根 | C 库的根:`/` 与 `X:/`(0.19.1) | +| `libcxx/src/filesystem/path.cpp` | (无,`/=` 的 POSIX 语义) | 规范化的结果 | 卷与根目录按原样开头(0.19.2) | ### `libcxx/src/filesystem/path_parser.h` 与 `port/include/__filesystem/path.h` — 卷下的名字(0.19.1) @@ -57,6 +58,13 @@ C 库(openkal-musl)在有卷的系统上把文件写作 `D:/...`,并像对 - `__filesystem/path.h` 的 `append(Source)` 在 POSIX 分支只看首字符是否为分隔符;覆盖件在 `OPENKAL` 或 `__OPENKAL__` 下改用 `path::is_absolute()` 判断,使 `path / "D:/y"` 与 `path / path("D:/y")` 结果相同。头文件按本文的约定放在 `port/include/` 覆盖,vendored 树不动。 +- 0.19.2:`X:` 只有后跟分隔符时才是根名,而 `lexically_normal` 逐段用 `/=` 重建结果:`D:` 被当作 + 普通名字,随后的根目录 `/` 按 POSIX 语义替换了它,`D:/a/./b` 变成 `/a/b`;以它收尾的 + `weakly_canonical` 同样丢卷。Windows 上经托管方式运行的程序因此把文件名放到了卷之外,启动器 + 无法启动其运行器(mcpp-community/mcpp#889)。`path.cpp` 的 `lexically_normal` 在 `OPENKAL` 下 + 以原样的卷与根目录开头(原地标记);覆盖件的 `operator/=` 与两个 `append` 在追加“有根目录、 + 无根名”的名字时保留本身的卷(`D:/a` / `/b` 为 `D:/b`),与 [fs.path.append] 对有根名系统的规定 + 一致。 ### `libcxx/src/atomic.cpp` — `std::atomic` 的等待与唤醒 diff --git a/llvm/libcxx/src/filesystem/path.cpp b/llvm/libcxx/src/filesystem/path.cpp index 400b6e89..b12f8e2e 100644 --- a/llvm/libcxx/src/filesystem/path.cpp +++ b/llvm/libcxx/src/filesystem/path.cpp @@ -224,8 +224,28 @@ path path::lexically_normal() const { path Result; Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep); +// ─── openkal ─── BEGIN ──────────────────────────────────────────────────────── +#if defined(OPENKAL) + // openkal: THE VOLUME AND ITS ROOT DIRECTORY BEGIN THE RESULT AS WRITTEN. + // + // `X:` is a root name only with the separator after it (path_parser.h), so + // appended part by part `D:` was an ordinary name and the root directory + // `/` replaced it: `D:/a/./b`.lexically_normal() was `/a/b`, and + // weakly_canonical, which ends in it, lost the volume the same way + // (0.19.2; mcpp-community/mcpp#889). + size_t First = 0; + if (Parts.size() >= 2 && Parts[1].second == PK_RootSep && has_root_name()) { + Result.__pn_ += Parts[0].first; + Result.__pn_ += Parts[1].first; + First = 2; + } + for (size_t I = First; I < Parts.size(); ++I) + Result /= Parts[I].first; +#else for (auto& PK : Parts) Result /= PK.first; +#endif +// ─── openkal ─── END ────────────────────────────────────────────────────────── if (NeedTrailingSep) Result /= PATHSTR(""); diff --git a/mcpp.toml b/mcpp.toml index 7faa78e9..3d8d864a 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-llvm-runtime" -version = "0.19.1" +version = "0.19.2" description = "LLVM's C++ runtime libraries — libc++, libc++abi and libunwind — configured for openkal-musl rather than for a host C library." license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/port/include/__filesystem/path.h b/port/include/__filesystem/path.h index c14d318b..423042a8 100644 --- a/port/include/__filesystem/path.h +++ b/port/include/__filesystem/path.h @@ -510,7 +510,34 @@ class _LIBCPP_EXPORTED_FROM_ABI path { return operator/=(path(__first, __last)); } # else +# if defined(OPENKAL) || defined(__OPENKAL__) + // openkal: A NAME BENEATH A VOLUME KEEPS ITS VOLUME (0.19.2). + // + // Appending a name that has a root directory and no root name (`/b`) to a + // name beneath a volume (`D:/a`) replaces the root directory and the + // relative path, and keeps the volume: `D:/b`, as [fs.path.append] states it + // for a system with root names. Taken as the whole name, `/` lost the + // volume in every builder that appends element by element: + // `D:/a/b`.lexically_normal() was `/a/b`, and so was a name that + // weakly_canonical and relative rebuild (0.19.1 made `X:/` a root name, + // openkal-llvm-runtime#64; mcpp-community/mcpp#889 found the loss). + _LIBCPP_HIDE_FROM_ABI bool __openkal_keep_volume(const path& __p) { + if (!__p.__root_name().empty() || __p.__root_directory().empty()) + return false; + const __string_view __volume = __root_name(); + if (__volume.empty()) + return false; + string_type __whole(__volume); + __whole += __p.__pn_; + __pn_ = std::move(__whole); + return true; + } +# endif _LIBCPP_HIDE_FROM_ABI path& operator/=(const path& __p) { +# if defined(OPENKAL) || defined(__OPENKAL__) + if (__openkal_keep_volume(__p)) + return *this; +# endif if (__p.is_absolute()) { __pn_ = __p.__pn_; return *this; @@ -543,6 +570,8 @@ class _LIBCPP_EXPORTED_FROM_ABI path { __pn_ = std::move(__whole.__pn_); return *this; } + } else if (__openkal_keep_volume(path(__src))) { + return *this; } # endif if (__source_is_absolute) @@ -558,6 +587,10 @@ class _LIBCPP_EXPORTED_FROM_ABI path { typedef typename iterator_traits<_InputIt>::value_type _ItVal; static_assert(__can_convert_char<_ItVal>::value, "Must convertible"); using _CVT = _PathCVT<_ItVal>; +# if defined(OPENKAL) || defined(__OPENKAL__) + if (__first != __last && filesystem::__is_separator(*__first) && __openkal_keep_volume(path(__first, __last))) + return *this; +# endif if (__first != __last && filesystem::__is_separator(*__first)) __pn_.clear(); else if (has_filename())