Cmake
- 缘起
- 由于 C 和 C++ 比较糟糕的库的链接设计的历史遗留问题,当编译源代码的时候,一般使用 Make 和 Makefile 来指定文件之间的依赖关系,这样可以带来两个好处:一是让编译工作尽量自动化,一条 Makefile 指令可以管一片;二是可以让编译工作加速,当只有少数几个文件中的代码出现变动时,则只需要编译与该代码有依赖或关联的部分文件即可,不需要将整个项目都重新编译,节省时间;
- 尽管已经有了 Make 这么好用的工具,仍然存在一个问题,即 Makefile 的编译工作跟操作系统有一定的依赖关系,即不同的操作系统,Makefile 的内容需要有所不同,才能正确编译代码;为了解决这个平台依赖的问题,引入了 CMake 工具,它的目标是只需写一次 CMakeLists,就可以根据所要编译的平台环境,自动生成对应的 Makefile,这样一来就可以极大的减少针对不同平台编写 Makefile 的工作量;
- CMake 有自己的语法,可以算是一种 DSL 了,按王垠说法,绝大部分 DSL 都设计的很糟糕,我也有同感,据说可能是因为它们都是由非 PL 专业的人员设计的,所以总是不如一门图灵完备的语言那么强大和高表达性;
- 后来,在 CMake 教程学完后,发现了另外一个以 python 作为作为语法表达的工具 SCons,估计这个工具就要强大多了,据说得到了很多名家的推荐,包括 Eric S. Raymond、Timothee Besset、Zed A. Shaw 等;有待日后抽空学习了解一下;
- 升级步骤
- 到官网下载源代码包,解压,进入解压后的文件夹
- cmake .
- make
- make install (注:此步可选)
- 语法
- 说明
- 由指令+指令参数组成;
- 指令的名称一般使用小写字母表示(实际并不区分大小写),指令的参数部分一般用圆括号括起来,例如 cmake_minimum_required (VERSION 2.8)
- 参数部分中的变量名称一般使用大写字母表示,例如 VERSION,引用变量时的格式为美元符加花括号,例如 ${VERSION}
- 注释使用 # 符号;间隔使用空格
- 后来我发现 CMake 有很多自己的全局变量;包括
- CMAKE_BINARY_DIR,顶级二进制文件目录
- CMAKE_SOURCE_DIR,顶级源文件目录
- CMAKE_CURRENT_BINARY_DIR,当前二进制文件目录
- CMAKE_CURRENT_SOURCE_DIR,当前源文件目录
- PROJECT_NAME,项目名称
- 具体指令
- cmake_minimum_required
- 用来指定版本要求,例如:cmake_minimum_required (VERSION 2.8)
- project
- 用来指定项目名称,例如 project (Demo)
- add_executable
- 用来指定要生成的目标,以及目标所依赖的源文件,例如 add_executable(Demo main.cc)
- 其他源文件支持多个;当有很多时,可以使用变量来替换表示;
- aux_source_directory
- 用来查找指定目录下的所有源文件,首参数为目录路径,第二个参数是变量名,用来存储找到的结果;
- 例如 aux_source_directory(. DIR_SRCS)
- 用来查找指定目录下的所有源文件,首参数为目录路径,第二个参数是变量名,用来存储找到的结果;
- add_subdirectory
- 用来添加子目录,这样子目录下的 CMakeLists 也会进行处理;
- 子目录的 CMakeLists 用来处理子目录里面的内容,例如生成库文件;
- include_directories
- 指定头文件的搜索目录
- link_directories
- 指定要链接的库文件的搜索目录
- link_libraries
- 指定要链接的库文件的名称和路径(绝对地址)
- target_link_libraries
- 指定要链接的库文件的名称,第一个参数表示主文件,第二个参数表示主文件需要链接的库文件;
- 例如 target_link_libraries(Demo MathFunctions)
- 如果有多个库,估计也支持多参数,以及使用变量来表示多个库文件;
- 指定要链接的库文件的名称,第一个参数表示主文件,第二个参数表示主文件需要链接的库文件;
- add_library
- 用来指定要生成的库文件,第一个参数表示要生成的库文件,第二个参数所用到的源文件;
- 例如 add_library (MathFunctions ${DIR_LIB_SRCS})
- 第二个参数也同样支持用变量表示多个文件;
- 格式
- add_library(
[STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [source1] [source2 …])
- add_library(
- 用来指定要生成的库文件,第一个参数表示要生成的库文件,第二个参数所用到的源文件;
- target_include_directories
- 指定编译 target 时要使用的 include 目录;其中的 target 需要由 add_library 或者 add_executable 指定;
- 格式
- target_include_directories(
[SYSTEM] [BEFORE] - <INTERFACE | PUBLIC | PRIVATE> [items1…]
- [<INTERFACE | PUBLIC | PRIVATE> [items2…] …])
- target_include_directories(
- configure_file
- 根据输入的文件,替换其中的变量,生成另外一个文件;第一个参数表示输入的文件,第二个参数表示要生成的文件
- 输入文件中的变量定义格式为: #CMakedefine VAR …
- 输出文件中的变量会被替换为:#define VAR … 或者 /* #undef VAR */ (表示此变量被注释掉了,不启用)
- 例如: configure_file ( “${PROJECT_SOURCE_DIR}/config.h.in” “${PROJECT_BINARY_DIR}/config.h” )
- 在输入的文件中,可以引用在 CMakeLists 文件中定义的变量
- 输出的文件可以作为源代码文件的一部分引用;
- options
- 用来定义可供用户选择的配置项开关,第一个参数是选项变量名,第二个参数是选项提示信息,第三个参数是默认初始值(可选,未注明则默认为 OFF )
- 选项只有两种默认值,ON 或者 OFF
- 例如 option (USE_MYMATH “Use custom math implementation” ON)
- if
- 用来设置条件判断,
- 格式为
- if (
) # command 可由多行组成,每行建议缩进以方便阅读
- elseif(
) - else()
- endif()
- if (
- 示例
- if (USE_MATH)
- include_directories (“${PROJECT_SOURCE_DIR}/math”)
- add_subdirectory (math)
- set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
- endif ()
- if (USE_MATH)
- 格式为
- 一般源文件中也会有基于全局条件变量的部分代码,例如
- #ifdef USE_MYMATH
- #include “math/MathFuncitons.h”
- #else
- #include
- #endif
- #ifdef USE_MYMATH
- 用来设置条件判断,
- set
- 用来设置变量及其初始值,第一个参数为变量名,第二个参数为变量值,第三个参数是父级作用域 [PARENT_SCOPE](可选)
- 格式:set (
… [PARENT_SCOPE]) - 我们在 CMakeLists 中设置的变量,是可以在 config.h.in 文件中引用的;
- 格式:set (
- 用来设置 CACHE,它用来将某个变量值存储到缓存中,重新运行 cmake 时,会使用缓存值,除非使用 [FORCE] 选项显式的重新赋值;
- 格式:set (
… CACHE [FORCE]) - 其中 type 表示要缓存的变量类型,支持以下五种
- BOOL,布尔值,ON/OFF 两个值;
- PATH,文件夹路径
- FILEPATH,文件名路径;
- STRING,字符串
- INTERNAL,同 STRING,也是字符串;据说是隐式的 FORCE,暂时不知道使用场景;
用来对缓存的变量做简单说明,仅支持字符串格式;
- 格式:set (
- 用来设置环境变量
- 格式:set (ENV{
} [ ])
- 格式:set (ENV{
- 用来设置变量及其初始值,第一个参数为变量名,第二个参数为变量值,第三个参数是父级作用域 [PARENT_SCOPE](可选)
- install
- 用来设置安装规则,DESTINATION 用来表示要安装的目标路径,FILES 用来表示文件安装规则,TARGETS 用来表示target 目标的安装规则
- 示例
- install (TARGETS Demo DESTINATION bin)
- install (FILES “${PROJECT_BINARY_DIR}/conifig” DESTINATION include)
- add_test
- 用来添加测试用例,格式为 add_test (NAME
COMMAND [ …]) - 第一个参数表示测试用例的名称,第二个名称表示要执行的测试命令,第三个参数表示测试选项
- 示例:add_test (test_5_2 Demo 5 2)
- 用来添加测试用例,格式为 add_test (NAME
- enable_testing
- 用来表示启用测试,可以没有参数
- 示例:enable_testing( )
- 用来表示启用测试,可以没有参数
- set_tests_properties
- 用为设置测试的参数,一般用来验证测试结果是否正确,格式 set_tests_properties (test1 [test2…] PROPERTIES prop1 value1 prop2 value2)
- 其中 test1, test2 等表示测试用例的名称,通过 add_test 指令设定;
- 示例
- set_tests_properties (test_5_2 PROPERTIES PASS_REGULAR_EXPRESSION “is 25”)
- 用为设置测试的参数,一般用来验证测试结果是否正确,格式 set_tests_properties (test1 [test2…] PROPERTIES prop1 value1 prop2 value2)
- include
- 加载指定的CMake文件或模块,以便加载并运行文件或模块中的 CMake 代码,格式为 include (<file | module> [OPTIONAL])
- 示例
- include (${CMAKE_ROOT}/Modules/CheckFunctinExists.cmake)
- 问题:Modules 是哪来的?CMake 自带的;
- check_function_exists
- 用来检查某个 C 函数是否可以被链接,格式为:check_function_exists(
) - 第一个参数 function 表示系统标准库提供的函数名,第二个参数是用来存储结果的变量 variable (临时创建,存储在缓存中)
- 示例
- include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake) # 需要先引入 CMake 自带的宏文件
- check_function_exists (pow HAVE_POW)
- 用来检查某个 C 函数是否可以被链接,格式为:check_function_exists(
- find_package
- 用来查找并导入外包的库
- 格式: find_package(
[version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components…]] [OPTIONAL_COMPONENTS components…] [NO_POLICY_SCOPE]) - REQUIRED 选项,表示该库文件是必须的,没有找到会终止 CMake 执行并退出;
- 示例:find_package( OpenCV REQUIRED )
- 这个指令执行完毕后,会有一些内置变量保存查询结果,包括
_FOUND,
- foreach
- 对列表中的元素进行遍历,并对每个元素执行一遍指令
- 格式
- foreach(
) - endforeach()
- foreach(
- 这个命令可以用来执行多个子项目的编译;
- 添加编译选项
- SET (GCC_COMPLIE_FLAGS “-lrt”)
- add_definitions(${GCC_COMPLIE_FLAGS})
- cmake_minimum_required
- 场景
- 构建安装包
- CMakeLists 配置
- include (InstallRequiredSystemLibraries) # 导入包安装的模块
- set (CPACK_RESOURCE_FILE_LICENSE “${CMAKE_CURRENT_SOURCE_DIR}/License.txt”) # 设置版本声明变量
- set (CPACK_PACKAGE_VERSION_MAJOR “${Demo_VERSION_MAJOR}”) # 设置大版本号
- set (CPACK_PACKAGE_VERSION_MINOR “${Demo_VERSION_MINOR}”) # 设置小版本号
- include (CPack) # 导入CPack 包;
- 命令行执行的命令
- cpack -C CPackConfig.cmake # 生成二进制安装包
- 或者 cpack -C CPackSourceConfig.cmake # 生成源码安装包
- cpack -C CPackConfig.cmake # 生成二进制安装包
- CMakeLists 配置
- 构建安装包
- 说明
Cmake
https://ccw1078.github.io/2019/05/17/Cmake/