PX4编译脚本
-------- 转载请注明出处
-------- 2014-11-25.冷月追风
-------- email:merafour@163.com
参考网站:
http://dev.ardupilot.com/wiki/table-of-contents/
http://dev.ardupilot.com/wiki/building-with-eclipse/
http://dev.ardupilot.com/wiki/building-px4-for-linux-with-make/
http://dev.ardupilot.com/wiki/building-px4-with-make/
http://dev.ardupilot.com/wiki/building-px4-for-linux-with-make/
https://pixhawk.org/dev/start
编译环境:xubuntu
在这里我不特别区分 APM, ArduCopter, pixhawk, PX4, PIX。
实际上
pixhawk可以在 Windows, Linux, MAC上编译。这里选择 xubuntu作为开发环境为个人意愿,个人比较钟情与
Linux中简单而高效的命令行。有人做过测试,MAC编译代码只需几分钟,但是我毕竟没有 MAC也就只好退而求其次。
但其实我编译源码使用的也只是虚拟机而已。但即便是虚拟机也比在 Windows编译要高效得多。
1. 编译源码
源码编译参考 APM网站
http://dev.ardupilot.com/wiki/building-the-code/ 即可,根据自己的系统选择相应的编译教程。我用的是 Linux系统,因此参考
http://dev.ardupilot.com/wiki/building-px4-for-linux-with-make/ 进行编译。
现将其编译过程简单摘录如下:
Quick start
For Ubuntu, follow these steps to build the code. For other distributions, see the advanced instructions below.
1 Setup
Install git
:
sudo apt
-get
-qq
-y install git
Clone the source
:
git clone https
://github.com/diydrones/ardupilot.git
Run the install
-prereqs
-ubuntu.sh script
:
ardupilot
/Tools
/scripts
/install
-prereqs
-ubuntu.sh
-y
Reload the path (log
-out and log
-in to make permanent)
:
.
~/.profile
2 Build
Build
for ArduCopter
:
cd ardupilot
/ArduCopter
make configure
make px4
-v2
Build
for ArduPlane
:
cd ardupilot
/ArduPlane
make configure
make px4
-v2
2. 脚本分析
我们始终需要记住一件事,那就是我们在哪个目录编译,以及我们的编译命令。编译目录为: ardupilot/ArduCopter,编译命令我们用的是 “make px4”,但我们也可以改变命令编译我们需要的。
下面是我们的 Makefile:
include ..
/mk
/apm.mk
实际上这是调用了外部的 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
include $(MK_DIR)
/environ.mk
#
short-circuit build
for the configure target
ifeq ($(MAKECMDGOALS),configure)
include $(MK_DIR)
/configure.mk
else
# 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_AVR_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
这个时候可能我们会很迷惑, $(HAL_BOARD)的值到底是多少?
但其实我们在执行 “make px4”命令编译的时候会有一行信息:
HAL_BOARD
=HAL_BOARD_PX4 HAL_BOARD_SUBTYPE
= TOOLCHAIN
=NATIVE EXTRAFLAGS
=-DGIT_VERSION
="b6d361a3" -DNUTTX_GIT_VERSION
="eba6b56f" -DPX4_GIT_VERSION
="cf208916"
这行信息非常清楚地告诉了我们用的是 “$(MK_DIR)
/board_px4.mk”文件。但这个 mk文件内容其实也不多:
TOOLCHAIN
= NATIVE
include $(MK_DIR)
/find_tools.mk
include $(MK_DIR)
/px4_targets.mk
所以最终将是有 “$(MK_DIR)
/px4_targets.mk”中的脚本进行编译:
px4
-v1
: showflags $(PX4_ROOT)
/Archives
/px4fmu
-v1.export $(SKETCHCPP) module_mk px4
-io
-v1
$(RULEHDR)
$(v) rm
-f $(PX4_ROOT)
/makefiles
/$(PX4_V1_CONFIG_FILE)
$(v) cp $(PWD)
/$(PX4_V1_CONFIG_FILE) $(PX4_ROOT)
/makefiles
/
$(v) $(PX4_MAKE) px4fmu
-v1_APM
$(v)
/bin
/rm
-f $(SKETCH)
-v1.px4
$(v) cp $(PX4_ROOT)
/Images
/px4fmu
-v1_APM.px4 $(SKETCH)
-v1.px4
$(v) echo
"PX4 $(SKETCH) Firmware is in $(SKETCH)-v1.px4"
px4
-v2
: showflags $(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 $(PWD)
/$(PX4_V2_CONFIG_FILE) $(PX4_ROOT)
/makefiles
/
$(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) echo
"PX4 $(SKETCH) Firmware is in $(SKETCH)-v2.px4"
px4
: px4
-v1 px4
-v2
px4-io-v1: $(PX4_ROOT)/Archives/px4io-v1.export
$(v) make -C $(PX4_ROOT) px4io-v1_default
$(v) /bin/rm -f px4io-v1.bin
$(v) cp $(PX4_ROOT)/Images/px4io-v1_default.bin px4io-v1.bin
$(v) cp $(PX4_ROOT)/Build/px4io-v1_default.build/firmware.elf px4io-v1.elf
$(v) mkdir -p $(MK_DIR)/PX4/ROMFS/px4io/
$(v) rm -f $(MK_DIR)/PX4/ROMFS/px4io/px4io.bin
$(v) cp px4io-v1.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/px4fmu_bl.bin $(MK_DIR)/PX4/ROMFS/bootloader/fmu_bl.bin
$(v) echo "PX4IOv1 Firmware is in px4io-v1.bin"
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"
当然这只是其中的部分内容。告诉我们 px4是怎样的一个目标。V1,V2是两个不同的版本,这里我就不分别进行分析了,主要以我所使用的 V2来分析。
3. px4-io-v2
为了帮助分析,我增加了下面的目标:
px4
-io
-v3
: $(PX4_ROOT)
/Archives
/px4io
-v2.export
echo
"$(v) make -C $(PX4_ROOT) px4io-v2_default"
$(v) make
-C $(PX4_ROOT) px4io
-v2_default
$(v)
/bin
/rm
-f px4io
-v2.bin
得到下面这样一串信息:
echo
"@ make -C /home/bitcraze/apm/PX4Firmware px4io-v2_default"
@ make
-C
/home
/bitcraze
/apm
/PX4Firmware px4io
-v2_default
这样我就知道了下面 make所在的目录,知道了目录就可以找到 Makefile,就可以继续往下分析。
但到了 “
PX4Firmware”目录,打开 Makefile却找不到 “px4io
-v2_default”这样一个目标。说明目标被处理过了。于是我只好去看编译输出信息了:
make[
1]
: Entering directory `
/home
/bitcraze
/apm
/PX4Firmware
'
%%%%
%%%% Building px4io-v2_default in /home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/
%%%%
这里需要说明的是我的源码是编译过的。
通过这串信息,我匹配到了这样一个目标:
$(FIRMWARES)
: $(BUILD_DIR)
%.build
/firmware.px4
:
$(ECHO)
"hello" $(FIRMWARES)
@$(ECHO)
%%%%
@$(ECHO)
%%%% Building $(config) in $(work_dir)
@$(ECHO)
%%%%
$(Q) $(MKDIR)
-p $(work_dir)
$(ECHO)
"hello2" $(PX4_MK_DIR)
$(Q) $(MAKE)
-r
-C $(work_dir) \
-f $(PX4_MK_DIR)firmware.mk \
CONFIG
=$(config) \
WORK_DIR
=$(work_dir) \
$(FIRMWARE_GOAL)
“hello”是我后来加的。通过 hello我就确定了 “$(FIRMWARES)”实际上就是我们编译的 “px4io
-v2_default”。但这究竟是怎么回事呢?不妨先看看我们加了 hello之后的输出:
echo
"hello" /home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v1_APM.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v1_default.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v2_APM.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v2_default.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v2_test.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4io
-v1_default.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4io
-v2_default.build
/firmware.px4
hello
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v1_APM.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v1_default.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v2_APM.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v2_default.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4fmu
-v2_test.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4io
-v1_default.build
/firmware.px4
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4io
-v2_default.build
/firmware.px4
在 Makefile中有这样一段内容:
FIRMWARES
= $(foreach config,$(KNOWN_CONFIGS),$(BUILD_DIR)$(config).build
/firmware.px4)
因为就是最后我们实际上编译的是 “
/home
/bitcraze
/apm
/PX4Firmware
/Build
/px4io
-v2_default.build
/firmware.px4”目标。
而通过 hello2那一串信息我知道下一个 mk是 “/home/bitcraze/apm/PX4Firmware/makefiles/firmware.mk ”。但我们这里看到并没有指定编译哪个目标,也就是说编译第一个目标,通常是 all。
#
# Set a
default target so that included makefiles or errors here don
't
# cause confusion.
#
# XXX We could do something cute here with $(DEFAULT_GOAL) if it's not one
# of the maintenance targets and set CONFIG based on it.
#
all
: firmware
.PHONY
: firmware
firmware
: $(PRODUCT_BUNDLE)
#
# Built product rules
#
$(PRODUCT_BUNDLE)
: $(PRODUCT_BIN)
@$(ECHO)
%% Generating $@
$(Q) $(MKFW)
--prototype $(IMAGE_DIR)
/$(BOARD).prototype \
--git_identity $(PX4_BASE) \
--image $
< > $@
$(PRODUCT_BIN)
: $(PRODUCT_ELF)
$(call SYM_TO_BIN,$
<,$@)
$(PRODUCT_ELF)
: $(OBJS) $(MODULE_OBJS) $(LIBRARY_LIBS) $(GLOBAL_DEPS) $(LINK_DEPS) $(MODULE_MKFILES)
$(call LINK,$@,$(OBJS) $(MODULE_OBJS) $(LIBRARY_LIBS))
这一段 Makefile我是自己整理了顺序的。这段 Makefile大致告诉了我们最终我们的程序是怎么编译出来的。那么现在剩下的就是这些 “OBJS”是怎么编译的问题了。通常都是有对应的源文件编译出来的。
4. 目标编译
这里所说的目标编译就是 “$(PRODUCT_ELF)”所依赖的这些目标。我们先来看看 “$(OBJS)”。
#
# Object files we will generate from sources
#
OBJS
:= $(foreach src,$(SRCS),$(WORK_DIR)$(src).o)
#
# SRCS
-> OBJS rules
#
$(OBJS)
: $(GLOBAL_DEPS)
$(filter
%.c.o,$(OBJS))
: $(WORK_DIR)
%.c.o
: %.c $(GLOBAL_DEPS)
$(call COMPILE,$
<,$@)
$(filter
%.cpp.o,$(OBJS))
: $(WORK_DIR)
%.cpp.o
: %.cpp $(GLOBAL_DEPS)
$(call COMPILEXX,$
<,$@)
$(filter
%.S.o,$(OBJS))
: $(WORK_DIR)
%.S.o
: %.S $(GLOBAL_DEPS)
$(call ASSEMBLE,$
<,$@)
这个时候如果我们试图去查看 “$(OBJS)”或是 “$(SRCS)”的值或许会得到一个让自己觉得莫名其妙的结果:
“/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build//home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/empty.c.o”,
而且你去查看文件 “empty.c”的内容你会看到内容为:
/* this is an empty file */
这可能让人有点费解。为什么会这样呢?如果我们去阅读 Makefile我们就会发现:
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: $@"
$(ECHO) "CMDS: $@"
$(Q) $(ECHO) '/* builtin command list - automatically generated, do not edit */' > $@
$(Q) $(ECHO) '#
include <nuttx
/config.h
>' >> $@
$(Q) $(ECHO) '#
include <nuttx
/binfmt
/builtin.h
>' >> $@
$(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_PROTO,$(subst ., ,$(spec)),$@))
$(Q) $(foreach spec,$(MODULE_COMMANDS),$(call BUILTIN_PROTO,$(subst ., ,$(spec)),$@))
$(Q) $(ECHO) 'const struct builtin_s g_builtins[]
= {
' >> $@
$(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_DEF,$(subst ., ,$(spec)),$@))
$(Q) $(foreach spec,$(MODULE_COMMANDS),$(call BUILTIN_DEF,$(subst ., ,$(spec)),$@))
$(Q) $(ECHO) ' {NULL,
0,
0, NULL}
' >> $@
$(Q) $(ECHO) '};
' >> $@
$(Q) $(ECHO) 'const int g_builtin_count
= $(words $(BUILTIN_COMMANDS) $(MODULE_COMMANDS));
' >> $@
SRCS += $(BUILTIN_CSRC)
EXTRA_CLEANS += $(BUILTIN_CSRC)
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 */' > $@
SRCS += $(EMPTY_SRC)
endif
这样我们就知道,如果 “
$(SRCS)”值为空,就会往文件 “
empty.c”中写入“
/* this is an empty file */”,并且给 “
SRCS”赋值为 “
$(WORK_DIR)empty.c”。这样我们就知道 “$(BUILTIN_COMMANDS)”值必须也为空,这样就跟我们观察到的现象是一致的。
通过搜索我们发现,
bitcraze@bitcraze
-vm
:~/apm
/ardupilot
/ArduCopter$ grep
-nr BUILTIN_COMMANDS ..
/..
/
..
/..
/PX4Firmware
/makefiles
/config_px4fmu
-v1_APM.mk
:81:BUILTIN_COMMANDS
:= \
..
/..
/PX4Firmware
/makefiles
/config_px4fmu
-v2_APM.mk
:80:BUILTIN_COMMANDS
:= \
..
/..
/PX4Firmware
/makefiles
/config_px4fmu
-v2_test.mk
:62:BUILTIN_COMMANDS
:= \
..
/..
/PX4Firmware
/makefiles
/config_px4fmu
-v1_default.mk
:156:BUILTIN_COMMANDS
:= \
..
/..
/PX4Firmware
/makefiles
/config_px4fmu
-v2_default.mk
:164:BUILTIN_COMMANDS
:= \
..
/..
/PX4Firmware
/makefiles
/README.txt
:59: ROMFS_ROOT to build a ROMFS and BUILTIN_COMMANDS to
include non
-module
..
/..
/PX4Firmware
/makefiles
/firmware.mk
:57:# BUILTIN_COMMANDS
..
/..
/PX4Firmware
/makefiles
/firmware.mk
:393:# The configuration supplies builtin command information in the BUILTIN_COMMANDS
..
/..
/PX4Firmware
/makefiles
/firmware.mk
:409:ifneq ($(BUILTIN_COMMANDS),)
..
/..
/PX4Firmware
/makefiles
/firmware.mk
:428: $(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_PROTO,$(subst ., ,$(spec)),$@))
..
/..
/PX4Firmware
/makefiles
/firmware.mk
:431: $(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_DEF,$(subst ., ,$(spec)),$@))
..
/..
/PX4Firmware
/makefiles
/firmware.mk
:435: $(Q) $(ECHO)
'const int g_builtin_count = $(words $(BUILTIN_COMMANDS) $(MODULE_COMMANDS));' >> $@
..
/..
/linux
/PX4Firmware
/makefiles
/config_px4fmu
-v1_APM.mk
:81:BUILTIN_COMMANDS
:= \
..
/..
/linux
/PX4Firmware
/makefiles
/config_px4fmu
-v2_APM.mk
:80:BUILTIN_COMMANDS
:= \
..
/..
/linux
/PX4Firmware
/makefiles
/config_px4fmu
-v2_test.mk
:62:BUILTIN_COMMANDS
:= \
..
/..
/linux
/PX4Firmware
/makefiles
/config_px4fmu
-v1_default.mk
:156:BUILTIN_COMMANDS
:= \
..
/..
/linux
/PX4Firmware
/makefiles
/config_px4fmu
-v2_default.mk
:164:BUILTIN_COMMANDS
:= \
..
/..
/linux
/PX4Firmware
/makefiles
/README.txt
:59: ROMFS_ROOT to build a ROMFS and BUILTIN_COMMANDS to
include non
-module
..
/..
/linux
/PX4Firmware
/makefiles
/firmware.mk
:57:# BUILTIN_COMMANDS
..
/..
/linux
/PX4Firmware
/makefiles
/firmware.mk
:393:# The configuration supplies builtin command information in the BUILTIN_COMMANDS
..
/..
/linux
/PX4Firmware
/makefiles
/firmware.mk
:409:ifneq ($(BUILTIN_COMMANDS),)
..
/..
/linux
/PX4Firmware
/makefiles
/firmware.mk
:427: $(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_PROTO,$(subst ., ,$(spec)),$@))
..
/..
/linux
/PX4Firmware
/makefiles
/firmware.mk
:430: $(Q) $(foreach spec,$(BUILTIN_COMMANDS),$(call BUILTIN_DEF,$(subst ., ,$(spec)),$@))
..
/..
/linux
/PX4Firmware
/makefiles
/firmware.mk
:434: $(Q) $(ECHO)
'const int g_builtin_count = $(words $(BUILTIN_COMMANDS) $(MODULE_COMMANDS));' >> $@
..
/..
/linux
/ardupilot
/mk
/VRBRAIN
/config_vrbrain
-v51_APM.mk
:167:BUILTIN_COMMANDS
:= \
..
/..
/linux
/ardupilot
/mk
/VRBRAIN
/config_vrbrain
-v45_APM.mk
:167:BUILTIN_COMMANDS
:= \
..
/..
/linux
/ardupilot
/mk
/VRBRAIN
/config_vrubrain
-v51_APM.mk
:167:BUILTIN_COMMANDS
:= \
..
/..
/linux
/ardupilot
/mk
/PX4
/config_px4fmu
-v1_APM.mk
:81:BUILTIN_COMMANDS
:= \
..
/..
/linux
/ardupilot
/mk
/PX4
/config_px4fmu
-v2_APM.mk
:80:BUILTIN_COMMANDS
:= \
..
/..
/ardupilot
/mk
/VRBRAIN
/config_vrbrain
-v51_APM.mk
:167:BUILTIN_COMMANDS
:= \
..
/..
/ardupilot
/mk
/VRBRAIN
/config_vrbrain
-v45_APM.mk
:167:BUILTIN_COMMANDS
:= \
..
/..
/ardupilot
/mk
/VRBRAIN
/config_vrubrain
-v51_APM.mk
:167:BUILTIN_COMMANDS
:= \
..
/..
/ardupilot
/mk
/PX4
/config_px4fmu
-v1_APM.mk
:81:BUILTIN_COMMANDS
:= \
..
/..
/ardupilot
/mk
/PX4
/config_px4fmu
-v2_APM.mk
:80:BUILTIN_COMMANDS
:= \
bitcraze@bitcraze
-vm
:~/apm
/ardupilot
/ArduCopter$
那么我们就知道 IO板编译的时候不会去创建 “
$(BUILTIN_CSRC)”目标,这个目标会生成一个源文件,就是
builtin_commands.c。我们不妨到核心板的相应目录下去查看下该文件的内容:
bitcraze@bitcraze
-vm
:~/apm
/ardupilot
/ArduCopter$ cat ..
/..
/PX4Firmware
/Build
/px4fmu
-v2_APM.build
/builtin_commands.c
/* builtin command list - automatically generated, do not edit */
#
include <nuttx
/config.h
>
#
include <nuttx
/binfmt
/builtin.h
>
extern int sercon_main(
int argc,
char *argv[]);
extern int serdis_main(
int argc,
char *argv[]);
extern int ArduPilot_main(
int argc,
char *argv[]);
extern int adc_main(
int argc,
char *argv[]);
extern int auth_main(
int argc,
char *argv[]);
extern int bl_update_main(
int argc,
char *argv[]);
extern int blinkm_main(
int argc,
char *argv[]);
extern int boardinfo_main(
int argc,
char *argv[]);
extern int ets_airspeed_main(
int argc,
char *argv[]);
extern int fmu_main(
int argc,
char *argv[]);
extern int gps_main(
int argc,
char *argv[]);
extern int hil_main(
int argc,
char *argv[]);
extern int hmc5883_main(
int argc,
char *argv[]);
extern int l3gd20_main(
int argc,
char *argv[]);
extern int ll40ls_main(
int argc,
char *argv[]);
extern int lsm303d_main(
int argc,
char *argv[]);
extern int mb12xx_main(
int argc,
char *argv[]);
extern int meas_airspeed_main(
int argc,
char *argv[]);
extern int mixer_main(
int argc,
char *argv[]);
extern int mkblctrl_main(
int argc,
char *argv[]);
extern int mpu6000_main(
int argc,
char *argv[]);
extern int ms5611_main(
int argc,
char *argv[]);
extern int mtd_main(
int argc,
char *argv[]);
extern int nshterm_main(
int argc,
char *argv[]);
extern int perf_main(
int argc,
char *argv[]);
extern int pwm_main(
int argc,
char *argv[]);
extern int px4io_main(
int argc,
char *argv[]);
extern int reboot_main(
int argc,
char *argv[]);
extern int rgbled_main(
int argc,
char *argv[]);
extern int tests_main(
int argc,
char *argv[]);
extern int tone_alarm_main(
int argc,
char *argv[]);
extern int top_main(
int argc,
char *argv[]);
extern int uorb_main(
int argc,
char *argv[]);
const struct builtin_s g_builtins[]
= {
{
"sercon", SCHED_PRIORITY_DEFAULT,
2048, sercon_main},
{
"serdis", SCHED_PRIORITY_DEFAULT,
2048, serdis_main},
{
"ArduPilot", SCHED_PRIORITY_DEFAULT,
4096, ArduPilot_main},
{
"adc", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, adc_main},
{
"auth", SCHED_PRIORITY_DEFAULT,
64000, auth_main},
{
"bl_update", SCHED_PRIORITY_DEFAULT,
4096, bl_update_main},
{
"blinkm", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, blinkm_main},
{
"boardinfo", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, boardinfo_main},
{
"ets_airspeed", SCHED_PRIORITY_DEFAULT,
2048, ets_airspeed_main},
{
"fmu", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, fmu_main},
{
"gps", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, gps_main},
{
"hil", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, hil_main},
{
"hmc5883", SCHED_PRIORITY_DEFAULT,
4096, hmc5883_main},
{
"l3gd20", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, l3gd20_main},
{
"ll40ls", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, ll40ls_main},
{
"lsm303d", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, lsm303d_main},
{
"mb12xx", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, mb12xx_main},
{
"meas_airspeed", SCHED_PRIORITY_DEFAULT,
2048, meas_airspeed_main},
{
"mixer", SCHED_PRIORITY_DEFAULT,
4096, mixer_main},
{
"mkblctrl", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, mkblctrl_main},
{
"mpu6000", SCHED_PRIORITY_DEFAULT,
4096, mpu6000_main},
{
"ms5611", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, ms5611_main},
{
"mtd", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, mtd_main},
{
"nshterm", SCHED_PRIORITY_DEFAULT,
3000, nshterm_main},
{
"perf", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, perf_main},
{
"pwm", SCHED_PRIORITY_DEFAULT,
4096, pwm_main},
{
"px4io", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, px4io_main},
{
"reboot", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, reboot_main},
{
"rgbled", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, rgbled_main},
{
"tests", SCHED_PRIORITY_DEFAULT,
12000, tests_main},
{
"tone_alarm", SCHED_PRIORITY_DEFAULT, CONFIG_PTHREAD_STACK_DEFAULT, tone_alarm_main},
{
"top", SCHED_PRIORITY_DEFAULT,
3000, top_main},
{
"uorb", SCHED_PRIORITY_DEFAULT,
4096, uorb_main},
{NULL,
0,
0, NULL}
};
const int g_builtin_count
= 33;
bitcraze@bitcraze
-vm
:~/apm
/ardupilot
/ArduCopter$
我们看到头文件用的是
nuttx的,源文件里有一个数组,放的全是入口函数。显然这是给 nuttx系统用的。那么这些入口函数也就来自 nuttx系统。而且我们可以进一步确定,这些入口是应用程序的,而非驱动的。
看到这里我就在想,从这段 Makefile中我们是不是可以认为 IO板是以裸机的方式在运行?我认为应该是可以的。
如果我们继续修改 Makefile进行调试,我们会发现:
define LINK
@$(ECHO)
"LINK: $1"
@$(MKDIR)
-p $(dir $
1)
$(ECHO)
"LINK1"
# $(Q) $(LD) $(LDFLAGS)
-Map $
1.map
-o $
1 --start
-group $
2 $(LIBS) $(EXTRA_LIBS) $(LIBGCC)
--end
-group
$(LD) $(LDFLAGS)
-Map $
1.map
-o $
1 --start
-group $
2 $(LIBS) $(EXTRA_LIBS) $(LIBGCC)
--end
-group
$(ECHO)
"LINK2"
endef
# Convert $
1 from a linked object to a raw binary in $
2
#
define SYM_TO_BIN
@$(ECHO)
"BIN: $2"
# @$(MKDIR)
-p $(dir $
2)
# $(Q) $(OBJCOPY)
-O binary $
1 $
2
$(MKDIR)
-p $(dir $
2)
$(OBJCOPY)
-O binary $
1 $
2
endef
LINK: /home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/firmware.elf
LINK1
arm-none-eabi-ld
--warn-common
--gc-sections -T/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/nuttx-export/build/ld.script
-L/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/nuttx-export/libs
-Map
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/firmware.elf.map
-o
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/firmware.elf
--start-group
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build//home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/empty.c.o
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build//home/bitcraze/apm/PX4Firmware/src/drivers/boards/px4io-v2/module.pre.o
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build//home/bitcraze/apm/PX4Firmware/src/drivers/stm32/module.pre.o
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build//home/bitcraze/apm/PX4Firmware/src/modules/px4iofirmware/module.pre.o -
lapps -
lnuttx /home/bitcraze/bin/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../../arm-none-eabi/lib/armv7-m/
libm.a /home/bitcraze/bin/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/4.7.3/armv7-m/
libgcc.a --end-group
LINK2
BIN: /home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/firmware.bin
mkdir -p /home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/
arm-none-eabi-objcopy
-O binary
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/firmware.elf
/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/firmware.bin
%% Generating /home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build/firmware.px4
make[2]: Leaving directory `/home/bitcraze/apm/PX4Firmware/Build/px4io-v2_default.build'
%% Copying /home/bitcraze/apm/PX4Firmware/Images/px4io-v2_default.px4
make[1]: Leaving directory `/home/bitcraze/apm/PX4Firmware'
这样我们就可以大概知道编译我们的 elf文件用到了那些源码。其中用到了几个库,最显眼的应该是
nuttx这个库,因为这个库很清楚地告诉了我我们的 IO板是跑系统的,而不是裸机运行。所以现在我有个疑问就是:“为什么运行在 Nuttx系统却不提供应用程序入口”?我想这也只有到源码中去寻找答案了。由于这里我们主要讨论的是 Makefile,暂时就不去深究了。
另外一个是
apps库,剩下的两个都是编译器的库,对于编译器的库,我是直接掠过的。我们的这两个库是由
$(LIBS)指定的,都源自 Nuttx系统。那么他们是怎么来的呢?我们继续到 Makefile中去寻找答案。
################################################################################
# NuttX libraries and paths
################################################################################
include $(PX4_MK_DIR)
/nuttx.mk
这还是 firmware.mk中的内容,引用了外部的 mk文件 nuttx.mk。其中有这样一段内容:
#
# Use the linker script from the NuttX export
#
LDSCRIPT
+= $(NUTTX_EXPORT_DIR)build
/ld.script
#
# Add directories from the NuttX export to the relevant search paths
#
INCLUDE_DIRS
+= $(NUTTX_EXPORT_DIR)
include \
$(NUTTX_EXPORT_DIR)arch
/chip \
$(NUTTX_EXPORT_DIR)arch
/common
LIB_DIRS
+= $(NUTTX_EXPORT_DIR)libs
LIBS
+= -lapps
-lnuttx
NUTTX_LIBS
= $(NUTTX_EXPORT_DIR)libs
/libapps.a \
$(NUTTX_EXPORT_DIR)libs
/libnuttx.a
LINK_DEPS
+= $(NUTTX_LIBS)
$(NUTTX_CONFIG_HEADER)
: $(NUTTX_ARCHIVE)
@$(ECHO)
%% Unpacking $(NUTTX_ARCHIVE)
$(Q) $(UNZIP_CMD)
-q
-o
-d $(WORK_DIR) $(NUTTX_ARCHIVE)
$(Q) $(TOUCH) $@
$(LDSCRIPT)
: $(NUTTX_CONFIG_HEADER)
$(NUTTX_LIBS)
: $(NUTTX_CONFIG_HEADER)
但是当我尝试去获取这两个库编译的细节的时候却失败了,具体原因不明。
5. px4-v2
现在我们来看下 px4-v2的编译。
核心板与 IO板在编译上存在一定的相似性。这里我们同样也可以构造一个目标 px4-v3来进行测试。
px4
-v3
: showflags $(PX4_ROOT)
/Archives
/px4fmu
-v2.export $(SKETCHCPP) module_mk
$(RULEHDR)
$(v) rm
-f $(PX4_ROOT)
/makefiles
/$(PX4_V2_CONFIG_FILE)
$(v) cp $(PWD)
/$(PX4_V2_CONFIG_FILE) $(PX4_ROOT)
/makefiles
/
$(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) echo
"PX4 $(SKETCH) Firmware is in $(SKETCH)-v2.px4"
showflags
:
@echo
"HAL_BOARD=$(HAL_BOARD) HAL_BOARD_SUBTYPE=$(HAL_BOARD_SUBTYPE) TOOLCHAIN=$(TOOLCHAIN) EXTRAFLAGS=$(EXTRAFLAGS)"
$(PX4_ROOT)
/Archives
/px4fmu
-v2.export
:
$(v) $(PX4_MAKE_ARCHIVES)
.PHONY
: module_mk
#
# Build the sketch.cpp file
$(SKETCHCPP): showflags $(SKETCHCPP_SRC)
@echo "building $(SKETCHCPP)"
$(RULEHDR)
$(v)$(AWK) -v mode=header '$(SKETCH_SPLITTER)' $(SKETCHCPP_SRC) >
$(v)echo "#line 1 \"autogenerated\"" >>
$(v)$(AWK) '$(SKETCH_PROTOTYPER)' $(SKETCHCPP_SRC) >>
$(v)$(AWK) -v mode=body '$(SKETCH_SPLITTER)' $(SKETCHCPP_SRC) >>
$(v)cmp $@ > /dev/null 2>&1 || mv $@
$(v)rm -f
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 = Build.$(SKETCH)/$(SKETCH).cpp $(SKETCHLIBSRCSRELATIVE)" >> $(SKETCHBOOK)
/module.mk.
new
$(v) echo
"MODULE_STACKSIZE = 4096" >> $(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
其中,我们裁掉了对 IO板的依赖。
那么这几个一来都分别做了哪些事情呢?第一个最明显,打印了编译的相关信息。其它的我们都需要经过一些测试。通过测试我们知道 “$(PX4_ROOT)
/Archives
/px4fmu
-v2.export”是用来编译 Nuttx系统的,编译命令为 “
make -C /home/bitcraze/apm/PX4Firmware NUTTX_SRC=/home/bitcraze/apm/PX4NuttX/nuttx/ archives MAXOPTIMIZATION="-Os"”,也就是编译 archives目标; “$(SKETCHCPP)”用于生成源文件 “
ardupilot/Build.ArduCopter/ArduCopter.cpp”,这部分
Makefile在 sketch_sources.mk文件中。是在 apm.mk中 include进来的。 “module_mk”则是用来生成
“ardupilot/module.mk”文件。该文件定义了三个变量:MODULE_COMMAND, SRCS,
MODULE_STACKSIZE。从字面上看分别是模块命令、源文件和模块的堆栈大小。关于这几个变量的作用,我们后面会看到。
当所有的这些依赖都被满足,便通过 “$(PX4_MAKE) px4fmu-v2_APM”命令进行编译。
PX4_MAKE
= $(v) GIT_SUBMODULES_ARE_EVIL
=1 make
-C $(SKETCHBOOK)
-f $(PX4_ROOT)
/Makefile EXTRADEFINES
="$(SKETCHFLAGS) $(WARNFLAGS) "'$(EXTRAFLAGS)' APM_MODULE_DIR
=$(SKETCHBOOK) SKETCHBOOK
=$(SKETCHBOOK) PX4_ROOT
=$(PX4_ROOT) NUTTX_SRC
=$(NUTTX_SRC) MAXOPTIMIZATION
="-Os"
该命令展开之后很长,主要是头文件很多。将头文件去掉,剩下的命令为:
GIT_SUBMODULES_ARE_EVIL
=1 make
-C
/home
/bitcraze
/apm
/ardupilot
-f
/home
/bitcraze
/apm
/PX4Firmware
/Makefile
-DARDUPILOT_BUILD
-DCONFIG_HAL_BOARD
=HAL_BOARD_PX4
-DSKETCHNAME
=\"ArduCopter\"
-DSKETCH_MAIN
=ArduPilot_main
-DAPM_BUILD_DIRECTORY
=APM_BUILD_ArduCopter
-Wno
-psabi
-Wno
-packed
-Wno
-error
=double-promotion
-Wno
-error
=unused
-variable
-Wno
-error
=unused
-but
-set
-variable
-Wno
-error
=reorder
-DGIT_VERSION
="\"b6d361a3\"" -DNUTTX_GIT_VERSION
="\"eba6b56f\"" -DPX4_GIT_VERSION
="\"cf208916\""APM_MODULE_DIR
=/home
/bitcraze
/apm
/ardupilot SKETCHBOOK
=/home
/bitcraze
/apm
/ardupilot PX4_ROOT
=/home
/bitcraze
/apm
/PX4Firmware NUTTX_SRC
=/home
/bitcraze
/apm
/PX4NuttX
/nuttx
/MAXOPTIMIZATION
=-Os
可以看到,命令依然很长。这里我们编译使用的头文件是 “PX4Firmware
/Makefile”,编译目标为默认 all目标。但是
#
# Built products
#
DESIRED_FIRMWARES
= $(foreach config,$(CONFIGS),$(IMAGE_DIR)$(config).px4)
STAGED_FIRMWARES
= $(foreach config,$(KNOWN_CONFIGS),$(IMAGE_DIR)$(config).px4)
FIRMWARES
= $(foreach config,$(KNOWN_CONFIGS),$(BUILD_DIR)$(config).build
/firmware.px4)
all
: $(DESIRED_FIRMWARES)
“$(DESIRED_FIRMWARES)”的值为:
“PX4Firmware/Images/px4fmu-v2_APM.px4”。过程不清楚,但可以确定的是最后还是匹配了
“$(FIRMWARES)”这个目标。于是下面有顺理成章地回到了 “firmware.mk”。
“firmware.mk”前面我们已经简单分析过期编译流程了。跟 IO板相比主要有两个不同,一是此时
“$(BUILTIN_COMMANDS)”值不为空,也就是会生成 builtin_commands.c文件;二是
“$(MODULE_OBJS)”值不一样,其值包含了 PX4的各种模块,如传感器,
PWM。模块多了编译的东西自然也就多了。这些模块在核心板看来就是命令。
#
# Transitional support
- add commands from the NuttX export archive.
#
# In general, these should move to modules over time.
#
# Each entry here is
<command
>.
<priority
>.
<stacksize
>.
<entrypoint
> but we use a helper macro
# to make the table a bit more readable.
#
define _B
$(strip $
1).$(or $(strip $
2),SCHED_PRIORITY_DEFAULT).$(or $(strip $
3),CONFIG_PTHREAD_STACK_DEFAULT).$(strip $
4)
endef
# command priority stack entrypoint
BUILTIN_COMMANDS
:= \
$(call _B, sercon, ,
2048, sercon_main ) \
$(call _B, serdis, ,
2048, serdis_main )
我们可以通过研究这段
Makefile去了解 “$(BUILTIN_COMMANDS)”的值是怎么来的,只是我现在可没这功夫去研究它。因为这段 Makefile在
IO板中根本就不存在,其值也就为空了。那么下面我们来看看 “$(MODULE_OBJS)”是怎么编译的。
“$(MODULE_OBJS)”是 “$(PRODUCT_ELF)”的依赖,所以当 “$(PRODUCT_ELF)”需要编译的时候, “$(MODULE_OBJS)”也会被编译。
# make a list of module makefiles and check that we found them all
MODULE_MKFILES
:= $(foreach module,$(MODULES),$(call MODULE_SEARCH,$(module)))
MISSING_MODULES
:= $(subst MISSING_,,$(filter MISSING_
%,$(MODULE_MKFILES)))
ifneq ($(MISSING_MODULES),)
$(error Can
't find module(s): $(MISSING_MODULES))
endif
# 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)%,%,$@)
$(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) \
MODULE_OBJ=$@ \
MODULE_MK=$(mkfile) \
MODULE_NAME=$(lastword $(subst /, ,$(workdir))) \
module
通过注释我们知道这段 Makefile是用来编译 module的。对 “MODULE_OBJS”进行跟踪,我们发现数据最终源自 “$(MODULES)”,那么这又是一个什么变量,它的值又是什么呢?
这就是 “PX4Firmware/makefiles/”目录下那些 “config”开头的 mk文件。例如 IO板 “config_px4io-v2_default.mk”内容如下:
#
# Makefile
for the px4iov2_default configuration
#
#
# Board support modules
#
MODULES
+= drivers
/stm32
MODULES
+= drivers
/boards
/px4io
-v2
MODULES
+= modules
/px4iofirmware
而处理到最后, “
mkfile”存放的是个模块目录下的 “
module.mk”文件。而最后编译使用的 mk文件为: “
PX4Firmware/makefiles/module.mk”,目标为:
################################################################################
# Build rules
################################################################################
#
# What we
're going to build
#
module: $(MODULE_OBJ) $(MODULE_COMMAND_FILES)
#
# Object files we will generate from sources
#
OBJS = $(addsuffix .o,$(SRCS))
#
# Dependency files that will be auto-generated
#
DEPS = $(addsuffix .d,$(SRCS))
#
# SRCS -> OBJS rules
#
$(OBJS): $(GLOBAL_DEPS)
vpath %.c $(MODULE_SRC)
$(filter %.c.o,$(OBJS)): %.c.o: %.c $(GLOBAL_DEPS)
$(call COMPILE,$<,$@)
vpath %.cpp $(MODULE_SRC)
$(filter %.cpp.o,$(OBJS)): %.cpp.o: %.cpp $(GLOBAL_DEPS)
$(call COMPILEXX,$<,$@)
vpath %.S $(MODULE_SRC)
$(filter %.S.o,$(OBJS)): %.S.o: %.S $(GLOBAL_DEPS)
$(call ASSEMBLE,$<,$@)
#
# Built product rules
#
$(MODULE_OBJ): $(OBJS) $(GLOBAL_DEPS)
$(call PRELINK,$@,$(OBJS))
这里是将各自的源文件编译成各自的目标文件,即 "*.o"文件。而这许多个目标文件最后又通过 “
PRELINK”链接成一个目标文件,也就是 “module.pre.o”。
而通过 “$(MODULE_COMMAND_FILES)”会输出这样一些信息:
CMD
: l3gd20
CMD
: ll40ls
CMD
: mpu6000
CMD
: ms5611
CMD
: fmu
CMD
: px4io
CMD
: reboot
也就是说 module并不特指硬件,正确的理解应当是应用程序。然而在操作系统里边像 Linux应用程序可以在 shell中以命令的方式进行调用,于是这里称之为命令也就不奇怪了。
6. archives
“$(PX4_ROOT)/Archives/px4fmu-v2.export”是
px4-v2的又一个依赖,前面我们已经知道其编译的命令为: “make -C /home/bitcraze/apm/PX4Firmware
NUTTX_SRC=/home/bitcraze/apm/PX4NuttX/nuttx/ archives
MAXOPTIMIZATION="-Os"”,我们找到 “archives”目标:
#
# Build the NuttX export archives.
#
# Note that there are no
explicit dependencies extended from these
# archives. If NuttX is updated, the user is expected to rebuild the
# archives
/build area manually. Likewise, when the
'archives' target is
# invoked, all archives are always rebuilt.
#
# XXX Should support fetching
/unpacking from a separate directory to permit
# downloads of the prebuilt archives as well...
#
NUTTX_ARCHIVES
= $(foreach board,$(BOARDS),$(ARCHIVE_DIR)$(board).export)
.PHONY
: archives
archives: $(NUTTX_ARCHIVES)
# We cannot build these parallel; note that we also force
-j1
for the
# sub
-make invocations.
ifneq ($(filter archives,$(MAKECMDGOALS)),)
.NOTPARALLEL
:
endif
$(ARCHIVE_DIR)
%.export
: board
= $(notdir $(basename $@))
$(ARCHIVE_DIR)
%.export
: configuration
= nsh
$(NUTTX_ARCHIVES)
: $(ARCHIVE_DIR)
%.export
: $(NUTTX_SRC)
@$(ECHO)
%% Configuring NuttX
for $(board)
$(Q) (cd $(NUTTX_SRC)
&& $(RMDIR) nuttx
-export)
$(Q) $(MAKE)
-r
-j1
-C $(NUTTX_SRC)
-r $(MQUIET) distclean
$(Q) (cd $(NUTTX_SRC)
/configs
&& $(COPYDIR) $(PX4_BASE)nuttx
-configs
/$(board) .)
$(Q) (cd $(NUTTX_SRC)tools
&& .
/configure.sh $(board)
/$(configuration))
@$(ECHO)
%% Exporting NuttX
for $(board)
$(Q) $(MAKE)
-r
-j1
-C $(NUTTX_SRC)
-r $(MQUIET) CONFIG_ARCH_BOARD
=$(board) export
$(Q) $(MKDIR)
-p $(dir $@)
$(Q) $(COPY) $(NUTTX_SRC)nuttx
-export.zip $@
$(Q) (cd $(NUTTX_SRC)
/configs
&& $(RMDIR) $(board))
于是最终将跳转到 “PX4NuttX/nuttx/”目的对 Nuttx进行编译。关于 Nuttx的编译就在这里点到为止,暂不做过深入的分析。
7. $(SKETCHCPP)
#
# Build the sketch.cpp file
$(SKETCHCPP)
: showflags $(SKETCHCPP_SRC)
@echo
"building $(SKETCHCPP)"
$(RULEHDR)
$(v)$(AWK)
-v mode
=header
'$(SKETCH_SPLITTER)' $(SKETCHCPP_SRC)
> $@.
new
$(v)echo
"#line 1 \"autogenerated\"" >> $@.
new
$(v)$(AWK)
'$(SKETCH_PROTOTYPER)' $(SKETCHCPP_SRC)
>> $@.
new
$(v)$(AWK)
-v mode
=body
'$(SKETCH_SPLITTER)' $(SKETCHCPP_SRC)
>> $@.
new
$(v)cmp $@ $@.
new > /dev
/null
2>&1 || mv $@.
new $@
$(v)rm
-f $@.
new
这部分
Makefile其实只做了一件事,那就是 “ardupilot/Build.ArduCopter/ArduCopter.cpp”文件,而
“$(SKETCHCPP_SRC)”就是所有的这些 pde文件。也就是说
“ardupilot/ArduCopter”目录下的源文件最终将被处理成一个 ArduCopter.cpp文件进行编译。
分析到这里,编译流程基本告一段路了,剩下的一些东西我们后面分析会借助源码进行理解。