CMake基础

本文详细介绍了C++源码的编译过程,包括预处理、编译、汇编和链接四个步骤,并重点讨论了CMake工具的使用,如配置、生成、构建和安装阶段的命令与选项。CMake的跨平台特性和预设变量简化了项目管理,同时讲解了CMakeLists.txt的编写、变量、宏、函数以及如何处理头文件和库的依赖。此外,还涵盖了CMake的调试、日志控制和图形化依赖关系展示。
摘要由CSDN通过智能技术生成

1. c++源码编译过程

主要包括四大部分:预处理、编译、汇编和链接。

参考文章01

2. CMake

参考书籍:《Modern-CMake-for-C++》

CGold教程

cmake --help来查看指令的作用

CMake 指令

配置、生成阶段:

1. cmake -S "源码路径" -B "构建路径"
cmake -S . -B ./build  
	等同于:
		mkdir build & cd ./build
		cmake ..
cmake -S ..
cmake -B ./build

2. cmake -G "MinGW Makefiles" -S . -B ./build
windows下选择 mingw 而不是默认的 visual studio作为生成器

3. cmake -S . -B ./build -C .
仅执行配置过程,生成配置文件cmakecache.txt

cmake - S -B ./build -D CMAKE_BUILD_TYPE=Release / Debug
用于添加配置变量, 指定生成的版本为Release 或者 Debug,仅对make、Ninaja有效,
对于Visual Studio,需要在构建阶段指定,在配置阶段指定无效

cmake -S . -B build -U CMAKE_BUILD_TYPE
用于删除配置变量

4. 用于调试和跟踪
cmake --system-information [file]  # 获取关于变量、命令、宏和其他设置的一般信息
cmake --log-level=<level>  # 指定日志级别
cmake --trace  # 打印每个命令的文件名和调用的确切行号及其参数

5. 预设变量
提供CMakePresets.json 文件,其指定了一些默认值
cmake --list-presets  # 列出所有可用的预设

构建阶段:

6. cmake --build <dir>
执行当前目录下的构建系统,生成构建目标。
等同于常用的make指令,对于这种--build的形式,多用于自动化脚本之中,或者IDE环境下。
为什么不直接 make,而是使用 cmake --build 形式的命令,主要是为了跨平台,使用这种形式后,不管你是使用的什么生成器,CMake 都能正确构建,否则如果使用的是 Ninja 或者其他生成器,那 make 就不生效了。

7. 并行构建
cmake --build <dir> --parallel [<number-of-jobs>]
cmake --build <dir> -j [<number-of-jobs>]
等同于make -j6, 另一种方法是使用CMAKE_BUILD_PARALLEL_LEVEL 环境变量来设置

8. 对于Ninja Multi-Config、Xcode 和Visual Studio
cmake --build <dir> --config <cfg>
指定构建版本为Debug, Release, MinSizeRel 或RelWithDebInfo, 默认为Debug

9. 打印调试信息
cmake --build <dir> --verbose / cmake --build <dir> -v

10. cmake [{-D <var>=<value>}...] -P <cmake-script-file> [-- <unparsed-options>...]
执行脚本

安装:

11. 安装
cmake --install <dir> [<options>]
cmake --install <dir> --config <cfg>
cmake --install <dir> --prefix <prefix>
可以在项目配置中指定的安装路径前面,加上已经选择的前缀,windows无效

可参看生成的./build/CMakeCache.txt来查看一些环境配置变量

脚本和模块:

脚本文件以.cmake结尾
cmake -P script.cmake

模块是用CMake 语言编写的,包含宏定义、变量和执行各种功能的命令
include(<MODULE>)

CMake语法

1. cmake_minimum_required(VERSION <版本号>)
设置要求的CMake的最低版本号

2. project(<项目名称> <可选项>)
命名项目,存储于PROJECT_NAME,并配置可选项,指定语言类型

3. message("要打印的内容")
通过提供MODE 参数,可以自定义输出的样式,并且在出现错误的情况下,可以停止代码执行
message(FATAL_ERROR "Stop processing")

4. add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
添加一个子目录并构建该子目录。该子目录下需要有一个CMakeLists.txt文件

5. 注释
# 单行注释
[=[
	前后相同任意数量的空格,多行注释
]=]

6. 指令参数:
	方括号参数、引号参数和引号参数
	
7. 变量: 普通变量、缓存变量和环境变量
设置变量:set()、unset()
${} 用于引用普通变量或缓存变量。
$ENV{} 用于引用环境变量。
$CACHE{} 用于引用缓存变量。	

8. 变量作用域:
函数作用域和目录作用域。

9. 列表
list()

10. 控制结构
if()-endif()、while()-endwhile()、foreach()-endforeach()

if(<condition>)
    <commands>
elseif(<condition>) # optional block, can be repeated
    <commands>
else() # optional block
    <commands>
endif()

if() 条件支持NOT、AND 和OR 逻辑操作符

在条件中使用普通变量名(例如VAR) 等于写入${VAR}。
比较:EQUAL,LESS,LESS_EQUAL,GREATER 和GREATER_EQUAL

11. 宏和函数
尽可能使用函数而不是宏
function(<name> [<argument>…])
    <commands>
endfunction()

12. include、include_guard
用于从文件或模块加载并运行cmake code.

13. file()
file() 指令会以一种与系统无关的方式读取、写入和传输文件,并使用文件系统、文件锁、路径和存档

CMake项目

14. add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
添加一个子目录并构建该子目录

15. include_directories():
用于添加 Headers 搜索路径( -I 标志)

16. target_include_directories()
指定目标包含的头文件路径

17.	add_library(<target> <target_files>)
	target_link_libraries(<target> ... <item>... ...)
使用add_library() 生成了全局可见的目标lib,并使用target_include_ directories() 将lib目录添加到其公共包括目录中。这允许main.cpp 包含xxx.h 文件而不用提供相对路径

18. add_executable()
可执行目标的名称和将成为其元素的文件列表

19. add_dependencies()
在顶级目标之间添加依赖关系。使顶层 target 依赖于其他顶层目标,以确保它们在 target 之前构建。顶级目标是由 add_executable(),add_library() 或 add_custom_target() 命令之一创建的,只能用于顶层目标,以设置它们的构建顺序。

20. cmake --graphviz=test.dot .
生成点graphviz 格式的依赖关系图。它支持内部和外部依赖关系。

21. target_compile_definitions(<source> <INTERFACE|PUBLIC|PRIVATE> [items1...])
将填充<source> 目标的COMPILE_DEFINITIONS 属性。编译定义只是传递给配置C++ 预处理器定义的编译器的-Dname= 定义标志.

项目结构

projec
	3rd
	cmake
	doc
	example
	src
	test
	CMakeLists.txt

环境变量

1. CMAKE_SYSTEM_NAME 目标系统、:CMAKE_HOST_SYSTEM_NAME 主机系统
操作系统名称
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
	message(STATUS "Linux")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    message(STATUS "Darwin")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
	message(STATUS "Window")
else()
	message(STATUS ${CMAKE_SYSTEM_NAME})
endif()

2. cmake_host_system_information(RESULT <VARIABLE> QUERY <KEY>…)
查看主机系统信息,需要提供一个目标变量和感兴趣的键的列表

3. CMAKE_SIZEOF_VOID_P
64位还是32位

编译Cpp

0. CMAKE_CXX_STANDARD、CMAKE_CXX_STANDARD_REQUIRED、CMAKE_CXX_EXTENSIONS
cpp标准,是否要求版本,是否允许扩展
set_property(TARGET <target> PROPERTY CXX_STANDARD <standard>)

1. target_compile_feature()
需要具有特定功能的编译器来编译此目标。指定在编译给定的<target>时所需的编译器功能。如果该功能(feature)未在CMAKE_C_COMPILE_FEATURES,CMAKE_CUDA_COMPILE_FEATURES或CMAKE_CXX_COMPILE_FEATURES变量中列出,则CMake将报告错误。如果该功能的使用需要额外的编译器标志(compiler flag),例如-std=gnu++11,则会自动添加该标志。

2. target_sources()
向已定义的目标添加源文件。

3. target_include_directories()
添加希望编译器检查的自定义路径,CMake 将它们添加到生成的构建系统中的编译器调用中,这将提供一个特定于编译器的标志(通常是-I)。

4.target_compile_definitions()
设置预处理定义

5.configure_file(<input> <output>)
利用CMake配置头文件。

6.target_precompile_headers()
头文件(.h) 在实际编译开始之前由预处理器包含在翻译单元中,所以每次.cpp 实现文件更改时都必须重新编译。启用头文件预编译,允许编译器将头文件与实现文件分开处理,从而加快编译速度。

7. 链接
动态库和模块的POSITION_INDEPENDENT_CODE 属性会自动设置为ON。然而,重要的是,若动态库链接到另一个目标,例如静态库或对象库,那么也需要在该目标上设置此属性。
set_target_properties(dependency_target PROPERTIES POSITION_INDEPENDENT_CODE ON)

8. 可视化依赖
cmake --graphviz=test.dot .
使用Graphviz查看 https://dreampuf.github.io/GraphvizOnline/

包配置文件

查找、使用第三方包是很常见的需求,第三方可能是从官方下载的,可能是自己生成的,可能已经包含XXXConfig.cmake文件用于查找,也可能不包含。使用find_package() 指令来包含包,描述包的CMake 文件命名为-config.cmake 和Config.cmake。使用包时,可以指定需要的包的版本。CMake 将检查相关的Version.cmake 文件。

// example
// FindORT.cmake
include(FindPackageHandleStandardArgs)
set(ORT_VERSION '1.18.2')
find_path(
    ORT_INCLUDE_DIR onnxruntime_cxx_api.h 
    PATHS 
        ${ORT_ROOT_DIR}/include/ 
    )
find_path(
    ORT_INCLUDE_DIR onnxruntime_cxx_api.h 
    PATHS 
        ${ORT_ROOT_DIR}/include/ ${ORT_ROOT_DIR}/include/core/session
    )
if(MSVC)
    find_library(ORT_LIBRARY_RELEASE libonnxruntime
        PATHS ${ORT_ROOT_DIR}
        PATH_SUFFIXES lib)
    set(ORT_LIBRARY ${ORT_LIBRARY_RELEASE})
elseif(APPLE)
    find_library(ORT_LIBRARY_RELEASE libonnxruntime.dylib
        PATHS ${ORT_ROOT_DIR}
        PATH_SUFFIXES lib)
    message(${ORT_LIBRARY_RELEASE})
    set(ORT_LIBRARY ${ORT_LIBRARY_RELEASE})
else()
    if(CMAKE_SYSTEM_PROCESSOR MATCHES "^x86")
        find_library(ORT_LIBRARY libonnxruntime.so
            PATHS ${ORT_ROOT_DIR}
            PATH_SUFFIXES lib)
        find_library(ORT_TRT_LIBRARY libonnxruntime_providers_tensorrt.so
            PATHS ${ORT_ROOT_DIR}
            PATH_SUFFIXES lib)
        # find_library(ORT_PARSER_LIBRARY libnvonnxparser.so
        #     PATHS ${ORT_ROOT_DIR}
        #     PATH_SUFFIXES lib)
    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
        find_library(ORT_LIBRARY libonnxruntime.so
            PATHS ${ORT_ROOT_DIR}
            PATH_SUFFIXES lib)
    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
        find_library(ORT_LIBRARY libonnxruntime.so
            PATHS ${ORT_ROOT_DIR}
            PATH_SUFFIXES lib64)
    endif()
endif()
find_package_handle_standard_args(ORT DEFAULT_MSG ORT_INCLUDE_DIR ORT_LIBRARY)

if(ORT_FOUND)
  set(ORT_INCLUDE_DIRS ${ORT_INCLUDE_DIR})
  set(ORT_LIBRARIES ${ORT_LIBRARY} ${ORT_PARSER_LIBRARY})
  message(STATUS "Found ORT    (include: ${ORT_INCLUDE_DIRS}, library: ${ORT_LIBRARIES})")
  mark_as_advanced(ORT_ROOT_DIR ORT_LIBRARIES ORT_INCLUDE_DIR)
endif()

子目录创建library

FILE(GLOB source_file CONFIGURE_DEPENDS ./*.cpp)
FILE(GLOB source_head_file CONFIGURE_DEPENDS ./*.h)
add_library([lib_name] ${source_file} ${source_head_file})
target_include_directories([lib_name] PUBLIC .)
target_link_libraries([lib_name] RIVATE XXX)

参考链接

http://e.betheme.net/article/show-1352972.aspx?action=onClick

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值