diff --git a/.github/workflows/openkal-compat.yml b/.github/workflows/openkal-compat.yml index ca077376..029cfaa4 100644 --- a/.github/workflows/openkal-compat.yml +++ b/.github/workflows/openkal-compat.yml @@ -42,7 +42,7 @@ env: # run if this drifts from pins.toml. See the comment beside `mcpp` in # pins.toml for why this pin, not just validate.yml's, is gated by the # index floor. - MCPP_VERSION: "2026.10.24.1" + MCPP_VERSION: "2026.10.26.1" # CROSS-VALIDATION BEFORE A RELEASE, NOT AFTER IT. # # Empty in an ordinary run, so this measurement keeps using the RELEASED diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index adfb68db..e238ed7e 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -431,7 +431,11 @@ env: # one module map of what it reaches; every unit of an ELF target is compiled # position-independent; `mcpp pack` states its build; `[build] jobs` bounds # `test` and `pack` (#751, #753). - MCPP_VERSION: "2026.10.24.1" + # + # 2026.10.26.1 (mcpp-community/mcpp#841): a graph-supplied libstdc++ under + # clang (gcc.libstdcxx, the libstdcxx-llvm member), `requires` as a table, + # and one C++ runtime per process -- compat.sycl-runtime's requirement. + MCPP_VERSION: "2026.10.26.1" jobs: lint: @@ -1476,15 +1480,15 @@ jobs: fi # `mp-units-module`: the reverse case. `mpusz.mp-units` requires - # libc++ on linux because GCC 16.1 cannot import its modules - # (`recursive lazy load`, mpusz/mp-units#717), so the gcc leg would - # only reach that refusal. Lift once xim ships a GCC that works. + # the llvm compiler on linux because GCC 16.1 cannot import its + # modules (`recursive lazy load`, mpusz/mp-units#717), so the gcc leg + # would only reach that refusal. Lift once xim ships a GCC that works. if [ "${{ matrix.platform }}" = "linux" ] && [ "${{ matrix.toolchain }}" = "default" ]; then kept="" for m in $mine; do case "$m" in mp-units-module) - echo "skipping '$m' on the gcc leg: the package requires libc++" ;; + echo "skipping '$m' on the gcc leg: the package requires the llvm compiler" ;; *) kept="$kept $m" ;; esac done diff --git a/.github/workflows/xpkgindex-update.yml b/.github/workflows/xpkgindex-update.yml index 1eb31460..6e26a37b 100644 --- a/.github/workflows/xpkgindex-update.yml +++ b/.github/workflows/xpkgindex-update.yml @@ -25,7 +25,7 @@ concurrency: env: PYTHONUTF8: '1' - MCPP_VERSION: '2026.10.24.1' + MCPP_VERSION: '2026.10.26.1' MCPP_INDEX_MIRROR: GLOBAL jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index bb012118..72a64386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,21 @@ ### Added +- **`gcc.libstdcxx` 16.1.0.1。** 向 llvm 构建提供 `mcpp:c++-abi=libstdc++`:安装时链接 + `xim:gcc` 16.1.0 载荷的头文件与运行库目录并写出自身清单(`std-module` 为 `bits/std.cc`), + clang 据此以 libstdc++ 编译、`import std` 与链接(需要 mcpp 2026.10.26.1,SPEC-002 §7)。 + 新增测试成员 `tests/examples/libstdcxx-llvm`。 + +### Changed + +- **`compat.sycl-runtime` 在 Linux 上要求 `mcpp:c++-abi=libstdc++`。** `libsycl.so` 在 libstdc++ + 上抛出 `sycl::exception`,而一个进程中的两套 C++ 运行时之间带类型的 `catch` 不会匹配 + (mcpp-community/mcpp#838)。gcc 工具链,或 llvm 加 `gcc.libstdcxx`,满足该要求;其他构建 + 在编译前被拒绝。测试成员 `sycl-runtime` 在 Linux 上陈述 gcc。 +- **`mpusz.mp-units` 要求 `mcpp:compiler=llvm`**(原为 `mcpp:c++-abi=libc++`):约束来自 GCC 16.1 + 的模块缺陷,是编译器而不是 C++ 库。**`compat.glfw`** 以 `requires = { "mcpp:c-abi=glibc" }` + 取代能力 `abi:glibc`。 + - **`openkal-macos` 0.14.1、`openkal-musl` 0.21.1、`openkal-llvm-runtime` 0.15.6。** macOS 上 经 `fork` 复制的程序此前不能启动线程(`pthread_create` 返回 `EAGAIN`, mcpplibs/openkal-musl#55):`kal_space_start` 只调用复制原语,而该系统的 `fork` 在原语前后 diff --git a/mcpp.toml b/mcpp.toml index 47b5fa63..5a817433 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -91,6 +91,7 @@ members = [ "tests/examples/imgui-module", "tests/examples/imgui-window", "tests/examples/jwt-cpp", + "tests/examples/libstdcxx-llvm", "tests/examples/marzer.tomlplusplus", "tests/examples/marzer.tomlplusplus-build-mcpp", "tests/examples/mcpp-plugins", diff --git a/pkgs/c/compat.glfw.lua b/pkgs/c/compat.glfw.lua index e99709f9..a4462886 100644 --- a/pkgs/c/compat.glfw.lua +++ b/pkgs/c/compat.glfw.lua @@ -101,10 +101,15 @@ package = { ["compat.opengl"] = "2026.05.31", }, linux = { + -- The C library this package's X11 and GLX code is written for, + -- as a requirement on the target side (mcpp SPEC-002 §2.2). It + -- was the capability `abi:glibc`, which mcpp 2026.10.26.1 reads + -- as this same requirement and reports as a compatibility reading. + requires = { "mcpp:c-abi=glibc" }, cflags = { "-D_DEFAULT_SOURCE", "-D_GLFW_X11" }, runtime = { dlopen_libs = { "libGLX.so.0", "libGL.so.1", "libGL.so" }, - capabilities = { "x11.display", "opengl.glx.driver", "abi:glibc" }, + capabilities = { "x11.display", "opengl.glx.driver" }, }, sources = { "src/x11_init.c", diff --git a/pkgs/c/compat.sycl-runtime.lua b/pkgs/c/compat.sycl-runtime.lua index f7a73ca0..46d6e6ee 100644 --- a/pkgs/c/compat.sycl-runtime.lua +++ b/pkgs/c/compat.sycl-runtime.lua @@ -166,6 +166,17 @@ package = { -- adapter for is not something a runtime adapter should leave -- unreachable. deps = { ["compat.opencl"] = "2026.09.11" }, + -- ONE C++ RUNTIME PER PROCESS, AND HERE IT IS libstdc++ (mcpp SPEC-002 + -- §7). `libsycl.so` is built on libstdc++ and throws `sycl::exception` + -- on it; a program whose own C++ library is libc++ catches that only + -- as `catch (...)` (mcpp-community/mcpp#838), because the two runtimes + -- share the ABI names. So the consumer's build takes libstdc++: a gcc + -- toolchain, or llvm with `gcc.libstdcxx` in the graph (mcpp + -- 2026.10.26.1). Any other build is refused before it compiles, naming + -- both C++ libraries. + linux = { + requires = { "mcpp:c++-abi=libstdc++" }, + }, runtime = { library_dirs = { "mcpp_generated/sycl_runtime/lib" }, capabilities = { "sycl.runtime" }, diff --git a/pkgs/g/gcc.libstdcxx.lua b/pkgs/g/gcc.libstdcxx.lua new file mode 100644 index 00000000..010c928c --- /dev/null +++ b/pkgs/g/gcc.libstdcxx.lua @@ -0,0 +1,158 @@ +-- libstdc++ as the C++ layer of an llvm build: `provides mcpp:c++-abi=libstdc++`. +-- +-- WHY IT EXISTS. One process holds one C++ runtime (mcpp SPEC-002 §7). A +-- component built on libstdc++ -- `libsycl.so`, whose `sycl::exception` a SYCL +-- program catches by type -- cannot share a process with the libc++ an llvm +-- build links: the two runtimes define the same ABI names, an exception thrown +-- by one is caught by the other's personality only as `catch (...)` +-- (mcpp-community/mcpp#838), and `std::runtime_error` has a different layout +-- in each. The build therefore takes libstdc++ as its own C++ library, and the +-- compiler stays clang: +-- +-- [dependencies] +-- gcc.libstdcxx = "16.1.0.1" +-- +-- [toolchain] +-- default = "llvm@22.1.8" +-- +-- THE MECHANISM IS THE ONE `llvm.libcxx` AND `openkal-llvm-runtime` USE, ONE +-- LIBRARY OVER: `provides` names the layer, the engine compiles with +-- `-nostdinc++` and these headers, precompiles `std` from `bits/std.cc`, and +-- links with `-stdlib=libstdc++`. Nothing is downloaded or compiled: install() +-- links the `xim:gcc` payload's headers and runtime directory into this +-- package's tree and writes its manifest, so the headers a unit is compiled +-- against, the `std` module it imports and the library it loads are one +-- release of GCC. +-- +-- THE VERSION IS GCC'S, and the fourth segment is the packaging revision, as +-- `llvm.libcxx` established. A descriptor's dependency edge is per platform, +-- not per version, so one descriptor follows one GCC release: the one mcpp's +-- default gcc toolchain uses. +-- +-- REQUIRES mcpp 2026.10.26.1: an earlier engine has no wiring for a +-- graph-supplied libstdc++ under clang and refuses the build at the target +-- side, naming the c++-abi layer. +package = { + spec = "1", + namespace = "gcc", + name = "libstdcxx", + description = "libstdc++ from the GCC payload as the C++ standard library of an llvm build (provides mcpp:c++-abi=libstdc++)", + licenses = {"GPL-3.0-or-later WITH GCC-exception-3.1"}, + repo = "https://gcc.gnu.org/git/gcc.git", + type = "package", + + xpm = { + linux = { + -- The install-time edge: the payload whose headers and runtime + -- this package links. Materialised when THIS package installs. + deps = { "xim:gcc@16.1.0" }, + ["16.1.0.1"] = { + -- Nothing downloaded matters: the content is the tree + -- install() links. A stable, tiny anchor keeps the entry + -- well-formed, as compat.sycl-runtime does. + url = "https://raw.githubusercontent.com/gcc-mirror/gcc/releases/gcc-16.1.0/COPYING.RUNTIME", + sha256 = "9d6b43ce4d8de0c878bf16b54d8e7a10d9bd42b75178153e3af6a815bdc90f74", + }, + ["latest"] = { ref = "16.1.0.1" }, + }, + }, + + -- Written by install(), because the target directory under the headers + -- (`x86_64-linux-gnu`, `aarch64-linux-gnu`) is the payload's to say. + mcpp = "mcpp.toml", +} + +import("xim.libxpkg.pkginfo") +import("xim.libxpkg.log") + +local GCC_VERSION = "16.1.0" + +-- The payload's install directory: the dependency edge's own answer first, +-- then the stores a shared registry keeps it in (the fallback +-- compat.sycl-runtime records the reason for). +local function gcc_dir() + local dir = pkginfo.install_dir("xim:gcc", GCC_VERSION) + if dir then return dir end + local roots = {} + local pfx = pkginfo.install_dir() + if pfx then roots[#roots + 1] = path.directory(path.directory(pfx)) end + local home = (os.getenv and os.getenv("XLINGS_HOME")) or "" + if home == "" then home = ((os.getenv and os.getenv("HOME")) or "") .. "/.xlings" end + roots[#roots + 1] = path.join(home, "data/xpkgs") + roots[#roots + 1] = path.join((os.getenv and os.getenv("HOME")) or "", + ".mcpp/registry/data/xpkgs") + for _, root in ipairs(roots) do + local cand = path.join(root, "xim-x-gcc", GCC_VERSION) + if os.isdir(cand) then return cand end + end + return nil +end + +-- The target directory under include/c++/: the one whose name ends +-- in `-linux-gnu`. `io.popen` rather than `os.files`, which the recipe sandbox +-- does not provide. +local function target_dir(cxx) + local p = io.popen(string.format([[ls -1 "%s" 2>/dev/null]], cxx)) + if not p then return nil end + local found + for line in p:lines() do + local name = line:gsub("[\r\n]+$", "") + if name:match("%-linux%-gnu$") and os.isdir(path.join(cxx, name)) then + found = name + break + end + end + p:close() + return found +end + +function install() + os.tryrm(pkginfo.install_dir()) + os.mkdir(pkginfo.install_dir()) + local root = pkginfo.install_dir() + + local gcc = gcc_dir() + if not gcc then + log.error("gcc.libstdcxx: the xim:gcc %s payload was not found", GCC_VERSION) + return false + end + local cxx = path.join(gcc, "include", "c++", GCC_VERSION) + local triple = target_dir(cxx) + if not os.isfile(path.join(cxx, "bits", "std.cc")) or not triple then + log.error("gcc.libstdcxx: %s has no bits/std.cc or no target directory", cxx) + return false + end + os.exec(string.format([[ln -sfn "%s" "%s"]], cxx, path.join(root, "cxx"))) + + -- The runtime: the C++ library and the unwinder it needs, by the names a + -- link asks for and the sonames the loader asks for. + local lib = path.join(root, "lib") + os.mkdir(lib) + for _, name in ipairs({ "libstdc++.so", "libstdc++.so.6", "libstdc++.a", + "libgcc_s.so", "libgcc_s.so.1" }) do + local f = path.join(gcc, "lib64", name) + if os.isfile(f) or os.islink(f) then + os.exec(string.format([[ln -sfn "%s" "%s"]], f, path.join(lib, name))) + end + end + + io.writefile(path.join(root, "mcpp.toml"), string.format([[ +[package] +namespace = "gcc" +name = "libstdcxx" +version = "%s" +mcpp = ">=2026.10.26.1" +provides = ["mcpp:c++-abi=libstdc++"] +requires = ["mcpp:compiler=llvm", "mcpp:c-abi=glibc"] + +[build] +sources = [] +include_dirs = ["cxx", "cxx/%s", "cxx/backward"] +std-module = "cxx/bits/std.cc" +std-compat-module = "cxx/bits/std.compat.cc" + +[runtime] +library_dirs = ["lib"] +]], pkginfo.version(), triple)) + return true +end diff --git a/pkgs/m/mpusz.mp-units.lua b/pkgs/m/mpusz.mp-units.lua index 7a8096fe..c80c50ed 100644 --- a/pkgs/m/mpusz.mp-units.lua +++ b/pkgs/m/mpusz.mp-units.lua @@ -62,8 +62,10 @@ package = { targets = { ["mp-units"] = { kind = "lib" } }, deps = {}, linux = { - -- 暂不支持 GCC:GCC 16.1 导入该模块时报 recursive lazy load(mpusz/mp-units#717) - requires = { "mcpp:c++-abi=libc++" }, + -- 暂不支持 GCC:GCC 16.1 导入该模块时报 recursive lazy load(mpusz/mp-units#717)。 + -- 约束的是编译器而不是 C++ 库:c++-abi 可以独立选择后(mcpp 2026.10.26.1, + -- llvm 加 gcc.libstdcxx),按编译器陈述。 + requires = { "mcpp:compiler=llvm" }, }, }, } diff --git a/tests/examples/libstdcxx-llvm/mcpp.toml b/tests/examples/libstdcxx-llvm/mcpp.toml new file mode 100644 index 00000000..83e40f65 --- /dev/null +++ b/tests/examples/libstdcxx-llvm/mcpp.toml @@ -0,0 +1,18 @@ +# gcc.libstdcxx: libstdc++ as the C++ library of an llvm build (mcpp SPEC-002 +# §7, mcpp 2026.10.26.1). The compiler is clang; the headers, the `std` module +# and the library come from the GCC payload the package links. Linux only, as +# the package is; elsewhere the member builds with the leg's own toolchain and +# its test asserts nothing about the C++ library. +[package] +name = "libstdcxx-llvm-tests" +version = "0.1.0" +mcpp = ">=2026.10.26.1" + +[toolchain] +linux = "llvm@23.1.3" + +[indices] +gcc = { path = "../../.." } + +[target.'cfg(linux)'.dependencies.gcc] +libstdcxx = "16.1.0.1" diff --git a/tests/examples/libstdcxx-llvm/tests/libstdcxx.cpp b/tests/examples/libstdcxx-llvm/tests/libstdcxx.cpp new file mode 100644 index 00000000..f0faa78f --- /dev/null +++ b/tests/examples/libstdcxx-llvm/tests/libstdcxx.cpp @@ -0,0 +1,26 @@ +// The C++ library under clang is libstdc++ (gcc.libstdcxx): `import std` comes +// from its `bits/std.cc`, a typed catch matches what its runtime threw, and the +// program carries no libc++. `` is included for the one macro a +// module does not export: the library's own name. +#include +import std; + +int main() { +#if defined(__linux__) +# if !defined(__GLIBCXX__) || !defined(__clang__) + std::println("expected clang over libstdc++"); + return 1; +# endif +#endif + std::vector v{3, 1, 2}; + std::ranges::sort(v); + try { + throw std::runtime_error(std::format("{}", v)); + } catch (const std::exception& e) { + if (std::string_view(e.what()) != "[1, 2, 3]") return 2; + } catch (...) { + return 3; + } + std::println("ok"); + return 0; +} diff --git a/tests/examples/sycl-runtime/mcpp.toml b/tests/examples/sycl-runtime/mcpp.toml index 186ae24e..22d84692 100644 --- a/tests/examples/sycl-runtime/mcpp.toml +++ b/tests/examples/sycl-runtime/mcpp.toml @@ -8,5 +8,11 @@ name = "sycl-runtime-tests" version = "0.1.0" +# ONE C++ RUNTIME PER PROCESS: the adapter requires the build's C++ library to +# be libstdc++ (libsycl.so throws on it), so on Linux the member states gcc, +# whichever leg runs it. llvm over libstdc++ is the libstdcxx-llvm member's. +[toolchain] +linux = "gcc@16.1.0" + [target.'cfg(linux)'.dependencies.compat] sycl-runtime = "2026.09.10"