CMake使用注意事项
1. CMakeLists.txt 文件名严格区分大小写
2. CMake指令可以不区分大小写,大小写混写都行,每条语句不需要加分号;
3. 参数严格区分大小写,名称只能用字母、数字、下划线、破折号
4. 使用${}来引用 , 参数之间使用空格分割
5. 使用 # 可以做注释
CMake常用指令
cmake_minimum_required(VERSION 最低版本)
声明CMake最低版本
project(工程名称)
设置项目名称
set (变量 参数)
给变量赋值为参数,这里的变量可以CMake自己变量也可是自己定义的变量
CMake常用变量
CMAKE_BUILD_TYPE:CMake编译类型,常用参数如下
Debug: 调试模式,输出调试信息,不做优化
Release: 发布模式,没有调试信息,全优化
RelWithDebInfo::类似Release, 但包括调试信息
MinSizeRel: 一种特殊的Release模式,会特别优化库的大小例:set (CMAKE_BUILD_TYPE Debug) 设置编译类型为Debug模式
CMAKE_CXX_FLAGS:对C++代码编译过程进行设置,常用参数如下
–std=c++11
:支持C++11标准
-Wall
:编译过程输出警告信息
-O3
:使用o3等级优化 注:优化等级越高,生成可执行文件越少,但可能会优化 掉一些需要的函数,如果实际测试发现一个应该执行到的函数没有执行可能是优化器给优化掉了,需要降低优化等级或者直接 -O0(不优化)-march=native
:加了该参数,编译器将自动检测CPU型号,并使用该型号的增强指令集,比如各个版本的SSE AVX等,编译时编译器将优先使用这些增强指令集替代原始指令集,达到更快的执行效率。但往往这块儿会存在一些问题。如果安装第三方库开启了-march=native,则调用第三方库的工程里必须也得加 -march=native,因为使用增强指令值时要求操作数需要按照一定格式在内存储存,如4字节对齐或者16字节对齐或者其他形式,如果不加-march=native操作数将采用默认形式,就会报错。
可执行文件的可移植性降低,如果电脑A使用-march=native编译,将可执行文件送给电脑B,如果电脑B和电脑A支持的增强指令集不一样,则会执行不了,电脑B需要使用源码进行重新编译生成可执行文件。
例:set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3 -march=native")
注:例子里添加${CMAKE_CXX_FLAGS}表示在CMAKE_CXX_FLAGS原有的基础上新增-Wall -O3 -march=native这些指令,如果不加${CMAKE_CXX_FLAGS},则会覆盖掉CMAKE_CXX_FLAGS 原有的配置。CMAKE_C_FLAGS:对C代码编译过程进行设置,用法类似于CMAKE_CXX_FLAGS
CMAKE_CXX_STANDARD C++标准
如set (CMAKE_CXX_STANDARD 11)
include_directories(xxx)
给工程添加头文件,例如使用opencv时需要包含/usr/local/include/opencv/cv.h这个头文件,则我们需要include_directories(/usr/local/include),在调用的函数里写 #include “opencv/cv.h”即可
target_include_directories()
为指定的目标添加搜索头文件
include_directories()和target_include_directories()区别
include_directories 会为当前CMakeLists.txt的所有目标,以及之后添加的所有子目录的目标添加头文件搜索路径。因此,慎用,会影响全局target
target_include_directories 只会为指定目标包含头文件搜索路径。如果想为不同目标设置不同的搜索路径,target_include_directories 更合适
PRIVATE INTERFACE PUBLIC区别,参考下面两个链接
cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE
https://zhuanlan.zhihu.com/p/82244559
例:target_include_directories(test xxx ${PROJECT_SOURCE_DIR}/include)
如果xxx为PRIVATE 则表示头文件只能由 test使用
如果xxx为INTERFACE 则表示头文件只能由 调用test的文件使用
如果xxx为PUBLIC 则test和任何调用test的文件都能使用头文件
add_executable(可执行文件名 1.cpp 2.ppp …)
添加可执行文件,其中可执行文件需要用到1.cpp和2.cpp 工程会给1.cpp和2.cpp编译生成一个可执行文件
add_library(生成的库名称 STATIC/SHARED 源文件.cpp)
将源文件生成 静态/共享 库文件 STSTIC 表示静态库最终会编入到可执行文件中成为一体,使用静态库生成可执行文件占用内存大,静态库升级时需要重新编译 SHARED(常用)表示共享库最终和可执行文件分离,运行时加载,占用空间小,升级方便
我们常用SHARED,假设我们工程里调用了一个函数库a,此时库a的作者进行了代码修改,如果我们使用STSTIC ,则会将库a和当前工程调用了库a函数的所有文件一起编译,往往实际开发中工程往往是庞大的且每个库更新频率频繁,所以大型项目中使用STSTIC 每次编译将很费事;如果使用SHARED,库a做了修改后,只需要重新编译库a即可,此时库a和工程可以看成完全隔离的状态,当前工程调用了库a的函数将不会重新编译,节省编译时间。
可以使用 add_library(hello::library ALIAS hello_library) 给库文件hello_library起别名为hello::library,后面使用hello_library和hello::library都行
target_link_libraries (库/可执行文件 library1 library2 ...)
为库或者可执行文件添加需要链接的库
注:需要放在add_executable之后
add_subdirectory(source_dir)
向当前工程添加存放其他源文件的子目录,用于多目录下都有CMakeLists.txt文件的情况
aux_source_directory( 文件目录 变量名)
把文件目录下的所有源文件储存在变量中
例:aux_source_directory(${PROJECT_SOURCE_DIR}/ DIR_SRCS) 表示把工程目录的源文件添加到DIR_SRCS变量中,搭配一些其他指令更方便,如add_executable(test ${DIR_SRCS}),这样就不一样一个个写源文件名称了
${PROJECT_SOURCE_DIR} 为工程目录。
message(模式 "output msg" )
打印输出信息,常见模式有FATAL_ERROR、WARNING、STATUS、DEBUG等
find_package(<NAME> 版本号 EXACT/QUIET/REQUIRED)
version:指定查找库的版本号。 EXACT:要求该版本号必须精确匹配。 QUIET:禁掉没有找到时的警告信息。如果没找到则忽略这一问题,继续执行 REQUIRED选项表示如果包没有找到的话,CMake的过程会终止,并输出警告信息
当find_package找到一个库的时候,以下变量会自动初始化:
<NAME>FOUND : 显示是否找到库的标记
<NAME>INCLUDE_DIRS 或 <NAME>INCLUDES : 头文件路径
<NAME>LIBRARIES 或 <NAME>LIBRARIES 或 <NAME>LIBS : 库文件注:如果CMake自动找不到路径,我们需要手动设置路径,如 先 set(OpenCV_INCLUDE_DIRS “home/hwh/Opencv3.1/build”) 设置路径 然后find_package(OpenCV 3.1 REQUIRED)
file(GLOB_RECURSE 变量 "路径")
会在路径下查找目标文件并保存在变量中
例:cartographer的CMakeLists.txt文件中这样对工程进行配置,如下
file(GLOB_RECURSE ALL_LIBRARY_HDRS "cartographer/*.h")
file(GLOB_RECURSE ALL_LIBRARY_SRCS "cartographer/*.cc")
其中*为通配符,如:cartographer/*.h表示cartographer文件夹及子文件夹下所有的.h文件,
往往该指令搭配其他指令更方便,如cartographer的CMakeLists.txt文件中
file(GLOB_RECURSE ALL_LIBRARY_HDRS "cartographer/*.h") file(GLOB_RECURSE ALL_LIBRARY_SRCS "cartographer/*.cc") file(GLOB_RECURSE TEST_LIBRARY_HDRS "cartographer/fake_*.h" "cartographer/*test_helpers*.h" "cartographer/mock_*.h") file(GLOB_RECURSE TEST_LIBRARY_SRCS "cartographer/fake_*.cc" "cartographer/*test_helpers*.cc" "cartographer/mock_*.cc") file(GLOB_RECURSE ALL_TESTS "cartographer/*_test.cc") file(GLOB_RECURSE ALL_EXECUTABLES "cartographer/*_main.cc") list(REMOVE_ITEM ALL_LIBRARY_SRCS ${ALL_EXECUTABLES}) //去除ALL_LIBRARY_SRCS中的ALL_EXECUTABLES list(REMOVE_ITEM ALL_LIBRARY_SRCS ${ALL_TESTS}) list(REMOVE_ITEM ALL_LIBRARY_HDRS ${TEST_LIBRARY_HDRS}) list(REMOVE_ITEM ALL_LIBRARY_SRCS ${TEST_LIBRARY_SRCS}) add_library(${PROJECT_NAME} STATIC ${ALL_LIBRARY_HDRS} ${ALL_LIBRARY_SRCS}) //此时更简洁,不需要一个个添加
如上,使用file后再使用add_library更方便,同理file后也可以跟add_executable也很方便