English | 简体中文
编写描述符前,应先判定库所属的形态,再选用对应模板。mcpp = {} 内的所有路径均为相对 verdir 的 GLOB:
前导 * 用于吸收 tarball 的 <repo>-<tag>/ wrap 层;* 匹配单段,** 匹配跨段(例如 */blas/*.cpp 合法)。
A–D 是四种基础形态,先按它们判定;E–G 是在基础形态之上叠加的处理方式,按需组合。
| 形态 | 特征 | 样例 | 关键字段 |
|---|---|---|---|
| A. C 源码 compat | 纯 C 或少量源码,用户 #include <foo.h> |
pkgs/c/compat.cjson.lua、compat.zlib.lua、compat.gtest.lua |
sources 与 c_standard |
| B. header-only | 纯头文件,无需编译 | pkgs/c/compat.eigen.lua、compat.opengl.lua、compat.khrplatform.lua |
include_dirs 与 anchor 源 |
| C. C++23 module | 暴露 import x.y; |
pkgs/n/nlohmann.json.lua |
modules 与 generated_files 或源 .cppm |
| D. 外部 Form-A 模块仓 | 上游自带 mcpp 描述符,独立仓库 | pkgs/i/imgui.lua、pkgs/m/mcpplibs.* |
mcpp = "<repo 路径>"(Form A) |
| E. 生成 config 的全源码直编 | 上游用 configure/CMake 生成配置头,此处以 generated_files 落一份快照 |
pkgs/c/compat.libpng.lua、compat.curl.lua、compat.sdl2.lua、compat.ffmpeg.lua |
generated_files + include_dirs |
| F. 共享库 compat | 必须是唯一的那个 .so(会被第三方 dlopen) |
pkgs/c/compat.x11.lua 等 X11 家族、compat.vulkan.lua(linux) |
targets = { kind = "shared", soname = … } |
| G. 宿主运行时适配 | 驱动之类无法 vendor 的东西,只做符号链接农场 + 元数据 | pkgs/c/compat.glx-runtime.lua、compat.vulkan-runtime.lua |
runtime.library_dirs / capabilities |
完整的样例索引见根 README 的「参考示例」表。
A、B、C 三类共用的骨架(package 头与 xpm)如下:
package = {
spec = "1",
namespace = "compat", -- 点分层级路径;compat / nlohmann / mcpplibs 等,决定 import 前缀与依赖 key
name = "<lib>", -- 单一原子段,不重复 namespace(SPEC-001 §3.2)
description = "…",
licenses = {"MIT"}, -- SPDX
repo = "https://…",
type = "package",
xpm = { -- 三平台均需声明;纯源码或纯头时三平台共用同一 url 与 sha256
linux = { ["1.2.3"] = { url = { GLOBAL = "https://…/v1.2.3.tar.gz",
CN = "https://gitcode.com/mcpp-res/<slug>/releases/download/1.2.3/<slug>-1.2.3.tar.gz" },
sha256 = "<计算所得>" } },
macosx = { ["1.2.3"] = { url = { GLOBAL = "…", CN = "…" }, sha256 = "…" } },
windows = { ["1.2.3"] = { url = { GLOBAL = "…", CN = "…" }, sha256 = "…" } },
},
mcpp = { … 见下文各形态 … },
}身份是 (namespace, name) 二元组:层级一律放 namespace,name 只写一段。文件名不参与解析,推荐
pkgs/<首字母>/<namespace>.<name>.lua(命中 mcpp 的快路径)。详见
仓库结构与 schema。
将 C 源码编译为 lib,头文件经 include_dirs 暴露,可选组件由 features 门控。
mcpp = {
language = "c++23", -- 与既有 compat 对齐;实际的 C 行为由 c_standard 决定
import_std = false,
c_standard = "c99", -- 或 c11
include_dirs = { "*" }, -- 暴露顶层头文件(*/foo.h)
sources = { "*/cJSON.c" }, -- 核心源码,始终编译
targets = { ["cjson"] = { kind = "lib" } },
features = { -- 可选扩展,默认不编译
["utils"] = { sources = { "*/cJSON_Utils.c" } },
},
deps = { },
}要点:多源码时可逐个列出(compat.zlib 列出了 15 个 .c)或使用 glob;需要配置头时可用 generated_files 合成
(compat.zlib 使用 mcpp_generated/include/mcpp_zlib_config.h 配合 cflags = {"-include …"})。
此类库无可编译源码:由 include_dirs 暴露头文件,并加入一个 trivial anchor .c,以提供一个可构建的 lib 目标。
mcpp = {
language = "c++23",
import_std = false,
c_standard = "c11",
include_dirs = { "*" }, -- 或更精确的 "*/include" / "*/api"
generated_files = {
["mcpp_generated/<lib>_anchor.c"] = "int mcpp_compat_<lib>_anchor(void) { return 0; }\n",
},
sources = { "mcpp_generated/<lib>_anchor.c" },
targets = { ["<lib>"] = { kind = "lib" } },
-- 若存在额外可编译源码的组件(非纯头),可实现为 source-gated feature:
features = {
["blas"] = { sources = { "*/blas/*.cpp", "*/blas/f2c/*.c" } }, -- eigen 实例
},
deps = { },
}注意:纯头形式的可选项无法隐藏(与核心共享 include 根),因此不应为其勉强构造 feature;只有额外可编译源码才能被门控
(compat.eigen 的 blas 即由 C++ 与 f2c 转换的 C 构成,不依赖 Fortran,因此可门控)。
使用户可 import x.y;。有两种实现路径:
- 上游已自带
.cppm:直接sources = { "*/path/to/unit.cppm" }。 - 上游 release 不含(较常见):以
generated_files合成 wrapper(#include <header>、export module x.y;、export using …),基底头 pin 至已发布 tag。应逐字复用上游官方 wrapper,而非自行推断符号清单。
mcpp = {
schema = "0.1",
language = "c++23",
import_std = false, -- wrapper 含上游头,启用 import std 易产生冲突
modules = { "nlohmann.json" },
include_dirs = { "*/single_include" }, -- 使 wrapper 内的 #include <…> 可解析
generated_files = {
["mcpp_generated/nlohmann.json.cppm"] = "module;\n#include <nlohmann/json.hpp>\nexport module nlohmann.json;\n…",
},
sources = { "mcpp_generated/nlohmann.json.cppm" },
targets = { ["nlohmann_json"] = { kind = "lib" } },
deps = { },
}注意:mcpp 段解析器不支持 Lua 长括号 [[ … ]],generated_files 的内容必须采用双引号字符串并对 \n、\"
转义,否则报 malformed mcpp segment。消费侧不应将 import x.y; 与文本 #include <string> 混用(会与 GCC
modules 冲突),应配合 import std;。
上游或独立仓库自带 mcpp 描述符,本仓仅充当指针:mcpp = "<相对或远程路径>"(Form A,而非内联的 Form B)。新增的
独立库通常归属于另一仓库(如 mcpplibs/imgui-m),本仓只负责登记。写法可参照 pkgs/i/imgui.lua 与
pkgs/x/xpkg.lua。
上游用 configure 或 CMake 生成一份配置头,而本仓要的是「列出 .c 文件」。可行的前提是这类库把未选中的后端
编成空 TU(curl 的 vtls/gtls.c 从头到尾是 #ifdef USE_GNUTLS,SDL 的 src/video/windows/*.c 同理),于是
源码列表可以是朴素的 glob,配置全部落在一份 generated_files 快照里。
只在上游有缺口的平台生成:curl 签入了 lib/config-win32.h(Windows 无需生成),SDL 签入了
SDL_config_windows.h / SDL_config_macosx.h(只有 linux 落到无用的 SDL_config_minimal.h)。生成时务必
用本索引的工具链跑 configure —— 用宿主 cc 生成的 curl 配置曾断言 ssize_t 不存在,导致 curl 编不过自己
的配置。
当这个库会被第三方 dlopen 时,它必须是进程里唯一的那一个,静态链接会出问题。
targets = { ["vulkan"] = { kind = "shared", soname = "libvulkan.so.1" } },soname 不是可选项:SDL2 的 SDL_CreateWindow(SDL_WINDOW_VULKAN) 会 dlopen("libvulkan.so.1") 并用它解析
surface 创建。若 loader 是静态的,应用最终会有两个 loader —— 自己那份建 instance,SDL 那份建 surface ——
createSurface 拿到一个对方没见过的 instance 而失败。
声明位置也有讲究:kind = "shared" 会把 -fPIC 传播给消费者,而 clang 对 msvc 目标直接拒绝该选项。因此
compat.vulkan 把它写在 linux 块内,Windows 走另一套(链接预生成的 import library)。平台块里的 targets
会覆盖顶层声明,compat.ffmpeg 亦如此。
GPU 驱动无法打包 —— ICD 必须匹配机器上的内核驱动。本仓的既定立场是把它建模为宿主能力,而不是假装厂商
驱动是可再分发的普通包(见 .agents/docs/2026-06-03-gl-runtime-packages-plan.md)。这类包不 vendor 任何东西,
只做符号链接农场加元数据:
runtime = {
library_dirs = { "mcpp_generated/<name>/lib" },
capabilities = { "vulkan.icd.driver" },
},之所以需要它:mcpp 的产物跑在自带的 glibc 下(interp 指向 xim-x-glibc,rpath 只覆盖 mcpp 自己的树),
因此裸 soname 的 dlopen 根本不搜索宿主库路径 —— loader 能找到全部 ICD manifest,却一个驱动都打不开。
两个反复踩到的细节:
- 农场里只放带版本号的 soname(
lib*.so.*)。runtime.library_dirs同时进链接行,一个裸libxcb.so会遮蔽本仓自己的compat.xcb,链接报undefined reference to XauDisposeAuth(mcpp#304)。带版本号的名字对 链接器不可见,而恰好是dlopen要的。 - 闭包必须完整。农场里有
libxcb.so.1却没有它依赖的libXau.so.6,会遮蔽掉本来能解析的宿主副本,可执行 文件直接起不来。
mcpp.toml(短式依赖与长式依赖二选一):
[package]
name = "<short>-example"
version = "0.1.0"
[toolchain]
default = "gcc@16.1.0"
# `compat` 由 workspace 根的 [indices] 继承,成员无需再写;
# 消费其他命名空间时才在此声明,例如 `[indices] fmtlib = { path = "../../.." }`
# —— 成员级声明会**替换**根级表而非与之合并(这正是保持单个项目索引 repo 的方式)。
[dependencies.compat]
<short> = "1.2.3" # 或:<short> = { version = "1.2.3", features = ["…"] }
[targets.<short>-example]
kind = "bin"
main = "src/main.cpp" # C 库可使用 .csrc/main.cpp 应包含有效断言并 return ok ? 0 : 1,而非仅打印输出。module 库使用 import std; import x.y;;
header-only 与 C 库使用文本 #include。