c 和 cpp 编译工具

参考:

工具:

  1. gcc 和 g++

    • compiler, 编译器
    • eg: gcc -o hello.o hello.c
  2. ld

    • linker, 链接器
    • eg: ld -o main hello.o nihao.o -lmylib
  3. ldd

    • obj 文件依赖打印工具,print shared object dependencies
    • 用来分析一个 可执行文件、obj 文件、so 文件或 .a 文件的依赖库(.so 文件)存放位置
    • eg:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      $ ldd /bin/ls
          linux-vdso.so.1 (0x00007ffcc3563000)
          libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f87e5459000)
          libcap.so.2 => /lib64/libcap.so.2 (0x00007f87e5254000)
          libc.so.6 => /lib64/libc.so.6 (0x00007f87e4e92000)
          libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f87e4c22000)
          libdl.so.2 => /lib64/libdl.so.2 (0x00007f87e4a1e000)
          /lib64/ld-linux-x86-64.so.2 (0x00005574bf12e000)
          libattr.so.1 => /lib64/libattr.so.1 (0x00007f87e4817000)
          libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f87e45fa000)
      • 查看哪个 .so 文件缺失(找不到)
  4. objdump

    • 用来打印,object 文件的各种不同信息,例如:汇编内容打印(反汇编)
    • 适用场景:编辑器开发者,使用编译器编译程序的人并不适合使用
    • eg:

      • 反汇编: objdump -d /usr/bin/ls
      • 源码解析: objdump -s /usr/bin/ls
  5. readelf

    • 打印 object 文件的各种信息,类似 objdump
    • eg:

      • 查看动态库信息部分: readelf -d /usr/bin/ls

        • 查看依赖那些 .so 文件

命令行

  • 参考:

  • 初始化

    • cmake -S <source_dir> -B <build_dir>
  • build

    • cmake –build <build_dir> –target <target_to_run>

      • 作用:build 指定的 target
      • eg:

        1
        2
        3
        4
        5
        6
        7
        
        cmake --build ./build --target main # 构建target-->main
        
        cmake --build ./build --target install # target --> install
        
        cmake --build ./build --target package # cpack 打包 binary 包
        
        cmake --build ./build --target package_source # cpack 打包 source 包
  • install

    • cmake –install <build_dir>

      • 注: cmake --build <build_dir> --target install 的快捷调用方法
  • 指定 编译器

    • CC=clang CXX=clang++ cmake ..

单文件

1
2
3
4
5
6
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo1)
# 指定生成目标
add_executable(Demo main.cc)

命令解析

  1. cmake_minimum_required

    • 版本限制
  2. project

    • project("Project information")
    • 项目信息
  3. add_executable

    • 生成目标,可执行文件

多文件

没有子文件夹 No sub_dir

同单文件没什么区别

1
2
3
4
5
6
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo2)
# 指定生成目标
add_executable(Demo main.cc MathFunctions.cc)

含文件夹 with sub_dir

根文件夹,即项目根目录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo3)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 添加 math 子目录
add_subdirectory(math)
# 指定生成目标
add_executable(Demo main.cc)
# 添加链接库
target_link_libraries(Demo MathFunctions)
命令解析
  1. 子文件夹 sub_dir —> 变量

    • aux_source_directory
    • aux_source_directory(dir_path, DIR_SRCS)

      • $DIR_SRCS 是变量名
  2. 子文件夹 sub_dir —> 具体指出来

    • add_subdirectory
    • add_subdirectory(sub_dir_name)
  3. 链接库

    • target_link_libraries
    • target_link_libraries(executable_file, link_lib_name)

      • link_lib_name

        1. 链接库的变量名
        2. 将在这个库的所在文件夹定义

子文件夹

1
2
3
4
5
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
命令解析
  1. aux_source_directory

    • aux_source_directory(. link_lib_path_name)
    • 把链接库路径,赋值给变量$link_lib_path_name(即:上例中的${DIR_LIB_SRCS})
  2. add_library

    • add_library(link_lib_name ${link_lib_path_name})
    • 定义链接库变量名称

注意

  1. aux_source_directory(. path_var_name)

    • 给路径设置一个名称
  2. add_library(lib_name ${lib_path_var_name})

    • 生成链接库名称
  3. target_link_libraries(executable_file lib_name)

    • 把链接库,添加到可执行文件 target(建立对应关系)
  4. 其它,命令

    • add_subdirectory
    • cmake_minimum_required(VERSION 2.8)
    • project

实战

技巧

1
cmake .. -DDLIBCLANG_LIBRARY="C:\Program Files\LLVM\bin\libclang.dll" -DCXX_COMPILER="C:\Program Files\LLVM\bin\clang++.exe"

来源: https://legends2k.github.io/note/install-irony-win32/

目的:编译 irony-server, rtags on windows.

命令

find_package

project 命令

参考:http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20151012/305757.html

policy CMP0048 NEW 错误

1
2
3
if(POLICY CMP0048)
  cmake_policy(SET CMP0048 NEW)
endif()

configure_file 命令

  • TutorialConfigure.h.in 设置

    • 原教程

      1
      2
      3
      4
      
      // 原教程
      // the configured options and settings for Tutorial
      #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
      #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
    • 实际(生效)文件

      1
      2
      
      #define Tutorial_VERSION_MAJOR 2  // @Tutorial_VERSION_MAJOR@
      #define Tutorial_VERSION_MINOR 3  //@Tutorial_VERSION_MINOR@
      
      • 注意:使用正常的 c++ 宏定义
  • CMakeLists.txt 配置

    1
    2
    3
    4
    5
    6
    7
    8
    
    configure_file(TutorialConfig.h.in TutorialConfig.h)
    
    add_executable(Tutorial tutorial.cxx)
    
    # target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}/")
    target_include_directories(Tutorial PUBLIC
                               "${PROJECT_BINARY_DIR}"
                               )
    • 指定 include 文件目录
    • target_link_libraries 要使用 PUBLIC + 路径

头文件路径

注意:

  • cmake 中 include() 命令是用来添加 list 文件(.cmake 文件或 CMakeLists.txt 文件)的
  • include_directories() 后缀的命令,才是用来指定 c 语言中的头文件目录的命令

include_directories

1
2
include_directories(${your_path})
include_directories("/path/to/needed/path")

target_include_directories

把 include_directory 添加到特定的 your_lib 和 your_excutable

1
2
3
4
target_include_directories(mylib PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
  $<INSTALL_INTERFACE:include/mylib>  # <prefix>/include/mylib
)

链接库

参考:

链接库类型

  • normal 正常类型

    • STATIC

      • 静态链接库

        • .a 或 .lib 文件
    • SHARED

      • 动态链接库

        • .so 或 .dll 文件
    • MODULE

      • 不导出 symbol 的库,一般通过 dlopen 打开
  • OBJECT

调用链接库方法

动态链接库

1
2
link_directories(${PROJECT_SOURCE_DIR}/lib) #添加动态连接库的路径
target_link_libraries(project_name -lmxnet ) #添加libmxnet.so

静态链接库

1
2
3
add_library(mxnet STATIC IMPORTED)
set_property(TARGET mxnet PROPERTY IMPORTED_LOCATION /path/to/libmxnet.a)
target_link_libraries(project_name mxnet ) #添加libmxnet.a

add_library()

特点:

  • 默认添加的是静态库(STATIC)

    • 修改默认行为的方法: set(BUILD_SHARED_LIBS ON)
    • 修改变量 BUILD_SHARED_LIBS

Windows 动态链接库 定义格式

函数

头文件

  • 注意

    • 定义的宏 PYTHON_TOOLS_EXPORT
    • 放置在 函数声明的最前面

      • 无指针时,也可放在类型声明后面
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// * 有指针
#ifdef WIN32
#include "python_tools_Export.h"

PYTHON_TOOLS_EXPORT PyObject* importAttr(PyObject* pMod, const char* attr);
PYTHON_TOOLS_EXPORT void printObj(PyObject* obj);
#endif // WIN32

#ifdef __LINUX__

PyObject* PYTHON_TOOLS_EXPORT importAttr(PyObject* pMod, const char* attr);
void printObj(PyObject* obj);
#endif

// * 无指针
#ifdef WIN32

#include "config_export.h"
bool CONFIG_EXPORT read_config(std::string &project_path);
int CONFIG_EXPORT read_config(const char* project_path);

#else
bool read_config(std::string &project_path);
int read_config(const char* project_path);
#endif // WIN32

cpp 代码文件

  • 只需添加对应的 head file 即可
  • 不必在使用定义的宏【 __declspec(dllexport)】 加以修饰

class 类

头文件 head file

  • 在 class 后面添加 __declspec 的宏即可

    • 实践证明

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      
      // * 错误
      DLLEXPORT class Hello {
      public:
        void print();
      }
      
      // * 正确
      class DLLEXPORT Hello {
      public:
        void print();
      }
  • 不用每个方法,都添加宏修饰符
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#ifdef WIN32
#include "thermocalc_Export.h"

class THERMOCALC_EXPORT ThermoCalc
#else
class ThermoCalc
#endif  // end of WIN32
{
private:
  PyObject* calcer = NULL;
  const char* error = "";
public:
  ThermoCalc(string formula, double temp, string enegy_unit, string degree_unit);
  ~ThermoCalc();
  string get_error(){ return this->error;};
  bool set_temp(double temp, string degree_unit="K");
};

cpp 代码文件

  • 只要 include 相应的头文件即可

切换 Release 与 Debug 版本

1
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release path\to\source\dir
  • cmake -DCMAKE_BUILD_TYPE=Release ..

帮助 与 find_package

查找 cmake 帮助

1
2
3
4
5
  cmake --help-module FindPythonLibs
  cmake --help-module-list          # 列出所有的Module.
  cmake --help-command find_package
  cmake --help-command include_directories
  cmake --help-command target_link_libraries

通过 cmake –help-module FindPythonLibs 查找 Module 的帮助文档,可以查看这个 Module 定义的变量的 “真实名称”

  • 注意

    • 这个真实名称很重要,大小写,版本号等,很容易错误(网上的人,也很多写的是错误的。)

判断平台 Platform

1
2
3
4
5
6
7
8
9
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
    MESSAGE(STATUS "current platform: Linux ")
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Windows")
    MESSAGE(STATUS "current platform: Windows")
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
    MESSAGE(STATUS "current platform: FreeBSD")
ELSE ()
    MESSAGE(STATUS "other platform: ${CMAKE_SYSTEM_NAME}")
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")

https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/How-To-Write-Platform-Checks

  • 注意

    • 这里是 MATCHES, 不是 MATCH

CMAKE_SYSTEM_NAME

CMAKE_SYSTEM_NAME ==> Linux-4.15.0-29deepin-generic CMAKE_SYSTEM ==> Linux

  • 相比 Win32, Unix, APPLE (存储的是 trule, false)更据跨平台特性

Windows 动态链接库

1
2
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1)
set(CMAKE_BUILD_SHARED_LIBS 1)
  • 注意

    • 有时候,必须要把 dll 文件复制到需要的目录,(通常只是指定目录)

实例, CMake

参考:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  #output library export file *.lib and
  #output macro definitions include file
  include (GenerateExportHeader)
  add_library(animallib SHARED ${SOURCES})
  GENERATE_EXPORT_HEADER (animallib
      BASE_NAME animallib
      EXPORT_MACRO_NAME animallib_EXPORT
      EXPORT_FILE_NAME animallib_Export.h
      STATIC_DEFINE animallib_BUILT_AS_STATIC
  )

  GENERATE_EXPORT_HEADER( myDLL  # dll文件名
    EXPORT_MACRO_NAME MYDLLExports  # export 宏
    EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include/myDLLExports.h  #宏的保存文件
    
  • 在 dll 的代码中

    1
    2
    3
    4
    
    #include "myDLLExports.h"
    class MYDLLExports A {
                          // definition
    }

GENERATE_EXPORT_HEADER

快捷方法

列表类型, list

1
2
3
4
find_package(
Boost 1.55 COMPONENTS asio)
list(APPEND INCLUDE_DIRS ${BOOST_INCLUDE_DIRS})
list(APPEND LIBRARIES ${BOOST_LIBRARIES})

变量,cmake 预定义变量

cmake-variables(7) — CMake 3.0.2 Documentation

directories

project variable

  • CMAKE_SOURCE_DIR –> PROJECT_SOURCE_DIR
  • CMAKE_BINARY_DIR –> PROJECT_BINARY_DIR

current variable

  • CMAKE_CURRENT_SOURCE_DIR
  • CMAKE_CURRENT_BINARY_DIR

特殊变量

CMAKE_PREFIX_PATH

CMAKE_PREFIX_PATH — CMake 3.24.0-rc3 Documentation

  • 一个通过分号“;”分割的路径 path 列表, eg: "/usr;/usr/local"
  • 用来指定安装库,软件等位置,用于用户需要查找的东西
  • 作用工具:find_package, find_program, find_library, find_file, find_path 等
  • 如何被使用的

    • 当 find_library 等命令调用 CMAKE_PREFIX_PATH 时, 会搜索其目录下的 bin,include,lib 等目录
  • 设定者:project 使用它

    • 我的理解:

      • 应当是用户在写一个“项目”时,定义它
  • 默认值:为空

CMAKE_LIBRARY_PATH

CMAKE_LIBRARY_PATH — CMake 3.24.0-rc3 Documentation

  • 用途:find_library
  • 分号“;”分割的路径 path 列表,
  • 设定者:project
  • 默认值:为空

CMAKE_FRAMEWORK_PATH

CMAKE_FRAMEWORK_PATH — CMake 3.24.0-rc3 Documentation

  • 用途:find_library, find_package, find_path, find_file
  • 平台:macOS
  • 分号“;”分割的路径 path 列表

CMAKE_CURRENT_SOURCE_DIR

  • 当前 CMakeLists.txt 文件所在目录

    • This the full path to the source directory that is currently being processed by cmake.

CMAKE_CURRENT_LIST_DIR vs CMAKE_CURRENT_SOURCE_DIR

参考:

辨析:

  1. 当 CMakeLists.txt 自身生效时,两者是同一个文件
  2. 当 module_name.cmake 文件被其他文件(一个 CMakeLists.txt)通过 include(module_path/module_name.cmake) 命令调用时

    • CMAKE_CURRENT_LIST_DIR 指的是: module.name.cmake 文件所在目录 project_root/module_path/
    • CMAKE_CURRENT_SOURCE_DIR 指的是:发起调用方(CMakeLists.txt)所在目录 project_root
    • 注意: listfile 可以是 module.cmake 文件,也可以是另一个 CMakeLists.txt 文件
  3. CMAKE_CURRENT_LIST_DIR 有动态作用域

    • 永远是正在处理的 listfile 所在文件目录
    • 处理 include 命令前,是发起调用方所在目录
    • 处理 include 命令过程中,是被调用文件所在目录
    • 处理 include 命令后,又变成调用方所在目录

CMAKE_SOURCE_DIR vs PROJECT_SOURCE_DIR

辨析:

  1. CMAKE_SOURCE_DIR: 项目根目录所存在的 CMakeLists.txt 文件所在目录

    • 需要有 project() 命令
    • 这里的项目根目录是 cmake 项目的根目录
  2. PROJECT_SOURCE_DIR: 最近的 有 project() 命令的 CMakeLists.txt 文件所在目录
  3. CMAKE_CURRENT_SOURCE_DIR: 当前 CMakeLists.txt 文件所在文件目录

    • 不一定有 project() 命令
  4. CMAKE_CURRENT_LIST_DIR: cmake 代码所在文件,不一定是 CMakeLists.txt 文件

    • 不一定有 project() 命令

find_library

默认搜索的路径

find_library — CMake 3.24.0-rc3 Documentation

下面的内容由 NO_DEFAULT_PATH 反推

被 find_package 在内部嵌套调用

  • 先搜索当前的 package

    • 搜索 cmake 指定的 <PackageName>_ROOT
    • 搜索 shell 的环境变量 ENV 中的<PackageName>_ROOT, 如果有的情况下
  • 再搜索父 package

    • 搜索 cmake 指定的<ParentName>_ROOT
    • 搜索 shell 环境变量<ParentName>_ROOT
  • 禁用方法

    • set CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE
  • 总之

    • 通过 ROOT 变量指定,如:BOOST_ROOT, Python_ROOT
    • 再通过 find_package 调用

搜索 cmake 定义的变量

  • cmake-specific cache variable

    • 命令行通过-DVAR=value 指定
    • set(VAR value) 指定
  • 禁用方法

    • This can be skipped if NO_CMAKE_PATH is passed or by setting the “CMAKE_FIND_USE_CMAKE_PATH” to FALSE.
  • 典型使用方法

    • 定义 CMAKE_PREFIX_PATH, CMAKE_LIBRARY_PATH

搜索环境变量

  • cmake 指定的环境变量
  • 格式

    • windows, 分号分割
    • linux, 冒号分割
  • 通过变量 CMAKE_LIBRARY_ARCHITECTURE 指定架构
  • 禁用方法

    • NO_CMAKE_ENVIRONMENT_PATH is passed or by setting the “CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH” to FALSE.

HINTS 和 PATHS

  • HINTS

    • 通常是系统产生的
    • 如别的 find_package 找到的等
  • PATHS

    • 用户硬性指定的

系统环境变量

  • shell 定义的环境变量
  • 典型环境变量

    • INCLUDE
    • PATH
  • windows

    • 通过变量 CMAKE_LIBRARY_ARCHITECTURE 指定架构

      • 如:x86 等
    • 用途

      • 搜索<prefix>/lib/<arch>

platform files 中定义的 cmake 变量

  • 系统平台
  • 相关变量

    • CMAKE_SYSTEM_PREFIX_PATH
    • CMAKE_SYSTEM_LIBRARY_PATH
  • 目的

    • 指定系统默认的路径等
    • 如:/usr/bin /usr/include /usr/lib (这里是猜测)

自定义搜索路径

指定 PATHS 选项

  • 调用形式

    1
    2
    
    find_library (<VAR> NAMES name PATHS paths... NO_DEFAULT_PATH)
    find_library (<VAR> NAMES name)
    • 先仅仅搜索自定义路径
    • 再搜索默认路径
    • 这种方法的优点

      • 前面自定义搜索,如果已经找到,后面的搜索就不会再调用了

定义 CMAKE_FIND_ROOT_PATH

  • 格式:冒号分割的路径列表
  • 用途:find_library, find_package
  • 作用方式

    • 添加搜索路径到,所有搜索路径的最前面,prepend
  • 默认值:为空

Modern CMake

面向对象

  • target 类似对象
  • 与 target 有关的函数 是 对应的方法

    • target_link_libraries
    • target_include_directories
    • target_compile_features

      • 编译特征– 指定 compiler 必须具有的特征

        1
        
        arget_compile_features(mylib PUBLIC cxx_constexpr)
    • target_compile_options

      1
      
      target_compile_options(MyTarget PRIVATE "$<$<CONFIG:Debug>:--my-flag>")
      • 注:如果 BUILD_TYPE 是 Debug, 添加 –my-flag 选项参数
    • target_compile_definitions
    • set_target_property – cmake 提供属性

      1
      2
      
      set_property(TARGET TargetName PROPERTY CXX_STANDARD 11)
      set_target_properties(TargetName PROPERTIES CXX_STANDARD 11)
    • get_target_property

      1
      
      get_property(ResultVariable TARGET TargetName PROPERTY CXX_STANDARD)

target_link_libraries 关键字

参考:

说明:

  • 清楚的解释

    • PRIVATE

      • header file 不包含 依赖库内容(即库的头文件,以下类推)
      • source file 包含 依赖库内容
    • INTERFACE

      • header file 包含 依赖库内容
      • source file 不包含 依赖库内容
    • PUBLIC

      • header file 包含 依赖库内容
      • source file 包含 依赖库内容
  • PRIVATE

    • 仅仅 target 需要
  • INTERFACE

    • 使用 target 时需要
  • PUBLIC

    • 当前 target 需要
    • 使用 target 时也需要
  • 例子 A:

    1
    2
    3
    4
    
    add_library(another STATIC another.cpp another.h)
    target_link_libraries(another PUBLIC one)
    
    target_include_directories(one PUBLIC include)

意义

  • 作用

    • 依赖的继承关系说明
  • target_include_directories

    • 这些关键字

      • 对于 executable 无特别意义
      • 对于 链接库

        • PUBLIC:

          • 当前 lib 需要给定链接库
          • 以后使用当前 lib 的其它 target 也需要这个链接库,找不到,报错
        • PRIVATE:

          • 只有当前 lib 需要 给定链接库
          • 用户使用 lib 时,不需要这个链接库
        • INTERFACE:

          • 当前 lib 不需要 给定链接库
          • 但是,用户使用 lib 时,需要这个链接库

推荐的项目结构(文件组织结构)

变量

变量类型

类型:

regular
不会被缓存的变量
cache
会被缓存的变量
1
2
message("Regular variable (before): ${abc}")
message("Cache variable (before): ${xyz}")

注释:

缓存
即,被存储到 CMakeCache.txt 文件中

异同:

  1. -D 设置

    • regular 不能被设置
    • cache 可以被设置
  2. 覆盖

    • regular 会被 cache 覆盖
  3. 缓存数据的影响

    • cache 变量,第一次执行 cmake 命令时缓存到文件中
    • 第二次直接读取缓存数据

      • 即使修改 CMakeLists.txt 文件也无效
      • 只能通过 cmake -D 覆盖修改
  4. 作用域

    • regular 有作用域
    • cache 是全局作用域,没有局部作用域
  5. 生效时间

    • CACHE set 语句只有第一次生效,后续会被跳过
    • 无 CACHE set 语句,总是被执行

      1
      2
      3
      4
      5
      6
      7
      
      cmake_minimum_required(VERSION 2.8)
      project(foo NONE)
      
      set(A "123")                    # 每次执行 cmake 命令时,都会被执行
      set(A "456" CACHE STRING "")    # 只有第一次执行 cmake 命令时,会被执行
      
      message("A: ${A}")

regular 变量

cache 变量

参考:

特性:

  1. CACHE 类型

    • BOOL: bool
    • STRING: 字符串
    • PATH: 文件夹路径
    • FILEPATH: 文件路径
    • 作用

      • cmake-gui 配置 cache 变量时,提供个性化的 widget
  2. FORCE 强制修改

    • cache 的强制修改, 使用 FORCE

      1
      
      set(A "123" CACHE STRING "" FORCE)
  3. STRINGS 枚举值

    • 作用

      • 设定用户可以选择的值
    • 例子:

      1
      2
      3
      
      set(FOO_CRYPTO "OpenSSL" CACHE STRING "Backend for cryptography")
      
      set_property(CACHE FOO_CRYPTO PROPERTY STRINGS "OpenSSL;Libgcrypt;WinCNG") #列表
  4. INTERNAL 内部使用

    • 作用

      • 不被 cmake-gui 显示用来设置
    • 特点

      • 默认时 STRING 类型,设置为 FORCE
  5. ADVANCE 高级功能

    • 作用

      • cmake -gui 高级功能配置
  6. option 命令

    • 作用

      • 创建一个 set(name CACHE BOOL) cache 变量
      • 快捷工具
  7. 删除命令

    • 方法: unset(name CACHE)
    • 删除 cache 在文件中的缓存
    • 注意: unset(name) 对 cache 变量无效,只对 regular 变量有效

注意:

  • cache 变量是全局变量

    • 推荐加上前缀(如 target 名称),避免名称冲突

环境变量

参考:

作用:

  • 系统环境变量的处理

方法:

  • 读取: 通过 $ENV{env_name} 获取
  • 设置: set(ENV{USERNAME} "Jane Doe")

    • 注意:

      • 这里的设置只是临时性的(只在 config 阶段)
      • 在 build 时,环境变量,又变成了系统默认值(shell 中的设置)
  • 删除: unset(ENV{USERNAME})

特点:

  • 可继承性

    • 子进程能够获取到父进程中 CMakeLists.txt 的环境变量设置

作用域

类别

产生新作用域的情况:

  • add_subdirectory()

    • 在子文件夹中的 CMakeLists.txt 中产生新作用域
  • function()

    • 定义新函数
    • 函数中有自己的作用域

不产生新作用域:

  • macro()

    • 定义宏
  • include()

    • 子模块 .cmake 文件

取消父作用域的值

unset(your_variable)

cmake listfiles 文件

参考:

类型

分类

  1. CMakeLists.txt 文件

    • 通过 add_subdirectory 被执行
  2. *.cmake Module 文件

    • 通过 cmake -P 命令行被执行
    • 通过 include 命令被执行

rpath 处理(动态库搜索路径)

参考:

动态库(.so 文件)搜索位置

  1. RPATH

    • 一个路径.so 文件存放列表
    • 被 linked into 可执行文件
    • 如果 RUNPATH 存在会被忽略
    • 大部份 unix 支持它
    • 一般它的优先级高于 LD_LIBRARY_PATH
    • 添加方法:

      1. ld 命令: ld -rpath=/given/path

        • 注意 ld -rpath-link=dir 只有链接时起作用,运行时无效, -rpath 在 runtime 运行时也有效
      2. gcc 命令: gcc -Wl,rpath,dir

  2. LD_LIBRARY_PATH

    • 环境变量

      • 用户自行配置
  3. RUNPATH

    • 类似 RPATH, 但是搜索优先级在 LD_LIBRARY_PATH 之后
    • 注意:只有新版 unix 支持
  4. /etc/ld.so.conf

    • ld.so 库的配置文件,额外的动态库存放目录
  5. builtin 内建目录

    • eg: /lib, /usr/lib

为什么除掉 builtin 内建目录外还有各种目录

  1. 用户可能安装了自己的库版本

macos 的 rpath 处理方法

参考:

  1. macos 中 RPATH 的搜索有限级的 DYLD_LIBRARY_PATH 之后
  2. RPATH 通过 @loader_path 和 @executable_path 来定义

cmake 与 rpath

cmake 中 rpath 有两种使用场景:

  1. build tree 编译位置

    • cmake 编译 target 产生的位置,运行 target 时,rpath 指向 build tree 中设置的动态库路径
  2. install location 安装位置

    • 安装后,动态库的路径改变了,因此 rpath 也要改变
  3. 注意:

    • 安装时 rpath 的 cmake 自动修改是通过 install() 命令触发的,因此手动复制 target, 不会触发 rpath 改变

与 rpath 相关的 cmake 变量 (在顶层 CMakeLists.txt 中设置):

  • CMAKE_INSTALL_RPATH

    • 安装后的 rpath 设置(这里我把它归类到,安装时 rpath)
    • 默认为空: ""
    • 关于 $ORIGIN 的处理:

      • 参考:CMP0095 — CMake 3.24.1 Documentation
      • 在 v3.15 及以前需要转义: set(CMAKE_INSTALL_RPATH "\\\${ORIGIN}/../lib")

        • 为什么需要转义:因为在 ld.so 中就是 ${ORIGIN} 或 $ORIGIN, cmake 中变量也是这种写法, 不做转义处理,cmake 会把 ${ORIGIN} 转换为空串 ""
      • 在 v3.16 及以后不需要转义: set(CMAKE_INSTALL_RPATH "${ORIGIN}/../lib")

        • 这是因为,cmake 添加了针对这种情况的特殊处理
      • 在 v3.24.1 及以后通过 cmake_policy(set CMP0095 NEW) 来支持新型写法(默认使用旧式写法)
  • CMAKE_BUILD_RPATH

    • build tree 中 target 使用的运行时 rpath, 编译时rpath,(v3.8 新特性)
    • 注意:它在 install location 不起作用(无效)
  • CMAKE_BUILD_RPATH_USE_ORIGIN

    • 是否允许 build tree 中 target 的 rpath 使用 ${ORIGIN}, (v3.14 新特性)

      • cmake 传统上,build tree rpath 使用的是绝对路径
    • 默认不支持:FALSE
    • 好处:允许把编译的代码直接移动的别的地方
    • 注意:它不影响 BUILD_RPATH 设置内容
  • CMAKE_SKIP_BUILD_RPATH

    • 是否跳过 编译时 rpath
    • 默认:不跳过,即添加编译时 rpath, FALSE
  • CMAKE_BUILD_WITH_INSTALL_RPATH

    • 是否把 install location rpath 安装时 rpath 编译进去
    • 默认:不把 安装时 rpath 编译进去,即 FALSE
  • CMAKE_INSTALL_RPATH_USE_LINK_PATH

    • 安装时 rpath, 是否使用搜索到的 rpath:
    • 默认:不使用自动搜索到的 rpath, FALSE
  • 注意:上面的选项默认值都是 ("" 或 FALSE)

适用默认设置,不修改 rpath

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# use, i.e. don't skip the full RPATH for the build tree
set(CMAKE_SKIP_BUILD_RPATH FALSE) # 跳过编译时 rpath

# when building, don't use the install RPATH already
# (but later on when installing)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # 不使用安装 rpath

# the RPATH to be used when installing
set(CMAKE_INSTALL_RPATH "")     # 不设置安装时 rpath 具体路径

# don't add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # 不使用自动搜索到的 rpath
  • rpath 实际使用方法:

    1. 通过 LD_LIBRARY_PATH
    2. 通过默认的 lib path(即 builtin rpath)
    3. 因此,这样就需要配置好 LD_LIBRARY_PATH 或者把依赖的动态库(.so)安装到指定的默认路径

完全决定路径 rpath, 不依赖 LD_LIBRARY_PATH

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# use, i.e. don't skip the full RPATH for the build tree
set(CMAKE_SKIP_BUILD_RPATH FALSE) # 不使用编译时 rpath

# when building, don't use the install RPATH already
# (but later on when installing)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # 不把 安装时 rpath 编译进target, 而是在安装过程中修改

set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") #设定安装时 rpath

# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # 安装时 rpath, 使用自动搜索到的 rpath

# the RPATH to be used when installing, but only if it's not a system directory
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
    set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif("${isSystemDir}" STREQUAL "-1")
  • rpath 实际使用情况:

    1. 编译时,使用的是自动寻找到的 rpath

      • 可以在 build tree 中执行 executable
    2. 安装时,会自动更改 rpath 到 CMAKE_INSTALL_PREFIX

      • 可以在安装完成后执行 executable
      • 但是这里不包括用户通过 add_library() 新创建的 .so 文件
      • 需要用户使用 install 命令安装到相应的位置:

        • install(TARGETS bar DESTINATION lib)
        • 即: ${CMAKE_INSTALL_PREFIX}/lib

完全不使用 rpath

CMAKE_SKIP_RPATH, 设置为 True 即可,它会导致其他 rpath 相关设置自动被忽略

不同 target 不同 rpath

  1. 通过 set_target_properties() 实现
  2. 上面使用的都是全局变量,省略 CMAKE_ 前缀就是 target 的私有属性

相关属性:

  • INSTALL_RPATH

    • 安装后的 rpath 设置(这里我把它归类到,安装时 rpath)
    • 默认为空: ""
  • SKIP_BUILD_RPATH

    • 是否跳过 编译时 rpath
    • 默认:不跳过,即添加编译时 rpath, FALSE
  • BUILD_WITH_INSTALL_RPATH

    • 是否把 install location rpath 安装时 rpath 编译进去
    • 默认:不把 安装时 rpath 编译进去,即 FALSE
  • INSTALL_RPATH_USE_LINK_PATH

    • 安装时 rpath, 是否使用搜索到的 rpath:
    • 默认:不使用自动搜索到的 rpath, FALSE
  • 注意:

    • 上面的选项默认值都是 ("" 或 FALSE)
    • 这些 target 属性都是默认通过对应的 CMAKE_ 开头变量初始化的

cmake 处理 $ORIGIN

$ORIGIN 的意思:target 所在目录

参考:

cmake 和 Windows 中的 rpath

参考:

宗旨:

  1. Windows 中不存在 rpath
  2. Windows 会在 executable 所在位置寻找 .dll 文件

cmake 和 conan rpath 处理

  1. conan 默认设置跳过 rpath 设置:

    • conan_basic_setup() 会设置 set(CMAKE_SKIP_RPATH 1) (在 macos 中)
  2. 跳过 conan 默认 rpath 设置方法:

    • conan_basic_setup(KEEP_RPATHS)
  3. 设置 conan 支持 rpath 方法:

    1. 配置 conan_basic_setup

      1
      2
      
      include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
      conan_basic_setup(KEEP_RPATHS)
    2. 配置 cmake 的 rpath 设置

      1
      2
      3
      4
      5
      6
      7
      
      if (APPLE)
          set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
      else()
          set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
      endif()
      
      set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
      • 解释: 把 install location 的 rpath 编译进 target
      • 目的: 让 target 知道在哪里找 .dll 或 .so 文件
    3. 设置 conanfile.txt, 处理 .so 或 .dll 方法

      1
      2
      3
      4
      5
      6
      7
      
      [requires]
      poco/1.9.4
      
      [imports]
      bin, *.dll -> ./bin # Copies all dll files from packages bin folder to my "bin" folder
      lib, *.dylib* -> ./lib # Copies all dylib files from packages lib folder to my "lib" folder
      lib, *.so* -> ./lib # Copies all so files from packages lib folder to my "lib" folder
      • 解释: 把 .so 文件复制到 ./lib 文件夹下,一般是 build/lib
  4. 总结:

    • conan 不能自动让 target 知道 shared library 在哪里
    • conan 只能把对应的.so 或 .dll 文件复制到给定的目标位置
    • 用户需要 hard code 硬编码,指定出安装的目标位置(rpath)

面向对象命令(面向 target)

命令列表:

  • target_include_directories()

    • 指定 target 的头文件目录
  • target_link_libraries()

    • 执行 target 需要链接的 链接库
  • target_compile_definitions()

    • C 语言 preprocessor 预处理器定义指令,eg: #define PI
  • target_compile_options()

    • 传递给编译器的选项,例如传递给 gcc 命令
    • 值通过 ";" 分号分割

target_compile_definitions()

  1. 用来添加预处理器指令
  2. 指令可以有值,也可以没有
  3. 添加的 -D 定义前缀会被 cmake 自身忽略
  4. eg:

    1
    2
    3
    4
    5
    6
    7
    8
    
    # * 下面的四种方法效果等价
    target_compile_definitions(foo PUBLIC FOO) # --> #define FOO
    target_compile_definitions(foo PUBLIC -DFOO)  # -D removed
    target_compile_definitions(foo PUBLIC "" FOO) # "" ignored
    target_compile_definitions(foo PUBLIC -D FOO) # -D becomes "", then ignored
    
    # * 添加 define 的值
    target_compile_definitions(foo PUBLIC FOO=1) # --> #define FOO 1

target_compile_options()

  1. 定义的是传递给编译器(eg: gcc)的命令选项
  2. eg:

    1
    
    target_compile_options(my_target PUBLIC -Wall -Wextra -pedantic -Werror)

interface 类属性

参考:

作用:

  • 通过有 INTERFACE_ 开头的属性,向 consumer 传递相应属性
  • eg:

    • my_lib 有 INTERFACE_POSITION_INDEPENDENT_CODE 属性
    • my_exe 通过 add_executable(my_exe PUBLIC my_lib) 语句继承了 my_lib
    • 这样,my_exe 就会获得 POSITION_INDEPENDENT_CODE 属性,(通过 my_lib 传递过来的)

继承触发语句

  • add_executable()

    • eg: add_executable(my_exe PUBLIC my_lib)

      • my_exe 从 my_lib 继承属性
  • add_library()

属性特点

  • 当前 target 不会被 interface 类属性直接影响
  • 使用者(consumer)会被影响(以 target_link_libraries() ):

    1. 如果 consumer 是通过 PUBLIC 链接的,consumer 会也拥有这个属性

      • 受影响
    2. 如果 consumer 是通过 PRIVATE 链接,consumer 不会有这个属性

      • 不受影响
    3. 如果 consumer 是通过 INTERFACE 链接的,consumer 不会有这个属性,只做属性传递

      • 部分受影响

        • 中继这个属性,但是不会拥有
        • 如果有 孙子 consumer, consumer 会把这个属性传递给 孙子 consumer

相关 target 属性

参考:

  1. INTERFACE_ 开头的 target 属性

    • eg:

      • INCLUDE_DIRECTORIES Vs. INTERFACE_INCLUDE_DIRECTORIES
      • COMPILE_OPTIONS Vs. INTERFACE_COMPILE_DEFINITIONS

设置工具

  1. 类似 target_link_libraries () + INTERFACE

    • eg: target_link_libraries(my_target INTERFACE my_lib)
  2. 类似 set_property() + INTERFACE_ 开头属性

    • eg: set_property(TARGET my_target PROPERTY INTERFACE_LINK_LIBRARIES my_lib)

设置冲突问题(兼容性)

参考:

为什么可能冲突:

  1. 可以通过 set_property() + POSITION_INDEPENDENT_CODE 直接设置一个属性
  2. 也可以通过 INTERFACE_POSITION_INDEPENDENT_CODE 继承获得 POSITION_INDEPENDENT_CODE
  3. 如果 两个来源的值 不同,就会导致冲突

cmake 对这种冲突的处理方式:

  • target 本身属性和依赖 lib 传递的 INTERFACE_ 属性冲突,报 diagnostic
  • target 给定属性的的多个依赖 lib 之间的 INTERFACE_ 属性冲突,直接报错 error
兼容性处理方法

方法:

  • 通过把相应的属性注册到一个列表中,来根据这个列表的名称,决定处理兼容性的方式

处理工具列表

  • COMPATIBLE_INTERFACE_BOOL: 布耳类型

    • 放入这个列表的属性,要保证不同依赖的这个属性的设定值,取值一致
    • 兼容性规则:要么都是 ON,要么都是 OFF
    • 不通过检查处理:报 diagnostic
  • COMPATIBLE_INTERFACE_STRING: 字符串类型

    • 检查规则: 取值一致的字符串
  • COMPATIBLE_INTERFACE_NUMBER_MAX: 数值--最大值

    • 检查规则:在 generate time(生成期), 取最大值
  • COMPATIBLE_INTERFACE_NUMBER_MIN: 数值--最小值

    • 检查规则:在 generate time(生成期), 取最小值

举例:

  1. 字符串类型

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    add_library(lib1Version2 SHARED lib1_v2.cpp)
    set_property(TARGET lib1Version2 PROPERTY INTERFACE_LIB_VERSION 2)
    set_property(TARGET lib1Version2 APPEND PROPERTY # 添加到字符串兼容类型
      COMPATIBLE_INTERFACE_STRING LIB_VERSION
    )
    
    add_library(lib1Version3 SHARED lib1_v3.cpp)
    set_property(TARGET lib1Version3 PROPERTY INTERFACE_LIB_VERSION 3)
    
    add_executable(exe1 exe1.cpp)
    target_link_libraries(exe1 lib1Version2) # LIB_VERSION will be "2", 没有冲突时
    
    add_executable(exe2 exe2.cpp)
    target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic, 冲突时
  2. 数值--最大值

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    add_library(lib1Version2 SHARED lib1_v2.cpp)
    set_property(TARGET lib1Version2 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 200)
    set_property(TARGET lib1Version2 APPEND PROPERTY # 添加到兼容类型
      COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
    )
    
    add_library(lib1Version3 SHARED lib1_v3.cpp)
    set_property(TARGET lib1Version3 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 1000)
    
    add_executable(exe1 exe1.cpp)
    # CONTAINER_SIZE_REQUIRED will be "200"
    target_link_libraries(exe1 lib1Version2)
    
    add_executable(exe2 exe2.cpp)
    # CONTAINER_SIZE_REQUIRED will be "1000"
    target_link_libraries(exe2 lib1Version2 lib1Version3) # 产生冲突,取最大值

Debug 方法, INTERFACE_ 属性来源

参考:

方法:

  • 通过设置 CMAKE_DEBUG_TARGET_PROPERTIES 列表实现
  • 把要 debug 的属性放入这个列表

例子:

1
2
3
4
5
6
7
8
set(CMAKE_DEBUG_TARGET_PROPERTIES
  INCLUDE_DIRECTORIES
  COMPILE_DEFINITIONS
  POSITION_INDEPENDENT_CODE
  CONTAINER_SIZE_REQUIRED
  LIB_VERSION
)
add_executable(exe1 exe1.cpp)

install() 命令

参考:

作用:

  • 用来说明一个项目(其中的文件等资源)是如何安装的
  • 用来生成安装脚本,用来在 安装时 运行, 执行文件安装

    • 脚本被 make install 调用 cmake installation module 触发
    • vscode 通过 INSTALL target 触发

接口类型

  1. install(TARGETS ...)

    • 通过 add_library() 或 add_executable() 创建的 target
  2. install(FILES ...)

    • 头文件,或任意文件
    • 用来安装通用文件
  3. install(PROGRAMS ...)

    • 任意可执行文件

      • 注意:这里不是 target, 是实实在在的本地文件
    • 与 install(FILES …) 接口的区别:

      • 它会设置文件的可执行属性
    • 例如:shell 脚本,非 cmake 项目编译生成的可执行文件等
  4. install(DIRECTORIES ...)

    • 文件夹的安装
    • 用来安装资源文件
    • 例如:包含图片的文件夹,数据文件夹
  5. install(SCRIPT ...)

    • 安装过程中执行的 cmake 脚本程序
    • 指定要执行的 .cmake 文件

      • eg: install(SCRIPT message.cmake)
  6. install(CODE ...)

    • cmake 脚本程序
    • 指定要执行的 cmake 代码,通过字符串指定

      • eg: install(CODE "MESSAGE(\"Installing My Project\")")
  7. install(EXPORT ...)

    • 安装 cmake 脚本程序,(从安装目录到到外部项目)

额外接口参数

  • DESTINATION

    • 安装位置

      • 绝对路径:就是指定的安装位置
      • 相对路径:相对与 CMAKE_INSTALL_PREFIX
  • PERMISSIONS

    • 安装文件权限

      • 用来覆盖默认权限
      • 注意:不同安装命令有默认权限
    • 可选值

      • OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, WORLD_READ, WORLD_WRITE, WORLD_EXECUTE, SETUID, and SETGID.
  • CONFIGURATIONS

    • 安装配置

      • 举例:Release, Debug
      • 用来指定安装规则 install rule 使用范围
      • 例如 Release 配置只能用来在 CMAKE_BUILD_TYPE 是 release 类型时起作用
  • COMPONENT

    • 安装规则分类(组件)

      • 举例:

        • Runtime: 运行需要的组件

          • 注意:一个组件,可以是多个文件
        • Development: 编译组件
        • Document: man page 和 help files 等文档相关文件
    • 作用:通过把 install rule 拆分成多个 component 组件,可以一次只安装部分组件或单个组件
    • 注意:

      • 组件名称不是 cmake 指定的,由项目(用户)自己指定
  • OPTIONAL

    • 可选规则

      • 类似 python None, 用来处理 target 或文件不存在文件
      • 不存在,也可以不报错

DIRECTORY 接口--文件名匹配

1
2
3
4
5
6
7
install(DIRECTORY data/icons DESTINATION share/myproject
        PATTERN ".git" EXCLUDE
        PATTERN "*.txt" EXCLUDE)

install(DIRECTORY data/icons DESTINATION share/myproject
        REGEX "/.git$" EXCLUDE
        REGEX "/[^/]*.txt$" EXCLUDE)

匹配工具

  1. PATTERN

    • 通配符匹配
  2. REGEX

    • 正则匹配

排除工具:

  1. EXCLUDE

    • 用来反向匹配,排除不需要的文件名

调用例子

参考 cmake install 官方: Installing Files — Mastering CMake

打包 -- cpack 和 cmake package

参考:

总体流程:

  1. CMakeLists.txt 配置

    1. add_executable()等脚本配置
    2. install() 命令设置安装规则
    3. include(CPack) 命令,包含 CPack 模块
  2. 编译程序

    1. cmake -S . -B build
    2. cmake –build .
  3. 打包

    1. cmake –build <build_directory> –target package

      • 打包二进制可执行程序
    2. cmake –build <build_directory> –target package_source

      • 源码打包

命令行调用方法

  1. make 命令法

    1
    2
    3
    
    make package
    
    make package_source
  2. cpack 命令法

    1
    2
    3
    4
    5
    
    cd <build_directory>
    
    cpack --config CPackConfig.cmake # 打包二进制可执行程序
    
    cpack --config CPackSourceConfig.cmake # 源码打包
  3. cmake –target 命令法

    1
    2
    3
    
    cmake --build <build_directory> --target package # 打包二进制可执行程序
    
    cmake --build <build_directory> --target package_source # 源码打包

Windows + NSIS

参考:

相关变量

  • icon

    • 安装包图标:CPACK_NSIS_MUI_ICON
    • 卸载工具图标:CPACK_NSIS_MUI_UNIICON

      • 即:uninstall.exe
    • 安装向导标题栏名称前缀:CPACK_NSIS_INSTALLED_ICON_NAME

      • 用于安装和卸载向导 wizard 标题栏
  • 安装位置

    • 推荐(默认)安装文件夹名称:CPACK_PACKAGE_INSTALL_DIRECTORY 或者 CPACK_NSIS_PACKAGE_NAME
  • 是否允许用户修改 Windows 的 PATH 变量

    • CPACK_NSIS_MODIFY_PATH
    • 类型:Bool
  • 向导文字背景图片

    • CPACK_PACKAGE_ICON
  • 协议(证书)文件

    • CPACK_RESOURCE_FILE_LICENSE
  • 开始菜单快捷方式

    • 参考:Packaging With CPack — Mastering CMake
    • 分类:

      • 可执行文件类型:CPACK_PACKAGE_EXECUTABLES

        • eg: set (CPACK_PACKAGE_EXECUTABLES "cmake-gui" "CMake" )
        • 注意:

          1. 值是成对的
          2. 第一个值是可执行文件的名称,eg: cmake-gui
          3. 第二个值是快捷方式的名称,eg: CMake
      • url 或到安装目录的链接:CPACK_NSIS_MENU_LINKS

        • eg: set (CPACK_NSIS_MENU_LINK "doc/cmake-${VERSION_MAJOR}.${VERSION_MINOR}/cmake-gui.html" "cmake-gui Help" "http://www.cmake.org" "CMake Web Site")
        • 注意:

          1. 值是成对的
          2. 链接是目录时,使用的是相对于安装目录的相对路径
  • 桌面快捷方式

    • CPack — CMake 3.24.2 Documentation
    • CPACK_CREATE_DESKTOP_LINKS
    • eg: set (CPACK_CREATE_DESKTOP_LINKS cmake-gui)
    • 注意:

      1. 它与开始菜单快捷方式 CPACK_PACKAGE_EXECUTABLES 相关联,使用的值是 CPACK_PACKAGE_EXECUTABLES 中设置过的
      2. 它是一个 list 类型,可以设置多个列表

FAQ

源码打包特别大问题

可能原因:

  • 把 build 文件夹也打包进去了
  • 递归压缩问题

    • 把临时文件夹 build/_CPack_Packages 递归打包了

解决方法:

  • 使用 CPACK_SOURCE_IGNORE_FILES
  • 举例:

    1
    2
    
    set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\\\\\.svn/;\\\\\\\\.swp$;\\\\\\\\.#;/#;/build/")
    include(CPack)
    • 注:这里跳过了 build 文件夹

add_subdirectory()

FAQ

添加失败 – 不是当前文件夹的子目录

参考:

解决办法:

  • add_subdirectory(source_dir binary_dir)

    • 手动指定 binary_dir

ctest 测试

参考:

相关命令:

  • include(CTest)
  • enable_testing()

    • 注意:

      • 需要在项目根目录下的 CMakeListst.txt 中调用
      • 默认内部调用 include(CTest)
      • 行为受变量 BUILD_TESTING 影响
  • add_test()

例子:

1
2
3
4
5
cmake_minimum_required(VERSION 3.0)

enable_testing()

add_test(test_name test_command --my-args1 arg1-value --myargs2 arg2-value)

制作 cmake 模块 – FindMyPackage.cmake