Skip to content

Commit 239c45f

Browse files
feat: 添加 mp-units 2.5.0 模块包(LLVM) (#515)
复用上游 mp_units.core / mp_units.systems / mp_units 三个模块,安装钩子仅将接口复制为 .cppm。 linux 要求 libc++:GCC 16.1 导入模块报 recursive lazy load(mpusz/mp-units#717),CI gcc 腿跳过该成员。 关闭实验性自然单位(LLVM 22 约束失败)。 Co-authored-by: HikariTish <helanlordts@163.com>
1 parent afe5e8a commit 239c45f

9 files changed

Lines changed: 141 additions & 0 deletions

File tree

‎.github/workflows/validate.yml‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,22 @@ jobs:
14561456
mine="${kept# }"
14571457
fi
14581458
1459+
# `mp-units-module`: the reverse case. `mpusz.mp-units` requires
1460+
# libc++ on linux because GCC 16.1 cannot import its modules
1461+
# (`recursive lazy load`, mpusz/mp-units#717), so the gcc leg would
1462+
# only reach that refusal. Lift once xim ships a GCC that works.
1463+
if [ "${{ matrix.platform }}" = "linux" ] && [ "${{ matrix.toolchain }}" = "default" ]; then
1464+
kept=""
1465+
for m in $mine; do
1466+
case "$m" in
1467+
mp-units-module)
1468+
echo "skipping '$m' on the gcc leg: the package requires libc++" ;;
1469+
*) kept="$kept $m" ;;
1470+
esac
1471+
done
1472+
mine="${kept# }"
1473+
fi
1474+
14591475
echo "MEMBERS=$mine" >> "$GITHUB_ENV"
14601476
echo "shard ${{ matrix.shard }}/${{ matrix.shards }}: ${mine:-<none>}"
14611477

‎docs/descriptor-examples.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ in the [root README](../README.md#reference-examples).
4949
| C++23 module, upstream's own unit | [`khronos.vulkan-hpp`](../pkgs/k/khronos.vulkan-hpp.lua) (Vulkan-Hpp 1.4.357.0 — Khronos generates `vulkan.cppm` / `vulkan_video.cppm` into every Vulkan-Headers release, so `sources` names the two units and NOTHING is authored here; the payload is the same tarball, URL and sha256 as `compat.vulkan-headers`, which is what makes the module and the headers it includes impossible to skew. `import_std = true` is forced by the unit's own unconditional `export import std;`. No `include_dirs`: the headers arrive with the `compat.vulkan` dependency, which is also what satisfies the STATIC dispatcher's direct calls at link — depending on headers alone gives a package that compiles and then fails at every consumer's link. Module names stay upstream's `vulkan` / `vulkan_video`, never `khronos.vulkan`. The second unit `import`s the first and mcpp orders the pair from the scan, which `tests/examples/vulkan-hpp-module/tests/video.cpp` is the regression for) |
5050
| C++23 module, upstream's CPU partitions | [`taskflow.taskflow`](../pkgs/t/taskflow.taskflow.lua) (Taskflow 4.1.0, `import tf;`; reuses the four upstream CPU module units and omits the competing CUDA entry point. The checked install hook removes two nonexistent exports, moves the umbrella include/version export into core and imports core first for GCC, and supplies `<algorithm>` to utility for libc++. Only three module files are patched; headers and scheduler implementation stay upstream. No fork; consumers use `tf::version()` because macros are not exported) |
5151
| C++23 module, upstream asynchronous logging | [`odygrd.quill`](../pkgs/o/odygrd.quill.lua) (Quill 13.0.0, upstream experimental `quill` module; the checked install hook generates `.cppm` from `src/quill.cc`, guards x86 intrinsics by target architecture and includes Apple Mach headers in the global module fragment. Consumers use `import std; import quill;` and the macro-free API, or define `QUILL_USE_MODULE` and include `quill/LogMacros.h` for logging macros. Bundled fmt needs no separate dependency; Linux links with `-pthread`. Multi-TU logger identity, worker-thread output, formatting and filtering are tested) |
52+
| C++23 modules, upstream units | [`mpusz.mp-units`](../pkgs/m/mpusz.mp-units.lua) (2.5.0; upstream `mp_units.core`, `mp_units.systems` and `mp_units`, copied verbatim to `.cppm` by the install hook so Clang treats them as interfaces. `import std`, `std::format`, runtime contracts off, experimental natural units off. Linux requires libc++ (LLVM): GCC 16.1 fails to import the module with `recursive lazy load`, mpusz/mp-units#717) |

‎docs/zh/descriptor-examples.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@
4747
| C++23 module,上游自带单元 | [`khronos.vulkan-hpp`](../../pkgs/k/khronos.vulkan-hpp.lua)(Vulkan-Hpp 1.4.357.0 —— Khronos 把 `vulkan.cppm` / `vulkan_video.cppm` 生成进每个 Vulkan-Headers release,所以 `sources` 点名这两个单元即可,本仓**不写一行**包装体;载荷与 `compat.vulkan-headers` 是同一份 tarball、同一个 URL 与 sha256,这让模块与它 include 的头不可能错配。`import_std = true` 由单元自身无条件的 `export import std;` 决定。不声明 `include_dirs`:头随 `compat.vulkan` 依赖到达,而该依赖同时满足静态 dispatcher 在链接期的直接调用 —— 只依赖头会得到一个「能编译、每个消费者都链接失败」的包。模块名保持上游的 `vulkan` / `vulkan_video`,绝不写成 `khronos.vulkan`。第二个单元 `import` 第一个,顺序由 mcpp 扫描决定,`tests/examples/vulkan-hpp-module/tests/video.cpp` 就是这条的回归)|
4848
| C++23 module,上游 CPU 分区 | [`taskflow.taskflow`](../../pkgs/t/taskflow.taskflow.lua)(Taskflow 4.1.0,`import tf;`;复用上游四个 CPU 模块单元,排除提供同名主模块的 CUDA 入口。安装钩子逐项校验匹配次数:移除两个不存在的导出,将总头文件及版本导出移至 core 并优先导入 core 以兼容 GCC,为 utility 补 `<algorithm>` 以兼容 libc++。仅适配三个模块文件,头文件和调度实现保持上游原样,无独立 fork;宏不随模块导出,版本查询使用 `tf::version()`) |
4949
| C++23 module,上游异步日志 | [`odygrd.quill`](../../pkgs/o/odygrd.quill.lua)(Quill 13.0.0,上游实验性 `quill` 模块;安装钩子以 `src/quill.cc` 生成 `.cppm`,精确适配 x86 intrinsic 的架构条件与 Apple Mach 头的全局模块归属。消费者使用 `import std; import quill;` 和无宏 API,或定义 `QUILL_USE_MODULE` 并包含 `quill/LogMacros.h` 使用日志宏。自带 fmt,无需额外依赖;Linux 链接使用 `-pthread`。测试覆盖多 TU logger 身份、工作线程输出、格式化和过滤) |
50+
| C++23 模块,上游自带单元 | [`mpusz.mp-units`](../../pkgs/m/mpusz.mp-units.lua)(2.5.0;复用 `mp_units.core`、`mp_units.systems`、`mp_units`,安装钩子将接口原样复制为 `.cppm` 供 Clang 识别。使用 `import std` 与 `std::format`,关闭运行时契约和实验性自然单位。Linux 要求 libc++(LLVM):GCC 16.1 导入模块时报 `recursive lazy load`,见 mpusz/mp-units#717) |

‎mcpp.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ members = [
147147
"tests/examples/sqlitecpp",
148148
"tests/examples/taskflow-module",
149149
"tests/examples/quill-module",
150+
"tests/examples/mp-units-module",
150151
"tests/examples/tinyhttps",
151152
"tests/examples/usockets",
152153
"tests/examples/opencl",

‎pkgs/m/mpusz.mp-units.lua‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package = {
2+
spec = "1",
3+
namespace = "mpusz",
4+
name = "mp-units",
5+
description = "Physical quantities and units through the upstream mp_units C++ modules",
6+
licenses = { "MIT" },
7+
repo = "https://github.com/mpusz/mp-units",
8+
type = "package",
9+
xpm = {
10+
linux = {
11+
["2.5.0"] = {
12+
url = "https://github.com/mpusz/mp-units/archive/refs/tags/v2.5.0.tar.gz",
13+
sha256 = "a6bd48bee699f11f0ed5b04b8c5006d15f76e6d898e058db3880554d2e47a400",
14+
},
15+
},
16+
macosx = {
17+
["2.5.0"] = {
18+
url = "https://github.com/mpusz/mp-units/archive/refs/tags/v2.5.0.tar.gz",
19+
sha256 = "a6bd48bee699f11f0ed5b04b8c5006d15f76e6d898e058db3880554d2e47a400",
20+
},
21+
},
22+
windows = {
23+
["2.5.0"] = {
24+
url = "https://github.com/mpusz/mp-units/archive/refs/tags/v2.5.0.tar.gz",
25+
sha256 = "a6bd48bee699f11f0ed5b04b8c5006d15f76e6d898e058db3880554d2e47a400",
26+
},
27+
},
28+
},
29+
mcpp = {
30+
language = "c++23",
31+
import_std = true,
32+
modules = { "mp_units.core", "mp_units.systems", "mp_units" },
33+
include_dirs = { "*/src/core/include", "*/src/systems/include" },
34+
-- 上游 import std 配置:标准格式化、关闭运行时契约;自然单位为实验特性,LLVM 22 下约束失败
35+
defines = {
36+
"MP_UNITS_IMPORT_STD",
37+
"MP_UNITS_API_STD_FORMAT=1",
38+
"MP_UNITS_API_CONTRACTS=0",
39+
"MP_UNITS_API_NATURAL_UNITS=0",
40+
},
41+
sources = {
42+
"*/src/core/mp-units-core.cppm",
43+
"*/src/systems/mp-units-systems.cppm",
44+
"*/src/mp-units.cppm",
45+
},
46+
-- 两个上游接口的 std 导入受 MP_UNITS_IMPORT_STD 守卫
47+
scan_overrides = {
48+
["*/src/core/mp-units-core.cppm"] = {
49+
provides = { "mp_units.core" }, imports = { "std" },
50+
},
51+
["*/src/systems/mp-units-systems.cppm"] = {
52+
provides = { "mp_units.systems" }, imports = { "mp_units.core", "std" },
53+
},
54+
},
55+
targets = { ["mp-units"] = { kind = "lib" } },
56+
deps = {},
57+
linux = {
58+
-- 暂不支持 GCC:GCC 16.1 导入该模块时报 recursive lazy load(mpusz/mp-units#717)
59+
requires = { "mcpp:c++-abi=libc++" },
60+
},
61+
},
62+
}
63+
64+
import("xim.libxpkg.pkginfo")
65+
66+
function install()
67+
local wrap = "mp-units-" .. pkginfo.version()
68+
-- 上游接口以 .cpp 命名,复制为 .cppm 供 Clang 识别为模块接口
69+
for _, relative in ipairs({ "src/core/mp-units-core", "src/systems/mp-units-systems", "src/mp-units" }) do
70+
os.cp(path.join(wrap, relative .. ".cpp"), path.join(wrap, relative .. ".cppm"))
71+
end
72+
local prefix = pkginfo.install_dir()
73+
os.tryrm(prefix)
74+
os.mkdir(prefix)
75+
os.mv(wrap, path.join(prefix, wrap))
76+
return true
77+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "mp-units-module-tests"
3+
version = "0.1.0"
4+
5+
[indices]
6+
mpusz = { path = "../../.." }
7+
8+
[dependencies.mpusz]
9+
mp-units = "2.5.0"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import std;
2+
import mp_units.core;
3+
import mp_units.systems;
4+
5+
#include "quantity_bridge.h"
6+
7+
distance add_distance(distance left, distance right) {
8+
return left + right;
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#pragma once
2+
3+
using distance = mp_units::quantity<mp_units::si::metre, int>;
4+
5+
distance add_distance(distance left, distance right);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import std;
2+
import mp_units;
3+
4+
#include "../src/quantity_bridge.h"
5+
6+
using namespace mp_units;
7+
using namespace mp_units::si::unit_symbols;
8+
9+
template<class Left, class Right>
10+
concept Addable = requires(Left left, Right right) { left + right; };
11+
12+
static_assert(1 * km == 1000 * m);
13+
static_assert(!Addable<decltype(1 * m), decltype(1 * s)>);
14+
static_assert((120 * km / (2 * h)).numerical_value_in(km / h) == 60);
15+
static_assert(quantity{std::chrono::minutes{2}} == 120 * s);
16+
17+
int main() {
18+
if (add_distance(20 * m, 22 * m) != 42 * m) return 1;
19+
if (std::format("{}", 42 * m) != "42 m") return 2;
20+
if (std::format("{}", 2 * ohm) != "2 \xce\xa9") return 3;
21+
return 0;
22+
}

0 commit comments

Comments
 (0)