CMake简单笔记

  1. cmake-examples参考示例
  • https://github.com/ttroy50/cmake-examples/tree/master/01-basic
  1. cmake-docs官方文档
  • https://cmake.org/cmake/help/latest/index.html
  1. 个人了解cmake写的一些笔记项目https://github.com/crack-dawn/cmake_cpp
  2. 强烈推荐 新手入坑xmake, vscode也有配套插件,方便简单,入门首选. cmake真的不论写着还是看着都脑壳疼.

文章目录

  • cmake变量
    • 变量作用域
  • cmake预设变量
    • 预定义变量表格
    • 设置CMAKE预定义变量 `set()`
      • 设置 项目构建
      • 设置编译选项 编译链 编译器
    • 设置 项目输出
  • 添加编译选项
    • `add_compile_options()`
    • `add_link_options()`
    • `add_definitions(-Ddefine1 -Ddefine2 ...)` 添加宏定义
  • 创建构建目标
    • `add_library()` 静态/动态/组件库
    • `add_executable()` 可执行文件
    • `add_subdirectory()` :纳入子目录下的CMake构建配置
  • 搜寻&添加 `源文件 .c .cpp .s`
    • `aux_source_directory()`
    • `file(GLOB/GLOB_RECURSE )`/
  • 添加 `头文件 .h .hpp`
    • `target_include_directories()` 目标局部作用
    • `include_directories()` 全局作用
  • 例:搜索&添加文件
  • 添加 `库文件 .a .so .dll .lib`
    • `target_link_directories` 链接库文件搜索目录
    • `target_link_libraries` 链接库文件
    • `link_directories` 库文件
    • 导入静态/动态库文件 示例
    • `target_link_options`
    • ==需要注意的是==
  • 设置目标属性
    • `set_target_properties()`
    • `set_property()`
    • `target_sources()`
    • `target_include_directories()`
  • `.cmake`的使用
    • `include()`
    • 用法1.封装函数
    • 用法2.封装变量
    • 用法3.封装宏
    • 用法4.封装脚本模块
    • 用法5.对接find_package( )
  • cmake构建主要流程


cmake变量

变量作用域

  • 子cmake对变量操作,需要加上PARENT_SCOPE 才会传递给其他cmakelist!
  • 函数function中,需要加上RETURN_VALUE 才会传递给函数外部cmakelist!

cmake预设变量

CMake 中有多个预定义变量用来指示项目的不同目录结构和构建相关的路径,这里是一些常见的预定义变量及其含义:

预定义变量表格

CMake 变量名描述
CMAKE_SOURCE_DIR当前 CMakeLists.txt 所在的源码目录的根路径
CMAKE_BINARY_DIR构建目录的根路径,用于存放构建生成的可执行文件、库和其他输出
CMAKE_CURRENT_SOURCE_DIR当前处理的 CMakeLists.txt 所在的源码目录路径
CMAKE_CURRENT_BINARY_DIR当前处理的 CMakeLists.txt 所在的构建目录路径
CMAKE_CURRENT_LIST_DIR当前处理的 CMakeLists.txt 文件的实际目录路径(源码或构建)
CMAKE_CURRENT_LIST_LINE当前正在处理的 CMakeLists.txt 文件中的行号
CMAKE_MODULE_PATH用于查找额外 CMake 模块(.cmake 文件)的搜索路径列表
CMAKE_INCLUDE_CURRENT_DIR若设为 ON,将在构建时自动将当前 CMakeLists.txt 目录添加到包含路径中
CMAKE_LIBRARY_OUTPUT_DIRECTORY库文件的输出目录
CMAKE_RUNTIME_OUTPUT_DIRECTORY可执行文件的输出目录
WIN32如果在windows 平台, 利用if(WIN32)判断
UNIX如果在UNIX平台,利用if(UNIX)判断
其他还有成千上万的cmake内置变量,有需要查文档

设置CMAKE预定义变量 set()

设置 项目构建

if(WIN32) 
  set(CMAKE_GNUtoMS ON) 
endif(WIN32) # 如果是window平台, 
set(CMAKE_BUILD_RPATH "${CMAKE_SOURCE_DIR}/build" ) 构建目录 ./build
set(CMAKE_BUILD_TYPE Debug)  

设置编译选项 编译链 编译器

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_C_COMPILER "your/gcc/path") 
set(CMAKE_CXX_COMPILER "your/g++/path")

设置 项目输出

# 设置动态/静态库文件的构建输出路径
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY dir1)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY dir2)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY dir3)

set_target_properties(${target_name}    PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/Debug/${target_name}_arch"
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/Debug/${target_name}_libs" 
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/Debug/${target_name}_runs"
)

添加编译选项

add_compile_options()

add_link_options()

add_definitions(-Ddefine1 -Ddefine2 ...) 添加宏定义


创建构建目标

add_library() 静态/动态/组件库

add_library(<name> [STATIC | SHARED | MODULE]
    [EXCLUDE_FROM_ALL] 
    [<source>...])

add_library(${PROJECT_NAME} STATIC KaTeX parse error: Expected 'EOF', got '#' at position 17: …SOURCE_FILES}) #̲ 添加静态库 add_libr…{PROJECT_NAME} ALIAS ${PROJECT_NAME}) # 添加别名

add_executable() 可执行文件

add_executable(<name> [WIN32] [MACOSX_BUNDLE]
    [EXCLUDE_FROM_ALL]
    [source1] [source2 ...])

add_subdirectory() :纳入子目录下的CMake构建配置

add_subdirectory(source_dir 
    [binary_dir] 
    [EXCLUDE_FROM_ALL] 
    [SYSTEM]
)
  • add_subdirectory( directory ) 用于将一个子目录及其内部的CMake构建配置纳入当前项目的构建过程。这个命令允许您组织大型项目为多个独立的子项目或模块,每个子项目都有自己的CMakeLists.txt文件,它们共同构成整个项目的构建规则。

  • source_dir:必填参数,指定包含子目录CMakeLists.txt文件的路径。可以是相对路径(相对于当前CMakeLists.txt文件所在目录)或绝对路径。CMake将递归地处理指定目录下的CMakeLists.txt文件,构建该子目录内的目标。

  • binary_dir:可选参数,指定子目录构建产物(如编译后的对象文件、库文件和可执行文件)存放的目录。如果未指定,CMake将默认在当前构建目录下创建一个与source_dir同名的子目录作为binary_dir。如果提供的是相对路径,它将相对于当前构建目录计算。

  • EXCLUDE_FROM_ALL:可选布尔标记,如果设置,该子目录中的目标将不会被默认构建。用户必须明确指定这些目标才能进行构建。这对于包含可选组件、测试套件或示例代码等的子目录非常有用,可以避免不必要的构建开销。


搜寻&添加 源文件 .c .cpp .s

aux_source_directory()

  • aux_source_directory:该命令可以搜索指定目录(第一个参数)下的所有源文件,将源文件的列表保存到指定的变量(第二个参数)。

aux_source_directory(your_directory FindSrcDir)

file(GLOB/GLOB_RECURSE )/

file(GLOB/GLOB_RECURSE )

  • file(GLOB/GLOB_RECURSE ):该命令可以搜索指定目录(第一个参数)下的所有源文件,将源文件的列表保存到指定的变量(第二个参数)。

file(GLOB FindSrcDir *.cpp)
file(GLOB_RECURSE FindSrcDir *.cpp) #递归查找

添加 头文件 .h .hpp

target_include_directories() 目标局部作用

  • 可选作用域
  • 这可以确保自定义路径只应用于特定的目标,并使得相关路径不会泄漏到其他目标中。
target_include_directories(target_name PRIVATE/PUBLIC/INTERFACE  Directory1 Directory2 ...)

include_directories() 全局作用

  • 添加头文件包含路径 全局
include_directories(path1 path2 ...)

例:搜索&添加文件

## 寻找所有源代码文件
file(GLOB_RECURSE SOURCE_FILES 
    "${CMAKE_CURRENT_SOURCE_DIR}/inc/*.c"
    "${CMAKE_CURRENT_SOURCE_DIR}/inc/*.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c"
    "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
)
# 寻找所有头文件路径
file(GLOB_RECURSE HEADER_FILES 
    "${CMAKE_CURRENT_SOURCE_DIR}/inc/*.h"
    "${CMAKE_CURRENT_SOURCE_DIR}/inc/*.hpp"
)
foreach(HEADER_FILE ${HEADER_FILES}) # Extract directory paths from the header files
    get_filename_component(DIRECTORY_PATH ${HEADER_FILE} DIRECTORY)
    list(APPEND HEADER_DIRECTORIES ${DIRECTORY_PATH})
endforeach()
list(REMOVE_DUPLICATES HEADER_DIRECTORIES)# Remove duplicate directory paths

message("头文件路径 : ${HEADER_DIRECTORIES}")
message("源文件路径 : ${SOURCE_FILES}")

添加 库文件 .a .so .dll .lib

target_link_directories 链接库文件搜索目录

  • 这可以确保自定义路径只应用于特定的目标,并使得相关路径不会泄漏到其他目标中。
# Add link directories to a target.

 target_link_directories(<target> [BEFORE]
   <INTERFACE|PUBLIC|PRIVATE> [items1...]
   [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

# 在链接一个目标时,CMake 会在链接时, 搜索指定路径下的库文件。
# 一个或多个链接目录路径,可以是绝对路径或相对于当前源目录的相对路径。
# 
# Specifies the paths in which the linker should search for libraries when
# linking a given target.  Each item can be an absolute or relative path,
# with the latter being interpreted as relative to the current source
# directory.  These items will be added to the link command.

target_link_libraries 链接库文件

  • 链接库文件
  • Specify libraries or flags to use when linking a given target and/or
 target_link_libraries(<target>
    <PRIVATE|PUBLIC|INTERFACE> <items>...
    [<PRIVATE|PUBLIC|INTERFACE> <items>...]...
    [LINK_INTERFACE_LIBRARIES <items>...]
    [LINK_PRIVATE <items>...]
    [LINK_PUBLIC <items>...]
    [LINK_INTERFACE_MULTIPLICITY <library> <count>...]
)
  • 负责将库文件、其他目标(如静态库或共享库)以及链接标志与给定的目标(通常是可执行文件或库)关联起来。
  • 可以清晰地定义项目中各个组成部分之间的依赖关系,确保编译器在构建目标时能正确链接所需的库文件。

link_directories 库文件

  • 添加头文件包含路径 全局
# Add directories in which the linker will look for libraries.

 link_directories([AFTER|BEFORE] directory1 [directory2 ...])

# Adds the paths in which the linker should search for libraries.
# Relative paths given to this command are interpreted as relative to
# the current source directory, see ``CMP0015``.

# The command will apply only to targets created after it is called.
# 添加的路径,作用于在这之后,创建的所有目标

find_library (
           <VAR>
           name | NAMES name1 [name2 ...] [NAMES_PER_DIR]
           [HINTS [path | ENV var]... ]
           [PATHS [path | ENV var]... ]
           [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
           [PATH_SUFFIXES suffix1 [suffix2 ...]]
           [VALIDATOR function]
           [DOC "cache documentation string"]
           [NO_CACHE]
           [REQUIRED]
)

导入静态/动态库文件 示例

https://zhuanlan.zhihu.com/p/373363335

target_link_options

Add options to the link step for an executable, shared library or module

  • 添加执选项到目标可执行文件、共享库或模块的链接阶段。
    library target.
 target_link_options(<target> [BEFORE]
   <INTERFACE|PUBLIC|PRIVATE> [items1...]
   [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

需要注意的是

  1. 尽量避免在 CMake 中使用 include_directorieslink_directories 命令来处理第三方库的头文件和链接库路径。
  2. 更好的做法是使用 find_package 命令或编写 Find 模块来查找和链接第三方库,这样可以更好地管理依赖关系和跨平台兼容性。
  3. 或是使用target_link_libraries,target_include_directories为指定目标添加头文件路径和链接库路径。可以理清项目构建结构,防止交错污染.

设置目标属性

set_target_properties()

set_target_properties(${target_name}    PROPERTIES
    property1 value1
    property2 value2
    .....
)
set_target_properties(my_target PROPERTIES COMPILE_DEFINITIONS MY_DEFINITION=ON)

set_property()

set_property(
    [GLOBAL | DIRECTORY | TARGET <target-name> |
     SOURCE <source-file> | TEST <test-name> |
     INSTALL <install-target> | CACHE | ENVIRONMENT]
    PROPERTY <property-name> [value1 [value2 ...]]
)
set_property(TARGET my_target PROPERTY COMPILE_DEFINITIONS MY_DEFINITION=ON)
  • PROPERTIES
  • OUTPUT_NAME 输出文件名字
  • PREFIX 输出文件前缀
  • ARCHIVE_OUTPUT_DIRECTORY 输出导入库路径 dll.a
  • LIBRARY_OUTPUT_DIRECTORY 输出库文件库路径 .a .lib
  • RUNTIME_OUTPUT_DIRECTORY 输出可执行文件路径 .exe .dll
  • 还有许多许多,不记录了,用到了查cmake文档…
  • set_target_properties 语法更紧凑
  • sett_property 作用范围更丰富

target_sources()

  • Add sources to a target.
target_sources(<target>
   <INTERFACE|PUBLIC|PRIVATE> [items1...]
   [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

target_include_directories()


.cmake的使用

include()

.cmake的使用需要include(path_to_your_.cmake_directory)

用法1.封装函数

例如

## 查找该目录下所有头文件所在的目录,并返回头文件列表
function(find_header_directories PARENT_DIRECTORY HEADER_DIRECTORIES_OUT)
    set(HEADER_DIRECTORIES "")
    
    # 使用GLOB_RECURSE查找父目录及其子目录下的.h和.hpp文件
    file(GLOB_RECURSE HEADER_FILES
        "${PARENT_DIRECTORY}/*.h"
        "${PARENT_DIRECTORY}/*.hpp"
    )

    foreach(HEADER_FILE ${HEADER_FILES})
        # 获取包含当前头文件的目录路径
        get_filename_component(DIRECTORY_PATH ${HEADER_FILE} DIRECTORY)

        # 添加目录到HEADER_DIRECTORIES列表
        list(APPEND HEADER_DIRECTORIES ${DIRECTORY_PATH})
    endforeach()

    # 移除列表中的重复目录
    list(REMOVE_DUPLICATES HEADER_DIRECTORIES)
    set(${HEADER_DIRECTORIES_OUT} ${HEADER_DIRECTORIES} PARENT_SCOPE)
    # 将结果赋OUT_HEAR_DIRS参数,供父级CMake脚本使用 
    # 必要的,不然这些变量只作用于函数内部,或者被其他重名变量覆盖
endfunction()

用法2.封装变量

用法3.封装宏

用法4.封装脚本模块

用法5.对接find_package( )


cmake构建主要流程

https://zhuanlan.zhihu.com/p/371257515

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值