4、编译工具链详解:Make与Waf构建系统对比、Waf基本命令

好,咱们接着聊。上一节我们把源码拉下来了,但光有源码没用,得把它变成能飞的固件。这一步,就得靠编译工具链了。

说到ArduPilot的编译,绕不开两个东西:MakeWaf。很多刚接触的朋友会问,这俩到底啥关系?我该用哪个?

嗯,我来给你捋一捋。

4.1 Make与Waf:两代构建系统的对比

先说说Make。这是Linux世界的老牌构建工具,历史很久了。ArduPilot早期就是用Make来管理的。它的核心逻辑很简单:你写一个Makefile,告诉它源文件在哪、依赖什么头文件、怎么链接,然后它按规则执行。

但问题也来了。ArduPilot支持的硬件平台太多了——Pixhawk、Cube、Here、Pix32……每个平台的编译器参数、链接脚本、外设驱动都不一样。你用Make去维护,Makefile会变得极其庞大。我在项目中就见过一个团队,他们的Makefile有上千行,改一个参数都得小心翼翼,生怕把别的平台搞崩了。

所以,后来ArduPilot引入了Waf。Waf是一个基于Python的构建系统,说白了,它比Make更聪明、更灵活。

对比项 Make Waf
配置方式 Makefile(纯文本) wscript(Python脚本)
跨平台支持 需要手动处理 原生支持,自动检测
依赖管理 基于文件时间戳 基于哈希值,更准确
并行编译 支持(-j参数) 支持,且更智能
扩展性 较差 强,可自定义任务
ArduPilot现状 已弃用 官方推荐

核心结论:现在ArduPilot官方已经全面转向Waf。你如果还在用Make,我建议尽快迁移。不是Make不好,而是Waf更适合这个项目。

4.2 Waf的基本命令:configure、build、clean

Waf的命令不多,但每个都很关键。我把它总结为三步走:配置 → 编译 → 清理

4.2.1 configure —— 配置编译环境

这是第一步。说白了,就是让Waf知道你用的是哪个编译器、目标平台是什么、需要哪些库。

./waf configure --board Pixhawk1

这条命令会做几件事:

  • 检查你的工具链是否安装(比如arm-none-eabi-gcc)
  • 检测系统头文件和库的路径
  • 生成一个build目录,里面存放编译中间文件
  • 把配置信息写入一个缓存文件,下次编译就不用重新检测了

小技巧:如果你换了编译器版本,或者改了系统环境变量,记得重新跑一次configure。否则Waf会沿用旧的缓存,导致编译失败。我遇到过好几次这种坑,排查了半天才发现是缓存没更新。

4.2.2 build —— 执行编译

配置好了,就可以编译了。

./waf build

这条命令会:

  • 解析所有源文件(.cpp、.c、.h)
  • 检查哪些文件有改动(基于哈希值,不是时间戳)
  • 只编译有改动的文件,没改动的直接跳过
  • 最后链接生成固件(.hex或.bin文件)

如果你想加快速度,可以加个参数:

./waf build -j4

-j4表示用4个线程并行编译。你的CPU核心数越多,这个数字可以设得越大。我一般设成CPU核心数+1,比如8核的机器用-j9。

注意:并行编译虽然快,但内存占用也会增加。如果你在虚拟机或者低配电脑上编译,建议用-j2或-j4,别贪多。我曾经在2GB内存的虚拟机上用-j8,结果直接卡死了……

4.2.3 clean —— 清理编译产物

有时候编译出了问题,或者你想从头来一遍,可以用clean命令。

./waf clean

这条命令会删除build目录下的所有中间文件,但保留配置缓存。如果你想连配置一起清掉,用:

./waf distclean

我个人习惯是:日常开发用build,遇到奇怪错误先clean再build,实在不行才distclean。因为distclean之后,下次编译还得重新configure,浪费时间。

4.3 配置编译选项

Waf的灵活性,很大程度上体现在编译选项的配置上。你可以通过环境变量、命令行参数、甚至修改wscript文件来定制编译行为。

4.3.1 常用的编译选项

选项 说明 示例
--board 指定目标板 --board Pixhawk1
--debug 开启调试模式 --debug
--enable-xxx 启用某个功能 --enable-uart
--disable-xxx 禁用某个功能 --disable-gps
CXXFLAGS 传递C++编译器参数 CXXFLAGS="-O2"
LINKFLAGS 传递链接器参数 LINKFLAGS="-lm"

4.3.2 实战:配置一个带调试信息的固件

假设你想编译一个Pixhawk1的固件,并且带上调试符号,方便用GDB调试。可以这样:

./waf configure --board Pixhawk1 --debug
./waf build

这样生成的固件会包含调试信息,体积会大一些,但调试时能看到变量名、行号等信息。

重要提醒:调试固件不要直接刷到飞控上飞!它体积大、运行慢,只适合在开发板上调试用。正式飞行一定要用release版本(不加--debug)。

4.3.3 通过环境变量传递参数

有时候你不想每次都在命令行写一堆参数,可以用环境变量。比如:

export CXXFLAGS="-O2 -mthumb"
./waf configure --board Pixhawk1
./waf build

这样CXXFLAGS就会自动生效。不过要注意,环境变量会影响所有后续的编译任务,如果你只是临时想改某个参数,还是用命令行更安全。

4.4 知识体系图

下面这张图,帮你把本章的核心逻辑串起来:

Waf构建系统核心流程 configure 配置编译环境 build 执行编译 clean 清理产物 检测工具链 生成build目录 解析源文件 增量编译 删除中间文件 保留配置缓存 日常开发:configure → build → clean → configure → build ...

4.5 避坑指南

最后,分享几个我踩过的坑,希望能帮你省点时间。

  • 路径不要有中文或空格:Waf对路径中的特殊字符支持不好。我曾经把项目放在“D:\我的项目\ArduPilot”下,结果configure直接报错。后来改成“D:\Projects\ArduPilot”就好了。
  • 不要用sudo跑waf:除非你明确知道自己在做什么。用sudo会导致编译产物权限混乱,下次普通用户编译时会报权限错误。
  • 注意Python版本:Waf需要Python 3.6以上。如果你系统默认的Python是2.7,记得用python3 ./waf来执行。
  • build失败时先看最后几行日志:Waf的输出信息很多,但真正的错误通常在最后几行。别从头看到尾,浪费时间。

好了,这一节的内容就到这。Waf的命令不多,但每个都很实用。你只要记住configure、build、clean这三个命令,再配合几个常用选项,就能应付绝大多数编译场景了。

专注资料整理