CMake基本入门手册

1、CMake概述

CMake 是一个项目构建工具,并且是跨平台的。关于项目构建我们所熟知的还有Makefile(通过 make 命令进行项目的构建),大多是IDE软件都集成了make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake等,如果自己动手写 makefile,会发现,makefile 通常依赖于当前的编译平台,而且编写 makefile 的工作量比较大,解决依赖关系时也容易出错。
而 CMake 恰好能解决上述问题, 其允许开发者指定整个工程的编译流程,在根据编译平台,自动生成本地化的Makefile和工程文件,最后用户只需make编译即可,所以可以把CMake看成一款自动生成 Makefile的工具,其编译流程如下图:
在这里插入图片描述

在这里插入图片描述

介绍完CMake的作用之后,再来总结一下它的优点:
跨平台
能够管理大型项目
简化编译构建过程和编译过程
可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能

2、CMake的使用

CMake支持大写、小写、混合大小写的命令。如果在编写CMakeLists.txt文件时使用的工具有对应的命令提示,那么大小写随缘即可,不要太过在意

2.1、注释

行注释:使用#
块注释 使用#[[ ]]

#cmake 所需要的最小Cmake版本
cmake_minimum_required(VERSION 3.31.0)
#[[ 这是一个 CMakeLists.txt 文件。
这是一个块注释
这是一个 CMakeLists.txt 文件]]

2.2、双引号的使用

  • 对命令参数的影响
    针对没有空格的命令 加上双引号和没加双引号两者的区别不大,但是针对存在空格的命令参数如 program file 是不同
message("program file")    # 输出的program file
message(program file)    # 输出的programfile   中间的空格会被忽略掉
  • 引用变量的时候
set(MY_LIST Hello World China)
message(${
   
   MY_LIST})
输出:
HelloWorldChina
set(MY_LIST Hello World China)
message("${MY_LIST}")
输出:Hello;World;China

"${MY_LIST}"这种形式的时候,要让 CMake 把这个数组的所有元素当成一个整体,而不是分散的个体。
为了保持数组的含义,又提供一个整体的表达方式,CMake 就会用分号“;” 把这数组的多个元素连接起来

2.3、Cmake常用的命令

2.3.1 定义变量

  • 设置简单变量
    语法:
set(VARIABLE_NAME value)   #这会将 VARIABLE_NAME 设置为指定的 value。如果变量已经存在,则会覆盖其旧值。

eg:
set(MY_VARIABLE "Hello, World!")
  • 使用简单变量
${
   
    VARIABLE_NAME}
  • 设置多个值 (列表)
#可以将多个值赋给一个变量,形成一个列表:
set(VARIABLE_NAME value1 value2 value3)

eg:
 set(SOURCES main.cpp utils.cpp helper.cpp) 

注意空格分隔的字符,如果使用带有空格的的字符串,这需要使用引号括起来

set(VARIABLE_NAME "This is a string with spaces")
  • 使用 set 追加值
set(VARIABLE_NAME ${
   
   VARIABLE_NAME} new_value)
  • 使用 list 命令来追加值,这样可以避免潜在的空格问题
set(SOURCES main.cpp)
list(APPEND SOURCES utils.cpp)
  • 设置缓存变量
    你可以将变量存储在 CMake 缓存中,这样它们可以在多次构建之间保持不变。缓存变量可以通过命令行进行修改。
set(VARIABLE_NAME value CACHE TYPE "Description")

set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build, options are: None Debug Release.")

TYPE 可以是以下几种之一:

  • STRING:普通字符串。
  • PATH:路径类型,通常用于文件或目录路径。
  • FILEPATH:文件路径类型。
  • BOOL:布尔类型,值为 ON 或 OFF。
  • INTERNAL:内部变量,不会显示在 CMake GUI 中。
    设置环境变量
    CMake 本身不直接操作环境变量,但你可以通过 set(ENV{VARIABLE_NAME} value) 来设置环境变量。 这种方式只会在 CMake 配置过程中生效,不会影响系统的环境变量。
    设置父作用域中的变量
    默认情况下,set 命令设置的变量只在当前作用域内可见。如果你想将变量设置为父作用域可见,可以使用 PARENT_SCOPE 选项。
function(my_function)
    set(MY_VARIABLE "Hello from function" PARENT_SCOPE)
endfunction()

my_function()
message(STATUS "MY_VARIABLE: ${MY_VARIABLE}")  # 输出: MY_VARIABLE: Hello from function

检查变量是否存在
使用 if 语句来检查变量是否存在:

if(DEFINED VARIABLE_NAME)
    message(STATUS "VARIABLE_NAME is defined")
else()
    message(STATUS "VARIABLE_NAME is not defined")
endif()
  • 设置变量为空
    变量设置为空字符串或未定义状态:
    设置变量为空
set(VARIABLE_NAME "")
  • 取消变量设置
unset(VARIABLE_NAME)
  • 设置变量的默认值
    可以使用 if(NOT DEFINED VARIABLE_NAME) 来检查变量是否已定义,并为其设置默认值:
if(NOT DEFINED MY_VARIABLE)
    set(MY_VARIABLE "default_value")
endif()
  • 使用 set 进行条件赋值
if(CONDITION)
    set(VARIABLE_NAME value1)
else()
    set(VARIABLE_NAME value2)
endif()

if(UNIX)
    set(OS_SPECIFIC_FLAGS "-Wall -Wextra")
elseif(WIN32)
    set(OS_SPECIFIC_FLAGS "/W4")
endif()
  • 使用 set 进行数学运算
    CMake 提供了 math(EXPR …) 和 math(EXPR …) 命令来进行数学运算,但你也可以使用 set 结合表达式来进行简单的数学运算
set(VARIABLE_NAME ${
   
   VARIABLE_NAME} + 1)

set(COUNT 0)
set(COUNT ${
   
   COUNT} + 1)
message(STATUS "COUNT: ${COUNT}")  # 输出: COUNT: 1
  • 使用 set 进行字符串拼接
    可以使用 ${} 语法进行字符串拼接:
set(VARIABLE_NAME "${PREFIX}_${SUFFIX}")

set(PREFIX "prefix")
set(SUFFIX "suffix")
set(FULL_NAME "${PREFIX}_${SUFFIX}")
message(STATUS "FULL_NAME: ${FULL_NAME}")  # 输出: FULL_NAME: prefix_suffix
  • 使用 set 进行路径操作
    可以使用 set 结合 file 命令进行路径操作,例如获取当前目录、拼接路径等
set(CURRENT_DIR ${
   
   CMAKE_CURRENT_SOURCE_DIR})
set(OUTPUT_DIR "${CURRENT_DIR}/build")

使用 set 进行多平台配置
可以根据不同的平台设置不同的变量值:

if(UNIX)
    set(OS_SPECIFIC_FLAGS "-Wall -Wextra")
elseif(WIN32)
    set(OS_SPECIFIC_FLAGS "/W4")
endif()
  • 使用 set设置 C++标准
#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
#增加-std=c++17
set(CMAKE_CXX_STANDARD 17)

2.3.2 搜索文件

find_file 命令用于在指定路径中搜索文件,并将找到的文件路径存储在一个变量中。
基本用法

find_file(VARIABLE_NAME file_name [PATHS path1 path2 ...] [PATH_SUFFIXES suffix1 suffix2 ...])
- VARIABLE_NAME:存储找到的文件路径的变量。
- file_name:要搜索的文件名。
- PATHS:可选参数,指定搜索的路径列表。
- PATH_SUFFIXES:可选参数,指定搜索路径的子目录列表。

示例
假设你要搜索一个名为 config.h 的文件,可以在项目的根目录及其子目录中进行搜索:

find_file(CONFIG_FILE_PATH config.h
        PATHS ${
   
   CMAKE_SOURCE_DIR}
        PATH_SUFFIXES include)

如果找到了 config.h 文件,CONFIG_FILE_PATH 变量将包含该文件的完整路径;否则,它将为空
也可以通过CMAKE_INCLUDE_PATH 宏定义指定搜索的路径

  • 设置 CMAKE_INCLUDE_PATH 变量
set(CMAKE_INCLUDE_PATH ${
   
   PROJECT_SOURCE_DIR}/src)
find_file(CONFIG_FILE_PATH config.h)

使用 file(GLOB …) 搜索多个文件
file(GLOB …) 命令用于根据通配符模式搜索多个文件,并将结果存储在一个列表中。

file(GLOB/GLOB_RECURSE VARIABLE_NAME [RELATIVE path] pattern1 pattern2 ...)

GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中
VARIABLE_NAME:存储找到的文件路径列表的变量。
RELATIVE:可选参数,指定相对于某个路径的相对路径。
pattern1 pattern2 …:通配符模式,用于匹配文件名。

eg
file(GLOB SOURCES *.cpp)
file(GLOB HEADERS *.h)

获取相对于项目根目录的相对路径,可以使用
RELATIVE 选项
file(GLOB_RECURSE SOURCES RELATIVE ${CMAKE_SOURCE_DIR} *.cpp)

  • 使用 find_path 搜索路径
    find_path 命令用于搜索包含特定文件的目录,并将找到的目录路径存储在一个变量中。
    find_path(VARIABLE_NAME file_name [PATHS path1 path2 …] [PATH_SUFFIXES suffix1 suffix2 …])

VARIABLE_NAME:存储找到的目录路径的变量。
file_name:要搜索的文件名。
PATHS:可选参数,指定搜索的路径列表。
PATH_SUFFIXES:可选参数,指定搜索路径的子目录列表。

#[[如果找到了 config.h 文件,
CONFIG_INCLUDE_DIR 变量将包含该文件所在目录的路径;否则,它将为空]]

find_path(CONFIG_INCLUDE_DIR config.h
          PATHS /usr/include /opt/include
          PATH_SUFFIXES myproject)
  • 使用 find_library 搜索库文件
    find_library 命令用于搜索库文件(如 .a、.lib 或 .so 文件),并将找到的库文件路径存储在一个变量中
find_library(VARIABLE_NAME library_name [PATHS path1 path2 ...] [PATH_SUFFIXES suffix1 suffix2 ...])

VARIABLE_NAME:存储找到的库文件路径的变量。
library_name:要搜索的库文件名(不带扩展名)。
PATHS:可选参数,指定搜索的路径列表。
PATH_SUFFIXES:可选参数,指定搜索路径的子目录列表。

eg:
    find_library(MYLIB_LIBRARY mylib
             PATHS /usr/lib /opt/lib
             PATH_SUFFIXES myproject)
  • 使用 find_package 搜索外部包
    find_package 命令用于查找并导入外部包,并设置相关的变量。通常,find_package 会调用相应的
    Find.cmake 脚本来执行搜索操作。
find_package(PackageName [version] [REQUIRED] [COMPONENTS component1 component2 ...])

PackageName:要查找的包名称。
version:可选参数,指定所需的最低版本。
REQUIRED:可选参数,表示如果找不到包则终止配置过程。
COMPONENTS:可选参数,指定需要的组件。

查找 Qt5 并导入其 Widgets 组件
find_package(Qt5 COMPONENTS Widgets REQUIRED)
aux_source_directory 命令可以查找某个路径下的所有源文件
aux_source_directory(< dir > < variable >)

dir:要搜索的目录
variable:将从dir目录下搜索到的源文件列表存储到该变量中

  • 搜索 src 目录下的源文件
aux_source_directory(${
   
   CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app  ${
   
   SRC_LIST})

2.3.3 包含头文件

使用 include_directories 添加头文件路径
include_directories 是 CMake 中最常用的命令之一,用于添加头文件的搜索路径。它会将指定的路径添加到编译器的
-I 选项中,使得编译器能够在这些路径中查找头文件。

include_directories([AFTER|BEFORE] [SYSTEM] path1 path2 ...)
AFTER 或 BEFORE
选项来指定是添加到列表的前面或者后面。
 SYSTEM 选项,会把指定目录当成系统的搜索目录。
path1 path2 ...:要添加的头文件路径列表。

eg:
cmake_minimum_required(VERSION 3.10)
project(MyProject)

# 添加头文件路径
include_directories(${
   
   CMAKE_SOURCE_DIR}/include)

# 查找源文件
aux_source_directory(src SOURCES)

# 添加可执行文件
add_executable(MyExecutable ${
   
   SOURCES})

注意事项

  • include_directories 的作用范围是全局的,即它会影响所有后续定义的目标(如 add_executable 或 add_library)。如果你只想为特定目标添加头文件路径,可以考虑使用 target_include_directories。
  • 使用 target_include_directories 为特定目标添加头文件路径
    target_include_directories 是一个更现代和推荐的方式,用于为特定目标(如可执行文件或库)添加头文件路径。它提供了更好的控制和灵活性,尤其是当你有多个目标时
target_include_directories(target_name PRIVATE|PUBLIC|INTERFACE path1 path2 ...)

target_name:目标的名称(如 add_executable 或 add_library 定义的目标)。
PRIVATE|PUBLIC|INTERFACE:指定头文件路径的可见性:
PRIVATE:仅对当前目标可见。
PUBLIC:对当前目标及其依赖的目标可见。
INTERFACE:仅对依赖当前目标的目标可见


eg:
cmake_minimum_required(VERSION 3.10)
project(MyProject)

# 查找源文件
aux_source_directory(src SOURCES)

# 创建可执行文件
add_executable(MyExecutable ${
   
   SOURCES})

# 为 MyExecutable 添加头文件路径
target_include_directories(MyExecutable PRIVATE ${
   
   CMAKE_SOURCE_DIR}/include)</
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值