好久没关注 PX4 的更新进度,最近正好想重新搭一个仿真环境,就下载了最新的代码来看。
原本的下载地址 https://github.com/PX4/Firmware 已经更名为 https://github.com/PX4/PX4-Autopilot ,目前在浏览器里输入这两个链接都能打开。
代码中只有主目录中一个 Makefile ,而且文件长度比原本 v1.7.3 之前减少了很多,易读性大大增强。(实际上以前的我从来没看懂过,这次反而十几分钟就看懂了。)
主要就干了两件事:1、定义所有可供 make 的 targets; 2、对主要的 targets 采用了统一的形式,都采用 cmake 来实现最终的编译。
其他一些功能,比较零散,大家平时都用不到,这里就不做记录了。
主要是 boards/ 文件夹下的 .cmake 文件,利用文件的路径和文件名来生成目标,其实就一个语句:
# Get a list of all config targets boards/*/*.cmake
ALL_CONFIG_TARGETS := $(shell find boards -maxdepth 3 -mindepth 3 ! -name '*common*' ! -name '*sdflight*' -name '*.cmake' -print | sed -e 's|boards\/||' | sed -e 's|\.cmake||' | sed -e 's|\/|_|g' | sort)
这个语句比较长,但是主体内容是用 “|” 管道连接了一个 “find” 命令,三个 “sed -e” 命令和一个 “sort” 命令。
将这句指令复制到终端里运行的效果如下所示:
aerotenna_ocpoc_default
airmind_mindpx-v2_default
atlflight_cmake_hexagon_bundle
atlflight_cmake_hexagon_fastrpc
atlflight_cmake_hexagon_hexagon_sdk
atlflight_cmake_hexagon_linux_app
atlflight_cmake_hexagon_qurt_flags
atlflight_cmake_hexagon_qurt_lib
atlflight_eagle_default
atlflight_eagle_qurt
atlflight_excelsior_default
atlflight_excelsior_qurt
av_x-v1_default
beaglebone_blue_default
bitcraze_crazyflie_default
cuav_nora_bootloader
cuav_nora_default
cuav_x7pro_bootloader
cuav_x7pro_default
cubepilot_cubeorange_bootloader
cubepilot_cubeorange_console
cubepilot_cubeorange_default
cubepilot_cubeyellow_console
cubepilot_cubeyellow_default
cubepilot_io-v2_default
emlid_navio2_default
holybro_durandal-v1_bootloader
holybro_durandal-v1_default
holybro_durandal-v1_stackcheck
holybro_kakutef7_default
holybro_pix32v5_default
intel_aerofc-v1_default
intel_aerofc-v1_rtps
modalai_fc-v1_default
mro_ctrl-zero-f7_default
mro_x21_default
mro_x21-777_default
nxp_fmuk66-e_default
nxp_fmuk66-e_socketcan
nxp_fmuk66-v3_default
nxp_fmuk66-v3_socketcan
nxp_fmurt1062-v1_default
nxp_rddrone-uavcan146_default
omnibus_f4sd_default
px4_fmu-v2_default
px4_fmu-v2_fixedwing
px4_fmu-v2_lpe
px4_fmu-v2_multicopter
px4_fmu-v2_rover
px4_fmu-v2_test
px4_fmu-v3_default
px4_fmu-v3_rtps
px4_fmu-v3_stackcheck
px4_fmu-v4_cannode
px4_fmu-v4_default
px4_fmu-v4_optimized
px4_fmu-v4_rtps
px4_fmu-v4_stackcheck
px4_fmu-v4pro_default
px4_fmu-v4pro_rtps
px4_fmu-v5_critmonitor
px4_fmu-v5_debug
px4_fmu-v5_default
px4_fmu-v5_fixedwing
px4_fmu-v5_irqmonitor
px4_fmu-v5_multicopter
px4_fmu-v5_optimized
px4_fmu-v5_rover
px4_fmu-v5_rtps
px4_fmu-v5_stackcheck
px4_fmu-v5x_base_phy_DP83848C
px4_fmu-v5x_default
px4_fmu-v6x_bootloader
px4_fmu-v6x_default
px4_fmu-v6x_stackcheck
px4_io-v2_default
px4_raspberrypi_default
px4_sitl_default
px4_sitl_nolockstep
px4_sitl_replay
px4_sitl_rtps
px4_sitl_test
spracing_h7extreme_default
uvify_core_default
这还没完,下面还一条指令,将所有带 “_default” 的字符串都去掉 “_default” 成为另外的指令。
# Filter for only default targets to allow omiting the "_default" postfix
CONFIG_TARGETS_DEFAULT := $(patsubst %_default,%,$(filter %_default,$(ALL_CONFIG_TARGETS)))
然后将所有字符串都作为可供 make 的 targets
all_config_targets: $(ALL_CONFIG_TARGETS)
all_default_targets: $(CONFIG_TARGETS_DEFAULT)
# All targets with just dependencies but no recipe must either be marked as phony (or have the special @: as recipe).
.PHONY: all px4_sitl_default all_config_targets all_default_targets
直接上代码:
# All targets.
$(ALL_CONFIG_TARGETS):
@$(eval PX4_CONFIG = $@)
@$(eval CMAKE_ARGS += -DCONFIG=$(PX4_CONFIG))
@$(call cmake-build,$(PX4_CONFIG)$(BUILD_DIR_SUFFIX))
$(CONFIG_TARGETS_DEFAULT):
@$(eval PX4_CONFIG = $@_default)
@$(eval CMAKE_ARGS += -DCONFIG=$(PX4_CONFIG))
@$(call cmake-build,$(PX4_CONFIG)$(BUILD_DIR_SUFFIX))
可以看到无论是直接从路径和文件名获取的 targets, 还是去除了 “_default” 之后的 targets 都是采用了相同的规则,也就是 call cmake-build 来进行 cmake 编译。
而这里的 cmake-build 就是 Makefile 中定义的一个内部规则:
# Functions
# --------------------------------------------------------------------
# describe how to build a cmake config
define cmake-build
@$(eval BUILD_DIR = "$(SRC_DIR)/build/$(1)")
@# check if the desired cmake configuration matches the cache then CMAKE_CACHE_CHECK stays empty
@$(call cmake-cache-check)
@# make sure to start from scratch when switching from GNU Make to Ninja
@if [ $(PX4_CMAKE_GENERATOR) = "Ninja" ] && [ -e $(BUILD_DIR)/Makefile ]; then rm -rf $(BUILD_DIR); fi
@# only excplicitly configure the first build, if cache file already exists the makefile will rerun cmake automatically if necessary
@if [ ! -e $(BUILD_DIR)/CMakeCache.txt ] || [ $(CMAKE_CACHE_CHECK) ]; then \
mkdir -p $(BUILD_DIR) \
&& cd $(BUILD_DIR) \
&& cmake "$(SRC_DIR)" -G"$(PX4_CMAKE_GENERATOR)" $(CMAKE_ARGS) \
|| (rm -rf $(BUILD_DIR)); \
fi
@# run the build for the specified target
@cmake --build $(BUILD_DIR) -- $(PX4_MAKE_ARGS) $(ARGS)
endef
# check if the options we want to build with in CMAKE_ARGS match the ones which are already configured in the cache inside BUILD_DIR
define cmake-cache-check
@# change to build folder which fails if it doesn't exist and CACHED_CMAKE_OPTIONS stays empty
@# fetch all previously configured and cached options from the build folder and transform them into the OPTION=VALUE format without type (e.g. :BOOL)
@$(eval CACHED_CMAKE_OPTIONS = $(shell cd $(BUILD_DIR) 2>/dev/null && cmake -L 2>/dev/null | sed -n 's|\([^[:blank:]]*\):[^[:blank:]]*\(=[^[:blank:]]*\)|\1\2|gp' ))
@# transform the options in CMAKE_ARGS into the OPTION=VALUE format without -D
@$(eval DESIRED_CMAKE_OPTIONS = $(shell echo $(CMAKE_ARGS) | sed -n 's|-D\([^[:blank:]]*=[^[:blank:]]*\)|\1|gp' ))
@# find each currently desired option in the already cached ones making sure the complete configured string value is the same
@$(eval VERIFIED_CMAKE_OPTIONS = $(foreach option,$(DESIRED_CMAKE_OPTIONS),$(strip $(findstring $(option)$(space),$(CACHED_CMAKE_OPTIONS)))))
@# if the complete list of desired options is found in the list of verified options we don't need to reconfigure and CMAKE_CACHE_CHECK stays empty
@$(eval CMAKE_CACHE_CHECK = $(if $(findstring $(DESIRED_CMAKE_OPTIONS),$(VERIFIED_CMAKE_OPTIONS)),,y))
endef
举个例子:
比如要编译软件在环仿真 SITL 并且用 gazebo 作为仿真器,只需要在命令行中敲一下 make px4_sitl gazebo 。
mkdir -p build/px4_sitl_default
cd build/px4_sitl_default
cmake "主目录" -G"Ninja" -DCONFIG=px4_sitl_default
cmake --build build/px4_sitl_default -- gazebo
这样一来,就可以在 build/ 文件夹下编译完成所需的目标了。
这里着重就要去看主目录下的 CMakeList.txt 和之后生成的编译目录中文件的内容了。