mcli - C/C++ 项目与包工作流工具
mcli 是面向多 target 的 C/C++ 项目、依赖、构建、测试和发布工作流工具。
它基于 Conan 管理包与构建环境,具体构建系统可以是 Meson、CMake 或其他
Conan recipe 支持的工具。MCLang 是可选的源码前端:需要时由 mcc
将 Python 方言生成 C++,再进入相同构建流程。
🚀 特性
- 项目创建: 从内置或第三方模板创建项目
- 依赖管理: 基于 Conan 的 C++ 依赖管理
- 交叉编译: 以
target为中心安装和使用交叉编译目标 - 模板系统: 内置项目模板和 stub 文件生成
📦 安装
pip install mcli-build
安装后使用 mcli 命令:
mcli --version
或从源码安装:
git clone https://gitcode.com/zjp99/mcli.git
cd mcli
pip install -e . --force-reinstall --no-deps
# 安装到全局环境
pip install -e . --break-system-packages --force-reinstall --no-deps
可选依赖:mcli 是通用构建工具,不强制绑定 MCLang 编译器。仅当项目需要将 Python 编译为 C++ 时,才需要安装 mcc:
pip install "mcli-build[mcc]"
安装故障排除
若曾用开发模式安装过 mcc(例如在 mcc 源码目录执行过 pip install -e .),当前环境里可能残留无 RECORD 的 mclang-compiler 安装,导致后续无法正常卸载或升级,并出现 Cannot uninstall mclang-compiler None (no RECORD file)。
建议:
- 先不卸载,直接覆盖安装(推荐):
pip3 install --ignore-installed --no-deps mclang-compiler --break-system-packages
- 若仍异常,可手动删除后再装:用
pip3 show mclang-compiler --break-system-packages查看Location,在该路径的site-packages下删除mclang_compiler*与mcc*相关目录,再执行pip3 install "mcli-build[mcc]" --break-system-packages。
从 mclang-cli 0.1.x 迁移
mcli-build 使用 ~/.mcli 保存 target、compiler、sysroot、toolchain 和全局配置。
升级后执行一次:
mcli migrate home --dry-run
mcli migrate home
mcli target list
迁移采用同文件系统内的目录重命名,不会复制大型 target;过渡期会保留
~/.mclang -> ~/.mcli 兼容链接。MCLANG_HOME 和 MCLANG_CONAN 等旧环境变量
仍可读取,新配置应使用 MCLI_HOME 和 MCLI_CONAN。
🔧 使用方法
mcli 项目管理
# 创建新项目
mcli create sample-core --template lib
# 构建项目
mcli build
# 构建并运行
mcli run
# 运行测试
mcli test
# 依赖管理(使用 Conan)
conan install . --user=dev
# 发布包
mcli publish --channel stable -bt release
# 安装交叉 target
# target 表示产物运行的平台;宿主机和交叉编译器由 mcli 自动处理
mcli target add linux-arm64
# 查看 target
mcli target list
mcli target info linux-arm64
# 按 target 构建/测试
mcli build --target linux-arm64
mcli test --target linux-arm64
# 迁移旧工具链
mcli migrate-toolchains --force
# 配置管理
mcli config
mcli config default_target
mcli config set default_target linux-arm64
mcli 在首次执行构建、测试或发布等 Conan 工作流时,会自动注册
mclang_public 公共只读仓库,并以匿名方式访问。如果相同 URL 已经以
mclang 等旧名称存在,mcli 会直接复用;用户如需认证访问,可按 Conan
标准命令自行登录,mcli 不会覆盖已有凭据。
项目配置
mcli.toml 是 mcli 的项目级配置文件,用于声明包信息、依赖、workspace、默认
target 和 target/profile 构建选项。完整字段见
mcli.toml 配置参考,多子项目仓库见
Workspace 与子项目。mds.* 显式引用和依赖 provider 已可用;
mds/service.json 继续兼容旧项目:
{
"name": "sample-core",
"version": "1.0.0",
"type": "library",
"author": "Your Name",
"license": "Mulan PSL v2",
"description": "Project description",
"dependencies": {
"build": [
{"conan": "boost/[>=1.87.0]"}
]
},
"mclang": {
"type": "native",
"stubs": {
"dir": "stubs",
"packages": ["mc", "gtest"]
}
}
}
编译能力声明
原生依赖或项目可以在 mclang.compile_capabilities 中声明当前 target 的
布尔编译事实:
{
"name": "renderer",
"version": "1.2.0",
"mclang": {
"compile_capabilities": {
"renderer.vulkan": {
"value": true,
"description": "Vulkan renderer is present"
}
}
}
}
每项必须同时提供布尔 value 和非空 description;capability 名称、owner
和配置来源会进入 mcc 的 schema 与生成缓存身份。需要按 target 改变取值时,在
已有 targets 覆盖中提供完整的 mclang.compile_capabilities 映射。
发布包会在 res/mclang/compile-capabilities.json 保存构建该二进制时使用的
完整、带指纹配置。下游构建合并所有依赖清单;target 不一致、清单损坏、同名定义
不一致或同名值冲突都会在 mcc 建立模块依赖图之前失败,不按依赖加载顺序覆盖。
依赖 options 与偏好声明
跨 target/profile 的包选项推荐写在项目根目录的 mcli.toml,例如
[target.hi1711.options]。命令行 -o 可作最高优先级的临时覆盖;provider 声明的
兼容约束除外,命令行给出不同值时会直接报冲突。
完整格式与 hi1711 示例见
mcli.toml 配置参考。
普通依赖写在 mcli.toml 的 [dependencies],测试依赖写在
[test-dependencies]。只要 mcli.toml 存在,最终配置结构就完全由 TOML 描述,
不会回退或隐式合并 service.json。需要复用既有配置时,可以用 mds.version、
mds.dependencies.build.<package> 等只读引用逐项取值;只有没有 mcli.toml 的
旧项目才进入完整的 service 兼容模式。
Workspace 成员自动提供自己的包名、版本和本地路径;其他成员通过
{ workspace = true } 声明对它的依赖,无需在根目录重复登记 ref + path。
需要版本约束时可在消费成员中直接写正式 Conan ref,mcli 会校验并优先使用
同名本地成员。[workspace.dependencies] 用于集中维护共享的外部依赖,
其中的定义也只有被成员显式选择时才进入依赖图。
[options] 及 profile/target 下的 options 表示 Conan 包 options;
依赖展开写法中的 traits 表示 self.requires() 的依赖特性,
例如 transitive_headers 和 visible。两者不是同一类配置:
[options]
"sample-core/*:shared" = true
[dependencies]
sample-core = { ref = "sample-core/[>=1.0.0]@example/stable", traits = { transitive_headers = true, visible = true } }
同一项逻辑依赖需要在不同 target 选择不同实现时,可以在 [providers] 中声明
多个实现,再通过 { provider = "..." } 显式选择。Provider 会解析成真实 Conan
运行时依赖、构建工具依赖和兼容 options,并参与导出 recipe、package ID、构建
指纹和 lockfile;它不是只在 mcli 本地生效的链接替换。完整规则见
mcli.toml 配置参考。
📝 命令参考
create - 创建项目
mcli create <project-name> [options]
选项:
-t, --template TYPE 项目模板 (bin/lib, 默认: bin)
--list 列出所有可用模板
build - 构建项目
mcli build [options]
选项:
--bt, --build-type TYPE 构建类型 (debug/release, 默认: debug)
--target TARGET 目标平台 (如 linux-arm64,用于交叉编译)
-j, --jobs NUM 并行构建任务数
-v, --verbose 详细输出
run - 构建并运行
mcli run [options] [-- <args>]
选项:
--bt, --build-type TYPE 构建类型
--target TARGET 构建 target (如 linux-arm64)
--name TARGET_NAME 要运行的可执行产物名称
-- 分隔符,后面传递给程序的参数
示例:
mcli run # 使用上次构建配置运行
mcli run -bt release # 指定构建参数运行
mcli run --target linux-arm64 # 先按交叉 target 构建,再运行产物
mcli run -- --arg1 --arg2 # 传递参数给程序
test - 运行测试
mcli test [test_names] [options] [-- <framework-args>]
选项:
--bt, --build-type TYPE 构建类型 (debug/release)
--target TARGET 目标平台 (如 linux-arm64)
-j, --jobs NUM 并行构建任务数
-v, --verbose mcli 详细输出(CTest -V)
-- 分隔符,后面传递给测试框架的参数
使用 -- 分隔符:
-- 之前:mcli 参数(测试名称用于 CTest -R 筛选)
-- 之后:直接转发给测试框架(绕过 argparse 识别)
示例:
mcli test # 运行所有测试
mcli test mcc_gtests # 运行指定测试
mcli test mcc_gtests mcc_pytests # 运行多个测试
mcli test mcc_pytests -- test_lambda.py # 转发参数给测试框架
mcli test mcc_pytests -- -v # pytest 详细输出
mcli test mcc_gtests -- --gtest_filter=*Arc* # GoogleTest filter
mcli test -v mcc_pytests -- -v # mcli 和 pytest 都详细输出
依赖管理
# 刷新依赖(更新 stub 文件)
mcli reload
# 刷新稳定版本依赖
mcli reload --channel stable -bt release
# 使用 Conan 安装依赖
conan install . --user=dev
# 查看已安装的包
conan list
target - Target 管理
mcli target list
mcli target info <target>
mcli target add <target>
mcli target remove <target>
示例:
mcli target add linux-arm64
mcli build --target linux-arm64
mcli test --target linux-arm64
Target Manifest
target 是用户唯一需要理解的交叉编译安装单位。一个 target manifest 描述:
- 该平台使用哪个 compiler
- 该平台使用哪个 sysroot
- 对应的目标 triple / cflags / ldflags
官方 linux-arm64 预设生成的程序直接使用 hi1711 的系统运行库:解释器路径为
/lib64/ld-linux-aarch64.so.1,C++ 使用旧版 libstdc++ dual ABI。部署时无需复制
target 自带的 libstdc++.so.6 或 libgcc_s.so.1,也没有额外兼容层。
mcli target add linux-arm64
示例 manifest:
[target]
name = "linux-arm64/gcc9"
aliases = ["linux-arm64"]
platform = "linux-aarch64"
triple = "aarch64-linux-gnu"
[target.compiler]
name = "bmc-sdk-compiler"
source = "./artifacts/bmc-sdk-compiler.tar.gz"
type = "gcc"
tool_prefix = "aarch64-target-linux-gnu"
[target.sysroot]
name = "bmc-sdk-sysroot"
source = "./artifacts/bmc-sdk-sysroot.tar.gz"
只配不带(用户自行安装编译器)
当编译器已通过系统包管理器安装时,manifest 可以声明版本约束而不打包编译器:
[target]
name = "linux-arm64/local-hcc"
aliases = ["linux-arm64"]
cflags = ["-Os", "-ffunction-sections"]
ldflags = ["-Wl,--gc-sections"]
[target.compiler]
type = "gcc"
source = "system"
tool_prefix = "hcc-arm64le"
min_version = "7.0"
max_version = "8.0"
mcli 会从 PATH 中查找 hcc-arm64le-g++,校验版本是否满足约束(7.0 <= version < 8.0),版本不满足时输出警告。
若组织内已配置 catalog,mcli target add <target> 可直接省略 --manifest。mcli 也内置了 openUBMC hi1711 等常见 target 模板(位于 targets/),支持自动检测 SDK 布局(/opt/ 或 ~/)和变量替换,mcli target add hi1711 一键完成导入。
兼容迁移
旧的 toolchain / compiler / sysroot 机制已经退出主使用路径;如果本机还有历史资产,请用迁移命令一次性转成 target。
mcli migrate-toolchains --force
config - 配置管理
mcli config # 查看所有配置
mcli config <key> # 查看特定配置项
mcli config set <key> <value> # 设置配置项
示例:
mcli target default linux-arm64 # 设置默认 target
mcli config default_target # 查看默认 target
publish - 发布包
mcli publish [options]
选项:
--user USER Conan 包所有者;不指定时按 stage 联动推导默认值
(stage=dev → openubmc.dev,其他 → openubmc),
可被环境变量 MCLI_DEFAULT_USER 整体覆盖;
传空串 (--user "") 表示「裸发,不带 user/channel」
--stage STAGE 发布阶段 (dev/rc/stable);与 bingo 的 --stage 对齐;
不指定时取 mcli 默认(dev,可被环境变量
MCLI_DEFAULT_STAGE 覆盖)
--channel CHANNEL --stage 的别名;二者不可同时指定
--bt, --build-type TYPE 构建类型 (debug/release)
-r, --remote REMOTE 上传到指定远端仓库(不指定则只导出到本地缓存)
--force 强制覆盖远端已存在的包
-o KEY=VALUE 透传 conan -o 选项,build/export-pkg 阶段都生效
(依赖项请用 pkg/*:opt 形式,例如 -o 'sample-runtime/*:enable_feature=True')
Conan 包版本格式: {name}/{version}@{user}/{stage}(默认)
或 {name}/{version}(裸发模式)
示例:
mcli publish # 默认 stage=dev → @openubmc.dev/dev:sample-core/1.0.0@openubmc.dev/dev
mcli publish --stage stable # 显式发到 stable → @openubmc/stable:sample-core/1.0.0@openubmc/stable
mcli publish --stage rc # 发到 rc → @openubmc/rc
mcli publish -r openubmc_sdk # 默认 dev,并上传到指定远端
mcli publish --user openubmc --stage dev # 显式覆盖:发到 @openubmc/dev(绕过 stage 联动)
mcli publish --user myorg --stage dev # 命令行同时覆盖 user 和 stage
mcli publish --user "" # 裸发:sample-core/1.0.0(不带 user/channel)
# 通过环境变量定制团队默认(写到 ~/.zshrc 或 CI 脚本里):
export MCLI_DEFAULT_USER=myteam # 整体覆盖默认 user(绕过 stage 联动)
export MCLI_DEFAULT_STAGE=rc # 默认 stage 改成 rc
设计原则:项目代码不绑死「会被发到哪里」 + 默认 user 跟 stage 联动
user/channel 都是发布行为的属性,不是项目代码的属性。所以
service.json 里不再支持 publish.user 这类配置 —— 否则一个项目的源码
仓库会硬编码 conan 仓库归属,团队 fork、镜像私服、个人实验都得改源码。
默认 user 与 stage 联动(CLI / env 都未指定时):
| stage | 默认 user | 含义 |
|---|---|---|
dev |
openubmc.dev |
本地/开发机构建,与 bingo conan create --user openubmc.dev 约定对齐,bingo 集成测试可直接 cache-hit mcli 发布的 binary |
rc / stable / 其他 |
openubmc |
远端 CI 出的正式产物归属 |
mcli 解析这两类元信息:
| 元信息 | 解析顺序 |
|---|---|
user |
CLI --user > env MCLI_DEFAULT_USER > 按 stage 推导 |
stage / channel |
CLI --stage/--channel > env MCLI_DEFAULT_STAGE > 内置 dev |
- 日常
mcli publish(不带任何参数)→@openubmc.dev/dev,跟 bingo 本地构建包同 ref,集成测试直接命中 - 想让远端 stable 仓库(依赖写的是
@openubmc/stable)拿到本机改动时, 显式mcli publish --stage stable(自动用@openubmc/stable,不带 .dev 后缀) - 别的团队默认 user 不是 openubmc:
export MCLI_DEFAULT_USER=myteam整体覆盖联动逻辑
解析优先级(统一两层 + stage 联动)
- stage/channel:CLI
--stage/--channel> envMCLI_DEFAULT_STAGE> 内置dev - user:CLI
--user> envMCLI_DEFAULT_USER> 按 stage 推导 (stage=dev →openubmc.dev,其他 →openubmc) - 命令行同时给出
--stage和--channel→ 报错(避免歧义) - CLI
--user ""→ 裸发模式(不带 user/channel),仅本地纯实验用
🏗️ 架构
mcli/
├── mcli/ # CLI 工具核心
│ ├── commands/ # 命令实现
│ │ ├── create.py # 项目创建
│ │ ├── build.py # 构建管理
│ │ ├── deps.py # 依赖管理
│ │ └── publish.py # 包发布
│ ├── toolchain/ # 工具链管理(内部模块)
│ │ ├── base.py # 工具链基类
│ │ ├── zig.py # Zig 工具链
│ │ ├── system.py # 系统工具链 (GCC/Clang)
│ │ └── manager.py # 工具链管理器
│ ├── package/ # 包管理(内部模块)
│ │ ├── manager.py # 包管理器
│ │ ├── conan.py # Conan 集成
│ │ └── abi.py # ABI 管理
│ ├── target/ # target 模型(主入口)
│ ├── template.py # 模板引擎(内置,支持 {{ }} 和 {% %} 语法)
│ ├── paths.py # 路径工具
│ ├── logging.py # 日志系统
│ └── config.py # 配置管理
└── templates/ # 项目模板
├── conanbase.py.mct # Conan 基类模板(自动生成到用户项目)
├── bin/ # 可执行程序模板
├── lib/ # 库项目模板
└── toolchain/ # 工具链配置模板
设计说明:
mcli/包含 CLI 的所有核心代码template.py是内置的模板引擎,支持 {{ }} 和 {% %} 语法target/是交叉编译主入口;toolchain/退回为内部兼容层templates/conanbase.py.mct是 Conan 基类模板,mcli build 时自动生成到用户项目目录- 用户项目的
conanfile.py通过from conanbase import ConanBase导入生成的基类 templates/存放项目模板文件
📚 文档
🔌 依赖关系
mcli 依赖于以下组件:
- conan: C++ 包管理器(>= 2.0.0)
- meson / ninja: 构建系统支持
可选依赖:
- mclang-compiler (
mccextra): Python -> C++ 编译器,仅 MCLang 项目需要(pip install "mcli-build[mcc]")
构建系统:mcli 使用 Conan 进行依赖管理和构建,用户可在项目的 conanfile.py 中选择具体的构建工具(CMake、Meson 等)。
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📄 许可证
Mulan PSL v2 - 详见 LICENSE 文件
Metadata
Release files for mcli-build 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcli_build-0.3.0.tar.gz | 292.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcli_build-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 564.9 kB
Release files / mcli_build-0.3.0.tar.gz
| Download URL | mcli_build-0.3.0.tar.gz |
|---|---|
| Size | 292.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b79f8f2f47126f72b5cf0b179c36663484e9053d880064a598a957c3830c289e
|
|
BLAKE2b-256 checksum How to use checksums |
6e65f2cfe8210e5dc98ab2c18df85a52b87988ad8792552b1eebd81a32dae353
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / mcli_build-0.3.0-py3-none-any.whl
| Download URL | mcli_build-0.3.0-py3-none-any.whl |
|---|---|
| Size | 272.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2235d58f4fd72ad9c254d46eba21914ad9c684491dbe4cffe151a5779e9e4789
|
|
BLAKE2b-256 checksum How to use checksums |
45d8fc97c6893ff9fdc71183bd1551097d5a026c63ec28c6570021cbaaad094b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|