Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 76 additions & 26 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1421,40 +1421,60 @@ jobs:
# A bare GitHub runner has no libav* under /usr/include, so a
# descriptor that loses its vendored headers to the host's would still
# be green here. That is precisely how compat.ffmpeg's -idirafter hole
# survived: nothing in CI had a system ffmpeg to lose to. Installing
# the dev headers is what turns this leg into a real test of include
# ORDER rather than of include EXISTENCE.
# survived: nothing in CI had a system ffmpeg to lose to. Headers of
# FFmpeg's own names under /usr/include are what turn this leg into a
# test of include ORDER rather than of include EXISTENCE.
#
# They are planted, not installed: each public header of the release
# compat.ffmpeg names, as a file that stops the compile, naming itself.
# A reach of the host's header is then an error at the line that made
# it, where an installed header of another FFmpeg compiled silently or
# failed somewhere else. And nothing is downloaded from Ubuntu's mirror,
# whose ffmpeg closure (~100 MiB at the 2026-11-06 rates) outlasted
# every timeout on ubuntu-26.04 runners.
#
# Deliberately only on the llvm leg. The default leg reaches its
# compiler through --sysroot, so it is immune by construction and
# installing there would change an existing job's environment for no
# planting there would change an existing job's environment for no
# coverage. Keeping the blast radius to the new leg means a red here
# is always about the thing this leg was added to watch.
- name: Install host ffmpeg dev headers (llvm leg only)
- name: Plant host ffmpeg headers that stop the compile (llvm leg only)
if: matrix.platform == 'linux' && matrix.toolchain == 'llvm'
shell: bash
# Only Ubuntu's own archive is refreshed: a bare `apt-get update` also
# walks every third-party source the runner image carries (PPAs,
# vendor repos), and one slow mirror among them held this step for
# minutes. Bounded timeouts and retries, and no recommended packages.
run: |
apt_opts=(-o Acquire::Retries=3 -o Acquire::http::Timeout=20 -o Acquire::https::Timeout=20)
if [[ -f /etc/apt/sources.list.d/ubuntu.sources ]]; then
mkdir -p "$RUNNER_TEMP/apt-ubuntu"
cp /etc/apt/sources.list.d/ubuntu.sources "$RUNNER_TEMP/apt-ubuntu/"
apt_opts+=(-o Dir::Etc::SourceList=/dev/null -o "Dir::Etc::SourceParts=$RUNNER_TEMP/apt-ubuntu")
fi
time sudo timeout 180 apt-get update -qq "${apt_opts[@]}"
for attempt in 1 2; do
if time sudo timeout 300 apt-get install -y -qq --no-install-recommends "${apt_opts[@]}" \
libavutil-dev libavcodec-dev libavformat-dev \
libavfilter-dev libavdevice-dev libswscale-dev libswresample-dev; then
break
fi
[[ $attempt == 2 ]] && { echo "::error::the ffmpeg dev headers did not install"; exit 1; }
echo "::warning::apt-get install failed (attempt $attempt); retrying"
done
dpkg -l | grep -E 'libav(util|codec)-dev|libswscale-dev'
python3 - <<'PY'
import hashlib, io, re, subprocess, tarfile, urllib.request
from pathlib import Path
lua = Path("pkgs/c/compat.ffmpeg.lua").read_text()
entry = re.search(r'linux\s*=\s*\{\s*\["([^"]+)"\]\s*=\s*\{.*?GLOBAL\s*=\s*"([^"]+)".*?CN\s*=\s*"([^"]+)".*?sha256\s*=\s*"([0-9a-f]{64})"', lua, re.S)
version, urls, sha = entry.group(1), (entry.group(2), entry.group(3)), entry.group(4)
data = None
for url in urls:
try:
data = urllib.request.urlopen(url, timeout=120).read()
except OSError as e:
print(f"::warning::{url}: {e}")
continue
if hashlib.sha256(data).hexdigest() == sha:
break
data = None
if data is None:
raise SystemExit(f"::error::the FFmpeg {version} source matching the descriptor could not be read")
libs = ("libavutil", "libavcodec", "libavformat", "libavfilter", "libavdevice", "libswscale", "libswresample")
planted = 0
with tarfile.open(fileobj=io.BytesIO(data)) as tar:
for name in tar.getnames():
parts = name.split("/")
if len(parts) == 3 and parts[1] in libs and parts[2].endswith(".h"):
target = Path("/usr/include") / parts[1] / parts[2]
text = f'#error "host <{parts[1]}/{parts[2]}> reached: a vendored FFmpeg header was not found first"\n'
subprocess.run(["sudo", "mkdir", "-p", str(target.parent)], check=True)
subprocess.run(["sudo", "tee", str(target)], input=text.encode(), check=True, stdout=subprocess.DEVNULL)
planted += 1
if planted < 100:
raise SystemExit(f"::error::only {planted} FFmpeg headers were planted")
print(f"planted {planted} FFmpeg {version} headers under /usr/include")
PY

# ── Select the toolchain for this leg ─────────────────────────────
# No version pin: `llvm` resolves to whatever the toolchain family's
Expand Down Expand Up @@ -1675,6 +1695,36 @@ jobs:
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.mimalloc@3.6.1 --deploy

- name: Installed zlib wrapper archive, actual default compression template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' zlib-compress ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.zlib@1.3.2 --deploy

- name: Installed zstd wrapper archive, actual default compression template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' zstd-squeeze ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.zstd@1.5.7 --deploy

- name: Installed xz wrapper archive, actual default compression template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' xz-pack ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.xz@5.8.3 --deploy

- name: Installed xxhash wrapper archive, actual default hashing template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' xxhash-digest ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.xxhash@0.8.3 --deploy

- name: Installed libarchive wrapper archive, actual default archive template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' libarchive-bundle ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.libarchive@3.8.7 --deploy

- name: Installed libc wrapper archive, actual default C stdio template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' libc-stdio ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.libc@17.0.0 --deploy

- name: Installed libwebp wrapper archive, actual default image template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' libwebp-image ') && matrix.toolchain == 'default'
shell: bash
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,24 @@
块,链接时找不到这两个归档;新增 `openkal` 块,不列链接参数。`-latomic` 上游只在找到时才链接,mimalloc 的原子操作在本索引
构建的各架构上都是无锁的,LLVM 工具链在图中 C 库之上没有这个归档。

### Chore

- **validate:llvm 腿不再从 Ubuntu 镜像安装 ffmpeg 开发包,改为在 `/usr/include` 下放置 FFmpeg 公开头文件同名、内容为
`#error` 的文件。** 文件名取自 compat.ffmpeg 描述符所指发布(按描述符的 sha256 校验)。宿主头文件一旦被包含即在包含处报错,
比安装另一版本的头文件更直接地检验包含顺序;2026-11-06 起 ubuntu-26.04 运行器上 ffmpeg 依赖闭包(约 100 MiB)的下载在每个
超时内都完成不了。

### Added

- **mcpplibs.mwrap.zlib 1.3.2、.zstd 1.5.7、.xz 5.8.3、.xxhash 0.8.3、.libarchive 3.8.7、.libc 17.0.0(新包)。** 六个 mwrap
包装把头文件收敛到各自的包中(mcpp 设计记录 2026-11-07 D10)。前五个以 C++23 模块使用同版本的 compat 实现:函数、类型、
枚举值以上游的全局名导出,常量宏与类型宏在 `.macros` 中为同名常量与别名,带参数的宏在 `<mwrap/<lib>/macros.hpp>`;
导出清单由各仓库的 `tools/generate.py` 按 clang 读到的头文件生成并在 CI 中审计。`mcpplibs.mwrap.libc` 映射 ISO C17
附录 B 的对象宏(`stdout()`、`errno()`、`EOF`、`INT_MAX` 等),实现为目标的 C 库。消费者测试 `tests/mwrap/zlib-compress`、
`zstd-squeeze`、`xz-pack`、`xxhash-digest`、`libarchive-bundle`、`libc-stdio`,以及已安装模板与重定位许可检查(检查脚本以
`STATIC_NOTICES` 表统一六个包的约定)。GLOBAL 为 GitHub 发布资产,CN 为 GitCode `mcpp-res/mwrap-<lib>` 镜像,sha256
两处一致。`docs/mwrap.md` 补充 C 标准库这一实现来源。

- **openkal-macos 0.22.1.** The confinement rules are sized to the grants of each start; sixteen grants named near
`MAXPATHLEN` no longer refuse a well-formed start with `kal_err_invalid`. OpenKal is the compatible range `^0.24.0`.
First member of the openkal 0.24.1 correction group (mcpp design record 2026-11-07 §4.2.3).
Expand Down
5 changes: 5 additions & 0 deletions docs/mwrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ A wrapper should depend on it when its configuration and ABI are sufficient.
Complex implementations may instead vendor pinned sources under `upstream/`.
Each implementation has one owner in a program: do not link both a vendored copy
and a compat copy of the same library.
The C standard library is the third source: its implementation is the target's
C library, which mcpp selects with the target (glibc, musl, the Windows CRT,
macOS libSystem, openkal-musl, mingw-w64). `mcpplibs.mwrap.libc` links nothing;
its version names the standard (17.0.0 for ISO/IEC 9899:2018), recorded with
`standard` in `[package.metadata.mwrap]`.

## Repository layout

Expand Down
3 changes: 3 additions & 0 deletions docs/zh/mwrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
包和模块名不带 -m,不创建另一套普通 API namespace。已发布旧身份、版本和内容保持不可变,
通过新 mwrap 身份迁移。简单实现优先依赖 compat.xxx;复杂库可以 vendor 到 upstream/。
同一程序的同一实现只能有一个所有者,不能把 compat 与 vendored 实现重复链接。
C 标准库是第三种实现来源:实现由目标的 C 库提供,由 mcpp 随目标选定(glibc、musl、Windows CRT、macOS libSystem、
openkal-musl、mingw-w64)。`mcpplibs.mwrap.libc` 不链接任何实现;版本号表示所依据的标准(ISO/IEC 9899:2018 为 17.0.0),
记录在 `[package.metadata.mwrap]` 的 `standard` 中。

## 仓库分层

Expand Down
6 changes: 6 additions & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ members = [
"tests/mwrap/libvpx-webm",
"tests/mwrap/box2d-physics",
"tests/mwrap/mimalloc-heap",
"tests/mwrap/zlib-compress",
"tests/mwrap/zstd-squeeze",
"tests/mwrap/xz-pack",
"tests/mwrap/xxhash-digest",
"tests/mwrap/libarchive-bundle",
"tests/mwrap/libc-stdio",
"tests/mwrap/miniaudio-engine",
"tests/mwrap/freetype-render",
"tests/mwrap/harfbuzz-shape",
Expand Down
51 changes: 51 additions & 0 deletions pkgs/mwrap/libarchive.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
-- Immutable upstream-aligned mwrap source release (libarchive-m mwrap-3.8.7);
-- GLOBAL and CN bytes are identical.
--
-- The wrapper is Apache-2.0; the library it links, compat.libarchive
-- (libarchive 3.8.7), is BSD-2-Clause with the exceptions its COPYING lists,
-- linked statically: the package deploys its README and COPYING beside
-- programs (runtime.deploy).
package = {
spec = "1",
namespace = "mcpplibs.mwrap",
name = "libarchive",
description = "libarchive-m: libarchive 3.8.7 (tar, cpio, zip, 7-Zip, ISO 9660 … with their compression filters, entries, disk access) as C++23 modules, with the constants of archive.h as C++ constants",
licenses = { "Apache-2.0" },
repo = "https://github.com/mcpplibs/libarchive-m",
type = "package",
extensions = {
autoupdate = {
tag = "mwrap-${version}",
url = "https://github.com/mcpplibs/libarchive-m/releases/download/${tag}/libarchive-m-${version}.tar.gz",
},
},
xpm = {
linux = {
["3.8.7"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/libarchive-m/releases/download/mwrap-3.8.7/libarchive-m-3.8.7.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-libarchive/releases/download/3.8.7/libarchive-m-3.8.7.tar.gz",
},
sha256 = "b55c102f8743112d94bd57ab8e79b31cdfe49935c1bebaed846b1a4508e9c8f8",
},
},
macosx = {
["3.8.7"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/libarchive-m/releases/download/mwrap-3.8.7/libarchive-m-3.8.7.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-libarchive/releases/download/3.8.7/libarchive-m-3.8.7.tar.gz",
},
sha256 = "b55c102f8743112d94bd57ab8e79b31cdfe49935c1bebaed846b1a4508e9c8f8",
},
},
windows = {
["3.8.7"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/libarchive-m/releases/download/mwrap-3.8.7/libarchive-m-3.8.7.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-libarchive/releases/download/3.8.7/libarchive-m-3.8.7.tar.gz",
},
sha256 = "b55c102f8743112d94bd57ab8e79b31cdfe49935c1bebaed846b1a4508e9c8f8",
},
},
},
}
49 changes: 49 additions & 0 deletions pkgs/mwrap/libc.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
-- Immutable upstream-aligned mwrap source release (libc-m mwrap-17.0.0);
-- GLOBAL and CN bytes are identical.
--
-- The wrapper is Apache-2.0 and links nothing: the implementation is the
-- target's C library. The version names the standard, ISO/IEC 9899:2018 (C17).
package = {
spec = "1",
namespace = "mcpplibs.mwrap",
name = "libc",
description = "libc-m: the C standard library (ISO C17) for C++ modules: std.compat with the object-like macros of ISO C (stdout, errno, EOF, INT_MAX …) as C++ constants and functions",
licenses = { "Apache-2.0" },
repo = "https://github.com/mcpplibs/libc-m",
type = "package",
extensions = {
autoupdate = {
tag = "mwrap-${version}",
url = "https://github.com/mcpplibs/libc-m/releases/download/${tag}/libc-m-${version}.tar.gz",
},
},
xpm = {
linux = {
["17.0.0"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/libc-m/releases/download/mwrap-17.0.0/libc-m-17.0.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-libc/releases/download/17.0.0/libc-m-17.0.0.tar.gz",
},
sha256 = "e601179504f0fc94bb4d2f002982b2f5b9cc4b95a50a13822bd9b272519a3a9c",
},
},
macosx = {
["17.0.0"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/libc-m/releases/download/mwrap-17.0.0/libc-m-17.0.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-libc/releases/download/17.0.0/libc-m-17.0.0.tar.gz",
},
sha256 = "e601179504f0fc94bb4d2f002982b2f5b9cc4b95a50a13822bd9b272519a3a9c",
},
},
windows = {
["17.0.0"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/libc-m/releases/download/mwrap-17.0.0/libc-m-17.0.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-libc/releases/download/17.0.0/libc-m-17.0.0.tar.gz",
},
sha256 = "e601179504f0fc94bb4d2f002982b2f5b9cc4b95a50a13822bd9b272519a3a9c",
},
},
},
}
50 changes: 50 additions & 0 deletions pkgs/mwrap/xxhash.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- Immutable upstream-aligned mwrap source release (xxhash-m mwrap-0.8.3);
-- GLOBAL and CN bytes are identical.
--
-- The wrapper is Apache-2.0; the library it links, compat.xxhash (xxHash
-- 0.8.3), is BSD-2-Clause, linked statically: the package deploys its README
-- and LICENSE beside programs (runtime.deploy).
package = {
spec = "1",
namespace = "mcpplibs.mwrap",
name = "xxhash",
description = "xxhash-m: xxHash 0.8.3 (XXH32, XXH64, XXH3 64 and 128 bits, one-shot and streaming) as C++23 modules",
licenses = { "Apache-2.0" },
repo = "https://github.com/mcpplibs/xxhash-m",
type = "package",
extensions = {
autoupdate = {
tag = "mwrap-${version}",
url = "https://github.com/mcpplibs/xxhash-m/releases/download/${tag}/xxhash-m-${version}.tar.gz",
},
},
xpm = {
linux = {
["0.8.3"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/xxhash-m/releases/download/mwrap-0.8.3/xxhash-m-0.8.3.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-xxhash/releases/download/0.8.3/xxhash-m-0.8.3.tar.gz",
},
sha256 = "4e389afbece2290b44c24c12b4ad1086c28f146e78de8cebf5c701f676dc36cc",
},
},
macosx = {
["0.8.3"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/xxhash-m/releases/download/mwrap-0.8.3/xxhash-m-0.8.3.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-xxhash/releases/download/0.8.3/xxhash-m-0.8.3.tar.gz",
},
sha256 = "4e389afbece2290b44c24c12b4ad1086c28f146e78de8cebf5c701f676dc36cc",
},
},
windows = {
["0.8.3"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/xxhash-m/releases/download/mwrap-0.8.3/xxhash-m-0.8.3.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-xxhash/releases/download/0.8.3/xxhash-m-0.8.3.tar.gz",
},
sha256 = "4e389afbece2290b44c24c12b4ad1086c28f146e78de8cebf5c701f676dc36cc",
},
},
},
}
Loading
Loading