Luban
Windows-first 的 C++ 工具链管理器 + cmake/vcpkg 辅助前端。 单一静态链接二进制,零 UAC,XDG-first 目录布局。
luban 解决什么问题
如果你在 Windows 上配过现代 C++ 工具链,你就知道墙在哪:
- 选 LLVM-MinGW 还是 MSVC 还是 mingw-w64
- 各自下 cmake / ninja / clangd / vcpkg
- 才发现 vcpkg 还要 git
- 学 cmake 的怪 variable / find_package / target_link_libraries
- 学 vcpkg.json schema、triplets、overlay ports
- 学 CMakePresets
- 才意识到 clangd 要
compile_commands.json才有补全 - 然后所有这些都不在 PATH 上——每个新 shell 还得 source 个 activate
每件事是半天的 yak-shaving。整套堆起来要好几天,每台新机器要重来一遍。
luban 把这堆全活变成两条命令:
luban setup # 一次性,装 LLVM-MinGW + cmake + ninja + mingit + vcpkg
luban env --user # 一次性,把所有工具注入 user PATH(rustup 风格)
之后开任何新终端,cmake / clang++ / ninja / clangd 直接可用。每个项目里:
luban new app foo # 脚手架;自动 build;clangd 立即可用
cd foo
luban add fmt # 一行加依赖
luban build # cmake 通过 vcpkg manifest mode 自动取 fmt
80% 的场景下,你永远不打开 CMakeLists.txt。
luban 不是什么
- 不是构建系统。 cmake + ninja 仍然干活;luban 只生成 cmake 胶水。
- 不是包管理器。 vcpkg 仍然解析 + 构建 C++ 包;luban 替你编辑
vcpkg.json。 - 不是替代品。 你
git clone一个 luban 项目到一台没装 luban 的机器 上,只要 cmake + vcpkg 在,cmake --preset default仍然能 build—— luban 生成的luban.cmake进 git 跟随项目。
设计哲学
| 原则 | 含义 |
|---|---|
| 辅助而非主导 | cmake / vcpkg / ninja 仍是项目主角。luban 写一个标准 cmake module(luban.cmake)让用户 include(),删一行就退出。 |
| 不发明新 manifest | 依赖在 vcpkg.json(vcpkg 自己的 schema)里。luban.toml 是可选的,只放项目级偏好(warning 等级、sanitizers)。 |
luban.cmake 进 git | 项目可重现:clone 到没装 luban 的机器仍能 build。 |
| XDG-first 目录 | ~/.local/share/luban/(data)、~/.cache/luban/(cache)、~/.local/state/luban/(state)、~/.config/luban/(config)。XDG_* 环境变量优先。 |
| 零 UAC | 所有 luban-managed 文件落在用户可写目录。绝不弹 admin 提示。 |
| 单静态二进制 | 一个 luban.exe 包一切。不依赖 Python / MSI / Visual Studio。 |
完整 8 条架构不变量见 设计总览。
接下来
安装
luban 是一个静态链接的 Windows 单二进制(~3 MB)。无 installer,无 PATH 仪式,无需 admin。
前置
- Windows 10(1809+)或 Windows 11,目前仅 x64
- 网络(
luban setup要拉 ~250 MB 工具链)
就这。不需要 Python / Visual Studio / Chocolatey / Scoop。
安装方式
方式 A —— 直接下二进制
- 从 GitHub Releases 下载
luban.exe和luban-shim.exe - 放到任意位置,例如
%USERPROFILE%\bin\luban.exe - 接下来要么直接用全路径调,要么放到 PATH(方式 C 自动做这事)
方式 B —— 从源码编译
如果你已经有 C++ 工具链(MSVC / MinGW / Clang)+ cmake + ninja:
git clone https://github.com/Coh1e/luban.git
cd luban
cmake --preset release
cmake --build --preset release
:: build/release/luban.exe + luban-shim.exe
方式 C —— 单文件自举(推荐)
luban.exe setup :: 装 LLVM-MinGW 22 + cmake 4.3 + ninja 1.13 + mingit 2.54 + vcpkg 2026.03
luban.exe env --user :: rustup 风格 HKCU PATH 注入
之后任何新 shell 都能直接 cmake / clang / clangd / ninja / git / vcpkg。再也不用 source activate 脚本。
小贴士:
luban env --user之后,把luban.exe自身也复制到<data>\bin\,这样它也在 PATH 上:copy luban.exe %LOCALAPPDATA%\luban\bin\luban.exe copy luban-shim.exe %LOCALAPPDATA%\luban\bin\luban-shim.exe
验证
luban doctor
期望看到:
→ Canonical homes
✓ data C:\Users\you\.local\share\luban
...
→ Installed components
✓ cmake 4.3.2
✓ llvm-mingw 20260421
✓ mingit 2.54.0
✓ ninja 1.13.2
✓ vcpkg 2026.03.18
→ Tools on PATH
✓ clang C:\Users\you\.local\share\luban\bin\clang.exe
...
如果某个工具显示 (not found)——先开个全新终端,env 改动只对新进程生效。
装在哪
%LOCALAPPDATA%\luban\
toolchains\
cmake-4.3.2-x86_64\ # cmake.exe + 共享数据
llvm-mingw-20260421-x86_64\ # clang/clang++/clangd/lld + sysroot
ninja-1.13.2-x86_64\
mingit-2.54.0-x86_64\
vcpkg-2026.03.18-x86_64\
bin\ # rustup 风格 shim 目录(env --user 后在 PATH 上)
cmake.cmd / cmake.ps1 / cmake / cmake.exe ...
env\ # 生成的 activate 脚本
%USERPROFILE%\.cache\luban\downloads\
%USERPROFILE%\.local\state\luban\
%USERPROFILE%\.config\luban\
详细 XDG 解析见 English: paths reference。
升级
luban self update :: 自动从 GH releases 拉最新版,校 SHA256,原子换
卸载
luban self uninstall --yes :: 完全清理(含工具链)
luban self uninstall --yes --keep-data :: 留工具链,仅清 luban 自己 + PATH 注入
luban 从不写 <data> / <cache> / <state> / <config> / HKCU\Environment 之外的位置。self uninstall 是逆向操作。
快速上手
从全新 Windows 11 到一个链了 vcpkg 库的 hello-world C++ 项目,五条命令搞定。
前置
- Windows 10/11 x64
luban.exe在某处可达
五条命令
luban setup :: 装工具链(~250 MB,~3 分钟)
luban env --user :: 注 user PATH(一次性)
:: --- 此时关掉当前终端,开新终端 ---
luban new app hello :: 脚手架 + 自动 build;clangd 即用
cd hello
luban add fmt :: 编辑 vcpkg.json + 重生成 luban.cmake
luban build :: cmake 通过 vcpkg 自动取 fmt + 编 + 链
build\default\src\hello\hello.exe
应该看到:
hello from hello!
现在改 src/hello/main.cpp 用上 fmt:
#include <fmt/core.h>
#include <fmt/color.h>
int main() {
fmt::print(fg(fmt::color::cyan), "hello from luban via vcpkg-installed fmt!\n");
return 0;
}
再 build 跑:
luban build
build\default\src\hello\hello.exe
在编辑器里打开
用 Neovim 或 VS Code 打开。两边都自动检测:
- 项目根的
compile_commands.json(luban build后产生)→ clangd 直读 - PATH 上的
clangd.exe→ LSP 立即就位
clangd 启动后,对 fmt:: 应有自动补全 + 跳定义到 vcpkg 装的 fmt 头。
刚刚发生了什么
hello/
├── CMakeLists.txt ← 用户拥有,4 行:project + include + register_targets
├── luban.cmake ← luban 生成;find_package(fmt) + target_link_libraries
├── vcpkg.json ← {"name":"hello", "version":"0.1.0", "dependencies":["fmt"]}
├── vcpkg-configuration.json ← baseline 锁定到具体 vcpkg commit(可重现性保证)
├── CMakePresets.json ← Ninja generator + vcpkg toolchain(VCPKG_ROOT 在时启用)
├── compile_commands.json ← 从 build/ 复制过来给 clangd
├── .gitignore
├── .clang-format
├── .clang-tidy
├── .vscode/
└── src/
└── hello/
├── CMakeLists.txt ← 用户拥有,2 行
└── main.cpp
你没打开 CMakeLists.txt。没写 find_package 调用。没读 vcpkg manifest mode 文档。luban 全替你做了。
接下来
- 日常使用循环 — 第二周长什么样
- 设计总览 — luban 为什么长这样
- English: full command reference →
- English: workflows →
日常使用循环
一次性配置(luban setup + luban env --user)做完后,一周 luban 用法是什么样。
周一:起新项目
luban new app weekly-experiment
cd weekly-experiment
nvim src/weekly-experiment/main.cpp # clangd 立即自动补全
luban new 末尾自动跑 luban build 一次,compile_commands.json 已落盘,clangd 一启动就能吃。
周二:要个 JSON parser
luban add nlohmann-json
luban build # vcpkg 拉它 + 编 + cache(首次 ~30s)
luban add 改了 vcpkg.json 和重写了 luban.cmake。你没打开 CMakeLists.txt。没查 cmake target 名(是 nlohmann_json::nlohmann_json,luban 内置映射表替你处理)。
周三:拆出一个库
main.cpp 越来越长。把 parsing 逻辑抽成静态 lib:
luban target add lib parser
得到 src/parser/{parser.h, parser.cpp, CMakeLists.txt}。改这三个文件。然后 src/weekly-experiment/CMakeLists.txt 加一行:
target_link_libraries(weekly-experiment PRIVATE parser)
luban build —— 两个 target 全编、exe 链 lib。
周四:写测试
luban add catch2
luban target add exe test-parser
把 test-parser 接到 parser + Catch2::Catch2WithMain。剩下都是标准 cmake。
周五:同事 clone 你的项目
同事的机器有 cmake + ninja + vcpkg,但没装 luban:
git clone <你仓库>
cd weekly-experiment
cmake --preset default
cmake --build --preset default
直接 work。luban.cmake 进了 git,cmake 直 include;vcpkg 用同 baseline 拉同版本依赖。同事根本不知道 luban 存在。
每天改了什么
| 天 | luban 改的文件 | 你改的文件 |
|---|---|---|
| 周一 | vcpkg.json luban.cmake(初始化) | 无 |
| 周二 | vcpkg.json luban.cmake | main.cpp |
| 周三 | luban.cmake(加 LUBAN_TARGETS)+ 新建 src/parser/* | main.cpp / src/weekly-experiment/CMakeLists.txt(+1 行)/ parser.{h,cpp} |
| 周四 | vcpkg.json luban.cmake + 新建 src/test-parser/* | 各种 |
| 周五 | (无——只跑 cmake) | (无——只跑 cmake) |
你整个周里没打开根 CMakeLists.txt 一次。没写 find_package 调用。没读 vcpkg manifest mode 文档。
什么时候应该跳出 luban
luban 擅长 80% 场景。下面这些让你直接写 cmake,不要走 luban:
- 每文件特殊编译 flag:写在
src/<target>/CMakeLists.txt,luban_apply()之后 - 非 vcpkg 的依赖:
include(luban.cmake)之后写自己的find_package(MyLib),luban 不会跟你抢 - 罕见 generator(Make / VS / Xcode):改
CMakePresets.json(用户拥有) - header-only lib target:在
src/<lib>/CMakeLists.txt把add_library(<name> STATIC ...)改成add_library(<name> INTERFACE)自行调整 - 非 vcpkg 来源的依赖:跳过
luban add,自己写find_package/ FetchContent
退出方式:把根 CMakeLists.txt 里的 include(luban.cmake) 删掉,整个项目变成普通 cmake 工程。
设计总览
一页讲清:luban 为什么长这样?
如果你只读一篇架构文档,读这篇。其他架构文章(philosophy two-tier-deps why-cmake-module roadmap)放大讲单个话题——这篇是综合。
luban 解决的问题
Windows 上的现代 C++ 项目要用户自己拼装:
- 工具链(LLVM-MinGW / MSVC / mingw-w64)
- cmake —— 项目级 meta-build
- ninja —— 实际构建器
- clangd —— 编辑器 LSP
- vcpkg —— 包管理
- git —— vcpkg 要它,自己也用
- 一堆胶水:
compile_commands.json/find_package/target_link_libraries/ triplets /CMakePresets/CMAKE_TOOLCHAIN_FILE/vcpkg-configuration.jsonbaseline 锁定 /target_include_directories(... PUBLIC)/ 等等
每件事是半天 yak-shaving。整套堆起来要好几天,每台新机器要重来。
luban 的工作:把这些变成两条一次性命令 + 一个很小的项目内循环:
luban setup :: ~3 min,~250 MB 工具链
luban env --user :: 注 user PATH(rustup 风格)
:: 开新终端
luban new app foo
cd foo
luban add fmt :: 编辑 vcpkg.json + luban.cmake
luban build :: vcpkg 在 cmake configure 期间自动拉 fmt
用户全程不打开 CMakeLists.txt,不写 find_package,不读 vcpkg manifest mode 文档。
luban 不是
- 不是构建系统。 cmake + ninja 仍然干构建。
- 不是包管理器。 vcpkg 仍然解析 + 构建 C++ 包。
- 不是 fork / 替换。 luban-managed 项目仍然是标准 cmake 项目——
clone 到没装 luban 的机器,
cmake --preset default && cmake --build --preset default照样能 build。
luban 是带主见的胶水:把对的工具用对的方式粘起来,给初学者默认就走在轨道上的体验。
8 条架构不变量
锁死的设计决策。打破任何一条都让 luban 的根本契约失效:
1. cmake 永远是项目主角
luban 不发明 IR。luban 不发明新 manifest 格式取代 CMakeLists.txt。
luban 写一个标准 cmake module(luban.cmake),用户代码 include() 即用。
删掉一行 luban 就退出。
2. luban.cmake 进 git
"辅助"二字的整个意义就是项目得在没有 luban 时仍能 build。luban.cmake
是普通 cmake module,进 git 跟随项目,永远可重现。
3. 不发明新 manifest
项目依赖在 vcpkg.json(vcpkg 自己的 schema)里。luban add 编辑它。没有
平行的 [deps] 字段在 luban.toml 里——那会造成双真相。
luban.toml 仅作为可选项目偏好存在(warning 等级、sanitizers、默认 preset、triplet)——
那些不适合放 vcpkg.json 的零碎东西。
4. luban.toml 是可选的
很多 luban 项目根本没有 luban.toml。只在用户真有偏好时才出现。默认值 cover 80% 场景。
5. XDG-first 路径,Windows 上也是
luban 尊重 XDG_DATA_HOME / XDG_CACHE_HOME / XDG_STATE_HOME /
XDG_CONFIG_HOME 和 LUBAN_PREFIX 总开关变量,优先于 %LOCALAPPDATA% /
%APPDATA% fallback。
让 container / CI / 多用户场景一行配置搞定,Linux port 时无惊喜。
6. 零 UAC
每个 luban-managed 文件落在用户可写目录。luban 永不写 Program Files、
永不碰 HKLM、永不弹 admin。luban env --user 只写 HKCU(当前用户)。
7. 单静态链接二进制
luban.exe 一个文件:release ~3 MB / debug ~32 MB(含 vendored miniz / json /
toml++ 单 header 三件)。无配套 DLL、无 Python、无 MSI。U 盘装得下、新 VM 拷过去就跑。
8. 二层依赖模型
| 层 | 内容 | 落点 | 谁管 |
|---|---|---|---|
| 系统层 | cmake / ninja / clang / clangd / lld / git / vcpkg | <data>/toolchains/ | luban setup |
| 项目层 | fmt / spdlog / boost / catch2 | <project>/vcpkg_installed/<triplet>/ | luban add(编辑 vcpkg.json) |
混淆是被拒绝的。luban add cmake 会报错,引导到 luban setup。系统工具
machine-wide 锁版本,项目库每项目独立 baseline。
为什么是这个形态(拒绝的备选)
备选 A:luban 自己的 DSL → lower 到 cmake
xmake / meson / build2 / premake 都走这条路。用户写 xmake.lua 或
meson.build,工具生成实际 build 文件。
为什么我们说 no:C++ 有几十年的 cmake 习惯。custom commands、 target-specific flags、conditional features——每个真实项目都会撞到 DSL 没覆盖的边界,fall through 到"反正还是要懂 cmake"。这时候 DSL 只是个 额外要学的东西,没真的把 cmake 藏住。更糟的是 DSL 与 cmake 的接缝很敌意 (eject 仪式、单向转换)。
备选 B:在 CMakeLists.txt 里用标记块原地编辑
像 code generator 插入 # >>> BEGIN luban / # <<< END luban 段。luban
拥有那些块;用户拥有其余。
为什么我们说 no:原地编辑很脆。用户在标记内手改的东西被覆盖。靠近 标记的改动跟重生成 race。边界处永远是混乱源。
luban.cmake 干净解决这事:luban 拥有整个文件,用户 include() 它,
边界是硬切。
备选 C:顶层一个统一 manifest
luban.toml 包一切——deps / scripts / profiles / workspace / toolchain pin。
Cargo 风格。
为什么我们说 no:vcpkg 的 vcpkg.json 已经存在,是 C++ 事实 manifest。
发明并行的 [deps] 段意味着维持双真相,两边永远 drift。让 vcpkg 拥有它的
schema、luban 通过 luban add 编辑它就好。
备选 D:luban-init.exe 自举安装器
rustup 风格:一个小 installer 下载 luban + 跑 setup。
为什么我们说 no:luban.exe 本来就是单文件下载。用户从 GH Releases
curl -O luban.exe 直接跑就好。bootstrapper 是无价值的中间层。改成
luban self update 和 luban self uninstall 把生命周期闭环放进同一个二进制,
uv 风格。
我们接受的 trade-off
- Windows-first。Linux/macOS port 是 M3+ 工作;短期失多平台卫生,长期 让我们在最需要帮助的平台(Windows C++ tooling)上快速迭代。
- 只 vendor 单 header。不消费系统
find_package(zlib)类。多 ~10MB 二进制 换"一个文件、随处能跑"。 .cmakemodule 模式假设用户会include(luban.cmake)。不 include 就没有 自动 find_package 魔法——掉进普通 cmake。这是设计,不是 bug。- curated pkg→target 表。
luban add内置 ~50 流行库的映射。未知 port 写find_package(<port>)但不自动 link,用户自己填 target 名。长尾未来通过 scrape vcpkg 的usage文件解决。
操作模型
luban 作为三层运作,每层都有稳定接口:
┌─ 用户 ─────────────────────────────────────────────────┐
│ luban CLI(16 verb,分 4 组) │
└──────────────────────────────────────────────────────┘
│ 编辑 vcpkg.json / luban.toml
│ 调 cmake / vcpkg
▼
┌─ luban-managed 产物 ──────────────────────────────────┐
│ luban.cmake ← luban 写;进 git │
│ <data>/bin/ ← shim 目录;在 user PATH 上 │
│ <state>/installed.json ← 组件 registry │
└──────────────────────────────────────────────────────┘
│ shell out to
▼
┌─ 外部工具 ────────────────────────────────────────────┐
│ cmake / ninja / clang / vcpkg / git │
│ (luban 下载 + 管理它们,但不侵入它们的 config │
│ —— 它们仍然是"现成"的) │
└──────────────────────────────────────────────────────┘
中间那层是 luban "辅助"性的体现:薄、稳、可 git 跟踪的 spec,桥接用户意图与 标准工具。抽掉中间层,下面那层仍然是能用的工具。
接下来读
- Design philosophy (英文版) →
- Two-tier dependency model (英文版) →
- Why no IR, why luban.cmake (英文版) →
- Roadmap (英文版) →
- 快速上手 —— 5 命令一遍走
命令清单
luban 共 16 个 verb,分 4 组。
工具链 / 环境(每台机器一次性)
| 命令 | 作用 |
|---|---|
luban setup → | 装 LLVM-MinGW + cmake + ninja + mingit + vcpkg |
luban env → | 显示 env 状态;改 activate 脚本;注册 HKCU PATH(rustup 风格) |
单项目(在项目目录里跑)
| 命令 | 作用 |
|---|---|
| [`luban new app | lib |
luban build → | cmake --preset && cmake --build;同步 compile_commands.json |
| [`luban target add | remove` →](https://luban.coh1e.com/commands/target.html) |
依赖管理(vcpkg.json + luban.cmake)
| 命令 | 作用 |
|---|---|
luban add <pkg>[@version] → | 编辑 vcpkg.json + 重生成 luban.cmake(自动 find_package + link) |
luban remove <pkg> → | luban add 反操作 |
luban sync → | 重读 vcpkg.json + luban.toml,重生成 luban.cmake |
luban search <pattern> → | 搜 vcpkg ports(包 vcpkg search) |
诊断 / 自治
| 命令 | 作用 |
|---|---|
luban doctor → | 报告目录、已装组件、PATH 上的工具 |
luban run <cmd> [args...] → | uv 风格透传执行;用 luban 工具链 env 跑 cmd |
luban which <alias> → | 打印 alias 解析到的绝对 exe 路径 |
luban describe [--json] → | dump 系统 + 项目状态(IDE / scripts 用) |
luban shim → | 重生成 <data>/bin/ shim(文本 + .exe;修复用) |
luban self {update,uninstall} → | 自更新二进制 / 完全卸载 luban |
全局 flag
放在子命令前:
| Flag | 作用 |
|---|---|
-V, --version | 打印 luban X.Y.Z 退出 |
-h, --help | 顶层帮助 |
-v, --verbose | 详细 log |
约定
- 幂等:每个命令都能安全重跑。
luban setup跳过已装组件、luban add替换已有 dep、luban target add拒绝同名 target。 - 原子文件写:每次配置 / manifest 写入走
tmp + rename,崩了就只剩 老文件或新文件,绝不会留半个文件。 - 退出码约定:
0成功,1运行失败(下载失败、cmake 出错),2用户错 (参数错、操作被拒绝)。 - log 走 stderr:
✓→!✗前缀的行进 stderr。stdout 留给机读输出 (比如compile_commands.json路径)。pipe stdout 干净。
详细单页参考
每个命令的 examples / flags / behavior 详细见 英文版命令参考。
英文完整文档站
中文版仅翻译了核心几页(简介、安装、快速上手、日常使用循环、 设计总览、命令清单)。完整内容在英文版上:
直链到英文站
| 类别 | 链接 |
|---|---|
| 完整命令参考(每个 verb 详细页) | https://luban.coh1e.com/commands/overview.html |
| 工作流(首次配置 / 加 vcpkg / 多 target / IDE / 复现) | https://luban.coh1e.com/workflows/first-run.html |
| 参考文档(XDG paths / luban.toml / vcpkg.json / luban.cmake) | https://luban.coh1e.com/reference/paths.html |
| 架构哲学(philosophy / two-tier-deps / why-cmake-module) | https://luban.coh1e.com/architecture/philosophy.html |
| Roadmap | https://luban.coh1e.com/architecture/roadmap.html |
| Doxygen 贡献者参考(C++ 源码层面) | https://luban.coh1e.com/api/ |
| Contributing 指南 | https://luban.coh1e.com/contributing.html |
中文版迭代会逐渐覆盖更多页——欢迎 PR 翻译你常用的章节。
反方向跳
- 在英文版页面上看到顶部的 🌐 中文版 → 链接可以回到本站