描述
对目前看过的CMakeLists.txt中的命令进行解释,同时给出一些案例
一、模板
cmake_minimum_required(VERSION 3.1.0) #定义cmake 支持的版本
project(sample) #项目名
set(CMAKE_BUILD_TYPE "Release")
include_directories(
${PROJECT_SOURCE_DIR}/include
# 需要添加的头文件路径
)
# 添加源文件路径
aux_source_directory(${PROJECT_SOURCE_DIR} SOURCE_LIST)
aux_source_directory(${PROJECT_SOURCE_DIR}/src SOURCE_LIST)
add_executable(sample main.cpp ${SOURCE_LIST})
target_link_libraries(sample -lpthread)
添加Opencv
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
include_directories(${OPENCV_INCLUDE_DIRS})
target_link_libraries(sample ${OpenCV_LIBRARIES})
endif(OpenCV_FOUND)
添加PCL
find_package(PCL 1.12 REQUIRED)
if(PCL_FOUND)
include_directories(${PCL_INCLUDE_DIRS})
target_link_libraries(sample ${PCL_LIBRARIES})
endif(PCL_FOUND)
命令
mkdir build
cd build
cmake ..
make -j8
二、基本语法规则及关键语句
- 存放源代码子目录下要编写CMakeLists.txt
- CMake变量使用
${}
来取值,IF语句中直接使用变量名 指令(参数1 参数2)
,参数以空格或分号隔开- 环境变量使用命令
SET(ENV{VAR) VALUE)
赋值,以$ENV{}
取值
常用关键字
CMakeLists.txt中会涉及到一些预定义的变量
PROJECT_BINARY_DIR # 工程的build路径
PROJECT_SOURCE_DIR # 工程的根路径
EXECUTABLE_OUTPUT_PATH # 可执行文件的输出路径
关键语句
2.1 加载外部库
2.1.1 格式
FIND_PACKAGE(<name> [version] [EXACT] [QUIET] [[REQUIRED|COMPONENTS] [ componets... ] ] )
- version: 需要一个版本号,它是正在查找的包应该兼容的版本号。
- EXACT 选项: 要求版本号必须精确匹配。如果在find-module内部对该命令的递归调用没有给定[version]参数,那么[version]和EXACT选项会自动地从外部调用前向继承。对版本的支持目前只存在于包和包之间。
例子find_package(Boost ${boost_version} EXACT REQUIRED)
- QUIET 参数:会禁掉包没有被发现时的警告信息。对应于Find.cmake模块中的 NAME_FIND_QUIETLY。
例子find_package(Boost ${boost_version} EXACT QUIET)
- REQUIRED 参数
其含义是指是工程必须的,表示如果报没有找到的话,cmake的过程会终止,并输出警告信息。对应于Find.cmake模块中的 NAME_FIND_REQUIRED 变量。
例子find_package(Boost REQUIRED COMPONENTS system)
- COMPONENTS参数
在REQUIRED选项之后,或者如果没有指定REQUIRED选项但是指定了COMPONENTS选项,在它们的后面可以列出一些与包相关(依赖)的部件清单(components list)
例子find_package(Boost REQUIRED COMPONENTS system)
2.1.2 例子
这是查找pcl库1.12版本的一个例子
find_package(PCL 1.12 REQUIRED)
-
find_package首先会在模块路径中寻找< name >.cmake
这是查找库的一个典型方式,具体查找路径依次为CMake:
变量${CMAKE_MODULE_PATH}中的所有目录。
如果没有, 然后再查看它自己的模块目录: /share/cmake-x.y/Modules/ 。
这称为模块模式。 -
如果没找到这样的文件:
find_package()会在:~/.cmake/packages/或/usr/local/share/中的各个包目录中查找,寻找:<库名字的大写>Config.cmake 或 <库名字的小写>-config.cmake (包和包是不一样的,有时大写有时小写)
(比如库Opencv,它会查找/usr/local/share/OpenCV中的OpenCVConfig.cmake或opencv-config.cmake)。**这称为配置模式。
-
不管使用哪一种模式,只要找到.cmake,.cmake里面都会定义下面这些变量:(不一定全部定义了,有些变量很可能是缺失的)
<NAME>_FOUND # 0和1,代表没找到和找到 <NAME>_INCLUDE_DIRS or <NAME>_INCLUDES # 头文件路径 <NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS # 库文件的枚举 <NAME>_DEFINITIONS
例如:当包的名字为OpenCV时,寻找的就是OpenCVConfig.cmake,OpenCVConfig.cmake之中定义的变量如下:
OpenCV_FOUND OpenCV_INCLUDE_DIRS OpenCV_LIBRARIES OpenCV_LIBS # 与OpenCV_LIBRARIES一致
(message打印一下,你会发现OPENCV_LIBS是不存在的,OpenCV_FOUND是1,OPENCV_FOUND是TRUE)
因此,包的大小写要特别注意,很可能写错大小写,也会找不到包
2.1.3 使用
如果找到这个包,则可以通过在工程的顶层目录中的CMakeLists.txt 文件添加 include_directories(< NAME>_INCLUDE_DIRS) 来包含库的头文件,添加target_link_libraries(< NAME>_LIBRARIES)命令将源文件与库文件链接起来。
例如:仍旧以OpenCV举例
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
include_directories(${OPENCV_INCLUDE_DIRS})
target_link_libraries (sample ${OpenCV_LIBRARIES})
endif(OpenCV_FOUND)
2.2 子路径编译
假设我们构建这样一个工程
sample——需要的关键代码
add——基本核心的代码
test_api——用于测试功能的代码
main——主程序
我们要达到这样的效果:
- main可以调用sample,add的代码
sample可以调用add的代码
test_api可以调用sample,add的代码,以保证可以测试main的功能 - 在cmake_learn路径下build
我们可以在cmake_learn/CMakeLists.txt
中,完成所有文件夹的编译。核心命令是add_subdirectory
。一个小示例如下。
option(TEST "BUILD TEST" OFF)
if (TEST)
message("build test!")
add_subdirectory(${PROJECT_SOURCE_DIR}/test)
endif()
在add目录下的cmake_learn/add/CMakeLists.txt
可以这么写,用于生成libadd.so
的库
set(CMAKE_BUILD_TYPE "Release")
include_directories(
${PROJECT_SOURCE_DIR}/include
)
aux_source_directory(./src ADD)
add_library(add STATIC ${ADD})
而对于test目录下的cmake_learn/test/CMakeLists.txt
,可以模仿cmake_learn/CMakeLists.txt
来写。只不过注意${PROJECT_SOURCE_DIR}
这时为cmake_learn/test/
因此
aux_source_directory(${PROJECT_SOURCE_DIR}/add/src SOURCE_LIST)
应该改为
aux_source_directory(${PROJECT_SOURCE_DIR}/../add/src SOURCE_LIST)
CMakeLists.txt中的${PROJECT_SOURCE_DIR}变量,等同于自身所在路径
三、常用命令
-
设定工程名字
project(smaple)
cmake预定义了两个变量
<projectname>_BINARY_DIR
和<projectname>_SOURCE_DIR
,它们等价于另外两个预定义的变量PROJECT_BINARY_DIR
和PROJECT_SOURCE_DIR
,建议使用后两者,因为它们可以不受project名字的限制 -
设置cmake最低版本需求
cmake_minimum_required(VERSION 3.1.0)
这句话可以不写,但一些需要指定高版本cmake时要加上这句
-
设置工程构建类型
常用的一般就是下面两个set(CMAKE_BUILD_TYPE "Release")
或
set(CMAKE_BUILD_TYPE "Debug")
一般来说我使用Release,可执行文件的运行速度会快一些
-
添加头文件路径
include_directories( ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include )
-
添加源文件路径
aux_source_directory(${PROJECT_SOURCE_DIR} SOURCE_LIST) aux_source_directory(${PROJECT_SOURCE_DIR}/src SOURCE_LIST)
得一行一行写,这样可以把需要的路径加到变量
SOURCE_LIST
中,SOURCE_LIST
名字是自定义的,可以更改 -
生成可执行文件
add_executable(sample ${SOURCE_LIST})
build会生成名字为
sample
的可执行文件
为了突出主要文件,也可以把主要文件写在这里add_executable(sample main.cpp ${SOURCE_LIST})
-
为可执行文件链接所需要的库文件
target_link_libraries(sample ${PCL_LIBRARIES} ${OpenCV_LIBS} -lpthread)
命令中的
PCL_LIBRARIES
和OpenCV_LIBS
,分别代表了PCL库和Opencv库的库文件集合,LIBRARIES
和LIBS
写哪个都行 -
更改可执行文件和生成库的输出路径
格式set(EXECUTABLE_OUTPUT_PATH [path]) set(LIBRARY_OUTPUT_PATH [path])
例子
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}) set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
在
add_executable
之前添加上面一句话,会发现可执行文件和生成的库,都生成在了工程根目录下,而不是build下了 -
编译子目录
add_subdirectory(${PROJECT_SOURCE_DIR}/test)
完整格式
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
选填的binary_dir,代表二进制生成文件会存放的位置,例如
add_subdirectory(${PROJECT_SOURCE_DIR}/add ${PROJECT_SOURCE_DIR}/add/build)
,会在子文件夹add下生成build文件夹,将生成文件存在其中。
可选参数EXCLUDE_FROM_ALL,代表将这个目录从编译过程中排除(一般不用),用了EXCLUDE_FROM_ALL参数后,该文件夹下的文件需要单独去构建 -
生成库
add_library(XXX [SHARED|STATIC])
让工程生成一个库文件,不填参数或者填参数STATIC,生成的是静态库
libXXX.a
,参数写SHARED,生成的是动态库libXXX.so
编译子目录时如果使用add_subdirectory
的参数指定了路径,库文件会生成在该路径;否则会生成在工程根目录的build下。 -
查找库
find_library(XXX libYYY.so PPP)
也可以这么写
find_library(XXX NAMES libYYY PPP)
在路径
PPP
下查找名字叫libYYY.so
的库,将这个库命名为XXX
之后链接这个库的时候,就可以替换成
XXX
这样写了target_link_libraries(${PROJECT_NAME} ${XXX})
-
设置变量
格式
SET(XXX YYY)
举例子
set(CMAKE_CXX_STANDARD 11)
将YYY命名为XXX,也可以是将变量XXX的值设置为YYY。
-
选项
例子option(TEST "BUILD TEST" OFF) if (TEST) message("build test!") add_subdirectory(${PROJECT_SOURCE_DIR}/test) endif()
使用
cmake .. -DTEST=ON
,可以把TEST
变量置为ON,从而编译子目录test
总结
cmake虽简单,功能如繁星,仍不足以全部说明,后期不断补充这篇文章吧