前面对于 PX4 的 Makefile 已经做了比较详细的分析, 见这里, 这里进一步对 PX4 的 CMakeLists.txt 文件结构进行进一步的分析.
CMake 是一个 DSL( Domain Specific Language), 主要用来辅助对代码的编译配置和管理, 其具有跨平台, 可维护性较好的特点, 目前在 C++ 项目中得到了普遍应用. 下面是对 CMAKE的简单介绍,如需了解更多,请参考 CMake Documentation和Modern CMake等文档及教程。
CMake 中包含了针对编译配置的特殊函数和变量定义, 语法类C, 包括了控制语句, 模块导入语句, 字符串处理行数等较为通用的功能.
CMake 中的文件结构按功能大致可以分为, Directories, Script 和 Module. 其中 Module 可以通过include 命令导入, include 命令会在 CMAKE_MODULE_PATH 下寻找指定的模块. 下表列出了一些出现频率较高的指令
CMake 指令列表 | 指令说明 |
---|---|
cmake_minimum_required | cmake 的最小的版本 |
set(variable value… [PARENT_SCOPE]) | 设置变量variable的值value, [PARENT_SCOPE] 可选表示变量的作用域 |
list(APPEND list element…) | 把 element 附加到 list 中 |
list(GET list index variable) | 从 list 中获取给定index的值放到变量中 |
include(file/module) | 导入file或 module 并执行 |
file(GLOB_RECURSE variable RELATIVE path expression) | 生成匹配expression的相对于 path 的文件列表 |
message(STATUS string) | 向终端输出string |
set_property(GLOBAL PROPERTY name value) | 设置全局属性 name 的值为 value |
option(variable help_string [value]) | 设置variable 的值 |
add_subdirectory(directory) | 添加一个需要 build的子文件夹 |
include_directories(directory) | 把目录directory添加到可以被cmake include 的文件的搜索路径中 |
link_directories(directory) | 把目录directory 添加到连接器的搜索路径中 |
add_custom_target(Name COMMAND COMMENT USES_TERMINAL) | 添加一个没有输出的并且总会被编译的目标, COMMAND 为一些待执行的命令, USES_TERMINAL 代表是否可以直接获取到终端 |
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
首先是说明需要的 CMake 最小的版本
set(PX4_SOURCE_DIR "\${CMAKE_CURRENT_SOURCE_DIR}")
set(PX4_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
第一句设置PX4_SOURCE_DIR
的值为 ${CMAKE_CURRENT_SOURCE_DIR}
, 该值是一个CMAKE内置变量, 表示的是当前的正在处理的 CMakeLists.txt 所在文件夹的路径, CMake 中还有另一个变量 CMAKE_SOURCE_DIR
表示的是当前编译项目的入口 CMakeLists.txt 所在的文件夹路径. 第二句设置 PX4_BINARY_DIR
的值为 ${CMAKE_CURRENT_BINARY_DIR}
路径, 该值表示的是当前编译的二进制文件夹的位置.
list(APPEND CMAKE_MODULE_PATH ${PX4_SOURCE_DIR}/cmake)
把 ${PX4_SOURCE_DIR}/cmake
添加到模块的搜索路径中. 这个文件夹对应着 /path/to/Firmware/cmake, 文件夹下有一些模块
cmake_hexagon gtest px4_add_module.cmake px4_metadata.cmake
configs px4_add_board.cmake px4_base.cmake sanitizers.cmake
coverage.cmake px4_add_common_flags.cmake px4_git.cmake
cygwin_cygpath.cmake px4_add_library.cmake px4_make_uavcan_bootloader.cmake
导入 px4_git
模块, 其中定义了px4_ add_git_submodule 模块, 主要用来更新模块.
execute_process(
COMMAND git describe --always --tags
OUTPUT_VARIABLE PX4_GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${PX4_SOURCE_DIR)
)
获取到当前版本号放到 PX4_GIT_TAG
变量中.
define_property(GLOBAL PROPERTY PX4_MODULE_LIBRARIES
BRIEF_DOCS "PX4 module libs"
FULL_DOCS "List of all PX4 module libraries" )
define_property(GLOBAL PROPERTY PX4_MODULE_PATHS
BRIEF_DOCS "PX4 module paths"
FULL_DOCS "List of paths to all PX4 modules"
)
定义了两个值为空的全局属性 PX4_MODULE_LIBRARIES
和 PX4_MODULE_PATHS
, 其中PX4_MODULE_LIBRARIES
为所有 PX4 的模块库的列表, PX4_MODULE_PATHS
为所有 PX4 模块的路径.
set(CONFIG "px4_sitl_default" CACHE STRING "desired configuration")
设置 CONFIG
的值为px4_sitl_default
, 这个值在执行make targets_default
是会被设置成targets_default
include(px4_add_module)
set(config_module_list)
set(config_df_driver_list)
px4_add_module.cmake
中定义了 px4_add_module
函数用来添加对不同平台的一些编译选项. 同时 unset 了 config_module_list
和 config_df_driver_list
.
# look for in tree board config that matches CONFIG input
if(NOT PX4_CONFIG_FILE)
file(GLOB_RECURSE board_configs
RELATIVE "${PX4_SOURCE_DIR}/boards"
"boards/*.cmake"
)
set(PX4_CONFIGS ${board_configs} CACHE STRING "PX4 board configs" FORCE)
foreach(filename ${board_configs})
# parse input CONFIG into components to match with existing in tree configs
# the platform prefix (eg nuttx_) is historical, and removed if present
string(REPLACE ".cmake" "" filename_stripped ${filename})
string(REPLACE "/" ";" config ${filename_stripped})
list(LENGTH config config_len)
if(${config_len} EQUAL 3)
list(GET config 0 vendor)
list(GET config 1 model)
list(GET config 2 label)
set(board "${vendor}${model}")
# __
boards 文件夹下所有子文件包括的cmake 文件中查找CONFIG
对应的 .cmake
文件, 比如当 CONFIG
的值为px4_sitl_default
是查找的文件为px4/sitl/default.cmake
. 对于每个通配符匹配到的.cmake
文件,把文件路径切分为三部分分别放入vendor
, model
和label
变量中, 例如对于 aerotenna/ocpoc/ubuntu.cmake
会被切分成aerotenna
, ocpoc
和 ubuntu
三部分, 然后通过不同的拼接组合和CONFIG
的值进行比较, 如果相等则设置 PX4_CONFIG_FILE
的值为当前的cmake
文件.
if(NOT PX4_CONFIG_FILE)
message(FATAL_ERROR "PX4 config file not set, try one of ${PX4_CONFIGS}")
endif()
如果遍历完所有文件之后, PX4_CONFIG_FILE
值为空, 则报错退出.
message(STATUS "PX4 config file: ${PX4_CONFIG_FILE}")
include(px4_add_board)
include(${PX4_CONFIG_FILE})
message(STATUS "PX4 config: ${PX4_CONFIG}")
message(STATUS "PX4 platform: ${PX4_PLATFORM}")
如果找到了该PX4_CONFIG_FILE
输出当前状态, 然后包括 px4_add_board
这个cmake 文件, 其中包含了 px4_add_board
这个函数. 同时导入 PX4_CONFIG_FILE
这个文件. 在该文件中调用了 px4_add_board
这个函数, 该函数设置了 PX4_CONFIG
和 PX4_PLATFORM
两个变量. 对于CONFIG
的值为px4_sitl_default
这两个值为px4_sitl_default
和 posix
.
if (ENABLE_LOCKSTEP_SCHEDULER)
add_definitions(-DENABLE_LOCKSTEP_SCHEDULER)
message(STATUS "PX4 lockstep: enabled")
else()
message(STATUS "PX4 lockstep: disabled")
endif()
目的未知…
# external modules
set(EXTERNAL_MODULES_LOCATION "" CACHE STRING "External modules source location")
if (NOT EXTERNAL_MODULES_LOCATION STREQUAL "")
get_filename_component(EXTERNAL_MODULES_LOCATION "${EXTERNAL_MODULES_LOCATION}" ABSOLUTE)
endif()
添加外部模块路径
set_property(GLOBAL PROPERTY PX4_MODULE_CONFIG_FILES)
include(platforms/${PX4_PLATFORM}/cmake/px4_impl_os.cmake)
list(APPEND CMAKE_MODULE_PATH ${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/cmake)
导入px4_impl_os.cmake
中针对于特定操作系统的一些命令, 这些命令用来生成一些程序源码和执行一些文件操作. 并把对应于这个platform 的cmake
文件夹放到 CMAKE_MODULE_PATH
中.
if(EXISTS "${PX4_SOURCE_DIR}/platforms/${PX4_PLATFORM}/cmake/init.cmake")
include(init)
endif()
导入初始化cmake 文件
# CMake build type (Debug Release RelWithDebInfo MinSizeRel Coverage)
if (NOT CMAKE_BUILD_TYPE) if (${PX4_PLATFORM} STREQUAL "nuttx")
set(PX4_BUILD_TYPE "MinSizeRel")
else()
set(PX4_BUILD_TYPE "RelWithDebInfo") endif()
set(CMAKE_BUILD_TYPE ${PX4_BUILD_TYPE} CACHE STRING "Build type" FORCE)
endif()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel;Coverage;AddressSanitizer;UndefinedBehaviorSanitizer")
设置编译类型
message(STATUS "PX4 version: ${PX4_GIT_TAG}")
message(STATUS "cmake build type: ${CMAKE_BUILD_TYPE}")
输出 PX4 版本信息和编译类型
project(px4 CXX C ASM)
set(package-contact "[email protected]")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
设置项目名称为px4
语言为 CXX C ASM
, 同时设置了包的沟通方式以及有关编程语言的一些编译设置和是否导出设置.
# For the catkin build process, unset build of dynamically-linked binaries
# and do not change CMAKE_RUNTIME_OUTPUT_DIRECTORY
if (NOT CATKIN_DEVEL_PREFIX)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PX4_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PX4_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PX4_BINARY_DIR})
else()
SET(BUILD_SHARED_LIBS OFF)
endif()
catkin 有自己的默认的二进制文件编译目录,因此这里对它进行一些特殊处理
# Setup install paths
if (${PX4_PLATFORM} STREQUAL "posix")
# This makes it possible to dynamically load code which depends on symbols
# inside the px4 executable.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_ENABLE_EXPORTS ON)
include(coverage)
include(sanitizers)
# Define GNU standard installation directories
include(GNUInstallDirs)
if (NOT CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Install path prefix" FORCE)
endif()
endif()
设置安装路径
# ccache
#
option(CCACHE "Use ccache if available" ON)
find_program(CCACHE_PROGRAM ccache)
if (CCACHE AND CCACHE_PROGRAM AND NOT DEFINED ENV{CCACHE_DISABLE})
get_filename_component(ccache_real_path ${CCACHE_PROGRAM} REALPATH)
get_filename_component(cxx_real_path ${CMAKE_CXX_COMPILER} REALPATH)
get_filename_component(cxx_abs_path ${CMAKE_CXX_COMPILER} ABSOLUTE)
if ("${ccache_real_path}" STREQUAL "${cxx_real_path}")
message(STATUS "ccache enabled via symlink (${cxx_abs_path} -> ${cxx_real_path})")
else()
message(STATUS "ccache enabled (export CCACHE_DISABLE=1 to disable)")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
endif()
endif()
ccache
是一个辅助编译的工具, 可以避免大量重复的编译工作
# see if catkin was invoked to build this
if (CATKIN_DEVEL_PREFIX)
message(STATUS "catkin ENABLED")
find_package(catkin REQUIRED)
if (catkin_FOUND)
catkin_package()
else()
message(FATAL_ERROR "catkin not found")
endif()
endif()
find_package(PythonInterp REQUIRED)
option(PYTHON_COVERAGE "Python code coverage" OFF)
if(PYTHON_COVERAGE)
message(STATUS "python coverage enabled")
set(PYTHON_EXECUTABLE coverage run -p)
else()
# run normally (broken under coveragepy)
px4_find_python_module(jinja2 REQUIRED)
endif()
Catkin build 设置
include(px4_add_common_flags)
px4_add_common_flags()
px4_os_add_flags()
设置一些编译选项, 包括是否 Debug
编译, error
和 warning
设置, 以及一些针对编译平台的选项设置
include(px4_metadata)
add_subdirectory(msg EXCLUDE_FROM_ALL)
px4_generate_airframes_xml(BOARD ${PX4_BOARD})
添加待编译目录msg
, 添加生成文档编译目标
px4_add_git_submodule(TARGET git_driverframework PATH "src/lib/DriverFramework")
set(OS ${PX4_PLATFORM})
add_subdirectory(src/lib/DriverFramework/framework)
# List the DriverFramework drivers
if (DEFINED config_df_driver_list)
message("DF Drivers: ${config_df_driver_list}")
endif()
set(df_driver_libs)
foreach(driver ${config_df_driver_list})
add_subdirectory(src/lib/DriverFramework/drivers/${driver})
list(APPEND df_driver_libs df_${driver})
message("Adding DF driver: ${driver}")
endforeach()
驱动框架
set(ep_base ${PX4_BINARY_DIR}/external)
set_property(DIRECTORY PROPERTY EP_BASE ${ep_base})
# add external project install folders to build
link_directories(${ep_base}/Install/lib)
include_directories(${ep_base}/Install/include)
# add the directories so cmake won't warn
execute_process(COMMAND cmake -E make_directory ${ep_base}/Install/lib)
execute_process(COMMAND cmake -E make_directory ${ep_base}/Install/include)
外部项目
set(external_module_paths)
if (NOT EXTERNAL_MODULES_LOCATION STREQUAL "")
message(STATUS "External modules: ${EXTERNAL_MODULES_LOCATION}")
add_subdirectory("${EXTERNAL_MODULES_LOCATION}/src" external_modules)
foreach(external_module ${config_module_list_external})
add_subdirectory(${EXTERNAL_MODULES_LOCATION}/src/${external_module} external_modules/${external_module})
list(APPEND external_module_paths ${EXTERNAL_MODULES_LOCATION}/src/${external_module})
endforeach()
endif()
外部模块
option(CMAKE_TESTING "Configure test targets" OFF)
if (${PX4_CONFIG} STREQUAL "px4_sitl_test")
set(CMAKE_TESTING ON)
endif()
if(CMAKE_TESTING)
include(CTest) # sets BUILD_TESTING variable
endif()
# enable test filtering to run only specific tests with the ctest -R regex functionality
set(TESTFILTER "" CACHE STRING "Filter string for ctest to selectively only run specific tests (ctest -R)")
# if testing is enabled download and configure gtest
list(APPEND CMAKE_MODULE_PATH ${PX4_SOURCE_DIR}/cmake/gtest/)
include(px4_add_gtest)
if(BUILD_TESTING)
include(gtest)
endif()
add_custom_target(test_results
COMMAND GTEST_COLOR=1 ${CMAKE_CTEST_COMMAND} --output-on-failure -T Test -R ${TESTFILTER} USES_TERMINAL
DEPENDS
px4
examples__dyn_hello
test_mixer_multirotor
USES_TERMINAL
COMMENT "Running tests"
WORKING_DIRECTORY ${PX4_BINARY_DIR})
set_target_properties(test_results PROPERTIES EXCLUDE_FROM_ALL TRUE)
测试
add_library(parameters_interface INTERFACE)
include(px4_add_library)
add_subdirectory(src/lib EXCLUDE_FROM_ALL)
add_subdirectory(src/platforms EXCLUDE_FROM_ALL)
add_subdirectory(src/modules/uORB EXCLUDE_FROM_ALL) # TODO: platform layer
add_subdirectory(src/drivers/boards EXCLUDE_FROM_ALL)
if(EXISTS "${PX4_BOARD_DIR}/CMakeLists.txt")
add_subdirectory(${PX4_BOARD_DIR})
endif()
foreach(module ${config_module_list})
add_subdirectory(src/${module})
endforeach()
添加子模块
# must be the last module before firmware
add_subdirectory(src/lib/parameters EXCLUDE_FROM_ALL)
target_link_libraries(parameters_interface INTERFACE parameters)
添加parameters
编译选项这个很重要因为系统需要它.
# firmware added last to generate the builtin for included modules
add_subdirectory(platforms/${PX4_PLATFORM})
添加platforms/${PX4_PLATFORM}
目录中的编译配置文件, 这个是针对不同平台的编译设置的入口.
set(uorb_graph_config ${PX4_BOARD})
set(graph_module_list "")
foreach(module ${config_module_list})
set(graph_module_list "${graph_module_list}" "--src-path" "src/${module}")
endforeach()
add_custom_command(OUTPUT ${uorb_graph_config}
COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/Tools/uorb_graph/create.py
${module_list}
--exclude-path src/examples
--file ${PX4_SOURCE_DIR}/Tools/uorb_graph/graph_${uorb_graph_config}
WORKING_DIRECTORY ${PX4_SOURCE_DIR}
COMMENT "Generating uORB graph"
)
add_custom_target(uorb_graph DEPENDS ${uorb_graph_config})
uorb
#=============================================================================
# Doxygen
#
option(BUILD_DOXYGEN "Build doxygen documentation" OFF)
if (BUILD_DOXYGEN)
find_package(Doxygen)
if (DOXYGEN_FOUND)
# set input and output files
set(DOXYGEN_IN ${CMAKE_SOURCE_DIR}/Documentation/Doxyfile.in)
set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
# request to configure the file
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
# note the option ALL which allows to build the docs together with the application
add_custom_target(doxygen ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating documentation with Doxygen"
DEPENDS uorb_msgs parameters
VERBATIM
USES_TERMINAL
)
else()
message("Doxygen needs to be installed to generate documentation")
endif()
endif()
Doxygen 文档
file(GLOB_RECURSE yaml_config_files ${PX4_SOURCE_DIR}/src/modules/*.yaml
${PX4_SOURCE_DIR}/src/drivers/*.yaml ${PX4_SOURCE_DIR}/src/lib/*.yaml)
add_custom_target(metadata_parameters
COMMAND ${CMAKE_COMMAND} -E make_directory ${PX4_BINARY_DIR}/docs
COMMAND ${PYTHON_EXECUTABLE}
${PX4_SOURCE_DIR}/Tools/serial/generate_config.py --all-ports --params-file ${PX4_SOURCE_DIR}/src/generated_serial_params.c --config-files ${yaml_config_files}
COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/src/lib/parameters/px_process_params.py
--src-path `find ${PX4_SOURCE_DIR}/src -maxdepth 4 -type d`
--inject-xml ${PX4_SOURCE_DIR}/src/lib/parameters/parameters_injected.xml
--markdown ${PX4_BINARY_DIR}/docs/parameters.md
COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/src/lib/parameters/px_process_params.py
--src-path `find ${PX4_SOURCE_DIR}/src -maxdepth 4 -type d`
--inject-xml ${PX4_SOURCE_DIR}/src/lib/parameters/parameters_injected.xml
--xml ${PX4_BINARY_DIR}/docs/parameters.xml
COMMENT "Generating full parameter metadata (markdown and xml)"
USES_TERMINAL
)
metadata_parameters 编译目标
add_custom_target(metadata_module_documentation
COMMAND ${CMAKE_COMMAND} -E make_directory ${PX4_BINARY_DIR}/docs
COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/Tools/px_process_module_doc.py -v --src-path ${PX4_SOURCE_DIR}/src
--markdown ${PX4_BINARY_DIR}/docs/modules
COMMENT "Generating module documentation"
USES_TERMINAL
)
metadata_module_documentation 编译目标
add_custom_target(all_metadata
DEPENDS
metadata_airframes
metadata_parameters
metadata_module_documentation
)
所有的文档目标
set(CPACK_PACKAGE_NAME ${PROJECT_NAME}-${PX4_CONFIG})
set(CPACK_PACKAGE_VERSION ${PX4_GIT_TAG})
set(CPACK_PACKAGE_CONTACT ${package-contact})
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF) # TODO: review packaging for linux boards
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "The PX4 Pro autopilot.")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PX4_CONFIG}-${PX4_GIT_TAG}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PX4_GIT_TAG}")
set(CPACK_SOURCE_GENERATOR "ZIP;TBZ2")
set(CPACK_PACKAGING_INSTALL_PREFIX "")
set(CPACK_SET_DESTDIR "OFF")
if ("${CMAKE_SYSTEM}" MATCHES "Linux")
set(CPACK_GENERATOR "TBZ2")
find_program(DPKG_PROGRAM dpkg)
if (EXISTS ${DPKG_PROGRAM})
list (APPEND CPACK_GENERATOR "DEB")
endif()
else()
set(CPACK_GENERATOR "ZIP")
endif()
include(CPack)
[1]: CMake Cookbook
[2]: Professional CMake – A Pratical Guide
[3]: https://github.com/ruslo/CGold