由于笔者没学过Linux等系统,对make文件所知甚少,本节分析可能有大量错误,只提供参考,随着技术积累,以后会回过头改正错误的地方,也非常欢迎提出指导意见。
其中分析大多数参考网上解释,加上自己的理解,对make源码逐行阅读并做了相关注释和分析。
统一规定:红字是注释分析;代码部分左边是程序,右边是注释。
下面开始分析
首先打开ardupilot/Arducopter/makefile
include ../mk/apm.mk
引用其他mk所以去看看apm.mk是什么
# find the mk/ directory, which is where this makefile fragment # lives. (patsubst strips the trailing slash.) SYSTYPE := $(shell uname) 系统环境是win还是Linux?
下述程序是为了根据系统的类型确定不同的工作目录MK_DIR
我们始终需要记住一件事,那就是我们在哪个目录编译,以及我们的编译命令。编译目录为: ardupilot/ArduCopter,编译命令我们用的是“make px4”,但我们也可以改变命令编译我们需要的。
ifneq ($(findstring CYGWIN, $(SYSTYPE)),) ifeq (<arg1>, <arg2>)比较参数“arg1”和“arg2”的值是否相同, (判断系统环境是否不为CYGWIN) 不同为真;ifneq (<arg1>, <arg2>),相同为真 $(findstring find,in) $(findstring a,a b c) $(findstring a,b c) 结果分别为‘a’ and ‘’ MK_DIR := $(shell cygpath -m ../mk) cygpath用于转换Unix和Windows的格式路径 -m, --mixed like --windows, but with regular slashes (C:/WINNT) -w, --windows print Windows form of NAMEs (C:\WINNT) else MK_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) $应该是取值的意思 根据gnu make定义,gnu make 会自动将所有读取的makefile路径都会加入到MAKEFILE_LIST变量中, 而且是按照读取的先后顺序添加。例如
#If a makefile named Makefile has this content: name1 := $(lastword $(MAKEFILE_LIST)) include inc.mk name2 := $(lastword $(MAKEFILE_LIST)) all: @echo name1 = $(name1) @echo name2 = $(name2) #then you would expect to see this output: #name1 = Makefile #name2 = inc.mk
格式:$(patsubst ,,)
名称:模式字符串替换函数——patsubst
功能:查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式,如果匹配的话,则以替换。这里,可以包括通配符“%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个“%”将是中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)
返回:函数返回被替换过后的字符串。
示例:
$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o
bar.o”
而dir是提取路径
$(dir src/foo.c hacks) #produces the result ‘src/ ./’.
所以$(patsubst%/,%,$(dir $(lastword $(MAKEFILE_LIST))))返回了当前的mk路径,到这里也就得出了 MK_DIR
信息。
endif include $(MK_DIR)/environ.mk 添加environ.mk文件,关键的环境变量在里面实现 # short-circuit build for the configure target ifeq ($(MAKECMDGOALS),configure) 如果编译参数是configure,则添加configure.mk include $(MK_DIR)/configure.mk configure.mk的内容为 configure: else @echo "make configure is no longer required" 所以可以直接执行下面的程序 # short-circuit build for the help target include $(MK_DIR)/help.mk 添加help.mk help.mk的内容主要是打印一些信息,对理解pixhawk程序有点帮助 # common makefile components 添加常用makefile include $(MK_DIR)/targets.mk 添加targets.mk include $(MK_DIR)/sketch_sources.mk 添加sketch_sources.mk ifneq ($(MAKECMDGOALS),clean) 判断命令不为clean是否为真 # board specific includes 根据板子添加相应的.mk文件 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 ifeq ($(HAL_BOARD),HAL_BOARD_QURT) include $(MK_DIR)/board_qurt.mk endif endif endif
这个时候可能我们会很迷惑, $(HAL_BOARD)的值到底是多少?
但其实我们在执行“make px4”命令编译的时候会有一行信息:
HAL_BOARD=HAL_BOARD_PX4HAL_BOARD_SUBTYPE= TOOLCHAIN=NATIVEEXTRAFLAGS=-DGIT_VERSION="b6d361a3" -DNUTTX_GIT_VERSION="eba6b56f"-DPX4_GIT_VERSION="cf208916"
这行信息非常清楚地告诉了我们用的是“$(MK_DIR)/board_px4.mk”文件。
所以apm.mk根据各种判断,添加environ.mk文件(关键的环境变量在里面实现), 添加help.mk(打印相关信息),添加targets.mk,添加sketch_sources.mk,添加board_px4.mk
接下来就分别分析environ.mk,targets.mk,sketch_sources.mk,board_px4.mk
environ.mk 关键的环境变量在里面实现
windows下的环境变量简单来说就是将某些数据,文件或文件夹设置为系统默认值,这样你调用的时候就不用给出完整路径和地址或进行设置,直接用名字就可以了。
这里实现的是查找SKETCHBOOK的位置,根据源文件路径判断SKETCH名称,算出要build的哪里,建立BUILDROOT目录,根据编译参数确定HAL_BOARD类型,等等为系统实现做基础工作。
以下是部分代码分析:
GIT_VERSION := $(shell git rev-parse HEAD | cut -c1-8) 获得git 版本 # # Locate the sketch sources based on the initial Makefile's path # SRCROOT := $(realpath $(dir $(firstword $(MAKEFILE_LIST)))) 通过判断是否有libraries来获取当前 ifneq ($(findstring CYGWIN, $(SYSTYPE)),) make的真实路径,不存在则返回为空 # Workaround a $(realpath ) bug on Cygwin libraries下的测试 makefile都是指向apm ifeq ($(SRCROOT),) SRCROOT := $(shell cygpath -m ${CURDIR}) $(warning your realpath function is not working) $(warning > setting SRCROOT to $(SRCROOT)) endif endif # # We need to know the location of the sketchbook. If it hasn't been overridden, # try the parent of the current directory. If there is no libraries directory # there, assume that we are in a library's examples directory and try backing up # further. 通过判断是否有libraries来获取当前SKETCHBOOK的位置 # 查找SKETCHBOOK的位置 ifeq ($(SKETCHBOOK),) wildcard通配符,如:列出该目录下所有的C文件为$(wildcard *.c) SKETCHBOOK := $(shell cd $(SRCROOT)/.. && pwd) ifeq ($(wildcard $(SKETCHBOOK)/libraries),) 此位置为该项目的根目录 SKETCHBOOK := $(shell cd $(SRCROOT)/../.. && pwd) endif ifeq ($(wildcard $(SKETCHBOOK)/libraries),) SKETCHBOOK := $(shell cd $(SRCROOT)/../../.. && pwd) endif ifeq ($(wildcard $(SKETCHBOOK)/libraries),) SKETCHBOOK := $(shell cd $(SRCROOT)/../../../.. && pwd) endif ifeq ($(wildcard $(SKETCHBOOK)/libraries),) $(error ERROR: cannot determine sketchbook location - please specify on the commandline with SKETCHBOOK=<path>) endif else ifeq ($(wildcard $(SKETCHBOOK)/libraries),) $(warning WARNING: sketchbook directory $(SKETCHBOOK) contains no libraries) endif endif ifneq ($(findstring CYGWIN, $(SYSTYPE)),) 如果是在win平台,转换为win格式的路径 # Convert cygwin path into a windows normal path SKETCHBOOK := $(shell cygpath ${SKETCHBOOK}) endif ifneq ($(wildcard $(SKETCHBOOK)/config.mk),) 如果config.mk不是空,include他 $(info Reading $(SKETCHBOOK)/config.mk) include $(SKETCHBOOK)/config.mk endif ifneq ($(wildcard $(SKETCHBOOK)/developer.mk),) 如果developer.mk不是空,include他 $(info Reading $(SKETCHBOOK)/developer.mk) include $(SKETCHBOOK)/developer.mk endif # # Work out the sketch name from the name of the source directory. #找出是哪个sketch,他的名字 SKETCH := $(lastword $(subst /, ,$(SRCROOT))) 根据源文件路径判断SKETCH名称 # Workaround a $(lastword ) bug on Cygwin subst是将源文件路径中所有的/替换为空格。 ifeq ($(SKETCH),) WORDLIST := $(subst /, ,$(SRCROOT)) SKETCH := $(word $(words $(WORDLIST)),$(WORDLIST)) Endif # # Work out where we are going to be building things #算出哪是我们要build的事 TMPDIR ?= /tmp ifneq ($(findstring px4, $(MAKECMDGOALS)),) # when building px4 we need all sources to be inside the sketchbook directory # as the NuttX build system relies on it BUILDROOT := $(SKETCHBOOK)/Build.$(SKETCH) 建立BUILDROOT目录:根据编译目标$(MAKECMDGOALS)建 Endif 立相关目录,如果编译目标不匹配,建立临时目录。 ifneq ($(findstring vrbrain, $(MAKECMDGOALS)),) # when building vrbrain we need all sources to be inside the sketchbook directory # as the NuttX build system relies on it BUILDROOT := $(SKETCHBOOK)/Build.$(SKETCH) endif ifneq ($(findstring vrubrain, $(MAKECMDGOALS)),) # when building vrbrain we need all sources to be inside the sketchbook directory # as the NuttX build system relies on it BUILDROOT := $(SKETCHBOOK)/Build.$(SKETCH) endif ifneq ($(findstring vrhero, $(MAKECMDGOALS)),) # when building vrbrain we need all sources to be inside the sketchbook directory # as the NuttX build system relies on it BUILDROOT := $(SKETCHBOOK)/Build.$(SKETCH) Endif ifneq ($(APPDIR),) # this is a recusive PX4 build HAL_BOARD = HAL_BOARD_PX4 endif # handle target based overrides for board type根据编译参数确定HAL_BOARD类型。 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
board_px4.mk文件代码:
TOOLCHAIN = NATIVE include $(MK_DIR)/find_tools.mk include $(MK_DIR)/px4_targets.mk find_tools.mk就是寻找编译工具 FIND_TOOL = $(firstword $(wildcard $(addsuffix /$(1),$(TOOLPATH)))) 快速查找 px4_targets.mk 部分代码分析 NUTTX_GIT_VERSION := $(shell cd $(NUTTX_SRC) && git rev-parse HEAD | cut -c1-8) 获取NUTTX_GIT_VERSION PX4_GIT_VERSION:= $(shell cd $(PX4_ROOT) && git rev-parse HEAD | cut -c1-8) 及PX4_GIT_VERSION版本 EXTRAFLAGS += -DNUTTX_GIT_VERSION="\"$(NUTTX_GIT_VERSION)\"" 添加EXTRAFLAGS EXTRAFLAGS += -DPX4_GIT_VERSION="\"$(PX4_GIT_VERSION)\"" = 是最基本的赋值 := 是覆盖之前的值 EXTRAFLAGS += -DUAVCAN=1 ?= 是如果没有被赋值过就赋予等号后面的值 += 是添加等号后面的值 PX4_V2_CONFIG_FILE=$(MK_DIR)/PX4/config_px4fmu-v2_APM.mk 更新PX4_V2_CONFIG_FILE配置文件 在这里又调用了px4_common.mk,这是一个很重要的东西;如定义了ROMFS_ROOT,定义了BUILTIN_COMMANDS,其中strip为去除空格; SKETCHFLAGS=$(SKETCHLIBINCLUDES) -DARDUPILOT_BUILD -DTESTS_MATHLIB_DISABLE -DCONFIG_HAL_BOARD=HAL_BOARD_PX4 -DSKETCHNAME="\\\"$(SKETCH)\\\"" -DSKETCH_MAIN=ArduPilot_main -DAPM_BUILD_DIRECTORY=APM_BUILD_$(SKETCH) WARNFLAGS = -Wall -Wextra -Wlogical-op -Werror -Wno-unknown-pragmas -Wno-redundant-decls -Wno-psabi -Wno-packed -Wno-error=double-promotion -Wno-error=unused-variable -Wno-error=reorder -Wno-error=float-equal -Wno-error=pmf-conversions -Wno-error=missing-declarations -Wno-error=unused-function OPTFLAGS = -fsingle-precision-constant 定义SKETCHFLAGS/WARNFLAGS/OPTFLAGS,其中-D表示为define,-I为添加库 PYTHONPATH=$(SKETCHBOOK)/mk/PX4/Tools/genmsg/src:$(SKETCHBOOK)/mk/PX4/Tools/gencpp/src确定PYTHONPATH路径 PX4_MAKE = $(v)+ GIT_SUBMODULES_ARE_EVIL=1 ARDUPILOT_BUILD=1 $(MAKE) -C $(SKETCHBOOK) -f $(PX4_ROOT)/Makefile.make EXTRADEFINES="$(SKETCHFLAGS) $(WARNFLAGS) $(OPTFLAGS) "'$(EXTRAFLAGS)' APM_MODULE_DIR=$(SKETCHBOOK) SKETCHBOOK=$(SKETCHBOOK) CCACHE=$(CCACHE) PX4_ROOT=$(PX4_ROOT) NUTTX_SRC=$(NUTTX_SRC) MAXOPTIMIZATION="-Os" UAVCAN_DIR=$(UAVCAN_DIR) PX4_MAKE_ARCHIVES = $(MAKE) -C $(PX4_ROOT) -f $(PX4_ROOT)/Makefile.make NUTTX_SRC=$(NUTTX_SRC) CCACHE=$(CCACHE) archives MAXOPTIMIZATION="-Os" 定义PX4_MAKE及PX4_MAKE_ARCHIVES $(MAKE) –C 表示进入指定文件夹执行 $(PX4_ROOT)/Makefile.make 编译PX4原生代码的 HASHADDER_FLAGS += --ardupilot "$(SKETCHBOOK)" 添加HASHADDER_FLAGS ifneq ($(wildcard $(PX4_ROOT)),) HASHADDER_FLAGS += --px4 "$(PX4_ROOT)" endif ifneq ($(wildcard $(NUTTX_SRC)/..),) HASHADDER_FLAGS += --nuttx "$(NUTTX_SRC)/.." endif HASHADDER_FLAGS += --uavcan "$(UAVCAN_DIR)" .PHONY: module_mk 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 = 4096" >> $(SKETCHBOOK)/module.mk.new $(v) echo "EXTRACXXFLAGS = -Wframe-larger-than=1300" >> $(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 px4-v2: $(BUILDROOT)/make.flags CHECK_MODULES $(MAVLINK_HEADERS) 最后建立px4的相关目标$(PX4_ROOT)/Archives/px4fmu-v2.export $(SKETCHCPP) module_mk px4-io-v2 $(RULEHDR) 注意:px4-v2的依赖条件中包含了$(SKETCHCPP),从这里调用该工程下的源文件; $(v) cp $(PX4_V2_CONFIG_FILE) $(PX4_ROOT)/makefiles/nuttx/ cp 复制copy $(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"
这个步骤就是编译 px4fmu-v2_APM的过程,仔细分析这段代码
首先通过echo命令将$(xxx)真正意思打印出来,分别用1到7对应起来
echo "1$(BUILDROOT) 2$(MAVLINK_HEADERS) 3$(PX4_ROOT) 4$(PX4_V2_CONFIG_FILE)5$(PX4_MAKE) 6$(SKETCH) 7$(SKETCHBOOK)" 1/c/ardupilot/Build.ArduCopter 2/c/ardupilot/Build.ArduCopter/libraries/GCS_MAVLink/include/mavlink/v1.0/ardupilotmega/mavlink.h 3/c/ardupilot/modules/PX4Firmware 4../mk/PX4/config_px4fmu-v2_APM.mk [email protected]+ GIT_SUBMODULES_ARE_EVIL=1 ARDUPILOT_BUILD=1 make -C /c/ardupilot -f /c/ardupilot/modules/PX4Firmware/Makefile.make EXTRADEFINES=-I/c/ardupilot/libraries/ -I/c/ardupilot/Build.ArduCopter/libraries/ -I/c/ardupilot/Build.ArduCopter/libraries/GCS_MAVLink/ -DARDUPILOT_BUILD -DTESTS_MATHLIB_DISABLE -DCONFIG_HAL_BOARD=HAL_BOARD_PX4 -DSKETCHNAME=\"ArduCopter\" -DSKETCH_MAIN=ArduPilot_main -DAPM_BUILD_DIRECTORY=APM_BUILD_ArduCopter -Wall -Wextra -Wlogical-op -Werror -Wno-unknown-pragmas -Wno-redundant-decls -Wno-psabi -Wno-packed -Wno-error=double-promotion -Wno-error=unused-variable -Wno-error=reorder -Wno-error=float-equal -Wno-error=pmf-conversions -Wno-error=missing-declarations -Wno-error=unused-function -fsingle-precision-constant '-DGIT_VERSION="3c5287e8" -I/c/ardupilot/libraries/AP_Common/missing -DNUTTX_GIT_VERSION="579e82d4" -DPX4_GIT_VERSION="55491083" -DUAVCAN=1 -DHAVE_STD_NULLPTR_T=0 -I/c/ardupilot/Build.ArduCopter/libraries/GCS_MAVLink/include/mavlink' APM_MODULE_DIR=/c/ardupilot SKETCHBOOK=/c/ardupilot CCACHE= PX4_ROOT=/c/ardupilot/modules/PX4Firmware NUTTX_SRC=/c/ardupilot/modules/PX4NuttX/nuttx/ MAXOPTIMIZATION=-Os UAVCAN_DIR=/c/ardupilot/modules/uavcan/ 6ArduCopter 7/c/ardupilot
然后根据脚本的语法,可知主要是$(PX4_MAKE)px4fmu-v2_APM完成编译任务
$(PX4_MAKE)真正意思对应上面第5条,再根据脚本基本语法可知
make-C /c/ardupilot -f /c/ardupilot/modules/PX4Firmware/Makefile.make主要完成编译任务
-D是定义、-I是添加库、=是赋值
由此可定位到/c/ardupilot/modules/PX4Firmware/Makefile.make
但是Makefile.make文件里没有px4fmu-v2_APM的目标,说明目标被处理过了。于是只好去看编译输出信息了
%%%% %%%% Building px4fmu-v2_APM in /c/ardupilot/modules/PX4Firmware/Build/px4fmu-v2_APM.build/ %%%%
通过这串信息,匹配到了这样一个目标
# # Generate FIRMWARES. # .PHONY: $(FIRMWARES) $(BUILD_DIR)%.build/firmware.px4: config = $(patsubst $(BUILD_DIR)%.build/firmware.px4,%,[email protected]) $(BUILD_DIR)%.build/firmware.px4: work_dir = $(BUILD_DIR)$(config).build/ $(FIRMWARES): $(BUILD_DIR)%.build/firmware.px4: generateuorbtopicheaders checksubmodules @$(ECHO) %%%% @$(ECHO) %%%% Building $(config) in $(work_dir) @$(ECHO) %%%% $(Q) $(MKDIR) -p $(work_dir) $(Q)+ $(MAKE) -r -C $(work_dir) \ 从这开始,完成编译工作 -f $(PX4_MK_DIR)firmware.mk \ 语句尾部“\”表示语句连接 CONFIG=$(config) WORK_DIR=$(work_dir) $(FIRMWARE_GOAL) $(PX4_MK_DIR)firmware.mk的真正意思是/c/ardupilot/modules/PX4Firmware/makefiles/firmware.mk
这样就定位到firmware.mk文件 此处主要分析px4_targets.mk,firmware.mk文件内容较多,下节再分析。
px4-io-v2: $(PX4_ROOT)/Archives/px4io-v2.export $(v)+ $(MAKE) -C $(PX4_ROOT) -f $(PX4_ROOT)/Makefile.make px4io-v2_default EXTRADEFINES="-DARDUPILOT_BUILD" $(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/ mkdir 创建目录 $(v) cp px4io-v2.bin $(MK_DIR)/PX4/ROMFS/px4io/px4io.bin $(v) mkdir -p $(MK_DIR)/PX4/ROMFS/bootloader/ $(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"
实际的内容是 make -C /e/ardupilot/modules/PX4Firmware px4io-v2_default 编译firmware基本文件 /bin/rm -f px4io-v2.bin cp /e/ardupilot/modules/PX4Firmware/Build/px4io-v2_default.build/firmware.bin px4io-v2.bin cp /e/ardupilot/modules/PX4Firmware/Images/px4io-v2_default.bin px4io-v2.bin cp /e/ardupilot/modules/PX4Firmware/Build/px4io-v2_default.build/firmware.elf px4io-v2.elf mkdir -p ../mk/PX4/ROMFS/px4io/ rm -f ../mk/PX4/ROMFS/px4io/px4io.bin cp px4io-v2.bin ../mk/PX4/ROMFS/px4io/px4io.bin mkdir -p ../mk/PX4/ROMFS/bootloader/ rm -f ../mk/PX4/ROMFS/bootloader/fmu_bl.bin cp /c/ardupilot/mk/PX4/bootloader/px4fmuv2_bl.bin ../mk/PX4/ROMFS/bootloader/fmu_bl.bin PX4IOv2 Firmware is in px4io-v2.bin 生成.bin .elf 文件
.NOTPARALLEL: $(PX4_ROOT)/Archives/px4fmu-v1.export $(PX4_ROOT)/Archives/px4fmu-v2.export $(PX4_ROOT)/Archives/px4fmu-v4.export $(PX4_ROOT)/Archives/px4io-v1.export $(PX4_ROOT)/Archives/px4io-v2.export
NOTPARALLEL新语法:Makefile中,如果出现目标“.NOPARALLEL”,则所有命令按照串行方式执行,即使存在make的命令行参数“-j”。但在递归调用的字make进程中,命令可以并行执行。此目标不应该有依赖文件,所有出现的依赖文件将被忽略。
px4_targets.mk
px4_common.mk添加驱动、系统命令等模块
firmware.mk 注释:GenericMakefile for PX4 firmware images. 通用MakefilePX4固件镜像。
include $(MK_DIR)/setup.mk /c/ardupilot/modules/PX4Firmware/makefiles/setup.mk 目的是Get path and tool config 即设置路径和tool include $(PX4_MK_DIR)/$(PX4_TARGET_OS).mk /c/ardupilot/modules/PX4Firmware/makefiles/nuttx.mk 目的是Rules and definitions related to handling the NuttX export archives when building firmware. 即Nuttx输出的相关规则和定义 # Make a list of the object files we expect to build from modules # Note that this path will typically contain a double-slash at the WORK_DIR boundary; this must be # preserved as it is used below to get the absolute path for the module.mk file correct. # MODULE_OBJS := $(foreach path,$(dir $(MODULE_MKFILES)),$(WORK_DIR)$(path)module.pre.o) # rules to build module objects .PHONY: $(MODULE_OBJS) $(MODULE_OBJS): relpath = $(patsubst $(WORK_DIR)%,%,[email protected]) $(MODULE_OBJS): mkfile = $(patsubst %module.pre.o,%module.mk,$(relpath)) $(MODULE_OBJS): workdir = $(@D) $(MODULE_OBJS): $(GLOBAL_DEPS) $(NUTTX_CONFIG_HEADER) $(Q) $(MKDIR) -p $(workdir) $(Q)+ $(MAKE) -r -f $(PX4_MK_DIR)module.mk -C $(workdir) MODULE_WORK_DIR=$(workdir) [email protected] MODULE_MK=$(mkfile) MODULE_NAME=$(lastword $(subst /, ,$(workdir))) Module
这段是module的obj文件生成规则,规定了module的路径、makefile文件路径、工作目录等,并生成obj文件
# make a list of phony clean targets for modules MODULE_CLEANS := $(foreach path,$(dir $(MODULE_MKFILES)),$(WORK_DIR)$(path)/clean) # rules to clean modules .PHONY: $(MODULE_CLEANS) $(MODULE_CLEANS): relpath = $(patsubst $(WORK_DIR)%,%,[email protected]) $(MODULE_CLEANS): mkfile = $(patsubst %clean,%module.mk,$(relpath)) $(MODULE_CLEANS): @$(ECHO) %% cleaning using $(mkfile) $(Q)+ $(MAKE) -r -f $(PX4_MK_DIR)module.mk MODULE_WORK_DIR=$(dir [email protected]) MODULE_MK=$(mkfile) Clean
这段是clean module的规则,清除的路径、makefile的路径。
# Make a list of the archive files we expect to build from libraries # Note that this path will typically contain a double-slash at the WORK_DIR boundary; this must be # preserved as it is used below to get the absolute path for the library.mk file correct. # LIBRARY_LIBS := $(foreach path,$(dir $(LIBRARY_MKFILES)),$(WORK_DIR)$(path)library.a) # rules to build module objects .PHONY: $(LIBRARY_LIBS) $(LIBRARY_LIBS): relpath = $(patsubst $(WORK_DIR)%,%,[email protected]) $(LIBRARY_LIBS): mkfile = $(patsubst %library.a,%library.mk,$(relpath)) $(LIBRARY_LIBS): workdir = $(@D) $(LIBRARY_LIBS): $(GLOBAL_DEPS) $(NUTTX_CONFIG_HEADER) $(Q) $(MKDIR) -p $(workdir) $(Q)+ $(MAKE) -r -f $(PX4_MK_DIR)library.mk -C $(workdir) LIBRARY_WORK_DIR=$(workdir) [email protected] LIBRARY_MK=$(mkfile) LIBRARY_NAME=$(lastword $(subst /, ,$(workdir))) Library
这段是库的obj文件生成规则,规定了库的路径、makefile文件路径、工作目录等,并生成obj文件
# make a list of phony clean targets for modules LIBRARY_CLEANS := $(foreach path,$(dir $(LIBRARY_MKFILES)),$(WORK_DIR)$(path)/clean) # rules to clean modules .PHONY: $(LIBRARY_CLEANS) $(LIBRARY_CLEANS): relpath = $(patsubst $(WORK_DIR)%,%,[email protected]) $(LIBRARY_CLEANS): mkfile = $(patsubst %clean,%library.mk,$(relpath)) $(LIBRARY_CLEANS): @$(ECHO) %% cleaning using $(mkfile) $(Q)+ $(MAKE) -r -f $(PX4_MK_DIR)library.mk LIBRARY_WORK_DIR=$(dir [email protected]) LIBRARY_MK=$(mkfile) clean
这段是clean
库的规则,清除的路径、makefile的路径。
以下是直接复制的,主要实现相关的编译功能,有相应的注释。
################################################################################ # Builtin command list generation ################################################################################ # # Builtin commands can be generated by the configuration, in which case they # must refer to commands that already exist, or indirectly generated by modules # when they are built. # # The configuration supplies builtin command information in the BUILTIN_COMMANDS # variable. Applications make empty files in $(WORK_DIR)/builtin_commands whose # filename contains the same information. # # In each case, the command information consists of four fields separated with a # period. These fields are the command's name, its thread priority, its stack size # and the name of the function to call when starting the thread. # # BUILTIN_COMMANDS # Contains a list of built-in commands not explicitly provided # by modules / libraries. Each entry in this list is formatted # as <command>.<priority>.<stacksize>.<entrypoint> BUILTIN_CSRC = $(WORK_DIR)builtin_commands.c # command definitions from modules (may be empty at Makefile parsing time...) MODULE_COMMANDS = $(subst COMMAND.,,$(notdir $(wildcard $(WORK_DIR)builtin_commands/COMMAND.*))) # We must have at least one pre-defined builtin command in order to generate # any of this. # ifneq ($(BUILTIN_COMMANDS),) # (BUILTIN_PROTO,<cmdspec>,<outputfile>) define BUILTIN_PROTO $(ECHO) 'extern int $(word 4,$1)(int argc, char *argv[]);' >> $2; endef # (BUILTIN_DEF,<cmdspec>,<outputfile>) define BUILTIN_DEF $(ECHO) ' {"$(word 1,$1)", $(word 2,$1), $(word 3,$1), $(word 4,$1)},' >> $2; endef # Don't generate until modules have updated their command files $(BUILTIN_CSRC): $(GLOBAL_DEPS) $(MODULE_OBJS) $(MODULE_MKFILES) $(BUILTIN_COMMAND_FILES) @$(ECHO) "CMDS: [email protected]" $(Q) $(ECHO) '/* builtin command list - automatically generated, do not edit */' > [email protected] $(Q) $(ECHO) '#include <nuttx/config.h>' >> [email protected] $(Q) $(ECHO) '#include <nuttx/binfmt/builtin.h>' >> [email protected] $(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_PROTO,$(subst ., ,$(spec)),[email protected])) $(Q) $(foreach spec,$(MODULE_COMMANDS),$(call BUILTIN_PROTO,$(subst ., ,$(spec)),[email protected])) $(Q) $(ECHO) 'const struct builtin_s g_builtins[] = {' >> [email protected] $(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_DEF,$(subst ., ,$(spec)),[email protected])) $(Q) $(foreach spec,$(MODULE_COMMANDS),$(call BUILTIN_DEF,$(subst ., ,$(spec)),[email protected])) $(Q) $(ECHO) ' {NULL, 0, 0, NULL}' >> [email protected] $(Q) $(ECHO) '};' >> [email protected] $(Q) $(ECHO) 'const int g_builtin_count = $(words $(BUILTIN_COMMANDS) $(MODULE_COMMANDS));' >> [email protected] SRCS += $(BUILTIN_CSRC) EXTRA_CLEANS += $(BUILTIN_CSRC) endif endif ################################################################################ # Default SRCS generation ################################################################################ # # If there are no SRCS, the build will fail; in that case, generate an empty # source file. # ifeq ($(SRCS),) EMPTY_SRC = $(WORK_DIR)empty.c $(EMPTY_SRC): $(Q) $(ECHO) '/* this is an empty file */' > [email protected] SRCS += $(EMPTY_SRC) endif ################################################################################ # Build rules ################################################################################ ifeq ($(PX4_TARGET_OS),nuttx) # # What we're going to build. # PRODUCT_BUNDLE = $(WORK_DIR)firmware.px4 PRODUCT_BIN = $(WORK_DIR)firmware.bin PRODUCT_ELF = $(WORK_DIR)firmware.elf PRODUCT_PARAMXML = $(WORK_DIR)/parameters.xml .PHONY: firmware firmware: $(PRODUCT_BUNDLE) endif # # Object files we will generate from sources # OBJS := $(foreach src,$(SRCS),$(WORK_DIR)$(src).o) # # SRCS -> OBJS rules 将源文件编译成obj文件 # $(OBJS): $(GLOBAL_DEPS) $(filter %.c.o,$(OBJS)): $(WORK_DIR)%.c.o: %.c $(GLOBAL_DEPS) $(call COMPILE,$<,[email protected]) $(filter %.cpp.o,$(OBJS)): $(WORK_DIR)%.cpp.o: %.cpp $(GLOBAL_DEPS) $(call COMPILEXX,$<,[email protected]) $(filter %.S.o,$(OBJS)): $(WORK_DIR)%.S.o: %.S $(GLOBAL_DEPS) $(call ASSEMBLE,$<,[email protected]) ifeq ($(PX4_TARGET_OS),nuttx) # # Built product rules # $(PRODUCT_BUNDLE): $(PRODUCT_BIN) @$(ECHO) %% Generating [email protected] ifdef GEN_PARAM_XML $(Q) $(PYTHON) $(PX4_BASE)/Tools/px_process_params.py --src-path $(PX4_BASE)/src --board CONFIG_ARCH_BOARD_$(CONFIG_BOARD) --xml $(Q) $(MKFW) --prototype $(IMAGE_DIR)/$(BOARD).prototype --git_identity $(PX4_BASE) --parameter_xml $(PRODUCT_PARAMXML) --image $< > [email protected] else $(Q) $(MKFW) --prototype $(IMAGE_DIR)/$(BOARD).prototype --git_identity $(PX4_BASE) --image $< > [email protected] endif $(PRODUCT_BIN): $(PRODUCT_ELF) $(call SYM_TO_BIN,$<,[email protected]) $(PRODUCT_ELF): $(OBJS) $(MODULE_OBJS) $(LIBRARY_LIBS) $(GLOBAL_DEPS) $(LINK_DEPS) $(MODULE_MKFILES) $(call LINK,[email protected],$(OBJS) $(MODULE_OBJS) $(LIBRARY_LIBS)) # # Utility rules 实用的规则 # .PHONY: upload upload: $(PRODUCT_BUNDLE) $(PRODUCT_BIN) $(Q) $(MAKE) -f $(PX4_MK_DIR)/upload.mk METHOD=serial CONFIG=$(CONFIG) BOARD=$(BOARD) BUNDLE=$(PRODUCT_BUNDLE) BIN=$(PRODUCT_BIN) .PHONY: clean clean: $(MODULE_CLEANS) @$(ECHO) %% cleaning $(Q) $(REMOVE) $(PRODUCT_BUNDLE) $(PRODUCT_BIN) $(PRODUCT_ELF) $(Q) $(REMOVE) $(OBJS) $(DEP_INCLUDES) $(EXTRA_CLEANS) $(Q) $(RMDIR) $(NUTTX_EXPORT_DIR) endif # Include the OS specific build rules # The rules must define the "firmware" make target # ifeq ($(PX4_TARGET_OS),nuttx) # TODO # Move above nuttx specific rules to $(MK_DIR)/nuttx_romfs.mk endif ifeq ($(PX4_TARGET_OS),posix) include $(MK_DIR)/posix_elf.mk endif ifeq ($(PX4_TARGET_OS),qurt) include $(MK_DIR)/qurt_elf.mk endif # # DEP_INCLUDES is defined by the toolchain include in terms of $(OBJS) # -include $(DEP_INCLUDES)
通过firmware.mk文件阅读,终于知道.px4是如何编译出来的了,如何添加lib、module,从而用于裁剪程序。其实.mk文件只是将ccs或者kile等软件的编译工作完成,自行设置编译规则、编译什么文件、分配芯片占用空间等等。至于具体程序的裁剪,若在.mk文件中去掉lib或者module,最后下载进板子的程序就不会有这部分内容,就没法运行;若.mk添加了,但在rcS、Rc.APM中不启动,只是占有了板子内存,并没有运行。
targets.mk,sketch_sources.mk暂时先放着