用 xmake 搭建 STM32 HAL 工程与自定义 C 构建链
最近想把 STM32 的构建从 IDE 里挪出来,试了试 xmake。它的文档挺全的,就是没有现成的 STM32 模板或专页,得把”交叉编译""工具链""规则”这几块自己拼起来用。这篇记一下我实际折腾的过程,文档没说透的地方,我把当时的做法也写出来,供参考。
一、先看一个最小工程
xmake create 出来的工程长这样(参考官方文档 create-project 页面):
hello├── src│ └── main.cpp└── xmake.lua最基础的 xmake.lua:
add_rules("mode.debug", "mode.release")target("hello") set_kind("binary") add_files("src/*.cpp")然后 xmake 构建、xmake run 运行。这个骨架对任何平台都一样,嵌入式无非是多配一个交叉工具链。
二、target 的常用配置
对照官方 configure-targets 文档,平时用得最多的几个:
| 接口 | 作用 | 生成的参数 |
|---|---|---|
set_kind("binary"/"static"/"shared"/...) | 目标类型 | — |
add_files("src/*.c", "src/*.asm") | 添加源文件 | — |
add_files("src/*.cpp", {excludes = "test.cpp"}) | 排除部分文件 | — |
add_defines("DEBUG") | 宏定义 | -DDEBUG |
add_includedirs("/tmp") | 头文件目录 | -I/tmp |
add_linkdirs("/tmp") | 链接库目录 | -L/tmp |
add_links("foo") | 链接库 | -lfoo |
add_cflags("...") / add_cxflags / add_cxxflags | 原始编译选项 | 直接透传 |
一个混合 C/汇编的 target 示例:
target("demo") set_kind("binary") add_files("src/*.c", "src/*.asm", "src/*.cpp") add_defines("DEBUG", "STM32F407xx") add_includedirs("lib", "Drivers/CMSIS/Include") add_links("myhal") add_linkdirs("build")三、创建工程的语言与模板
xmake create 的语言参数是 -l,模板参数是 -t(文档原文如此,别记反了):
xmake create -l c hello # 指定语言xmake create -t static test # 指定模板文档列出模板有 console、static、shared、module.binary、module.shared、qt.、tbox.、wxwidgets 等,最常用的就是 console、static、shared 这三个。
模板列表里没有 stm32/arm 这类。
-P可以指定工程父目录,这条文档里没细讲,xmake create --help里有。
四、STM32 HAL 工程怎么搭
官方没有 STM32 专页,但交叉编译文档里写了内置的 gnu-rm 工具链,原话是”gnu-rm 就是内置的 GNU Arm Embedded Toolchain”:
$ xmake f -p cross --toolchain=gnu-rm --sdk=/xxx/cc-arm-none-eabi-9-2019-q4-major$ xmake结合前面的 target 配置,一个 HAL 工程的 xmake.lua 大概是这么拼的:
add_rules("mode.debug", "mode.release")
target("firmware") set_kind("binary") -- HAL 库源码与芯片外设代码(路径按你 CubeMX 生成的工程填) add_files("Core/Src/*.c") add_files("Drivers/STM32F4xx_HAL_Driver/Src/*.c") -- 头文件搜索路径 add_includedirs("Core/Inc", "Drivers/STM32F4xx_HAL_Driver/Inc", "Drivers/CMSIS/Include", "Drivers/CMSIS/Device/ST/STM32F4xx/Include") -- 芯片型号与 HAL 开关宏 add_defines("STM32F407xx", "USE_HAL_DRIVER") -- 启动文件(汇编) add_files("startup_stm32f407xx.s") -- 链接脚本与数学库 add_ldflags("-Tstm32f407xx_flash.ld", "--specs=nano.specs")命令行切工具链:
xmake f -p cross --toolchain=gnu-rm --sdk=/opt/gcc-arm-none-eabixmake几个文档没细讲的点
下面这几个点文档里没有专门展开,是我按平时的嵌入式习惯补的,只当参考:
-
-T链接脚本和--specs=nano.specs怎么传<上面用>上面用>add_ldflags是常见做法,官方文档没给 HAL 的完整示范,自己试出来的 -
HAL 库的目录结构<那是>那是> STM32 生态(比如 CubeMX 生成)的约定,不是 xmake 的约定,按你实际工程填就行
-
启动汇编、链接脚本本身<由芯片工具链侧定义>由芯片工具链侧定义>,不归 xmake 管
五、自定义其他 C 构建链
命令行方式
官方 cross-compilation 页面给了一串配置:
# 设置编译器$ xmake f -p cross --sdk=/user/toolsdk --cc=armv7-linux-clang --cxx=armv7-linux-clang++# 设置链接器/归档器$ xmake f -p cross --sdk=/user/toolsdk --ld=armv7-linux-clang++ --sh=armv7-linux-clang++ --ar=armv7-linux-ar# 设置前缀与 bin 目录$ xmake f -p cross --cross=armv7-linux- --bin=/opt/bin设了 --sdk 之后,xmake 会自动加 -I{sdk}/include 和 -L{sdk}/lib,官方说不需要手动配,实测也是。
在 xmake.lua 里声明自定义工具链
官方 custom-toolchain 页面,最简单的 standalone 工具链只要指定 SDK 目录:
toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross")toolchain_end()
target("hello") set_kind("binary") add_files("apps/hello/*.c")切换方式有两种。命令行:xmake f --toolchain=my_toolchain;或者直接在 target 里绑定——官方原话是”这对于嵌入式开发来讲尤其有用,因为嵌入式平台的交叉编译工具链非常多”:
target("hello") set_toolchains("my_toolchain")其他常用的工具链 API:set_toolset("cc", "clang")(可配 cc/cxx/ld/sh/ar/ex/strip/mm/mxx/as)、set_bindir("/tmp/sdkdir/bin")、在 on_load 里加编译参数 toolchain:add("cxflags", "-march=armv7-a", {force = true})、手动加系统库 toolchain:add("syslinks", "gcc", "c")。
六、用自定义规则扩展构建流程
构建完总得干点别的,比如 elf 转 hex/bin、bin2c、生成校验,这些都能用 rule() 挂进构建流程(custom-rule 页面):
rule("rulename") -- 关联的文件扩展名 set_extensions(".ext1", ".ext2") -- 构建阶段回调 on_build_file(function(target, sourcefile, opt) ... end)rule_end()应用到 target 有两种方式:
add_rules("markdown") -- 全局应用add_files("src/*.md", {rule = "markdown"}) -- 按文件应用(优先级更高)可用的生命周期阶段:on_load / on_config / before_build / on_build / after_build / on_clean / on_package / on_install。
在规则里调用外部命令的写法(官方示例,用 pandoc 举例):
on_build_file(function(target, sourcefile, opt) os.vrunv("pandoc", {"-s", "-o", targetfile, sourcefile}) import("core.project.depend") depend.on_changed(function() ... end, {files = sourcefile})end)内置规则也有现成的,比如 add_rules("mode.debug", "mode.release")、add_rules("qt.widgetapp")。
七、总结
- 没有 STM32 模板的情况下,「gnu-rm 工具链 +
-p cross+ target API」拼一拼就够搭 HAL 工程;链接脚本、--specs这类细节要对着手上的芯片工具链确认 - 自定义构建链两条路<命令行>命令行>
--sdk/--cc/--ld/--cross快速起步;或者用toolchain()固化进工程,多工具链切换也方便 - 更复杂的构建流程用
rule()+ 生命周期回调扩展 - 遇到文档没写清楚的,先
xmake f --help或翻一下项目的 issues,比自己瞎猜快
参考:
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


