1. c++源码编译过程
主要包括四大部分:预处理、编译、汇编和链接。
2. CMake
参考书籍:《Modern-CMake-for-C++》
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