跳转到内容

Xmake v3.1.1 发布,Addons 扩展 Xmake 自身

在此版本中,我们新增了 addons 扩展支持。上个版本我们重构了 xmake plugin,让插件可以像包一样分发,而 addons 把这件事做得更彻底:一个 addon 不仅可以携带插件,还能携带规则、工具链、工程模板、lua 模块和 includes 文件,相当于一整套开发工具包,一条命令就能装好,也可以由工程声明后自动安装。

此外,我们还新增了 package.host.install_locally 策略和一批新的交叉编译架构,重构了模板的分发方式,并且新增了 BSD 上的 pkg 包管理器支持。

新特性介绍

Addons 扩展支持

我们平时用的包,提供的是程序要链接的库,而 addon 扩展的是 xmake 本身,给构建工具增加新的能力。

一个 addon 可以提供什么

下面这几类扩展内容都是可选的,addon 提供哪些就带哪些:

目录提供的能力使用方式
plugins/新的 xmake 命令xmake hello
rules/构建规则add_rules("@addon/my-addon/app")
toolchains/工具链set_toolchains("@addon/my-addon/mycc")
templates/工程模板xmake create -t myapp
modules/可导入的 lua 模块import("@addon.my-addon.utils")
includes/可包含的配置片段includes("@addon/my-addon/board")

这些扩展内容都是带命名空间的,统一通过 @addon/<name>/... 来引用,所以两个 addon 即使提供了同名的规则也不会冲突。只有插件和模板是例外,因为命令名和模板 id 是全局的,如果安装的 addon 会覆盖掉别的 addon 的命令,xmake 会直接拒绝。

addon 的管理

sh
$ xmake addon --install my-addon                         # 从仓库索引安装
$ xmake addon --install github:myrepo/my-addon           # 从 github 安装,支持 `#branch`
$ xmake addon --install https://github.com/user/repo.git # 从任意 git url 安装
$ xmake addon --install /path/to/my-addon                # 从本地目录安装

$ xmake addon --list
$ xmake addon --search my
$ xmake addon --remove my-addon
$ xmake addon --upgrade

在工程里声明

工程也可以直接声明自己需要哪些 addon,这样别人克隆下来就不用手动安装了,加载工程的时候,xmake 会自动把缺失的 addon 拉下来:

lua
add_addons("my-addon 1.0.x")

includes("@addon/my-addon/board")

target("hello")
    add_rules("@addon/my-addon/app")
    add_files("src/*.c")

最终解析出来的版本会记录到 xmake.lua 同级目录下的 xmake-addons.lock 中,保证团队里每个人构建时用的都是同一批版本。

编写 addon

一个 addon 其实就是一个目录,一份 manifest 加上若干扩展目录,不需要编译,也不需要注册:

my-addon/
├── addon.lua
└── src/
    ├── plugins/hello/
    ├── rules/app/
    ├── toolchains/mycc/
    ├── modules/
    ├── includes/board/
    └── templates/c/foo/
lua
-- addon.lua
addon("my-addon")
    set_homepage("https://github.com/me/my-addon")
    set_description("这个 addon 的功能描述")
    set_license("Apache-2.0")
    set_sourcedir("src")
    add_deps("serial-tools")

addon 的名字是在 manifest 里自己定的,跟仓库名、跟分发它的包名都没有关系。而在 addon 内部,引用自己的东西统一用 @self

lua
import("@self.private.board")

开发的时候,可以直接从本地目录安装来测试,走的是和用户完全一样的安装流程:

sh
$ xmake addon --install .
$ xmake addon --remove my-addon

提交到 xmake-repo

addon 放在 addons/<首字母>/<name>/xmake.lua 下,和 C/C++ 包平级,版本管理、sha256 校验、依赖、镜像下载这些包已有的能力,addon 都可以直接复用:

lua
package("my-addon")
    set_kind("addon")
    set_homepage("https://github.com/me/my-addon")
    set_description("这个 addon 的功能描述")
    set_license("Apache-2.0")

    add_urls("https://github.com/me/my-addon/archive/refs/tags/$(version).tar.gz",
             "https://github.com/me/my-addon.git")
    add_versions("v1.0.0", "<sha256>")

    add_deps("serial-tools", {kind = "addon"})

    on_test(function (package)
        assert(package:has_addon({rules = "app", toolchains = "mycc"}))
    end)

相关接口

addon 相关的接口不多,主要就是下面三个地方:

lua
-- 1. 在 xmake.lua 中,声明工程需要哪些 addon
add_addons("my-addon 1.0.x")
lua
-- 2. 在 addon.lua 中,声明这个 addon 自身的信息
addon("my-addon")
    set_description("...")      -- `xmake addon --list` 会显示
    set_homepage("...")
    set_license("Apache-2.0")
    set_sourcedir("src")        -- 仓库内存放扩展目录的根目录
    add_deps("serial-tools")    -- 依赖的其他 addon
    add_globalmodules("detect.tools.find_mytool")   -- 只有需要被 xmake 按名字查找的模块才用得上
lua
-- 3. 在 xmake-repo 的包配置中,声明它如何分发
package("my-addon")
    set_kind("addon")
    add_versions("v1.0.0", "<sha256>")
    add_deps("serial-tools", {kind = "addon"})
    on_test(function (package)
        assert(package:has_addon({rules = "app", toolchains = "mycc"}))
    end)

如果要在脚本里查询运行期的信息,可以用 core.package.addon 模块:

lua
import("core.package.addon")

addon.owner()          -- 当前脚本属于哪个 addon
addon.addons()         -- 已安装的 addon 及其提供的扩展
addon.versions("yaml") -- 某个 addon 已安装的版本
addon.installdir()     -- ~/.xmake/addons

为什么要带命名空间

addon 提供的所有东西,都要通过 @addon/<name>/... 或者 @addon.<name>.<module> 来访问,不存在直接用裸名字引用的情况。这样一来,两个 addon 各自提供一个叫 app 的规则也不会有任何问题。真正全局的只有命令名和模板 id,所以我们在安装时也只检查这两类的冲突。

而在 addon 自己的代码里,一律用 @self 引用自身,addon 就不用把自己的名字硬编码进去,以后换个仓库、改个名字,内部代码都不用动。

addon 可以自带包定义

工具链类的 addon 通常还需要对应的二进制工具,与其让用户自己去装,不如让 addon 把包定义一起带上,再通过 includes 文件暴露给工程:

lua
-- src/includes/packages/xmake.lua
package("mycc")
    set_kind("toolchain")
    add_urls("https://.../mycc-$(version).tar.gz")
    add_versions("1.0.0", "<sha256>")
    on_install(function (package)
        os.cp("*", package:installdir())
    end)
package_end()
lua
-- src/includes/board/xmake.lua
includes("../packages")
option("board", {default = "myboard", description = "Set the target board."})
add_requires("mycc", "myflasher")

工程里只需要 includes("@addon/my-addon/board") 一行,配置选项、工具链和烧写工具就都齐了。

目前提供的官方 addon

第一批 addon 已经进了 xmake-repo:

Addon说明
esp32-develESP32 的工具链、构建规则、烧写和 blink 模板
avr-devel8 位 AVR 板子(uno / nano / mega2560)的工具链、规则和模板
serial-toolsxmake monitor 命令,以及可供其他 addon 复用的串口模块
yaml纯 lua 实现的 yaml 解析和生成模块,另外还提供了命令行工具
xmake-harnessxmake ai,用 xmake lua 写的终端编码助手
format-plugin / doxygen-plugin / macro-plugin原来的几个内置命令
basic-templates依赖外部 SDK 的工程模板(sdl、qt、verilator)

esp32-devel 为例,交叉工具链、构建规则、烧写逻辑和工程模板都由它提供,所以整个 ESP32 的开发流程就这么几行,用户不需要再单独去装什么东西:

sh
$ xmake addon --install esp32-devel
$ xmake create -t esp32.blink -l c blink
$ cd blink
$ xmake f --board=esp32c3
$ xmake
$ xmake install                       # 烧写到板子

xmake ai,用 xmake lua 写的 AI 助手

xmake-harness 这个 addon 算是把 addon 的能力用到了极致,它是一个完整的 AI Agent 框架,会话记录、agent 主循环、工具管线、权限策略、沙箱、skills、子 agent、斜杠命令和终端 UI,全部用 xmake lua 实现,没有任何第三方依赖。装上之后就多了一个 xmake ai 命令:

sh
$ xmake addon --install xmake-harness
$ xmake ai --setup                     # 选服务商、填 api key、选模型
$ xmake ai                             # 交互式界面
$ xmake ai "给 foo 加个单元测试"
$ xmake ai --print "这个工程构建出什么?"   # 非交互模式,适合脚本和 CI

因为它本身就是一个 xmake addon,所以对所在的工程是了解的,而且让它执行构建时,输出会直接打到终端,不会占用模型的上下文:

/xmake build                 直接执行 xmake 构建,输出打到终端
/xmakedocs                   拉取 xmake 文档,方便它查接口
/skills install xmake        加载 xmake-skills
/model deepseek-reasoner     切换模型
/context                     查看上下文的占用情况

另外还可以用权限模式来控制它的自由度:

sh
$ xmake ai --mode=plan           # 只读,先规划
$ xmake ai --mode=acceptedits    # 自动接受文件修改,执行命令仍然询问
$ xmake ai --sandbox             # 在沙箱里执行命令

终端里的 xmake ai

web 界面

除了终端,同一个 agent 也可以跑在浏览器里:

sh
$ xmake ai --web
$ xmake ai --web --port=9800 --cwd=/path/to/project

它会在本地回环地址上起一个 http 服务,打印一个带 token 的 url(每次运行都会重新生成),然后打开浏览器。后面跑的是同一个 harness,工具、skills、权限模式和会话文件都是同一套,所以它只是换了个前端,并不是另一个 agent。打开时进入的也是这个工程上一次的会话,而不是空会话。

web 界面:对话

刚打开时是一个占满整屏的对话界面,当你去看它改了哪些文件时,界面会展开成一个工作区,左边是对话,中间是文件内容,这次对话改过的地方会标注在上面,右边则是工程的文件树。中间的文件可以直接编辑,页面里写文件和 agent 写文件走的是同一套流程:

web 界面:工作区

配置

配置是分层合并的,一共五层,依次是内置默认值、~/.xmake/harness/config.json、工程配置、XMAKE_HARNESS_* 环境变量和命令行参数。写入时只会写用户那一层,所以 api key 不会被误提交到工程仓库里:

sh
$ xmake ai --config=providers.deepseek.apikey=sk-xxxxxx
$ xmake ai --provider=deepseek --model=deepseek-chat
$ xmake ai --smallmodel=deepseek-chat    # 用于标题、摘要和轻量子 agent
$ xmake ai --showconfig
$ xmake ai --doctor                      # 检查当前配置缺了什么

内置支持的服务商有 deepseek(默认)、anthropicopenaimoonshotdashscopesiliconflowopenrouterzhipu,其他 OpenAI 接口兼容的服务,配置一下 baseurl 也可以直接用。

会话和 skills

sh
$ xmake ai -c                            # 继续当前目录的上一次会话
$ xmake ai -r                            # 交互式选择要恢复的会话
$ xmake ai --new                         # 强制开新会话
$ xmake ai --list=skills                 # 还可以是 agents、tools、commands、providers、sessions
$ xmake ai --command=doctor              # 不进 TUI 直接执行一条斜杠命令

skills 不会自动拉取,只有你主动装了才会加载。其中 xmake-skills 就是专门教它怎么用 xmake 的:

/skills install xmake

更完整的配置说明、服务商列表和命令列表,可以看 xmake-harness 文档。

format / doxygen / macro 插件迁移为 addon

这三个命令原本是内置在 xmake 里的,但大部分用户其实用不到,现在把它们拆成了独立的 addon:

sh
$ xmake addon --install format-plugin
$ xmake addon --install doxygen-plugin
$ xmake addon --install macro-plugin

内置的版本暂时还保留着,只是会打印一个废弃提示。装了对应 addon 之后,命令就由 addon 接管了,因为任务的搜索路径中,addon 目录现在排在内置插件前面。

package.host.install_locally 策略

package.install_locally 可以把工程用到的包装到 build/.packages 下,而不是全局的 ~/.xmake/packages。但之前它对所有包都生效,包括交叉工具链在内,而工具链这类包体积大、可以跨工程复用,又和工程配置无关,装到本地并不合适。

现在这个策略只影响平台侧的包了,主机侧的包(也就是 xmake 判定的 host 包,比如工具链)仍然装在全局,如果确实需要,可以用新加的策略单独控制:

lua
set_policy("package.install_locally", true)        -- 库包 -> build/.packages
set_policy("package.host.install_locally", true)   -- 如果你确实想让工具链也进本地

这样嵌入式开发中最常见的那个需求就能直接表达了:源码包跟着工程走,交叉工具链下载一次到处复用。

新增交叉编译架构

为 cross 和 linux 平台新增了一批架构支持,比如 SPARC64。

重构模板分发

我们重新组织了模板目录,也重构了模板的分发方式,addon 的 templates/ 目录正是基于这个改动才能实现,xmake create -t 用到的模板现在可以由 addon 提供,而不必内置在 xmake 安装包里。

BSD 的 pkg 包管理器

新增了 pkg 系统包管理器支持,FreeBSD 及其衍生系统上安装缺失的系统库时会走它。

改进

包的向量扩展传递给使用者

包里声明的向量扩展(add_vectorexts)现在会传递给使用它的目标,通过组件引入的也一样。比如链接了一个用 avx2 构建的包,目标就能自动拿到对应的编译选项。

hlsl2spv / glsl2spv 提前到模块扫描之前

着色器规则现在会在 C++ 模块扫描之前执行,这样扫描器读取的时候,生成的头文件已经准备好了。

核心改进

这个版本对 interpreter 的 includes 解析、工程加载、搜索缓存和 semver 都做了一轮改进。另外还收紧了 addon 导出全局模块时的冲突检查,addon 不能再覆盖 xmake 自身的模块。

更新日志

新特性

  • #7696: 添加 addons 支持,可以用插件、规则、工具链、模板、模块和 includes 文件扩展 xmake
  • #7702: 自动安装工程声明的 addon,并用 xmake-addons.lock 锁定版本
  • #7714: 支持在 xmake.lua 中使用 add_addons(...)
  • #7706: 添加 addon 测试
  • #7707: 添加 addon 分发 package 定义的测试
  • #7717: 将 format / doxygen / macro 插件迁移为 addon
  • #7723: 添加新的交叉编译架构,例如 SPARC64
  • #7721: 添加 package.host.install_locally 策略
  • #7699: 重构模板目录和模板分发
  • 添加 BSD 的 pkg 包管理器支持

改进

  • #7719: 将包的向量扩展配置传递给使用它的目标
  • #7713: 让 hlsl2spv / glsl2spv 在 C++ 模块扫描之前执行
  • #7722: 改进 interpreter、工程加载、搜索缓存和 semver
  • #7733: 检查 addon 全局模块的命名冲突
  • #7726: 改进 mingw 工具链对 clang 和 libc++ 的支持

Bug 修复

  • #7737: 修复本地安装包时,嵌套构建重复安装包的问题
  • #7738: 修复远程编译下的平台菜单,例如 xmake f -p windows --wdk=xxx
  • #7710: 修复 .lib 结尾的库名被错误裁剪的问题
  • #7709: 修复 clang 的 runtime flags 检测
  • #7703: 修复 wix 传递 -arch 时无法识别 x86_64 的问题
  • #7701: 修复依赖检测无法发现嵌套值追加的问题
  • #7698: 修复 semver 的版本选择和 build metadata 排序
  • 修复 scheduler.co_resume,让被恢复协程的错误能够正常抛出