研究基于PX4平台的Ardupilot代码工程的makefile结构

时间:2022-12-13 18:37:23

最近有空,于是想花时间好好研究下ardupilot的软件架构,经过对ardupilot一段时间的熟悉和使用后,对其软件架构已经有了一定的理解,但还称不上特别完备,所以想把每一部分的细节都弄清楚。

makefile定义了一个软件工程的编译规则,以便于其实现自动化编译,这也就是为什么我们在命令行中输入指令:

make px4-v2

就可以自动完成整个工程的编译。
看似简单的一条命令,其中makefile完成了很多工作。

然后ardupilot工程那么繁杂,命名为makefile的文件也很多,我们应该从哪个文件开始分析起呢。
回想我们编译的过程,以旋翼机型为例,我们首先是打开“\ardupilot\ArduCopter”路径,那么在这个路径下有没有我们要找的makefile呢,答案是肯定的。
这个makefile中只有一条指令:

include ../mk/apm.mk

然后我们对应地找到apm.mk文件,这个文件的内容就比较多了。一行行地分析,首先看开头几行:

SYSTYPE         :=  $(shell uname)

ifneq ($(findstring CYGWIN, $(SYSTYPE)),)
MK_DIR := $(shell cygpath -m ../mk)
else
MK_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
endif

这几行代码是要确定MK_DIR,这个在下面查找mk文件时都会用到,根据我们电脑的操作系统类型确定,如果是Windows应该是执行第一个指令,所以 MK_DIR = “../mk”。也就是“…\ardupilot\mk”文件夹,该文件夹中有许多mk文件,有好多都是我们后面要用到的。

继续往下看:

include $(MK_DIR)/environ.mk

打开environ.mk,该文件主要是确定一些编译相关的变量:EXTRAFLAGS,SRCROOT,SKETCHBOOK,BUILDROOT以及HAL_BOARD等,这些变量在后面都会用到。我们重点看下HAL_BOARD的确定,APM代码支持多种硬件平台,HAL_BOARD就代表了我们指定运行的硬件平台。关于HAL_BOARD有一长段的if语气对其进行赋值, 其中我们有找到这样一句:

ifneq ($(findstring px4, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_PX4
endif

MAKECMDGOALS代表命令行参数列表,如果我们在命令行中输入“make px4-v2”则MAKECMDGOALS就是“px4-v2”,包含px4字符段,所以if语句会执行该条件,HAL_BOARD = HAL_BOARD_PX4。从这里就可以看到我们在命令行中的指令对于编译条件的影响了,后续我们会看到HAL_BOARD的作用。

我们回到APM.mk中,继续往下看:

# short-circuit build for the configure target
ifeq ($(MAKECMDGOALS),configure)
include $(MK_DIR)/configure.mk

else

# short-circuit build for the help target
include $(MK_DIR)/help.mk

# common makefile components
include $(MK_DIR)/targets.mk
include $(MK_DIR)/sketch_sources.mk

ifneq ($(MAKECMDGOALS),clean)
# board specific includes
ifeq ($(HAL_BOARD),HAL_BOARD_APM1)
include $(MK_DIR)/board_avr.mk
endif

ifeq ($(HAL_BOARD),HAL_BOARD_APM2)
include $(MK_DIR)/board_avr.mk
endif

ifeq ($(HAL_BOARD),HAL_BOARD_SITL)
include $(MK_DIR)/board_native.mk
endif

ifeq ($(HAL_BOARD),HAL_BOARD_LINUX)
include $(MK_DIR)/board_linux.mk
endif

ifeq ($(HAL_BOARD),HAL_BOARD_PX4)
include $(MK_DIR)/board_px4.mk
endif

ifeq ($(HAL_BOARD),HAL_BOARD_VRBRAIN)
include $(MK_DIR)/board_vrbrain.mk
endif

ifeq ($(HAL_BOARD),HAL_BOARD_FLYMAPLE)
include $(MK_DIR)/board_flymaple.mk
endif

endif

endif

以指令“px4-v2”为例,会依次包含help.mk,targets.mk,sketch_sources.mk以及board_px4.mk,注意前三个是通用的,而board_px4.mk则是跟硬件平台有关,这里就是依据我们之前确定的HAL_BOARD来选择的。
help.mk中是一些帮助信息,响应“help”指令显示这些帮助信息;
targets.mk是对一些通用目标的响应,主要是支持一些其他类型硬件平台,同时还包含进了module.mk;
module.mk:

# git submodule support

.PHONY: CHECK_MODULES

# PX4 build needs submodules
px4-%: CHECK_MODULES

CHECK_MODULES:
$(v)$(MK_DIR)/check_modules.sh

module-update:
git submodule update

这个后面会用到,对于px4平台,由于其代码工程包含子模块,每次编译都会执行CHECK_MODULES。

sketch_sources.mk会基于SRCROOT里面的make.inc文件定义SKETCHLIBS,SKETCHLIBNAMES等变量,如前所述,SRCROOT是在environ.mk中规定的,在此例中,SRCROOT就是“……/ardupilot/arducopter”,在此路径中,可以找到mak.inc文件:

LIBRARIES += AP_Common
LIBRARIES += AP_Progmem
LIBRARIES += AP_Menu
LIBRARIES += AP_Param
LIBRARIES += StorageManager
LIBRARIES += AP_HAL
LIBRARIES += AP_HAL_AVR
LIBRARIES += AP_HAL_SITL
LIBRARIES += AP_HAL_PX4
LIBRARIES += AP_HAL_VRBRAIN
LIBRARIES += AP_HAL_FLYMAPLE
LIBRARIES += AP_HAL_Linux
LIBRARIES += AP_HAL_Empty
LIBRARIES += GCS
LIBRARIES += GCS_MAVLink
....

该文件中定义了LIBRARIES 变量。SKETCHLIBS变量是根据LIBRARIES 定义的。sketch_sources.mk同时也定义了一些指令的响应规则,如(BUILDROOT)/make.flags在后面会用到。

下面到了我们的重点board_px4.mk,该文件一开始首先是对PX4_ROOT,NUTTX_SRC和UAVCAN_DIR进行赋值处理,其实就是Ardupilot用到的三个子模块PX4Firmware、PX4NuttX和uavcan的文件路径。然后就是对EXTRAFLAGS变量进行扩展,EXTRAFLAGS表示附加标示,之前在environ.mk文件中已经对其添加了主模块的git版本,在这里会继续添加三个子模块的git版本号,git版本号用于统一主模块和子模块的版本对应关系,在git管理中应用。
board_px4.mk的重点在很多命令的定义,比如我们用到的px4-v2:

px4-v2: $(BUILDROOT)/make.flags CHECK_MODULES $(PX4_ROOT)/Archives/px4fmu-v2.export $(SKETCHCPP) module_mk px4-io-v2
$(RULEHDR)
$(v) rm -f $(PX4_ROOT)/makefiles/$(PX4_V2_CONFIG_FILE)
$(v) cp $(PX4_V2_CONFIG_FILE) $(PX4_ROOT)/makefiles/nuttx/
$(PX4_MAKE) px4fmu-v2_APM
$(v) /bin/rm -f $(SKETCH)-v2.px4
$(v) cp $(PX4_ROOT)/Images/px4fmu-v2_APM.px4 $(SKETCH)-v2.px4
$(v) $(SKETCHBOOK)/Tools/scripts/add_git_hashes.py $(HASHADDER_FLAGS) "$(SKETCH)-v2.px4" "$(SKETCH)-v2.px4"
$(v) echo "PX4 $(SKETCH) Firmware is in $(SKETCH)-v2.px4"

下面逐个分析:
$(BUILDROOT)/make.flags:可以在sketch_sources.mk找到定义:

$(BUILDROOT)/make.flags: FORCE
@mkdir -p $(BUILDROOT)
@echo "// BUILDROOT=$(BUILDROOT) HAL_BOARD=$(HAL_BOARD) HAL_BOARD_SUBTYPE=$(HAL_BOARD_SUBTYPE) TOOLCHAIN=$(TOOLCHAIN) EXTRAFLAGS=$(EXTRAFLAGS)" > $(BUILDROOT)/make.flags.new
@cmp $(BUILDROOT)/make.flags $(BUILDROOT)/make.flags.new > /dev/null 2>&1 || rm -f $(SRCROOT)/*.o
@cmp $(BUILDROOT)/make.flags $(BUILDROOT)/make.flags.new > /dev/null 2>&1 || mv $(BUILDROOT)/make.flags.new $(BUILDROOT)/make.flags
@rm -f $(BUILDROOT)/make.flags.new
@cat $(BUILDROOT)/make.flags

该指令会在命令行中打印一些基本信息,并且将这些信息存放在
“…\Ardupilot\Build.ArduCopter\make.flags”文件中。

CHECK_MODULES在modules.mk中定义:

CHECK_MODULES:
$(v)$(MK_DIR)/check_modules.sh

调用脚本文件check_modules.sh进行子模块检测。

$(PX4_ROOT)/Archives/px4fmu-v2.export是文件。

$(SKETCHCPP)是文件arducopter.cpp。

module_mk:

module_mk:
$(v) echo "Building $(SKETCHBOOK)/module.mk"
$(RULEHDR)
$(v) echo "# Auto-generated file - do not edit" > $(SKETCHBOOK)/module.mk.new
$(v) echo "MODULE_COMMAND = ArduPilot" >> $(SKETCHBOOK)/module.mk.new
$(v) echo "SRCS = $(wildcard $(SRCROOT)/*.cpp) $(SKETCHLIBSRCSRELATIVE)" >> $(SKETCHBOOK)/module.mk.new
$(v) echo "MODULE_STACKSIZE = 8192" >> $(SKETCHBOOK)/module.mk.new
$(v) echo "EXTRACXXFLAGS = -Wframe-larger-than=1200" >> $(SKETCHBOOK)/module.mk.new
$(v) cmp $(SKETCHBOOK)/module.mk $(SKETCHBOOK)/module.mk.new 2>/dev/null || mv $(SKETCHBOOK)/module.mk.new $(SKETCHBOOK)/module.mk
$(v) rm -f $(SKETCHBOOK)/module.mk.new

该指令会在Ardupilot主目录下生成一个module.mk文件。

px4-io-v2:

px4-io-v2: $(PX4_ROOT)/Archives/px4io-v2.export
$(v)+ $(MAKE) -C $(PX4_ROOT) px4io-v2_default
$(v) /bin/rm -f px4io-v2.bin
$(v) cp $(PX4_ROOT)/Build/px4io-v2_default.build/firmware.bin px4io-v2.bin
$(v) cp $(PX4_ROOT)/Images/px4io-v2_default.bin px4io-v2.bin
$(v) cp $(PX4_ROOT)/Build/px4io-v2_default.build/firmware.elf px4io-v2.elf
$(v) mkdir -p $(MK_DIR)/PX4/ROMFS/px4io/
$(v) rm -f $(MK_DIR)/PX4/ROMFS/px4io/px4io.bin
$(v) cp px4io-v2.bin $(MK_DIR)/PX4/ROMFS/px4io/px4io.bin
$(v) mkdir -p $(MK_DIR)/PX4/ROMFS/bootloader/
$(v) rm -f $(MK_DIR)/PX4/ROMFS/bootloader/fmu_bl.bin
$(v) cp $(SKETCHBOOK)/mk/PX4/bootloader/px4fmuv2_bl.bin $(MK_DIR)/PX4/ROMFS/bootloader/fmu_bl.bin
$(v) echo "PX4IOv2 Firmware is in px4io-v2.bin"

(v)+ (MAKE) -C $(PX4_ROOT) px4io-v2_default会根据子模块PX4Firmware中的makefile来执行,涉及到PX4Firmware中的多个makefile,具体暂不讨论。接下来的指令就是将生成px4io-v2.bin等文件复制到相应的文件路径中去。

$(RULEHDR),RULEHDR是在sketch_sources.mk中定义的:

# common header for rules, prints what is being built
define RULEHDR
@echo %% $(subst $(BUILDROOT)/,,$@)
@mkdir -p $(dir $@)
endef

它会在命令行中打印:%% px4-v2。

接下来的:

$(v) rm -f $(PX4_ROOT)/makefiles/$(PX4_V2_CONFIG_FILE) 
$(v) cp $(PX4_V2_CONFIG_FILE) $(PX4_ROOT)/makefiles/nuttx/

这两句是用“…\Ardupilot\mk\PX4\config_px4fmu-v2_APM.mk”替换掉“…、Ardupilot\modules\PX4Firmware\makefiles\nuttx\config_px4fmu-v2_APM.mk”。

$(PX4_MAKE) px4fmu-v2_APM完成目标编译,同样与子模块的makefile有关,后续再做详细分析。
剩下来的指令就是完成arducopter-v2.px4文件的复制和存放。至此,make px4-v2”指令的分析就到此结束,后续会对子模块px4firmware的makefile做进一步分析。