‎2020‎年‎3‎月‎26‎日,‏‎17:37:33

cmake相关知识提纲

  1. cmake
  2. cmake特性
  3. cmake
  4. 数据结构
  5. 预处理
  6. 编译
  7. 组织源文件
  8. aux_source_directory
  9. add_library MODULE
  10. add_subdirectory
  11. cmake组织的包
  12. find_package
  13. 包含源文件
  14. include_directories
  15. 生成
  16. add_executable
  17. 链接
  18. 链接库
  19. target_link_libraries
  20. 生成
  21. 安装

安装、卸载和升级

基本概念

  • cmake脚本中的过程有几个执行的时间点——1. Configure,比如运行cmake时发生;2. Build,若是Unix Makefile的情况,则是运行make时发生;3. Install,比如运行make install时发生;
  • cmake $<…>这样尖括号包裹的具体内容是在Build时才填充的;调试方法见:cmake-generator-expressions(7) — CMake 3.14.7 Documentation
  • 关于变量、变量的Scope:看cmake set命令的官方文档即可;子目录继承父目录的变量的值,但子目录中的修改不会传导到父目录,除非使用 PARENT_SCOPE;
  • 关于库的scope:cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE - 知乎

基本命令

  • cmake常见命令一言以蔽之 CMake命令之项目命令 - Jason886 - 博客园
  • cmake命令不区分大小写
  • cmake 命令会默认将当前目录作为build生成目录;(所以不要在代码的根目录执行 cmake . 污染源码文件夹);

启动cmake构建命令行示例:

  1. cmake -G "Visual Studio 16 2019" -A x64 \
  2. -D CMAKE_INSTALL_PREFIX=C:\WORKSPACE\OPENCL\samples\build \ # Define install dir
  3. -S <source-dir> # -- CMAKE_CURRENT_SOURCE_DIR
  4. -B <build-dir> # -- CMAKE_BINARY_DIR

用来删除cmake生成的东西的脚本

  1. find . -name "CMakeFiles" -type d -print -exec rm -rf {} \;
  2. find . -name "cmake_install.cmake" -type f -print -exec rm {} \;
  3. find . -name "Makefile" -type f -print -exec rm {} \;

全局和目标性质

在CMake脚本中定义宏

  1. add_definitions(-D宏)

编译【compile】

包含头文件【include】

  • 尽量要用target_include_directories而不是include_directories——具体情况具体分析,决定对特定目标包含还是对全局包含;
  • target_include_directories和include_directories好像是互斥的,即使用target_include_directories的目标无法感知到include_directories中的目录;【未证实】
  • 优化
    • 将相关联的一系列包含路径合并为变量,便于模块化引入和复用,提升可读性;

包含源文件【source】

交叉编译

  • cmake的交叉编译范例:CMake交叉编译_samssm的专栏-CSDN博客

    形成库【library】

    Object库

  • 优化

    • 可以使用 set 将Object库的 target object 浓缩成一个变量,使用PARENT_SCOPE或者CACHE穿透到外面或者其他目录参与编译;

      形成可执行文件【executable】

      链接【link】

      理解CXX的头文件/源文件/库,声明/定义/符号的模型

  • target_link_libraries 链接顺序:与gcc/g++链接顺序一致;被依赖的放在后面;cmake target_link_libraries 链接库顺序 - Swack

静态库

动态库

嵌套其他库【import】

find

了解cmake加入第三方库的机制
CMake之find_package - 简书
//可以通过set()设置<包名>_DIR这样的值,来设置find_package的默认查找目录

常识使用cmake构建sln项目;
已安装VS2019;
安装CMake;
写源代码;
在项目文件夹创建CmakeLists.txt;其中指定了项目名称,包含的文件;
在当前文件夹执行cmake .;进行内构建;
没有进行任何设置,其自动检测到了VS2019的环境并展开成一个sln项目;

  1. PS C:\WORKSPACE\CMAKE\cmake_tutorial> cmake -S . -B .\build\ # 可以用-S和-B指定Source和Build的路径;
  2. -- Building for: Visual Studio 16 2019
  3. -- The C compiler identification is MSVC 19.24.28314.0
  4. -- The CXX compiler identification is MSVC 19.24.28314.0
  5. -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe
  6. -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe -- works
  7. -- Detecting C compiler ABI info
  8. -- Detecting C compiler ABI info - done
  9. -- Detecting C compile features
  10. -- Detecting C compile features - done
  11. -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe
  12. -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe -- works
  13. -- Detecting CXX compiler ABI info
  14. -- Detecting CXX compiler ABI info - done
  15. -- Detecting CXX compile features
  16. -- Detecting CXX compile features - done
  17. -- Configuring done
  18. -- Generating done
  19. -- Build files have been written to: C:/WORKSPACE/CMAKE/cmake_tutorial/build
  20. PS C:\WORKSPACE\CMAKE\cmake_tutorial>

打开生成的sln项目,一般都具有多个“启动项目”,如ALL_BUILD,ZERO_CHECK等;
运行
若运行时出现“ 无法启动程序 xxx\ALL_BUILD 系统找不到指定文件”,则在解决方案资源管理器中,右击源代码对应的项目,设为启动项目;

Cmake以及在软件工程中的一系列构建项目流程

config

范例

  1. project(subProject1)
  2. add_subdirectory(lib11)
  3. add_subdirectory(lib12)
  4. add_subdirectory(libmain)
  5. set(LIB_ALL
  6. lib11_obj
  7. lib12_obj
  8. libmain_obj
  9. )
  10. add_executable(TEST_EXE dummy.cpp)
  11. target_link_libraries(TEST_EXE PRIVATE ${LIB_ALL})
  12. set_target_properties(TEST_EXE PROPERTIES RUNTIME_OUTPUT_NAME test_exe)
  13. # add_dependencies(TEST_EXE
  14. # product product_ups
  15. # )

变量【variable】

add_library(libmain_obj OBJECT ${SRC_LIST})

target_include_directories(libmain_obj PUBLIC ${PRODUCT_INCLUDE})

目录

message(“##### in ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt”) # 当前 CMakeLists.txt所在的目录 message(“CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}”) # 当前目录对应的源文件目录,如果处在subdirectories中,路径也会加上子目录的前缀 message(“CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}”) get_filename_component(CMAKE_CUR_SRC_DIR_ABS “${CMAKE_CURRENT_SOURCE_DIR}” ABSOLUTE) message(“CMAKE_CUR_SRC_DIR_ABS = ${CMAKE_CUR_SRC_DIR_ABS}”) message(“CMAKE_SOURCE_DIR = ${CMAKE_SOURCE_DIR}”) # 运行 cmake 命令对应的源文件目录,(即cmake命令 —source 对应的或比如 cmake .. 则 ‘..’ 对应的目录就是;即使处在嵌套的子项目中也不会被子项目覆盖 message(“PROJECT_SOURCE_DIR = ${PROJECT_SOURCE_DIR}”) # 当前 Project 的源文件目录;如果处在根项目包含的子项目中,则显示的是子项目的源文件目录;如果处在subdirectories中,路径也仍然是project的路径; message(“PROJECT_BINARY_DIR = ${PROJECT_BINARY_DIR}”) # 当前 Project 对应的生成目录,(即cmake命令 —build 对应或默认为运行 cmake .. 所在的目录);如果处在根项目包含的子项目中,则显示的是子项目对应的生成目录 message(“CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}”) # 安装目录前缀 message(“CMAKE_PROJECT_NAME = ${CMAKE_PROJECT_NAME}”) message(“CMAKE_PROJECT_INCLUDE = ${CMAKE_PROJECT_INCLUDE}”)

本CMakeList

message(“CMAKE_CURRENT_LIST_FILE = ${CMAKE_CURRENT_LIST_FILE}”) # 当前CMakeLists.txt文件名(带路径) message(“CMAKE_CURRENT_LIST_LINE = ${CMAKE_CURRENT_LIST_LINE}”) # 当前CMakeLists.txt中的当前行号

编译选项

message(“CMAKE_CXX_FLAGS = ${CMAKE_CXX_FLAGS}”) message(“CMAKE_C_FLAGS = ${CMAKE_C_FLAGS}”) message(“CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}”) message(“CMAKE_CXX_FLAGS_DEBUG = ${CMAKE_CXX_FLAGS_DEBUG}”)

将动态库/共享库链接到目标,目标文件本身并非直接添加动态库的内容,而仅添加按照CMake指定的路径依赖(相对or绝对?待查)

若按该路径未找到依赖的动态库,则启动失败;

CMake 打印信息;注意message参数不同打印的时间点不同,所以同一上下文的message不要用不同类型打印;

  1. - 变量的几个种类和作用域:[ CMake 两种变量原理 - 佛系师兄 - 博客园](https://www.cnblogs.com/ncuneugcj/p/9756324.html#2%E3%80%81cache-variables)
  2. - 常用变量一览:[CMake常见变量——ProjectCMake相关信息_山庄来客的专栏-CSDN博客](https://blog.csdn.net/fuyajun01/article/details/8891749)
  3. - 理解CMake变量作用机制:[CMake interpret string as variable - Stack Overflow](https://stackoverflow.com/questions/32631168/cmake-interpret-string-as-variable) 用变量的值索引变量`${${var}}`
  4. <a name="81afc658"></a>
  5. ## 宏和函数【function】/CMake脚本调试【DEBUG】
  6. 调试CMakeLists脚本:
  7. ```json
  8. # 简单版本
  9. macro(DUMP _var)
  10. message("[DUMP] in " ${CMAKE_CURRENT_LIST_FILE} ":" " ${_var} = " ${${_var}} )
  11. endmacro(DUMP)
  12. macro(DUMPLIST _var)
  13. message("[DUMPLIST] in " ${CMAKE_CURRENT_LIST_FILE} ":" " ${_var} = ")
  14. foreach(next_ITEM ${${_var}} )
  15. message("\t${next_ITEM}")
  16. endforeach(next_ITEM ${${_var}} )
  17. endmacro(DUMPLIST)
  18. # 例子
  19. DUMP(变量)
  20. DUMPLIST(列表变量)
  21. # 高级版本
  22. function(D)
  23. message("[dump] in " ${CMAKE_CURRENT_LIST_FILE} ":" ${ARGV1} " ${ARGV0} = " ${${ARGV0}} )
  24. endfunction(D)
  25. # 例子
  26. # 第二个参数放行号的值,很遗憾cmake暂时(2021)没有能够省去传入行号的值的其他办法
  27. D(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_LIST_LINE})
  28. # 绝对路径和相对路径
  29. macro(DUMP VAR_NAME_STR)
  30. get_filename_component(_TEMP_PATH "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
  31. message("[DUMP] in " ${_TEMP_PATH} ":" " ${VAR_NAME_STR} = " ${${VAR_NAME_STR}} )
  32. endmacro(DUMP)
  1. # 函数示例
  2. function(test_parse)
  3. set( options op1 op2 op3 op4) # 定义选项参数,值为TRUE或者FALSE
  4. set( oneValueArgs v1 v2 v3 ) # 单个值的变量
  5. set( multiValueArgs m1 m2 ) # 多个值的变量
  6. message( STATUS "options = ${options}" )
  7. message( STATUS "oneValueArgs = ${oneValueArgs}" )
  8. message( STATUS "multiValueArgs = ${multiValueArgs}" )
  9. # 按照设定好的顺序解析,并获得指定前缀的变量;
  10. cmake_parse_arguments( MYPRE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
  11. message("op1 = ${MYPRE_op1}")
  12. message("op2 = ${MYPRE_op2}")
  13. message("op3 = ${MYPRE_op3}")
  14. message("op4 = ${MYPRE_op4}")
  15. message("v1 = ${MYPRE_v1}")
  16. message("v2 = ${MYPRE_v2}")
  17. message("v3 = ${MYPRE_v3}")
  18. message("m1 = ${MYPRE_m1}")
  19. message("m2 = ${MYPRE_m2}")
  20. endfunction()
  21. # 例子
  22. test_parse(
  23. op1 ON
  24. OP2 ON
  25. op3
  26. op4 haha
  27. v1 v1_val
  28. v2 haha
  29. m1 C1 C2 C3 C4
  30. m2 emmmmmmmmmm)
  31. # 输出:
  32. [cmake] -- options = op1;op2;op3;op4
  33. [cmake] -- oneValueArgs = v1;v2;v3
  34. [cmake] -- multiValueArgs = m1;m2
  35. [cmake] op1 = TRUE
  36. [cmake] op2 = FALSE
  37. [cmake] op3 = TRUE
  38. [cmake] op4 = TRUE
  39. [cmake] v1 = v1_val
  40. [cmake] v2 = haha
  41. [cmake] v3 =
  42. [cmake] m1 = C1;C2;C3;C4
  43. [cmake] m2 = emmmmmmmmmm

ref:CMake中cmake_parse_arguments的使用简介 - 知乎

可变参数

  1. macro(argn_test hello world)
  2. MESSAGE(STATUS ARGV=${ARGV})
  3. MESSAGE(STATUS ARGN=${ARGN})
  4. MESSAGE(STATUS ARGV0=${ARGV0})
  5. MESSAGE(STATUS ARGV1=${ARGV1})
  6. MESSAGE(STATUS ARGV2=${ARGV2})
  7. MESSAGE(STATUS ARGV3=${ARGV3})
  8. endmacro()

调试宏:
可以让编译器来展开宏,生成预处理后的文件(preprocessed file);
如果使用CMake:用CMake Configure之后,make src.cpp.i即可;
如果直接使用GCC: gcc -C -E input -I header-path -o output
ref:Generating preprocessed sources in CMake projects - antek’s tech blog