文章目录
CMake是什么
CMake是一个管理源代码构建的工具。现在CMake用来生成现代的构建系统,包括Visual Studio和XCode等等。通过CMake编译可以生成Visual Studio和XCode项目文件。CMake广泛用于C/C++语言项目,但它也适用于其它语言项目。
CMake基本用法
# 指定CMake最低版本
cmake_minimum_required(VERSION 3.10)
# 设置项目的名称
project(Tutorial)
# 添加执行文件
add_executable(Tutorial tutorial.cxx)
# 设置项目名称和版本号
project(Tutorial VERSION 1.0)
# 配置一个代码文件用来记录版本号
configure_file(TutorialConfig.h.in TutorialConfig.h)
基本语法
设置编译类型
add_executable(test demo.cpp) # 生成可执行文件
add_library(test STATIC test.cpp) # 生成静态库
add_library(test SHARED test.cpp) # 生成动态库或共享库
定义变量
set(SRC_LIST main.cpp test.cpp) #定义变量
add_executable(demo ${SRC_LIST}) #使用变量
#追加变量对应的值
set(SRC_LIST main.cpp)
set(SRC_LIST ${SRC_LIST} test.cpp)
add_executable(demo ${SRC_LIST})
#向list追加和删除变量的值
set(SRC_LIST main.cpp)
list(APPEND SRC_LIST test.cpp)
list(REMOVE_ITEM SRC_LIST main.cpp)
add_executable(demo ${SRC_LIST})
设置包含的目录
# CMAKE_CURRENT_SOURCE_DIR 指的是当前 CMakeLists.txt 文件所在的源代码目录。
# CMAKE_CURRENT_BINARY_DIR 指的是当前 CMakeLists.txt 文件对应的构建目录
# 也就是CMake执行命令的目录
# 影响全局的文件包含
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
)
#Linux 下还可以通过如下方式设置包含的目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}")
# 给特定对象添加包含目录 可以指定影响范围 PUCBLIC 影响全局 PRIVATE 影响局部
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
# 包含其它CMake文件
include(./common.cmake) # 指定包含文件的全路径
include(def) # 在搜索路径中搜索def.cmake文件
# 设置include的搜索路径
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
指定包含的源文件
#明确指定包含哪些源文件
add_library(demo demo.cpp test.cpp util.cpp)
#发现一个目录下所有的源代码文件并将列表存储在一个变量中
aux_source_directory(dir VAR)
aux_source_directory(. SRC_LIST) # 搜索当前目录下的所有.cpp文件
add_library(demo ${SRC_LIST})
#自定义搜索规则
#@1 指定工作模式(GLOB不递归 GLOB_RECURSE递归) @2文件路径存储变量
#@3 CONFIGURE_DEPENDS 文件列表中的文件发生变化,CMake会重新配置项目
#@4 指定匹配的文件
file(GLOB SRC_LIST CONFIGURE_DEPENDS "src/*.cpp")
add_library(demo ${SRC_LIST})
# 或者
file(GLOB SRC_PROTOCOL_LIST "protocol/*")
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# 递归查找
file(GLOB_RECURSE SRC_LIST "*.cpp")
# 相对protocol目录下搜索
FILE(GLOB SRC_PROTOCOL RELATIVE "protocol" "*.cpp")
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# @1目录名称 @2变量
aux_source_directory(. SRC_LIST)
aux_source_directory(protocol SRC_PROTOCOL_LIST)
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
指定C++标准
# specify the C++ standard
# CMAKE_CXX_SDTANDARD 声明一定要放在add_executable前面
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
链接外部文件
# 设置链接库搜索目录
link_directories(
${CMAKE_CURRENT_SOURCE_DIR}/libs
)
# 链接特定的库
# @1目标对象 @2被链接的库
target_link_libraries(demo libface.a) # 链接libface.a
target_link_libraries(demo libface.so) # 链接libface.so
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a)
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.so)
# 查找QT5的Core模块 REQUIRED说明模块是必须得 没有就报错
find_package(Qt5 COMPONENTS Core REQUIRED)
# 查找并配置 pkg-config 工具, 用于管理和查找库的配置信息
find_package(PkgConfig REQUIRED)
# pkg-config 检查和配置 libisoburn-1 库,并将其标记为已导入目标
# @1 定义的 CMake 变量,用于存储 pkg-config 检查的结果
# @2 REQUIRED 说明这个库是必须得 @3库的名称
# IMPORTED_TARGET 表示是一个外部库,将其配置为 CMake 中的导入目标,便于在后续的CMake脚本中使用
pkg_check_modules(isoburn REQUIRED libisoburn-1 IMPORTED_TARGET)
# 导入对应的库
target_link_libraries(${BIN_NAME}
Qt5::Core
PkgConfig::isoburn
)
打印信息
message(${PROJECT_SOURCE_DIR})
message("build with debug mode")
message(WARNING "this is warnning message")
# FATAL_ERROR 会导致编译失败
message(FATAL_ERROR "this build has many error")
安装和测试
# 将生成的文件放到对应的目录中
# @1安装的文件类型(TARGETS 生成的库或可执行文件 FILES安装单个或多个文件 DIRECTORY安装整个目录 CODE 执行CMake代码)
# @2要安装的文件地址
# @3安装的目录 include 为系统include目录
# @4FILES_MATCHING PATTERN "*.h" 仅安装以.h结尾的文件
install(DIRECTORY
${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}
DESTINATION include
FILES_MATCHING PATTERN "*.h"
)
# LIBRARY 指定安装的类型是库
# DESTINATION${CMAKE_INSTALL_LIBDIR} 指定将库文件安装到CMAKE_INSTALL_LIBDIR变量指定的目录(通常是 lib)
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
# 用于生成pkg-config文件${PROJECT_NAME}.pc.in 是模板文件,${PROJECT_NAME}.pc是生成的文件
# pkg-config文件的作用: 1.提供编译和链接信息 2.简化构建过程 3.维护库的兼容性和配置
configure_file(${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY)
# install(FILES ...)将生成的.pc文件安装到pkgconfig目录,供pkg-config使用
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
# 生成和安装 CMake 配置文件,使其他 CMake 项目能够找到并使用这个库
configure_file(${PROJECT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in ${PROJECT_NAME}Config.cmake @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
# 可以指定安装的根路径
cmake --install .
cmake --install . --prefix "/home/myuser/installdir"
添加对应的cmake测试
enable_testing()
# 判断程序能否正常执行
add_test(NAME Runs COMMAND Tutorial 25)
# 判断程序能否正常使用
add_test(NAME Usage COMMAND Tutorial)
# 添加对应的测试属性,验证输出的字符串中是否包含对应的字符串
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# 定义函数来进行传参测试
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction(do_test)
# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is [-nan|nan|0]")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
CMake系统变量
### 预定义变量
# 工程的根目录
PROJECT_SOURCE_DIR
# 运行cmake命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build
PROJECT_BINARY_DIR
# 返回通过 project 命令定义的项目名称
PROJECT_NAME
# CMakeLists.txt 所在的路径
CMAKE_CURRENT_SOURCE_DIR
# 编译目录
CMAKE_CURRENT_BINARY_DIR
# CMakeLists.txt 的完整路径
CMAKE_CURRENT_LIST_DIR
# 当前所在的行
CMAKE_CURRENT_LIST_LINE
# 定义自己的 cmake 模块所在的路径,然后可以用INCLUDE命令来调用自己的模块
CMAKE_MODULE_PATH
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# 重新定义目标二进制可执行文件的存放位置
EXECUTABLE_OUTPUT_PATH
# 重新定义目标链接库文件的存放位置
LIBRARY_OUTPUT_PATH
# 使用环境变量
$ENV{Name}
#写入环境变量
set(ENV{Name} value) # 这里没有“$”符号
# 系统信息
# cmake 主版本号,比如 3.4.1 中的 3
CMAKE_MAJOR_VERSION
# cmake 次版本号,比如 3.4.1 中的 4
CMAKE_MINOR_VERSION
# cmake 补丁等级,比如 3.4.1 中的 1
CMAKE_PATCH_VERSION
# 系统名称,比如 Linux-2.6.22
CMAKE_SYSTEM
# 不包含版本的系统名,比如 Linux
CMAKE_SYSTEM_NAME
# 系统版本,比如 2.6.22
CMAKE_SYSTEM_VERSION
# 处理器名称,比如 i686
CMAKE_SYSTEM_PROCESSOR
# 在所有的类 UNIX 平台下该值为 TRUE,包括 OS X 和 cygwin
UNIX
# 在所有的 win32 平台下该值为 TRUE,包括 cygwin
WIN32
CMake配置开关
# 使用 add_library 又没有指定库类型的情况下 默认编译生成的库都是静态库
set(BUILD_SHARED_LIBS ON) # 默认生成的为动态库
# 设置 C 编译选项,也可以通过指令 add_definitions()添加
CMAKE_C_FLAGS
# 设置 C++ 编译选项,也可以通过指令 add_definitions()添加
CMAKE_CXX_FLAGS
配置项目版本信息
在源码目录中创建TutorialConfig.h.in文件
文件内容如下:
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
CMake配置的时候会将 @Tutorial_VERSION_MAJOR@ 和 @Tutorial_VERSION_MINOR@ 替换掉
在代码中获取项目的版本信息
if (argc < 2)
{
// report version
std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
<< Tutorial_VERSION_MINOR << std::endl;
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
组织多个项目文件
将库放到子目录MathFunctions中,这个目录中存在一个头文件MathFunctions.h和源文件mysqrt.cxx。
在这个目录中添加CMakeLists.txt文件文件中只包含一行
add_library(MathFunctions mysqrt.cxx)
在顶层的CMakeLists.txt中添加子目录add_subdirectory() ,确保库文件被编译。
为了将新库引入到可执行文件中,我们需要包含对应的目录,确保对应的文件能被找到。
# 添加对应的子目录
add_subdirectory(MathFunctions)
# 添加执行程序
add_executable(Tutorial tutorial.cxx)
# 链接对应的库文件
target_link_libraries(Tutorial PUBLIC MathFunctions)
# 添加链接搜索的文件路径
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
将MathFunctions设置为可选的,可开启可关闭
在顶层的CMakeList.txt文件中添加对应的变量开关
# 添加标志位默认为开
option(USE_MYMATH "Use tutorial provided math implementation" ON)
# 将一部分配置信息写入到配置文件中
configure_file(TutorialConfig.h.in TutorialConfig.h)
在下面的代码中使用这个开关来选择添加对应的lib库
# 开关开启的时候添加对应的库和包含目录
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()
# 添加执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
# 添加搜索路径和目录
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)
在对应的调用文件中使用对应的库,通过宏来判断来添加对应的文件
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif
#ifdef USE_MYMATH
const double outputValue = mysqrt(inputValue);
#else
const double outputValue = sqrt(inputValue);
#endif
由于我们的代码现在需要USE_MYMATH,所以我们需要在TutorialConfig.h.in中添加对应的CMake宏
#cmakedefine USE_MYMATH
通过命令行来修改变量开关
cmake ../Step2 -DUSE_MYMATH=OFF
source_group逻辑分组
source_group是一个在CMake中使用的命令,用于组织源代码文件。它通常与add_executable或add_library命令一起使用,以将源文件组织成逻辑分组,并在生成的项目文件中显示这些分组。
source_group 命令的基本语法如下:
# <group_name> 分组名称
# 如果你希望创建子分组, 可以在分组名称中使用反斜杠 \(在Windows上)或正斜杠/(跨平台)来表示路径层级
source_group(<group_name> FILES <source1> <source2> ...)
下面是这个命令的用法示例:
# 定义源代码文件
set(SOURCES
src/main.cpp
src/utils/util1.cpp
src/utils/util2.cpp
include/utils/util1.h
include/utils/util2.h
)
# 创建可执行文件并将源代码文件添加到项目中
add_executable(my_executable ${SOURCES})
# 使用source_group命令将源代码文件组织成逻辑分组
# @1分组名称 @2FILES @3文件的路径
source_group("Source Files" FILES src/main.cpp)
source_group("Source Files\\Utils" FILES src/utils/util1.cpp src/utils/util2.cpp)
source_group("Header Files" FILES include/utils/util1.h include/utils/util2.h)