Skip to content

Commit 90e8b01

Browse files
committed
feat(pkgs): add Eigen 5.0.1 (compat, header-only) + CN mirror
- compat.eigen @5.0.1 — latest Eigen (libeigen/eigen on GitLab; 5.x line). Header-only: include_dirs={"*"} exposes the source root so `#include <Eigen/Dense>` works out of the box; a trivial anchor TU gives mcpp a buildable lib target (same shape as compat.opengl/khrplatform). Both the stable `Eigen/` modules and the experimental `unsupported/Eigen/` modules resolve from the single root (verified: MatrixFunctions + AutoDiff compile). - CN mirror mcpp-res/eigen (GitCode), byte-identical to the GitLab archive (sha256 e9c326dc…, http 200). - feature mechanism: deliberately NOT used — analysis recorded in the descriptor + design doc. mcpp 0.0.68 features gate sources only; Eigen is header-only with no gateable source, and `unsupported/` shares the include root with core so it can't be hidden behind a sources-only feature. Eigen's other knobs (EIGEN_MPL2_ONLY, …) are compile defines the feature table can't carry. Documented for a future define-capable feature gate. - tests/examples/eigen/ minimal project (2x2 linear-algebra round-trip), wired into the per-package CI selector (detect → smoke-examples (eigen)). Verified locally on mcpp 0.0.68 (CI version, MCPP_INDEX_MIRROR=GLOBAL): `tests/run_example.sh eigen` → `eigen ok=1 y=[3 7] det=-2 dot=5`. Design: .agents/docs/2026-06-28-add-eigen-plan.md
1 parent 5456b4d commit 90e8b01

5 files changed

Lines changed: 215 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# 新增 Eigen 收录(compat 源码/header-only)+ GitCode CN 镜像方案
2+
3+
**日期**: 2026-06-28
4+
**本仓**: `mcpp-community/mcpp-index`(github 别名 `mcpplibs/mcpp-index`)
5+
**参考**: PR #48(cJSON compat + nlohmann.json module + per-pkg CI)
6+
**目标**:
7+
1. 收录 [`libeigen/eigen`](https://gitlab.com/libeigen/eigen) 最新版 **5.0.1** —— header-only C++ 线性代数库,
8+
**全兼容(compat)** 形态,用户 `#include <Eigen/Dense>` 开箱即用。文件名 `pkgs/c/compat.eigen.lua`
9+
2.**GitCode CN 镜像** `mcpp-res/eigen`(`gitcode.com/mcpp-res/eigen/...`),用本仓 `tools/gtc` 推送。
10+
3. 评估 Eigen 是否有可走 mcpp **feature 机制** 的"类 feature"可选项(见 §3 结论:当前不适用,已说明原因)。
11+
4.`tests/examples/eigen/` 放最小工程,CI 选跑(detect → `eigen` matrix job)。
12+
13+
---
14+
15+
## 1. 最新版本与上游布局(关键前置)
16+
17+
- 最新 tag:`git ls-remote --tags` 显示 **5.0.1**(>3.4.x,Eigen 已跨大版本到 5.x;另有 5.0.0 / 3.4.1)。收录 `5.0.1`
18+
- 上游托管在 **GitLab**(非 GitHub):
19+
GLOBAL = `https://gitlab.com/libeigen/eigen/-/archive/5.0.1/eigen-5.0.1.tar.gz`(归档 sha256 已实测两次稳定:`e9c326dc…`)。
20+
- tarball 单层 wrap `eigen-5.0.1/`,glob `*` 吸收。根目录含:
21+
- `Eigen/` —— **稳定模块**(Dense / Core / LU / QR / SVD / Sparse / Geometry …)
22+
- `unsupported/Eigen/` —— **实验模块**(CXX11/Tensor、AutoDiff、Splines、MatrixFunctions、FFT …)
23+
- `blas/` `lapack/` —— Eigen 自带的 BLAS/LAPACK 实现(含 12 个 **Fortran `.f`**,需 Fortran 编译器)
24+
- 许可:主体 **MPL-2.0**(另有少量 Apache/BSD/MINPACK 文件)
25+
- **`.cppm`**:Eigen 5.0.1 仍是纯 header,未提供 C++ module 单元 → 走 compat header-only 形态(非 nlohmann 那种 module wrapper)。
26+
27+
## 2. descriptor 形态(header-only,参照 compat.opengl/khrplatform)
28+
29+
Eigen 无可编译源(纯模板头),故:
30+
- `include_dirs = {"*"}` 暴露 tarball 根 —— 即上游推荐放入 include path 的目录;`#include <Eigen/...>`
31+
`#include <unsupported/Eigen/...>` 均可解析。
32+
- 用一个 trivial anchor TU(`mcpp_generated/eigen_anchor.c`)给 mcpp 一个可构建的 `lib` 目标(同 opengl/khrplatform)。
33+
- `language=c++23``c_standard=c11`(anchor 是 C)、`deps={}`
34+
35+
## 3. feature 机制评估 —— 当前不适用(已记录原因)
36+
37+
用户要求"如果 Eigen 有类似 feature 的东西,可走 mcpp feature 机制"。结论:**当前(mcpp 0.0.68)不适用**,原因经源码核实:
38+
39+
- mcpp 包描述符的 `features`**只能门控 `sources`**(feature 贡献额外源码 glob,默认排除、请求时编入 ——
40+
`compat.cjson``utils``compat.gtest``main`;源码 `manifest.cppm` 中 feature 子字段仅识别 `sources`,
41+
其余被 skip)。
42+
- Eigen 是 header-only,**没有可门控的可选源码**。其天然的"可选轴"是 `unsupported/` 实验模块,但它与 `Eigen/`
43+
**同处 tarball 根** —— 任何暴露稳定核(`*`)的 include path 都不可避免地一并暴露 `unsupported/`(已实测:
44+
`<unsupported/Eigen/MatrixFunctions>` / `<AutoDiff>` 直接可编)。**既无源可门控,也无法用 sources-only 的
45+
feature 把头藏起来**
46+
- 其它 Eigen 开关(`EIGEN_MPL2_ONLY``EIGEN_USE_BLAS/LAPACKE`…)是编译 **define**,feature 表同样不能携带。
47+
- `blas/` 是唯一可门控的真实源,但需 Fortran(`.f`)+ 独立 lib 目标,feature 仅能往既有目标 *追加* 源 → 不匹配,过重。
48+
49+
→ 故本期 **暴露完整头集(core + unsupported)、不加 feature**,并在 descriptor 注释中完整记录该分析。
50+
若 mcpp 未来允许 feature 携带 define/cflags,`mpl2only`(→ `-DEIGEN_MPL2_ONLY`)是干净的接入点。
51+
52+
## 4. CN 镜像(gtc)
53+
54+
repo 名 = 包名去 `compat.` 前缀 = `eigen`:
55+
```
56+
gtc repo create mcpp-res/eigen
57+
gtc repo push mcpp-res/eigen <init> # 新仓需先有 main 分支才能发 release
58+
gtc release publish mcpp-res/eigen --tag 5.0.1 --target main --asset eigen-5.0.1.tar.gz
59+
```
60+
上传的就是 GLOBAL 同一个 tarball → CN/GLOBAL **byte-identical**(实测 sha 一致 + CN http 200)。
61+
CN url:`https://gitcode.com/mcpp-res/eigen/releases/download/5.0.1/eigen-5.0.1.tar.gz`
62+
63+
## 5. 最小工程 + CI
64+
65+
- `tests/examples/eigen/{mcpp.toml, src/main.cpp}` —— `#include <Eigen/Dense>`,2x2 线代往返断言
66+
(`A*x`、determinant、dot、QR solve)。纯文本 include,不混 `import std;`
67+
- `validate.yml` 的 detect 把 `compat.eigen.lua``eigen` → 命中 `tests/examples/eigen``smoke-examples (eigen)` 单跑。
68+
- `mirror-cn-reachable` 覆盖新 CN url。
69+
70+
## 6. 落地记录(2026-06-28)
71+
72+
- CN 镜像:`mcpp-res/eigen@5.0.1` 已建 + 发布,CN 与 GLOBAL byte-identical(sha `e9c326dc…`),http 200。
73+
- 本地实测(mcpp **0.0.68**,与 CI 同版本,`MCPP_INDEX_MIRROR=GLOBAL`):
74+
`tests/run_example.sh eigen``eigen ok=1 y=[3 7] det=-2 dot=5`,`OK: eigen`
75+
- `unsupported/` 可达性单独实测:`g++ -std=c++23 -I<root>` 编 `<Eigen/Dense> + <unsupported/Eigen/MatrixFunctions> +
76+
<AutoDiff>` → rc=0。
77+
- 全量 lint 本地模拟通过(语法 / 必填字段 / 无前导 v / mirror url 检查)。
78+
- ****:compat 包目录按 **完整包名首字母** 归类 —— `compat.eigen``pkgs/c/`(不是短名 `eigen``pkgs/e/`),
79+
否则本地 path index 扫不到(`dependency 'compat.eigen': not found in local index`)。

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ mcpp build # 自动拉取源码 + 构建
3939
| `glfw` | 3.4 | GLFW 窗口与输入库(X11/null 后端源码构建) |
4040
| `gtest` | 1.15.2 | Google Test 测试框架 |
4141
| `cjson` | 1.7.19 | 超轻量 ANSI C JSON 解析库(`#include <cJSON.h>`,`compat` 源码构建) |
42+
| `eigen` | 5.0.1 | C++ 模板线性代数库(header-only,`#include <Eigen/Dense>`;`unsupported/` 实验模块亦可用) |
4243
| `imgui` | 1.92.8 | Dear ImGui immediate-mode GUI 核心源码 |
4344
| `opengl` | 2026.05.31 | Khronos OpenGL API 头文件 |
4445
| `glx-runtime` | 2026.06.03 | Linux host GLVND/GLX/OpenGL runtime adapter |

pkgs/c/compat.eigen.lua

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
-- Form B inline descriptor for Eigen — a C++ template library for linear
2+
-- algebra (matrices, vectors, numerical solvers, related algorithms). Eigen
3+
-- is HEADER-ONLY: there is nothing to compile, so the package just exposes
4+
-- the source tree's root on the include path (`#include <Eigen/Dense>` etc.)
5+
-- and carries a tiny anchor translation unit so mcpp still has a buildable
6+
-- `lib` target (same shape as compat.opengl / compat.khrplatform).
7+
--
8+
-- `include_dirs = {"*"}` points at the tarball root (`eigen-<ver>/`), which is
9+
-- exactly the directory upstream tells you to put on the include path. That
10+
-- makes BOTH the stable modules under `Eigen/` and the experimental modules
11+
-- under `unsupported/Eigen/` (Tensor, AutoDiff, Splines, MatrixFunctions, …)
12+
-- resolvable — see the note on features below for why `unsupported` is not a
13+
-- separate opt-in here.
14+
--
15+
-- On the `feature` mechanism (asked for, deliberately omitted — analysis):
16+
-- mcpp's package-descriptor `features` table gates **sources only** (a
17+
-- feature contributes extra source globs that are excluded by default and
18+
-- compiled in when the feature is requested — see compat.cjson's `utils`
19+
-- and compat.gtest's `main`). Eigen has no such optional *source*: it is
20+
-- header-only. Its natural opt-in axis would be the `unsupported/` modules,
21+
-- but those are headers that live BESIDE `Eigen/` under the same tarball
22+
-- root, so any include path that exposes the stable core (`*`) inevitably
23+
-- exposes `unsupported/` too — there is no source to gate and no way to
24+
-- hide the headers behind a feature with the current (sources-only) gate.
25+
-- Other Eigen knobs (EIGEN_MPL2_ONLY, EIGEN_USE_BLAS/LAPACKE, …) are
26+
-- compile *defines*, which the feature table also cannot carry on mcpp
27+
-- 0.0.68. So a feature here would be cosmetic/misleading; we expose the
28+
-- full header set instead and document it. If mcpp later lets a feature
29+
-- contribute defines/cflags, an `mpl2only` (-> -DEIGEN_MPL2_ONLY) feature
30+
-- would be the clean fit.
31+
--
32+
-- All `mcpp` paths are GLOBS relative to the verdir; the leading `*` absorbs
33+
-- the GitLab archive's `eigen-<tag>/` wrap layer.
34+
package = {
35+
spec = "1",
36+
namespace = "compat",
37+
name = "compat.eigen",
38+
description = "C++ template library for linear algebra (header-only)",
39+
licenses = {"MPL-2.0"},
40+
repo = "https://gitlab.com/libeigen/eigen",
41+
type = "package",
42+
43+
xpm = {
44+
linux = {
45+
["5.0.1"] = {
46+
url = {
47+
GLOBAL = "https://gitlab.com/libeigen/eigen/-/archive/5.0.1/eigen-5.0.1.tar.gz",
48+
CN = "https://gitcode.com/mcpp-res/eigen/releases/download/5.0.1/eigen-5.0.1.tar.gz",
49+
},
50+
sha256 = "e9c326dc8c05cd1e044c71f30f1b2e34a6161a3b6ecf445d56b53ff1669e3dec",
51+
},
52+
},
53+
macosx = {
54+
["5.0.1"] = {
55+
url = {
56+
GLOBAL = "https://gitlab.com/libeigen/eigen/-/archive/5.0.1/eigen-5.0.1.tar.gz",
57+
CN = "https://gitcode.com/mcpp-res/eigen/releases/download/5.0.1/eigen-5.0.1.tar.gz",
58+
},
59+
sha256 = "e9c326dc8c05cd1e044c71f30f1b2e34a6161a3b6ecf445d56b53ff1669e3dec",
60+
},
61+
},
62+
windows = {
63+
["5.0.1"] = {
64+
url = {
65+
GLOBAL = "https://gitlab.com/libeigen/eigen/-/archive/5.0.1/eigen-5.0.1.tar.gz",
66+
CN = "https://gitcode.com/mcpp-res/eigen/releases/download/5.0.1/eigen-5.0.1.tar.gz",
67+
},
68+
sha256 = "e9c326dc8c05cd1e044c71f30f1b2e34a6161a3b6ecf445d56b53ff1669e3dec",
69+
},
70+
},
71+
},
72+
73+
mcpp = {
74+
language = "c++23",
75+
import_std = false,
76+
c_standard = "c11",
77+
-- Tarball root: exposes `Eigen/` (stable) and `unsupported/Eigen/`
78+
-- (experimental) to consumers writing `#include <Eigen/...>`.
79+
include_dirs = { "*" },
80+
-- Header-only: a trivial anchor TU gives mcpp a buildable lib target.
81+
generated_files = {
82+
["mcpp_generated/eigen_anchor.c"] = "int mcpp_compat_eigen_headers_anchor(void) { return 0; }\n",
83+
},
84+
sources = { "mcpp_generated/eigen_anchor.c" },
85+
targets = { ["eigen"] = { kind = "lib" } },
86+
deps = { },
87+
},
88+
}

tests/examples/eigen/mcpp.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Minimal example: Eigen consumed header-only via the compat source build.
2+
# Run from this directory: mcpp run (index path is relative to repo root)
3+
[package]
4+
name = "eigen-example"
5+
version = "0.1.0"
6+
7+
[toolchain]
8+
default = "gcc@16.1.0"
9+
10+
[indices]
11+
compat = { path = "../../.." }
12+
13+
[dependencies.compat]
14+
eigen = "5.0.1"
15+
16+
[targets.eigen-example]
17+
kind = "bin"
18+
main = "src/main.cpp"

tests/examples/eigen/src/main.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Eigen is header-only; the compat package puts the source tree root on the
2+
// include path so `#include <Eigen/...>` just works. Plain textual includes
3+
// here (Eigen is not a module), so we do NOT mix in `import std;`.
4+
#include <Eigen/Dense>
5+
#include <cstdio>
6+
#include <cmath>
7+
8+
int main() {
9+
// A * x for a 2x2 system: [[1,2],[3,4]] * [1,1]^T = [3,7]^T
10+
Eigen::Matrix2d A;
11+
A << 1, 2,
12+
3, 4;
13+
Eigen::Vector2d x(1.0, 1.0);
14+
Eigen::Vector2d y = A * x;
15+
16+
double det = A.determinant(); // 1*4 - 2*3 = -2
17+
double dot = x.dot(Eigen::Vector2d(2.0, 3.0)); // 1*2 + 1*3 = 5
18+
19+
// Solve A z = y, expect z back to [1,1].
20+
Eigen::Vector2d z = A.colPivHouseholderQr().solve(y);
21+
22+
bool ok = y(0) == 3.0 && y(1) == 7.0
23+
&& det == -2.0 && dot == 5.0
24+
&& std::abs(z(0) - 1.0) < 1e-9 && std::abs(z(1) - 1.0) < 1e-9;
25+
26+
std::printf("eigen ok=%d y=[%g %g] det=%g dot=%g\n",
27+
ok, y(0), y(1), det, dot);
28+
return ok ? 0 : 1;
29+
}

0 commit comments

Comments
 (0)