⚔️ Nimmake vs Meson — 嵌入式构建工具全面对比
Meson 是现代构建系统的代表,以速度和可读性著称;Nimmake 是专为嵌入式 MCU 打造的 Python 构建系统。两者都强调"开发者体验",但设计哲学和服务的目标场景截然不同。
📋 一句话概括
| Meson | 跨平台 构建系统生成器(meta-build) | 快速、可读、用户友好,生成 Ninja 文件执行编译 |
| Nimmake | 嵌入式专用 构建系统 | Python 驱动,MCU 优先,零中间步骤直接编译 |
🔍 核心差异总览
| 语言 | 自定义 DSL(Python 风格但不是 Python) | 纯 Python | 🏆 Nimmake |
| 学习曲线 | ⭐⭐ 较低(语法简洁,但 DSL 仍需学习) | ⭐ 极低(会 Python 即会用) | 🏆 Nimmake |
| 芯片预设 | ❌ 无,需手写 -mcpu=cortex-m4 等 | ✅ 内置 ARM/RISC-V 预设,一行搞定 | 🏆 Nimm |
| 头文件路径 | include_directories() 手动声明 | 自动推导,按 Party 自动包含 | 🏆 Nimmake |
| 宏定义 | add_project_arguments('-Dxxx', …) 手动 | 声明式传入,自动注入 | 🏆 Nimmake |
| 依赖管理 | dependency() / subproject() | DependOn() 编译级依赖,自动排序 | 🏆 Nimmake |
| 多工具链切换 | 交叉编译文件(cross file),静态配置 | 动态配置,运行时切换 | 🏆 Nimmake |
| RISC-V 支持 | 需手写 -march=rv32imac | 内置 RISCV_32_IMAC_CFG,开箱即用 | |
| Ninja 后端 | ✅ 默认(核心设计) | ✅ -n / –ninja 可选 | 构建缓存 |
| 增量编译 | ✅ 基于 Ninja | ✅ 基于 Ninja + 文件哈希 + 头文件追踪 | 平手 |
| 编译数据库 | ✅ compile_commands.json 自动生成 | ✅ –compiledb 生成 | 平手 |
| IDE 集成 | ⭐⭐⭐⭐ VS Code/CLion 支持好 | ⭐⭐⭐⭐ compile_commands.json | 平手 |
| 构建步骤 | 配置 + 编译(两步) | 一步到位 | 🏆 Nimmake |
| 大型项目 | ⭐⭐⭐⭐⭐ GNOME/Android 级 | ⭐⭐ 中小型嵌入式 | 🏆 Meson |
| 桌面应用 | ✅ 支持 | ❌ 不支持 | 🏆 Meson |
| Python 扩展 | ❌ DSL 不兼容 Python | ✅ 100% Python,可 import 任何库 | 🏆 Nimmake |
📝 实战对比:STM32F407 HAL 项目
同一个 STM32F407 + HAL 驱动库项目,分别用 Meson 和 Nimmake 实现。
🔴 Meson 实现
meson.build(主构建文件)
project('stm32f407', 'c',
version: '1.0.0',
default_options: ['c_std=gnu99']
)
# —- 芯片参数(手动查手册!) —-
cpu = 'cortex-m4'
fpu = 'fpv4-sp-d16'
float_abi = 'hard'
# —- 编译参数(逐个手动写) —-
common_args = [
f'-mcpu={cpu}',
f'-mfpu={fpu}',
f'-mfloat-abi={float_abi}',
'-mthumb',
'-O2',
'-g',
'-Wall',
'-fdata-sections',
'-ffunction-sections',
]
link_args = common_args + [
'-nostartfiles',
'-Wl,–gc-sections',
'-T' + meson.source_root() + '/STM32F407XX_FLASH.ld',
'-lm', '-lc', '-lgcc',
]
# —- 宏定义 —-
defines = [
'-DSTM32F407xx',
'-DUSE_HAL_DRIVER',
]
# —- 源文件(手动列每个文件!) —-
core_srcs = files(
'Core/main.c',
'Core/stm32f4xx_it.c',
'Core/system_stm32f4xx.c',
)
# GLOB 在 Meson 中不推荐,必须手动或使用 run_command
drv_srcs = files(
'Drivers/Src/stm32f4xx_hal.c',
'Drivers/Src/stm32f4xx_hal_cortex.c',
'Drivers/Src/stm32f4xx_hal_gpio.c',
'Drivers/Src/stm32f4xx_hal_dma.c',
'Drivers/Src/stm32f4xx_hal_rcc.c',
'Drivers/Src/stm32f4xx_hal_uart.c',
'Drivers/Src/stm32f4xx_hal_spi.c',
'Drivers/Src/stm32f4xx_hal_i2c.c',
'Drivers/Src/stm32f4xx_hal_tim.c',
# … 还有 15+ 个文件,写到崩溃 …
)
asm_srcs = files('startup_stm32f407xx.s')
# —- 头文件路径 —-
inc_dirs = include_directories(
'Core/Inc',
'Drivers/Inc',
)
# —- 可执行目标 —-
elf = executable('test.elf',
core_srcs + drv_srcs + asm_srcs,
include_directories: inc_dirs,
c_args: common_args + defines,
link_args: link_args,
link_language: 'c',
name_suffix: 'elf',
)
# —- 自定义目标:bin/hex —-
custom_target('bin',
output: 'test.bin',
input: elf,
command: ['arm-none-eabi-objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
)
custom_target('hex',
output: 'test.hex',
input: elf,
command: ['arm-none-eabi-objcopy', '-O', 'ihex', '@INPUT@', '@OUTPUT@'],
)
交叉编译文件(cross file)
# arm-cross.ini
[binaries]
c = 'arm-none-eabi-gcc'
cpp = 'arm-none-eabi-g++'
ar = 'arm-none-eabi-ar'
objcopy = 'arm-none-eabi-objcopy'
strip = 'arm-none-eabi-strip'
[built-in options]
c_args = ['-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16', '-mfloat-abi=hard', '-mthumb']
c_link_args = ['-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16', '-mfloat-abi=hard', '-mthumb']
[host_machine]
system = 'none'
cpu_family = 'arm'
cpu = 'cortex-m4'
endian = 'little'
构建命令:
# 配置 + 编译(两步)
meson setup build –cross-file arm-cross.ini
meson compile -C build
🟢 Nimmake 实现
from nimmake.datasets import CORTEX_M4_CFG
from nimmake.Helper import Helper
hlp = Helper()
# ★ 芯片配置:一行搞定,自动生成 -mcpu/-mfpu/-mthumb
CFG = CORTEX_M4_CFG.clone()
CFG.set("model", "STM32F407")
hlp.Config(CFG)
hlp.set_cfg("linkscript", "STM32F407XX_FLASH.ld")
# ★ 工具链配置
hlp.Update({
"TOOLPATH": r"D:\\LLVM\\arm-none-eabi-gcc14\\bin",
"TOOL": "gcc",
"TOOL_PREFIX": "arm-none-eabi-",
})
hlp.Refresh()
# ★ Party 模块:自动扫描源码
core = hlp.Parties("CORE", "Core")
driver = hlp.Parties("Driver", "Drivers",
defines={"STM32F407xx": "", "USE_HAL_DRIVER": ""},
)
core.DependOn(driver)
# ★ 构建目标
t = hlp.Program("test", sources=["startup_stm32f407xx.s"])
hlp.DefaultTarget(t)
构建命令:
# 一步搞定
nimmake
🔄 构建流程对比
| 配置 | meson setup build(解析 DSL,生成 Ninja) | 不存在!Python 直接执行 |
| 编译 | meson compile -C build | nimmake(一步到位) |
| 烧录 | 额外 run_target() 或手动命令 | nimmake flash |
| 清理 | meson compile –clean | nimmake -c |
| 总步数 | 2-3 步 | 1 步 |
Meson 配置阶段耗时
Meson 每次 setup / reconfigure 都需要:
Nimmake 没有配置阶段,Python 脚本直接生成构建图并编译。
🧩 关键特性深度对比
1️⃣ 芯片预设 vs 手写参数
# Meson — 每个参数手动写
common_args = [
'-mcpu=cortex-m4',
'-mfpu=fpv4-sp-d16',
'-mfloat-abi=hard',
'-mthumb',
]
# 换芯片?全部重写!
# Nimmake — 预设切换
CFG = CORTEX_M4_CFG.clone()
# 换 RISC-V 只需改一行:
# CFG = RISCV_32_IMAC_CFG.clone()
# → 自动生成 -march=rv32imac -mabi=ilp32 -mcmodel=medany
2️⃣ 交叉编译文件 vs 字典配置
# Meson — 需要维护独立的 cross file
# arm-cross.ini 和 riscv-cross.ini 两个文件
# 构建时通过 –cross-file 指定
meson setup build –cross-file arm-cross.ini
meson setup build-riscv –cross-file riscv-cross.ini # 不同的构建目录
# Nimmake — 运行时动态切换
# 在同一个脚本中,一个变量控制
toolchain = ToolChain_armgcc # 或 ToolChain_riscv
hlp.Update(toolchain)
hlp.Refresh()
# 甚至可以在同一个构建中处理多架构!
3️⃣ 源码管理
# Meson — 明确不鼓励 GLOB,必须手写文件列表
srcs = files(
'Drivers/Src/stm32f4xx_hal.c',
'Drivers/Src/stm32f4xx_hal_gpio.c',
'Drivers/Src/stm32f4xx_hal_dma.c',
# 新增文件?回来加一行!
)
# 或用 run_command 模拟 GLOB,但官方不推荐
# Nimmake — 自动扫描目录
driver = hlp.Parties("Driver", "Drivers")
# 自动递归扫描 Drivers/ 下所有 .c/.S 文件
# 新增文件到目录 → 无需修改构建脚本!
4️⃣ 第三方库集成
# Meson — wrap/subproject 系统
# 需要创建 wraps/freertos.wrap 文件
[wrap-git]
url = https://github.com/FreeRTOS/FreeRTOS.git
revision = V10.5.1
# meson.build 中:
freertos_dep = subproject('freertos').get_variable('freertos_dep')
# 但 FreeRTOS 官方没有提供 meson.build!
# 要么自己写,要么手动配置源文件列表
# Nimmake — 内置配置
freertos = hlp.Parties("FREERTOS", "FreeRTOS",
third_party="FREERTOS",
include_macros={"chip": "ARM_CM4F", "heap": "4"},
)
# 内置模板自动处理:源文件、头文件路径、portable 层、堆方案
5️⃣ 构建缓存
# Meson — 无内置缓存
# 需要外部 ccache
export CCACHE_DIR=.ccache
meson setup build -Dc_args='-DCCACHE=1'
# 跨平台配置复杂
# Nimmake — 内建缓存
nimmake –cache # 首次编译
nimmake –cache # 二次编译命中缓存,瞬间完成
6️⃣ Python 生态融合
# Meson — DSL 是封闭的
# 不能 import 任何 Python 库
# 不能调用外部 Python 脚本(需要 run_command,有限制)
# 条件逻辑用 #if,功能有限
# Nimmake — 100% Python
import json
import sys
import re
from pathlib import Path
# 可以读取 JSON 配置文件
with open("project_config.json") as f:
cfg = json.load(f)
# 可以用正则表达式过滤源文件
# 可以用任何第三方 Python 库
# 可以写单元测试验证构建脚本!
📊 适用场景建议
选 Meson 的场景
- ✅ Linux 桌面/服务端项目(GNOME、systemd 等)
- ✅ 需要精确的依赖版本管理(wrap 系统)
- ✅ 项目规模大(数百个模块、数千源文件)
- ✅ 需要多个子项目/subproject 协作
- ✅ 团队已统一使用 Meson
选 Nimmake 的场景
- 🏆 ARM / RISC-V MCU 固件开发
- 🏆 多芯片快速切换(ARM ↔ RISC-V)
- 🏆 快速原型验证(几分钟搭好环境)
- 🏆 使用 FreeRTOS/LVGL 等嵌入式库(即插即用)
- 🏆 团队熟悉 Python 但不熟悉 Meson
- 🏆 需要动态/条件化构建逻辑(Python 的灵活性)
- 🏆 CI/CD 嵌入式流水线
🎯 总结评分
| MCU 专精度 | ⭐⭐ 通用工具,需手动适配 | ⭐⭐⭐⭐⭐ 为嵌入式而生 |
| 上手速度 | ⭐⭐⭐ 语法简洁但仍是 DSL | ⭐⭐⭐⭐⭐ 会 Python 即会用 |
| 芯片预设 | ❌ 无 | ✅ 内置 ARM/RISC-V 预设 |
| 厂商型号 | ❌ 无 | ✅ VENDOR_MODEL_OF 系统 |
| 第三方库集成 | ⭐⭐ wrap 系统但需库本身支持 | ⭐⭐⭐⭐⭐ 内置模板即插即用 |
| 多芯片切换 | ⭐⭐ 不同 cross file + 不同 build dir | ⭐⭐⭐⭐⭐ 运行时一键切换 |
| 构建缓存 | ❌ 无内置(需 ccache) | ✅ 内建 –cache |
| Python 生态 | ❌ 封闭 DSL | ✅ 100% Python,任意 import |
| 大型项目 | ⭐⭐⭐⭐⭐ GNOME 级 | ⭐⭐ 适合中小型 |
| 桌面应用 | ⭐⭐⭐⭐ 支持良好 | ❌ 不支持 |
| 代码量(MCU) | 80-120 行 | 30-50 行 |
💬 总结
Meson 是"现代化 CMake"——它解决了 CMake 的语法混乱问题,但仍然是通用构建系统生成器。
Nimmake 是"嵌入式原生"——从第一天起就只为 MCU 开发服务,不考虑桌面或服务端。
选择建议:
你的项目是 Linux 桌面/系统软件? → Meson ✅
你的项目需要精确的版本依赖管理? → Meson ✅
你的项目 > 50 万行代码? → Meson ✅
你的项目是 ARM/RISC-V MCU 固件? → Nimmake 🏆
你需要快速切换多种芯片架构? → Nimmake 🏆
你需要在构建脚本中使用 Python 逻辑? → Nimmake 🏆
你希望构建脚本简洁到 30 行? → Nimmake 🏆
🚀 立即体验 Nimmake
# 安装
pip install nimmake
# 查看示例
git clone https://github.com/pophu/nimmake.git
cd nimmake/samples/07_riscv32
# 一键编译(对比 meson setup build + meson compile)
nimmake
📎 相关资源
- Nimmake GitHub
- Nimmake vs Makefile
- Nimmake vs CMake
- [示例:ARM 项目](file:///d:/MyCodeNew/nimmake/samples/05_arm32/Nimmake.py)
- [示例:RISC-V 项目](file:///d:/MyCodeNew/nimmake/samples/07_riscv32/Nimmake.py)
- [示例:厂商型号自动配置](file:///d:/MyCodeNew/nimmake/samples/60_vendor_model/Nimmake.py)


