mcli:基于 MCLang 工具链的命令行项目管理工具

可用于 MCLang 项目的创建、构建、依赖管理及工具链配置。提供项目模板生成、多工具链支持(Clang、GCC、Zig)、Conan 依赖管理和包发布等核心功能。【此简介由AI生成】

Branch2Tags0
FilesLast commitLast update
9 months ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
9 months ago
2 months ago
1 month ago
1 month ago
9 months ago
2 months ago
9 months ago
1 month ago

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)。

建议:

  1. 先不卸载,直接覆盖安装(推荐):
    pip3 install --ignore-installed --no-deps mclang-compiler --break-system-packages
    
  2. 若仍异常,可手动删除后再装:用 pip3 show mclang-compiler --break-system-packages 查看 Location,在该路径的 site-packages 下删除 mclang_compiler* 与 mcc* 相关目录,再执行 pip3 install "mcli-build[mcc]" --break-system-packages。

🔧 使用方法

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

# OpenUBMC 初始化环境;下载平台不可达时自动使用 wheel 内置 manifest
mcli target add openubmc
mcli target add openubmc-hi1711

# 查看 target
mcli target list
mcli target info linux-arm64
mcli target group list
mcli target group info openubmc-arm64

`mcli target list` 默认显示当前 host 在 target catalog 中支持的目标,并分为“已安装”和
“未安装”两个区域。圆括号中的标签由 catalog 配置,用于帮助识别 target;`[default]` 和
`[host]` 只表示本机状态。`[default]` 一定属于已安装 target;首次安装的 target 自动成为
默认项。列表不会探测或导入未配置的 host 编译器;用户通过自定义 manifest 导入的工具链也会
在已安装条目中显示。构建摘要分别显示 target 的简短名称、canonical preset、目标 platform 和内部 toolchain。

全新环境中首次执行 `mcli build`/`test`/`publish` 时,命令行指定或项目
`mcli.toml` 配置的 target 若尚未注册,会先用内置 catalog 清单按需准备:只使用
本机可检测的 wheel 模板与 `~/.mcli/catalog/targets/` 组织级模板,不隐式下载远端
工具链,也不改动全局默认 target;被中断的安装重试即可自愈。

# 按 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-group 和 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-x86_64/local-gcc9"
aliases = ["linux-x86_64"]
platform = "linux-x86_64"
triple = "x86_64-linux-gnu"

[target.compiler]
type = "gcc"
source = "system"
candidates = ["x86_64-linux-gnu-g++-9", "g++-9", "g++"]
min_version = "9.0"
max_version = "10.0"

candidates 是按顺序尝试的命令名或绝对路径。mcli 通过 PATH 查找命令,并校验实际 版本和 target triple;不满足约束时继续尝试下一项,全部不匹配才终止安装。安装成功后, 解析出的编译器真实绝对路径会写入本机 target manifest,后续构建不受 PATH 顺序变化影响。

source = "system" 必须声明 candidates;缺失时 manifest 无效。工具链归档仍可使用 tool_prefix 指定归档内部的编译器前缀。

若组织内已配置 catalog,mcli target add <target> 可直接省略 --manifest。mcli-build wheel 也在 mcli/targets/ 中内置了 OpenUBMC target 模板,支持自动检测 SDK 布局(/opt/ 或 ~/)和变量替换,mcli target add openubmc-hi1711 可在下载平台不可达时完成导入;旧名称 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 > env MCLI_DEFAULT_STAGE > 内置 dev
  • user:CLI --user > env MCLI_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 (mcc extra): Python -> C++ 编译器,仅 MCLang 项目需要(pip install "mcli-build[mcc]")

构建系统:mcli 使用 Conan 进行依赖管理和构建,用户可在项目的 conanfile.py 中选择具体的构建工具(CMake、Meson 等)。

🤝 贡献

欢迎提交 Issue 和 Pull Request!

📄 许可证

Mulan PSL v2 - 详见 LICENSE 文件

Introduction

可用于 MCLang 项目的创建、构建、依赖管理及工具链配置。提供项目模板生成、多工具链支持(Clang、GCC、Zig)、Conan 依赖管理和包发布等核心功能。【此简介由AI生成】

Customize your domain