对于CMake推荐在项目使用中学习,通过查阅文档和实际测试每个指令的用处能有效的掌握CMake。
CMake是一种常用的C++项目编译工具,通常一个完备的CMake项目包括了如下几部分:
src
:源代码文件夹include
:头文件文件夹doc
:项目文档文件夹build
:编译文件夹devel
:ROS1用于保存编译后程序的文件夹run.sh
:项目启动脚本(ROS中无)CMakeList.txt
:CMake文件,应在项目根目录和各级子文件夹内存在(例如ROS内就是在工作空间根目录和各个功能包根目录下存在)对于一个C++项目,在编译完成后还可能需要使用make install
将其安装至指定位置。
对于一个项目会构建一个build
文件夹用于存放编译生成的中间文件,从而避免污染工作空间:
mkdir build & cd build # 新建build目录并进入
cmake .. # 构建MakeFile(build父目录)
make -j5 # 编译
cmake ..
表示使用cmake
构建当前文件夹build
的父文件夹(项目根目录)。上述指令还可简化为使用参数--build
:
mkdir build & cd build # 新建build目录并进入
cmake --build .. # 构建并编译(build父目录)
cmake指令的更多参数和使用可参考官方手册:cmake
CMakeList
文件的编写主要遵从如下几条原则:
${变量名}
被用于引用CMake变量,使用IF控制语句除外project(projectname [cxx])
"fn nc.c"
#
注释cmake_minimum_required
用于指定所使用CMake的最低版本号,置于CMakeList.txt
的开头;project
指令则用于指定项目的名称,指令的用法如下:
cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR])
project(<PROJECT-NAME> [<language-name>...])
VERSION
:关键字,表明后续添加为版本号min
: 设置所使用CMake的最低版本号,格式为major.minor[.patch[.tweak]]
policy_max
:设置所使用的CMake的最高版本号,应高于min
:设置项目名称
:设置项目支持的语言,不加表示默认所有编程语言使用示例如下所示:
cmake_minimum_required(VERSION 3.2.4)
project(Demo)
project
指令同时隐式的定义了几个变量:
_BINARY_DIR
:项目编译目录的绝对路径_SOURCE_DIR
:项目源目录的绝对路径PROJECT_BINARY_DIR
:自动绑定_BINARY_DIR
的内容PROJECT_SOURCE_DIR
:自动绑定_SOURCE_DIR
的内容通常在调用上述CMake变量时,使用PROJECT_BINARY_DIR
和PROJECT_SOURCE_DIR
进行,从而避免CMake项目名称修改后需要额外修改其余内容。
更多内容可参考官方指导手册:cmake_minimum_required、project
以及博文Cmake命令之cmake_minimum_required介绍和Cmake命令之project介绍
用于设置variable
的值为 value
,指令用法如下:
set(<variable> <value>... [PARENT_SCOPE])
variable
:被设置的参数value
:设置的值例如在CMake
中指定使用的C++
标准为C++ 14
,则可定义变量${CMAKE_CXX_STANDARD}
的值为14:
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
更多用法(环境变量、缓存变量、定义域等)可查看官方手册:set,以及博文Cmake命令之set介绍
用于查找包(通常由项目外部的第三方库提供),并加载其包特定的详细信息。指令使用方式如下:
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER])
PackageName
:需要查找的包名version
:可指定查找包的版本要求
versionMin...versionMax
:如1.1.1.1…1.2.2.1(包括1.2.2.1)versionMin...[<]versionMax
:如1.1.1.1…<1.2.2.1(不包括1.2.2.1)QUIET
:禁止输出查找日志信息REQUIRED
:当未找到满足条件的包时停止构建COMPONENTS
:指定要查找的组件例如如下即为常见的ROS查找catkin包相关组件:
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
message_generation
nav_msgs
roscpp
rospy
std_msgs
tf
visualization_msgs
)
更多使用方法可以参考博文Cmake命令之find_package介绍
以及官方手册:find_package
用于从源码文件编译得到一个可执行文件,指令用法如下:
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
name
:编译所得的可执行文件命名source
:用于编译的源文件[EXCLUDE_FROM_ALL]
:设置后,表明该可执行文件将被排除在all
外,需要手动确定才能执行生成通常仅指定构建的可执行文件名和使用的源码,如:
add_executable(Demo main.c)
更多用法参考博文Cmake命令之add_executable介绍
以及官方手册:add_executable
当一个文件夹内存在多个源代码时,可使用该指令将其定义为一个变量,从而方便调用:
aux_source_directory(<dir> <variable>)
dir
:源代码路径variable
:变量名例如,下列示例将当前目录下所有源代码加入至DIR_SRCS
,并用其编译生成可执行文件Demo
cmake_minimum_required (VERSION 2.8)
project (Demo2)
aux_source_directory(. DIR_SRCS)
add_executable(Demo ${DIR_SRCS})
更多用法可参考官方手册:aux_source_directory
两者都用于将给定目录添加到编译器用于搜索包含文件的目录中,不同之处在于include_directories
将对整个CMakeList
内每个目标添加搜索路径,而target_include_directories
则仅针对指定目标添加搜索路径,两者用法如下:
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
:target_include_directories
中的指定目标[AFTER|BEFORE]
:可显式指定添加头文件搜索路径的位置
dir
:include_directories
中添加的搜索路径,相对路径将被解释为基于当前源目录开始
INTERFACE
:仅target
的头文件可使用该路径PUBLIC
:target
的头文件和源文件均可以使用PRIVATE
:target
对应的源文件可以使用items
:target_include_directories
中添加的搜索路径,相对路径将被解释为基于当前源目录开始此处可参考博文CMake 添加头文件搜索路径 include_directories, target_include_directories
以及官方手册:include_directories、target_include_directories
用于从源码文件编译得到一个库文件,指令用法如下:
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[<source>...])
:构建的库文件的名称,最终生成的库文件名字将根据本机惯例进行产生,如:lib.lib
或.a
[STATIC | SHARED | MODULE]
:构建库文件的类型
STATIC
:静态库文件SHARED
:动态库文件MODULE
:模块库文件source
:用于编译的源文件[EXCLUDE_FROM_ALL]
:设置后,表明该库将被排除在all
外,需要手动确定才能执行生成CMake
参数以INTERFACE_
开头生成的库文件将被输出至${ARCHIVE_OUTPUT_DIRECTORY}
、${LIBRARY_OUTPUT_DIRECTORY}
和${RUNTIME_OUTPUT_DIRECTORY}
中:
ARCHIVE_OUTPUT_DIRECTORY
:静态库文件存储位置LIBRARY_OUTPUT_DIRECTORY
:Lib文件存储位置RUNTIME_OUTPUT_DIRECTORY
:动态库文件存储位置若需要导入一个已经生成的库文件还可以使用如下方式:
add_library(<name> [STATIC | SHARED | MODULE]
IMPORTED [GLOBAL])
IMPORTED
关键字用于说明该库文件已生成无需编译GLOBAL
:设置后表明该库文件全局可见CMake
参数以IMPORTED_
开头更多用法可以参考博文:cmake : add_library详解或者官方手册:add_library
用于添加需要被构建的子目录,指令用法如下:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM])
source_dir
:指定子目录,其内应包含CMakeList.txt
文件、源码文件binary_dir
:指定输出可执行文件位置例如项目内包含子文件夹src
,其内包括CMakeList.txt
和相关源码。则在项目根目录CMakeList.txt
中添加如下指令使其被构建:
add_subdirectory(src)
更多用法可以参考博文Cmake命令之add_subdirectory介绍以及官方手册:add_subdirectory
用于连接可执行文件和对应的库文件:
target_link_libraries(<target> ... <item>... ...)
target
:被连接的可执行文件item
:库文件名称、路径等例如,可执行文件main
连接生成的库文件B
:
add_executable(main main.c)
add_library(B SHARED b.c)
target_link_libraries(main B)
更多使用方式可参考官方手册:target_link_libraries
用于连接可执行文件和用于查找依赖库文件的路径,指令用法如下:
target_link_directories(<target> [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
更多使用方式可参考官方手册:target_link_directories
在CMake中安装使用install
指令实现,针对不同的文件类型,该指令具备不同的使用方式,可参考官方手册查看详细内容:install
install(TARGETS targets... [EXPORT <export-name>]
[RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>|CXX_MODULES_BMI]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
[INCLUDES DESTINATION [<dir> ...]]
)
TARGETS
:关键字,表明安装的对象为Targettargets
:指定目标文件名[ARCHIVE|LIBRARY|RUNTIME……]
:指定文件类型
ARCHIVE
:静态库LIBRARY
:动态库RUNTIME
:可执行目标二进制文件 [DESTINATION ]
:指定安装路径
为绝对路径,以"/"开头
为相对路径,则实际安装路径为:${CMAKE_INSTALL_PREFIX}
[PERMISSIONS permissions...]
:用户权限,最高777(参考liunx权限)
OWNER_WRITE
:拥有者写入权限OWNER_READ
:拥有者读取权限OWNER_EXECUTE
:组成员执行权限GROUP_WRITE
:组成员写入权限GROUP_READ
:组成员读取权限GROUP_EXECUTE
:组成员执行权限WORLD_WRITE
:其他人写入权限WORLD_READ
:其他人读取权限WORLD_EXECUTE
:其他人执行权限例如,如下使用方式:
install(TARGETS myExe mySharedLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
install(TARGETS mySharedLib DESTINATION /some/full/path)
上述例子实现如下目标:
myExe
安装至${CMAKE_INSTALL_PREFIX}/bin
下mySharedLib
安装至${CMAKE_INSTALL_PREFIX}/lib
和/some/full/path
下myStaticLib
安装至${CMAKE_INSTALL_PREFIX}/lib/static
下install(<FILES|PROGRAMS> files...
TYPE <type> | DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])
:关键字,表明安装的对象类型为FILES
或PROGRAMS
,应选择其一填写
FILES
:权限为拥有者读写、组成员读、其余人读(644)PROGRAMS
:权限为拥有者读写执行、组成员读执行、其余人读执行(755)files…
:指定文件名称TYPE
:关键字,表明文件的类型 [DESTINATION ]
:指定安装路径install(DIRECTORY dirs...
TYPE <type> | DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>] [EXCLUDE_FROM_ALL]
[FILES_MATCHING]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
[FILE_PERMISSIONS permissions...]
:目录内文件权限[DIRECTORY_PERMISSIONS permissions...]
:目录本身权限[USE_SOURCE_PERMISSIONS]
:若未指定FILE_PERMISSIONS permissions...
,则根据源文件权限赋予[PATTERN
:采用模式匹配进行筛选内容
REGEX
:采用正则匹配进行筛选内容
[EXCLUDE]
:安装时,排除筛选得到的文件[PERMISSIONS permissions...]
:指定筛选得到的文件的权限例如使用该命令执行如下:
INSTALL(DIRECTORY icons scripts/
DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*" PERMISSIONS
OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ WORLD_EXECUTE
)
上述指令执行如下操作:
icons
安装至${CMAKE_INSTALL_PREFIX}/share/myproj
scripts/
中的内容安装${CMAKE_INSTALL_PREFIX}/share/myproj
scripts/*
文件的权限指定为731用于向终端中输出用户定义的编译日志信息,指令用法如下:
message([<mode>] "message text" ...)
mode
:日志类型
FATAL_ERROR
:红色CMake Error,将终止CMake编译SEND_ERROR
:CMake Error,将跳过生成但不影响编译WARNING
:黄色CMake Warning,不影响CMake编译AUTHOR_WARNING
:CMake Warning (dev),不影响CMake编译STATUS
:输出一些编译过程中的简明信息VERBOSE
:输出一些编译过程中的详细信息message text
:输出的日志内容更多使用方式可参考官方手册:message