CMake基本语法大全

文章目录

CMake是什么

CMake是一个管理源代码构建的工具。现在CMake用来生成现代的构建系统,包括Visual Studio和XCode等等。通过CMake编译可以生成Visual Studio和XCode项目文件。CMake广泛用于C/C++语言项目,但它也适用于其它语言项目。

CMake基本用法

bash 复制代码
# 指定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)

基本语法

设置编译类型

bash 复制代码
add_executable(test demo.cpp)     # 生成可执行文件
add_library(test STATIC test.cpp) # 生成静态库
add_library(test SHARED test.cpp) # 生成动态库或共享库

定义变量

bash 复制代码
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})

设置包含的目录

bash 复制代码
# 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) 

指定包含的源文件

bash 复制代码
#明确指定包含哪些源文件
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++标准

bash 复制代码
# specify the C++ standard
# CMAKE_CXX_SDTANDARD 声明一定要放在add_executable前面
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

链接外部文件

bash 复制代码
# 设置链接库搜索目录  
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
)

打印信息

bash 复制代码
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") 

安装和测试

bash 复制代码
# 将生成的文件放到对应的目录中
# @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测试

bash 复制代码
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系统变量

bash 复制代码
### 预定义变量  

# 工程的根目录  
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配置开关

bash 复制代码
# 使用 add_library 又没有指定库类型的情况下 默认编译生成的库都是静态库
set(BUILD_SHARED_LIBS ON)  # 默认生成的为动态库

# 设置 C 编译选项,也可以通过指令 add_definitions()添加
CMAKE_C_FLAGS
# 设置 C++ 编译选项,也可以通过指令 add_definitions()添加
CMAKE_CXX_FLAGS

配置项目版本信息

在源码目录中创建TutorialConfig.h.in文件

文件内容如下:

cpp 复制代码
// 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@ 替换掉

在代码中获取项目的版本信息

cpp 复制代码
 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文件文件中只包含一行

bash 复制代码
add_library(MathFunctions mysqrt.cxx)

在顶层的CMakeLists.txt中添加子目录add_subdirectory() ,确保库文件被编译。

为了将新库引入到可执行文件中,我们需要包含对应的目录,确保对应的文件能被找到。

bash 复制代码
# 添加对应的子目录
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文件中添加对应的变量开关

bash 复制代码
# 添加标志位默认为开
option(USE_MYMATH "Use tutorial provided math implementation" ON)
# 将一部分配置信息写入到配置文件中
configure_file(TutorialConfig.h.in TutorialConfig.h)

在下面的代码中使用这个开关来选择添加对应的lib库

bash 复制代码
# 开关开启的时候添加对应的库和包含目录
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}
                           )

在对应的调用文件中使用对应的库,通过宏来判断来添加对应的文件

bash 复制代码
#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宏

bash 复制代码
#cmakedefine USE_MYMATH

通过命令行来修改变量开关

bash 复制代码
cmake ../Step2 -DUSE_MYMATH=OFF  

source_group逻辑分组

source_group是一个在CMake中使用的命令,用于组织源代码文件。它通常与add_executable或add_library命令一起使用,以将源文件组织成逻辑分组,并在生成的项目文件中显示这些分组。

source_group 命令的基本语法如下:

shell 复制代码
# <group_name> 分组名称
# 如果你希望创建子分组, 可以在分组名称中使用反斜杠 \(在Windows上)或正斜杠/(跨平台)来表示路径层级  
source_group(<group_name> FILES <source1> <source2> ...)

下面是这个命令的用法示例:

shell 复制代码
# 定义源代码文件
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)
相关推荐
怀澈1221 小时前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming19871 小时前
STL关联式容器之set
开发语言·c++
威桑1 小时前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc
熬夜学编程的小王2 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins2 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
Mr.132 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
阿史大杯茶2 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
C++忠实粉丝2 小时前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp
我们的五年2 小时前
【Linux课程学习】:进程描述---PCB(Process Control Block)
linux·运维·c++
程序猿阿伟2 小时前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链