文章目录索引
- What is CMake?
- How to CMake?
- 编译可执行文件
- 单文件
- 多个文件
- 编译库文件(动态库、静态库)
- 链接指令的比较(link_directories、target_link_libraries)
- 自定义编译
- 安装——install
- 测试——add_test
- 设置支持gdb
- 环境检查
- 版本号
- 安装包——CPack
What is CMake?
不同的平台有不同的编译器,例如 GNU Make , qmake , MS nmake等,如果想要软件跨平台编译就得写不同的makefile文件。
CMake让开发者编写 CMakeList.txt 文件(无关平台),用来规定整个编译的流程,然后再根据平台进一步生成所需的本地化 Makefile 和工程文件。
How to CMake?
使用vscode比较方便,只需将CMakeList.txt与源文件和头文件放入同一文件夹下,即可编译。
linux环境下,在命令行中进入所在目录,输入一下命令亦可:
cmake . # 会有信息提示,无误之后才可make
make
编译可执行文件
单文件
main.c
#include <stdio.h>
int main()
{
printf("Hello World !");
return 0;
}
CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo1)
# 指定生成目标
add_executable(Demo1 main.c)
#号后面跟的是注释
多个文件
同一目录:
Demo2-----
|---main.c
|---test.c
|---test.h
CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo2)
# 指定生成目标
add_executable(Demo main.c test.c)
如果源文件比较多,那么最后一行添加的文件也会比较繁琐,所以采用如下格式:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo2)
# 将当前目录所有源文件的文件名赋值给变量 DIR_SRCS
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo2 ${DIR_SRCS})
aux_source_directory命令,语法如下:
aux_source_directory(<dir> <variable>)
不同目录:
Demo3------
|---main.c
|---Test------
|---test.h
|---test.c
Demo3 / CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo3)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 添加 math 子目录
add_subdirectory(Test)
# 指定生成目标
add_executable(Demo main.c)
# 添加链接库
target_link_libraries(Demo TEST)
Demo3 / Test / CMakeLists.txt
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
# 生成静态链接库
add_library (TEST ${DIR_LIB_SRCS})
编译库文件(动态库、静态库)
目录结构:
|---CMakeLists.txt
|---include
|---hello.h
|---src
|---hello.cpp
CMakeLists.txt:
cmake_minimum_required(VERSION 3.1)
project(libhello)
set(libhello_src src/hello.cxx) # 指定库的目录变量
include_directories("${PROJECT_SOURCE_DIR}/include") # 指定头文件搜索路径
add_library( hello SHARED ${libhello_src}) # 动态库
add_library( hello_static STATIC ${libhello_src}) # 静态库
如果想要将”hello_static“输出为hello,可以进行如下设置:
SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")
cmake在构建一个新的target时,会尝试清理掉同名库。(即在构建libhello.a时,就会清理掉libhello.so,或者反之)。
为了回避这个问题,可以再次使用SET_TARGET_PROPERTIES定义 CLEAN_DIRECT_OUTPUT属性
SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
动态库应该包含一个版本号
SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)
- VERSION指代动态库版本
- SOVERSION指代API版本
链接指令的比较(link_directories、target_link_libraries)
link_directories | target_link_libraries |
指定库路径 | 只需给出动态链接库的名字 |
find_package、find_library可得到库的绝对路径 | / |
用在add_executable之前 | 用在add_executable之后 |
自定义编译
CMake 可以为项目增加编译选项,例如,可以将 TEST库设为一个可选的库,如果该选项为 ON ,就调用。
类似于C语言中的预定义功能
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo4)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 是否使用自己的 MathFunctions 库
option (USE_MYLIB "Use provided lib" ON)
# 是否加入TEST库
if (USE_MYLIB)
include_directories ("${PROJECT_SOURCE_DIR}/Test")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} Test)
endif (USE_MYLIB)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo4 ${DIR_SRCS})
target_link_libraries (Demo4 ${EXTRA_LIBS})
- configure_file 命令加入一个配置头文件 config.h (由 CMake 从 config.h.in 生成)
- option 命令添加一个 USE_MYLIB 选项,并默认为 ON(类似于C++中设置变量为true)
- if (USE_MYLIB) 对其进行判断,是否使用TEST库
编写config.h.in 文件:
#cmakedefine USE_MYLIB
编写main.c文件:
#include <stdio.h>
#include "config.h"
#ifdef USE_MYMATH
#include "Test/test.h"
#else
// do something
#endif
int main()
{
#ifdef USE_MYLIB
printf("Use our own library. \n");
// do something
#else
printf("Not use our own library. \n");
// do something
#endif
printf("Hello World !");
return 0;
}
安装——install
定制安装规则
在 Test/CMakeLists.txt 里添加如下内容:
# 指定 TEST 库的安装路径
install (TARGETS TEST DESTINATION bin)
install (FILES test.h DESTINATION include)
根目录的CMakeList.txt末尾添加如下内容:
# 指定安装路径
install (TARGETS Demo DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/config.h"
DESTINATION include)
- 生成的 Demo 文件和TEST库 libTEST.o 文件将会被复制到 /usr/local/bin 中
- test.h 和生成的 config.h 文件会被复制到 /usr/local/include 中
- 可以修改 CMAKE_INSTALL_PREFIX 变量的值来指定这些文件拷贝的目录
测试——add_test
CMake 提供了一个测试工具CTest,只需要在根目录的 CMakeLists 文件中调用一系列的 add_test 命令。
# 启用测试
enable_testing()
# 测试程序是否成功运行
add_test (test_run Demo)
# 测试帮助信息是否可以正常提示
add_test (test_usage Demo)
set_tests_properties (test_usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage: .* base exponent")
# 测试 2 的 10 次方
# DomeX为一个计算数字的程序 2和10为输入的参数
add_test (test_2_10 DemoX 2 10)
set_tests_properties (test_2_10
PROPERTIES PASS_REGULAR_EXPRESSION "1024")
- test_run 测试程序是否成功运行并返回 0
- PASS_REGULAR_EXPRESSION 用来测试结果是否包含后面的字符串
如果要测试更多的输入数据可以通过编写宏来实现(此处仍然调用数字计算程序DemoX):
# 定义一个宏,用来简化测试工作
macro (do_test arg1 arg2 result)
add_test (test_${arg1}_${arg2} DemoX ${arg1} ${arg2})
set_tests_properties (test_${arg1}_${arg2}
PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro (do_test)
# 使用该宏测试
do_test (5 2 "25") # 测试 5 的 2 次方 是否输出为25
do_test (10 5 "100000") # 测试 10 的 5 次方 是否输出为100000
do_test (2 10 "1024") # 测试 2 的 10 次方 是否输出为1024
设置支持gdb
只需要指定 Debug 模式下开启 -g 选项:
set(CMAKE_BUILD_TYPE "Debug") #设为debug模式
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb") # debug设置
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") # release设置
- CMAKE_C_FLAGS代表c文件,CFLAGS代表c的编译器参数
- $ENV{CXXFLAGS} 表示用于c++的编译器选项
环境检查
在最上层 CMakeLists 中添加宏 CheckFunctionExists.cmake ,并使用命令 check_function_exists 测试链接器是否能够在链接阶段找到对应的Mypow函数
config.h.in 文件修改如下:
#cmakedefine HAVE_POW
顶层CMakeLists(需要在configure_file 命令前编写):
# 检查系统是否支持 pow 函数
include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
check_function_exists (Mypow HAVE_POW)
main.c调用大致如下:
#ifdef HAVE_POW
// find the function
// do something
#else
// not find the function
// do something
#endif
版本号
顶层 CMakeLists(project 命令之后):
set (Demo_VERSION_MAJOR 1) # 主版本号
set (Demo_VERSION_MINOR 0) # 副版本号
config.h.in 文件添加如下命令:
#define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@
#define Demo_VERSION_MINOR @Demo_VERSION_MINOR@
代码中打印版本信息:
printf("Version %d.%d\n", Demo_VERSION_MAJOR, Demo_VERSION_MINOR);
安装包——CPack
首先在顶层的 CMakeLists.txt 文件尾部添加下面几行:
# 构建一个 CPack 安装包
include (InstallRequiredSystemLibraries) # 导入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模块
- 导入InstallRequiredSystemLibraries 模块,以便之后导入 CPack 模块
生成二进制安装包:
cpack -C CPackConfig.cmake
会生成3 个不同格式的二进制包文件(内容一样),任选一个执行就会出现一个由 CPack 自动生成的交互式安装界面。
生成源码安装包
cpack -C CPackSourceConfig.cmake