笔者最近研究ardupilot的软件架构,代码量太过于庞大,希望记录自己的学习历程。
首先,makefile文件定义的是软件工程的编译顺序和编译过程,以便于实现自动化的编译,也就是我们可以通过输入make指令就可以完成整个工程的编译工作。
为了熟悉makefile的语法规则,笔者特意在某宝上买了《GNU MAKE 中文手册》https://item.taobao.com/item.htm?spm=a230r.1.14.39.cb303beeqHA1u2&id=593739913695&ns=1&abbucket=15#detail 毕竟关于make方面的中文书籍还是很难得的,如果想要完全理解,读懂,甚至编写Makefile,那这本书势必要仔细阅读的。
下面对Makefile文件中的部分语法简单的说明一下:
:= --直接式变量展开,引用的变量或者函数都会被其定义值替换,如果:= 右端为空,可能是赋 值了一个空格;
$ --取变量值操作,可以嵌套使用,例如 ( ( ((X))。同时可以用于调用内嵌函数:$(FCTION ARGUMENTS);
?= --条件赋值,只有变量尚未被赋值的情况下,才能将?=后边的值赋给变量;
+= --追加赋值,VAR定义时给一个基本值,+=就在原来的变量基础上增加一 个赋值,注意不是覆盖;
ifeq、ifneq --条件判断关键字;
ifdef、ifndef --条件判断关键字;
话不多说直接进入makefile文件的阅读。
因为笔者最近一直在研究四旋翼,那就以旋翼机型为例:
打开代码"\ardupilot\ArduCopter",下拉列里可以看到Makefile,每种机型都可以看的到。
include ../mk/apm.mk
接着按照提示文件就可以找到相应的文件位置了:
# find the mk/ directory, which is where this makefile fragment
# lives. (patsubst strips the trailing slash.)
SYSTYPE := $(shell uname)
ifneq ($(findstring CYGWIN, $(SYSTYPE)),)
MK_DIR := $(shell cygpath -m ../mk)
else
MK_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
endif
按照上面的语法规则,可以看出这几行代码是确定MK_DIR。
继续…
include $(MK_DIR)/environ.mk
我们打开environ.mk文件了,可以看到这个文件主要是确定一些编译相关的变量:EXTRAFLAGS,SRCROOT,SKETCHBOOK,BUILDROOT,HAL_BOARD。我们主要看下HAL_BOARD的确定,APM支持多硬件平台,HAL_BOARD就代表了我们用到的是哪种硬件平台。
ifneq ($(APPDIR),)
# this is a recusive PX4 build
HAL_BOARD = HAL_BOARD_PX4
endif
# handle target based overrides for board type
ifneq ($(findstring px4, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_PX4
endif
ifneq ($(findstring sitl, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_SITL
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_NONE
endif
ifneq ($(findstring linux, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_NONE
endif
ifneq ($(findstring erleboard, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_ERLEBOARD
endif
ifneq ($(findstring zynq, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_ZYNQ
endif
ifneq ($(findstring pxf, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_PXF
endif
ifneq ($(findstring bebop, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_BEBOP
endif
ifneq ($(findstring navio, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_NAVIO
endif
ifneq ($(findstring raspilot, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_RASPILOT
endif
ifneq ($(findstring erlebrain2, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_ERLEBRAIN2
endif
ifneq ($(findstring bbbmini, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_BBBMINI
endif
ifneq ($(findstring minlure, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_MINLURE
endif
ifneq ($(findstring vrbrain, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_VRBRAIN
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_NONE
endif
ifneq ($(findstring vrubrain, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_VRBRAIN
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_NONE
endif
ifneq ($(findstring vrcore, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_VRBRAIN
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_NONE
endif
ifneq ($(findstring bhat, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_BH
endif
ifneq ($(findstring qflight, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_QFLIGHT
endif
ifneq ($(findstring qurt, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_QURT
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_NONE
endif
ifneq ($(findstring pxfmini, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_PXFMINI
endif
# default to SITL
ifeq ($(HAL_BOARD),)
HAL_BOARD = HAL_BOARD_SITL
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_NONE
endif
ifneq ($(findstring navio2, $(MAKECMDGOALS)),)
HAL_BOARD = HAL_BOARD_LINUX
HAL_BOARD_SUBTYPE = HAL_BOARD_SUBTYPE_LINUX_NAVIO2
endif
上面一连串的if语句是对HAL_BOARD进行赋值,我们找到MAKECMDGOALS,其代表命令行参数列表。
继续…
回到apm.mk
# 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
include $(SKETCHBOOK)/modules/uavcan/libuavcan/include.mk
ifneq ($(MAKECMDGOALS),clean)
# board specific includes
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_QURT)
include $(MK_DIR)/board_qurt.mk
endif
endif
endif
因为前段时间搭建APM的编译环境,就是以"px4-v2"make编译的,那就以"px4-v2"为例,以此包含help.mk,targets.mk,sketch_sources.mk以及以及board_px4.mk,前面三个是通用的,而board_px4.mk则是跟硬件平台有关,这里就是依据我们之前确定的HAL_BOARD来选择的。
targets.mk是对一些通用目标的响应,主要是支持一些其他类型硬件平台,同时还包含进了module.mk;
module.mk:
define echowarning
@echo "WARNING: $1" >&2
endef
define echoallwarnings
$(call echowarning)
$(call echowarning)
$(call echowarning,make build system is deprecated for Linux boards)
$(call echowarning,new features are not going to be added anymore)
$(call echowarning,See README-WAF.md: https://github.com/ArduPilot/ardupilot/blob/master/README-WAF.md)
$(call echowarning)
$(call echowarning,The make build system will soon be removed)
$(call echowarning)
$(call echowarning)
endef
CHECK_MODULES:
$(if $(BUILDSYS_DEPRECATED),$(call echoallwarnings))
$(v)$(MK_DIR)/check_modules.sh
.PHONY: CHECK_MODULES
all: CHECK_MODULES
module-update:
git submodule update
这个后面会用到,对于px4平台,由于其代码工程包含子模块,每次编译都会执行CHECK_MODULES。
sketch_sources.mk会基于make.inc文件定义SKETCHLIBS,SKETCHLIBNAMES等变量,SRCROOT是在environ.mk中规定的,在此例中,SRCROOT就是“……/ardupilot/arducopter”,在此路径中,可以找到mak.inc文件:
LIBRARIES += AP_Common
LIBRARIES += AP_Menu
LIBRARIES += AP_AdvancedFailsafe
LIBRARIES += AP_Param
LIBRARIES += StorageManager
LIBRARIES += GCS
LIBRARIES += GCS_MAVLink
LIBRARIES += AP_SerialManager
LIBRARIES += AP_GPS
LIBRARIES += DataFlash
LIBRARIES += AP_Baro
LIBRARIES += AP_Compass
LIBRARIES += AP_Math
LIBRARIES += AP_InertialSensor
LIBRARIES += AP_AccelCal
LIBRARIES += AP_AHRS
LIBRARIES += AP_NavEKF2
LIBRARIES += AP_NavEKF3
LIBRARIES += AP_Mission
LIBRARIES += AP_Rally
LIBRARIES += AC_PID
LIBRARIES += AC_PI_2D
LIBRARIES += AC_HELI_PID
LIBRARIES += AC_P
LIBRARIES += AC_AttitudeControl
LIBRARIES += AC_AttitudeControl_Heli
LIBRARIES += AC_PosControl
LIBRARIES += RC_Channel
LIBRARIES += AP_Motors
LIBRARIES += AP_RangeFinder
LIBRARIES += AP_OpticalFlow
LIBRARIES += AP_RSSI
LIBRARIES += Filter
LIBRARIES += AP_Buffer
LIBRARIES += AP_Relay
LIBRARIES += AP_ServoRelayEvents
LIBRARIES += AP_Camera
LIBRARIES += AP_Mount
LIBRARIES += AP_Airspeed
LIBRARIES += AP_Vehicle
LIBRARIES += AP_InertialNav
LIBRARIES += AC_WPNav
LIBRARIES += AC_Circle
LIBRARIES += AP_Declination
LIBRARIES += AC_Fence
LIBRARIES += AC_Avoidance
LIBRARIES += AP_Scheduler
LIBRARIES += AP_RCMapper
LIBRARIES += AP_Notify
LIBRARIES += AP_BattMonitor
LIBRARIES += AP_BoardConfig
LIBRARIES += AP_Frsky_Telem
LIBRARIES += AC_Sprayer
LIBRARIES += AP_Parachute
LIBRARIES += AP_LandingGear
LIBRARIES += AP_Terrain
LIBRARIES += AP_RPM
LIBRARIES += AC_PrecLand
LIBRARIES += AP_IRLock
LIBRARIES += AC_InputManager
LIBRARIES += AP_ADSB
LIBRARIES += AP_Avoidance
LIBRARIES += AP_Proximity
LIBRARIES += AP_Stats
LIBRARIES += AP_Gripper
LIBRARIES += AP_Beacon
LIBRARIES += AP_Arming
LIBRARIES += AP_VisualOdom
该文件中定义了LIBRARIES 变量。SKETCHLIBS变量是根据LIBRARIES 定义的。sketch_sources.mk同时也定义了一些指令的响应规则。
还在继续…
针对board_px4.mk
TOOLCHAIN = NATIVE
include $(MK_DIR)/find_tools.mk
include $(MK_DIR)/px4_targets.mk
打开px4_targets.mk我们可以看到文件对PX4_ROOT,NUTTX_SRC和UAVCAN_DIR进行赋值处理,这其实就是Ardupilot用到的三个子模块PX4Firmware、PX4NuttX和uavcan的文件路径。然后就对EXTRAFLAGS变量进行扩展,EXTRAFLAGS表示附加标示,之前在environ.mk文件中已经对其添加了主模块的git版本,在这里会继续添加三个子模块的git版本号,git版本号用于统一主模块和子模块的版本对应关系,在git管理中应用。
在px4_targets.mk文件我们可以看到对px4-v2命令的很多定义:
px4-v2: $(BUILDROOT)/make.flags CHECK_MODULES $(MAVLINK_HEADERS) $(UAVCAN_HEADERS) $(PX4_ROOT)/Archives/px4fmu-v2.export $(SKETCHCPP) module_mk px4-io-v2
$(v) echo Building px4-v2
$(RULEHDR)
$(v) cp $(PX4_V2_CONFIG_FILE) $(PX4_ROOT)/makefiles/nuttx/
$(PX4_MAKE) px4fmu-v2_APM
$(v) arm-none-eabi-size $(PX4_ROOT)/Build/px4fmu-v2_APM.build/firmware.elf
$(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
# common header for rules, prints what is being built
define RULEHDR
@echo %% $(subst $(BUILDROOT)/,,$@)
@mkdir -p $(dir $@)
endef
该指令会在命令行中打印一些基本信息,并且将这些信息存放在
“…\Ardupilot\Build.ArduCopter\make.flags”文件中。
CHECK_MODULES在modules.mk中定义:
CHECK_MODULES:
$(if $(BUILDSYS_DEPRECATED),$(call echoallwarnings))
$(v)$(MK_DIR)/check_modules.sh
.PHONY: CHECK_MODULES
all: CHECK_MODULES
module-update:
git submodule update
调用脚本文件check_modules.sh进行子模块检测。
$(PX4_ROOT)/Archives/px4fmu-v2.export是文件。
$(SKETCHCPP)是文件arducopter.cpp。
笔者目前只看到这个地步,任重道远,欢迎留言交流。