libxpkg-best-practices

star 2

Use when working on libxpkg — adding modules, writing tests, modifying xmake build config, working with Lua hooks, or fixing CI failures on Windows/Linux/macOS

openxlings By openxlings schedule Updated 3/1/2026

name: libxpkg-best-practices description: Use when working on libxpkg — adding modules, writing tests, modifying xmake build config, working with Lua hooks, or fixing CI failures on Windows/Linux/macOS

libxpkg Best Practices

Overview

libxpkg 是 xpkg 规范的 C++23 参考实现,分为四个子模块。以下是开发过程中积累的关键模式和陷阱。

模块架构

mcpplibs.xpkg              ← 纯 C++ 数据模型,零外部依赖
mcpplibs.xpkg.loader       ← model + lua;解析 .lua 包文件
mcpplibs.xpkg.index        ← model;纯 C++ 索引操作
mcpplibs.xpkg.executor     ← model + lua;执行 Lua 钩子

规则: 每个模块独立一个测试目标(xpkg_model_test 等)。模块间不允许循环依赖。


1. C++23 模块文件规范

禁止在模块声明后使用 #include 引入标准头:

// ❌ 错误
export module mcpplibs.xpkg.loader;
#include <string>   // 非法:模块声明后不能 #include

// ✅ 正确
module;
#include "lua.h"            // 全局模块分段:只放无法模块化的 C 头文件
export module mcpplibs.xpkg.loader;
import std;                 // 标准库统一用 import std;
import mcpplibs.xpkg;

全局模块分段module;export module ...; 之间)仅用于 Lua 等 C 库头文件。


2. GCC 15 模块 ABI — 含容器成员的结构体析构函数

症状: -O2 下链接出现 undefined reference to ~_Vector_impl / ~_Hashtable_alloc-O0 无问题。

根因: GCC 15 会将类体内定义的析构函数(包括隐式 inline)标记为可内联进 BMI,导入方内联展开后产生对 std 内部 inline 函数的悬空调用。

修复:std::vector / std::unordered_map 成员的结构体,析构函数必须在类体外定义:

// xpkg.cppm — export namespace 内:仅声明
export namespace mcpplibs::xpkg {
struct Package {
    std::string name;
    std::vector<std::string> authors;
    PlatformMatrix xpm;
    ~Package();   // ← 只声明,不定义
};
} // namespace mcpplibs::xpkg

// export namespace 之外:定义为 = default
namespace mcpplibs::xpkg {
Package::~Package() = default;   // ← outlined symbol,链接方可见
}

修改模块接口后必须:

xmake clean --all
xmake build <target>

否则旧 BMI 缓存会导致 Bad file data 错误。


3. C++23 结构化绑定限制

C++23 不支持匿名占位符(该特性属于 C++26):

// ❌ C++23 不支持
for (auto& [, members] : index.mutex_groups) { ... }

// ✅ 用具名变量 + (void) 压制警告
for (auto& [key, members] : index.mutex_groups) {
    (void)key;
    ...
}

4. xmake 跨平台路径注入

问题: $(projectdir) 在 Windows 上展开为反斜杠路径(D:\a\libxpkg\...)。直接用 add_defines 嵌入 C 字符串字面量时,\a 被解释为 BEL 字符,路径损坏。

错误写法:

add_defines('XPKG_TEST_PKGINDEX="$(projectdir)/tests/fixtures/pkgindex"')
-- Windows 结果:D:libxpkglibxpkg/tests/...(路径损坏)

正确写法: 使用 on_config 回调 + gsub 规范化为正斜杠:

on_config(function(target)
    -- Normalize to forward slashes: Windows $(projectdir) contains backslashes
    -- which are misinterpreted as C escape sequences in string literals.
    local dir = path.join(os.projectdir(), "tests", "fixtures", "pkgindex")
    dir = dir:gsub("\\", "/")
    target:add("defines", 'XPKG_TEST_PKGINDEX="' .. dir .. '"')
end)

所有注入路径的 add_defines 均须改为此模式。


5. Lua 钩子文件规范

xpkg 包 .lua 文件必须正确捕获 import() 的返回值:

-- ❌ 错误:import() 不注入全局,pkginfo/xvm 为 nil
import("xim.libxpkg.pkginfo")
import("xim.libxpkg.xvm")

-- ✅ 正确:捕获为局部变量
local pkginfo = import("xim.libxpkg.pkginfo")
local xvm     = import("xim.libxpkg.xvm")

钩子函数模板:

-- installed() — 检查是否已安装,返回版本字符串或 nil
function installed()
    local dir = pkginfo.install_dir()
    if dir and io.exists(dir .. "/.installed") then
        return pkginfo.version()
    end
    return nil
end

-- install() — 安装逻辑
function install()
    local dir = pkginfo.install_dir()
    os.execute('mkdir -p "' .. dir .. '"')
    io.writefile(dir .. "/.installed", pkginfo.version())
    if xvm then xvm.add(pkginfo.name(), pkginfo.version(), dir) end
end

-- uninstall() — 卸载逻辑
function uninstall()
    local dir = pkginfo.install_dir()
    os.remove(dir .. "/.installed")
    if xvm and xvm.has(pkginfo.name()) then
        xvm.remove(pkginfo.name())
    end
end

6. 测试 Fixture 结构

tests/fixtures/pkgindex/ 是自包含测试索引,不依赖外部仓库:

tests/fixtures/pkgindex/
└── pkgs/
    └── h/
        └── hello.lua    # 完整钩子示例

测试文件通过宏获取路径(带 #ifndef 回退):

#ifndef XPKG_TEST_PKGINDEX
#  define XPKG_TEST_PKGINDEX "tests/fixtures/pkgindex"
#endif
static const fs::path PKGINDEX{ XPKG_TEST_PKGINDEX };

7. CI — 并行构建竞态

问题: 依赖链极短的目标(如 basic,仅依赖 mcpplibs-xpkg)在并行构建时,std.gcm / std.ifc 尚未就绪就开始编译,报 could not find module 'std'

修复: 对依赖链短的示例/工具目标,使用 #include 替代 import std;

// examples/basic.cpp — 避免 import std; 的并行竞态
#include <iostream>
#include <string>
import mcpplibs.xpkg;

功能更丰富的目标(lifecycle)通过 add_deps 传递完整依赖链,std 模块会提前就绪,可安全使用 import std;


快速参考

场景 解决方案
链接失败 ~_Vector_impl -O2 析构函数类体外 = default
神秘构建错误 Bad file data xmake clean --all
路径在 Windows 损坏 on_config + gsub("\\", "/")
Lua hook 中 pkginfo 为 nil local pkginfo = import(...)
CI import std; 找不到模块 改用 #include 或加全依赖链
C++23 匿名绑定 [,x] 改为 [key, x] + (void)key
Install via CLI
npx skills add https://github.com/openxlings/libxpkg --skill libxpkg-best-practices
Repository Details
star Stars 2
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator