CMake-构建目标与属性

二进制构建目标 - 可执行文件

  • 在构建过程中被构建的可执行文件、库文件或目标文件都可以作为二进制构建目标
  • 二进制构建目标是全局可见的,无论在哪个子目录的CMake目录程序中创建了一个二进制构建目标,都可以被当前项目中的其他所有CMake目录程序访问到

可执行文件目标

  • 创建一个可执行文件类型的构建目标
css 复制代码
add_executable(<目标名称> 
        [WIN32] [MACOSX_BUNDLE] 
        [EXCLUDE_FROM_ALL]
        [<源文件>...]
    )
  • 第一个参数<目标名称>是必选参数

    • 目标名称并不一定是最终可执行文件的名称。最终生成的文件名可以通过OUTPUT_NAME目标属性来修改
  • <源文件>参数用于指定该可执行文件目标的源文件路径

  • WIN32:该属性仅作用于Windows中,表示使用WinMain而不是main作为入口函数,常用于图形界面程序

  • MACOSX_BUNDLE:该属性仅作用于Apple平台中,表示将可执行文件构建为应用程序Bundle,常用于图形界面程序

  • EXCLUDE_FROM_ALL

  • 示例

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(myProgram)
add_executable(myProgram main.c)
add_executable(myProgramExcludedFromAll 
       EXCLUDE_FROM_ALL
       main.c
)

一般库目标

  • 一般库目标包括静态库目标、动态库目标和模块库目标

  • 静态库动态库模块库的区别

    • 静态库.lib:多个目标文件(.o)的打包集合,链接时被完整复制到可执行文件中,编译链接阶段,由链接器(ld)完成链接
    • 动态库.dll:运行时才加载的二进制代码,程序编译时仅记录引用,运行时,由动态链接器(ld.so)加载链接
    • 模块库.dll:一种特殊的动态库,用于**「运行时动态加载」**(Dynamic Loading),而非程序启动时自动加载
    • 静态库是编译的时候就链接到可执行文件中的,而后两种都是在可执行文件在已经编译后,在运行的时候去调用的
  • 定义形式

xml 复制代码
add_library(<目标名称> <库类型>
        [EXCLUDE_FROM_ALL]
        [<源文件>...]
    )
  • <目标名称>参数是必选参数,且应当在项目中唯一

  • <库类型>参数有以下三个取值

    • STATIC,代表该构建目标为静态库构建目标
    • SHARED,代表该构建目标为动态库构建目标。动态库构建目标的
    • MODULE,代表该构建目标为模块库构建目标。模块库是一种插件形式的动态链接库,不会在构建时被链接到任何一个程序中,仅用于运行时动态链接(通过LoadLibrary或dlopen等API)。模块库构建目标的POSITION_INDEPENDENT_CODE属性也会被设置为真值
  • EXCLUDE_FROM_ALL<源文件>参数的含义与定义可执行文件目标时一致

  • 示例

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(myLib)
add_library(myLib lib.c)

目标文件库目标

  • 目标文件库类型的构建目标仅编译其包含的源文件,生成一系列目标文件,并不会将这些目标文件打包或链接到某个库文件
  • 目标文件库是一个逻辑上的概念,实际是很多目标文件的集合
  • 目标文件库核心目的是解决多个目标都需要使用同一组源文件,为避免重复编译,目标文件库通过将源文件**「只编译一次」**,生成中间对象文件(.o.obj),然后让多个最终目标共享这些对象文件,从而避免了重复编译的开销,显著提升了构建速度
css 复制代码
add_library(<目标名称> OBJECT
        [<源文件>...]
    )
  • 该命令会创建一个目标文件库构建目标,其参数与创建一般库目标时基本一样

  • 如果想在构建其他可执行文件或库时链接目标文件库对应的目标文件,只需在add_executableadd_library命令的<源文件>参数中指定下面这个表达式:

    ruby 复制代码
    $<TARGET_OBJECTS:<目标文件库的目标名称>>
  • 示例

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(myObjLib)
add_library(myObjLib OBJECT a.c b.c)
add_executable(main main.c $<TARGET_OBJECTS:myObjLib>)
add_executable(text text.c $<TARGET_OBJECTS:myObjLib>)

指定源文件的方式

  • 在向项目中指定<源文件>参数的时候 如果源文件很多一一罗列出来 很麻烦 但是最好是就是如此
  • file(GLOB)命令或aux_source_directory命令等获取目录中的全部源文件并将它们传入<源文件>参数 是很好的方案 但是问题是 添加了新源文件后 CMake可能不能识别出来不会重新构建
scss 复制代码
aux_source_directory(<目录> <结果变量>)
  • 该命令用于遍历指定<目录>中的源文件,并将它们的路径存入<结果变量>

伪构建目标 - 仅具有使用要求的可链接的对象

  • 伪构建目标,指那些并不会被构建二进制文件的目标,通常用于表明仅具有使用要求的可链接的对象

接口库目标

  • 接口库目标通常用于对头文件库的抽象
scss 复制代码
add_library(<目标名称> INTERFACE)
  • 该命令会创建一个目标文件库的构建目标,只有<目标名称>INTERFACE两个参数

  • 示例

    • include/a.h
arduino 复制代码
static int add(int a, int b) { return a + b; }
  • main.c
arduino 复制代码
#include <a.h>
#include <stdio.h>
int main() {
      printf("%d\n", add(1, 2));
      return 0;
}
  • CMakeLists.txt
scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(myInterfaceLib)
#这里项目名和接口库名一样 是可以 因为他们是不同层级的概念
add_library(myInterfaceLib INTERFACE)
# 声明myInterfaceLib的使用要求:头文件搜索目录应为include
# 当其他目标链接myInterfaceLib时,会自动的将include添加到目标的头文件搜索路径中
target_include_directories(myInterfaceLib INTERFACE include)
add_executable(main main.c)
# 声明main的构建要求:链接myInterfaceLib库,也就是说
# 将myInterfaceLib接口库的使用要求作为main的构建要求
target_link_libraries(main myInterfaceLib)

导入目标

可执行文件导入目标

  • 指导入项目之外的可执行文件或库的目标,但不会构建它
scss 复制代码
add_executable(<目标名称> IMPORTED [GLOBAL])
  • 默认情况下,导入目标的作用域为当前目录及其子目录的目录程序
  • GLOBAL参数则用于打破作用域限制,使该目标全局可见
  • 示例
scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(import-notepad)
add_executable(notepad_exe IMPORTED)
set_target_properties(notepad_exe PROPERTIES 
       IMPORTED_LOCATION "C:/Windows/System32/notepad.exe")
add_custom_target(run-notepad ALL notepad_exe ${CMAKE_CURRENT_LIST_FILE})

库导入目标

vbnet 复制代码
add_library(<目标名称> 
      <STATIC|SHARED|MODULE|UNKNOWN|OBJECT|INTERFACE> 
      IMPORTED [GLOBAL])
  • 作用域和导入目标一致
  • STATICSHAREDMODULE参数分别用于导入外部的静态库、动态库和模块库
  • UNKNOWN参数表示导入目标的库类型不确定,一般仅用于自动查找依赖库的功能模块中
  • OBJECT参数用于导入外部的目标文件库
  • INTERFACE参数用于导入外部的接口库。接口库本身没有对应的二进制文件
  • 示例
scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(import-boost)
#创建了库导入目标
add_library(boost-regex STATIC IMPORTED)
if(WIN32)
      set(BOOST_REGEX_LIB_PATH 
          "C:/boost/stage/lib/libboost_regex-vc142-mt-gd-x64-1_74.lib")
      set(BOOST_INCLUDE_DIR "C:/boost")
else()
      set(BOOST_REGEX_LIB_PATH 
            "$ENV{HOME}/boost/stage/lib/libboost_regex.a")
      set(BOOST_INCLUDE_DIR "$ENV{HOME}/boost")
endif()
# 设置boost-regex目标的头文件目录的使用要求
# 当其他目标链接boost-regex时,会自动的将${BOOST_INCLUDE_DIR}添加到目标的头文件搜索路径中
target_include_directories(boost-regex INTERFACE ${BOOST_INCLUDE_DIR})
# 设置boost-regex目标的导入路径
set_target_properties(boost-regex PROPERTIES 
    IMPORTED_LOCATION "${BOOST_REGEX_LIB_PATH}")
add_executable(main "../../ch001/09.链接Boost/main.cpp")
# 声明main的构建要求:链接boost-regex库,也就是说
# 将boost-regex导入库的使用要求作为main的构建要求
target_link_libraries(main boost-regex)

按构建模式导入

别名目标

  • 别名目标即另一个构建目标的别名,是只读的构建目标
scss 复制代码
add_executable(<目标名称> ALIAS <指向的实际目标名称>)
add_library(<目标名称> ALIAS <指向的实际目标名称>)
  • 上面这两个命令都可以用于创建指向<指向的实际目标名称>的别名目标
  • 当别名目标指向非全局的导入目标时,其作用域同样会被限制在当前目录及其子目录中
  • 示例
scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(alias-target)
# 添加静态库my_liba
add_library(my_liba STATIC "a.cpp")
#判断用户是否要使用外部库
if(USE_EXTERNAL_LIBA)
    #添加外部静态库
      add_library(liba STATIC IMPORTED)
    #对目标liba设置其库文件实际路径
      set_target_properties(liba PROPERTIES IMPORTED_LOCATION "liba.lib")
else()
    #将my_libe起别名liba
      add_library(liba ALIAS my_liba)
endif()
#创建可执行目标main
add_executable(main "main.cpp")
#给可执行目标main链接库目标liba
target_link_libraries(main liba)

子目录

  • 添加子目录:add_subdirectory
css 复制代码
add_subdirectory(<源文件目录> [<二进制目录>] [EXCLUDE_FROM_ALL])
  • 该命令用于将<源文件目录>这个子目录加入项目
  • 该子目录中必须含有一个CMake目录程序,即CMakeLists.txt
  • <源文件目录>可以为绝对路径或相对于当前源文件目录的相对路径
  • <二进制目录>也可以为绝对路径或相对于当前二进制目录的相对路径,省略该参数时,其值根据<源文件目录>的相对位置决定
  • EXCLUDE_FROM_ALL参数的作用与创建构建目标时的同名参数一致:指定该参数意味着子目录中定义的所有构建目标不会被包括在父目录的"构建全部"目标中

项目

xml 复制代码
project(<项目名称>
      [VERSION <主版本号>[.<次版本号>[.<补丁版本号>[.<修订版本号>]]]]
       [DESCRIPTION <项目描述>]
       [HOMEPAGE_URL <项目主页URL>]
       [LANGUAGES <编程语言>...])
  • <编程语言>参数支持C、C++

  • 通过该命令定义的项目属性,均可以通过CMake变量获取,参考下表

    变量 描述
    <项目名称>_SOURCE_DIR 指定项目源文件目录的绝对路径
    <项目名称>_BINARY_DIR 指定项目二进制文件目录的绝对路径
    <项目名称>_VERSION 指定项目的版本号
    <项目名称>_VERSION_MAJOR 指定项目主版本号
    <项目名称>_VERSION_MINOR 指定项目次版本号
    <项目名称>_VERSION_PATCH 指定项目的补丁版本号
    <项目名称>_VERSION_TWEAK 指定项目的修订版本号
    <项目名称>_DESCRIPTION 指定项目的描述文本
    <项目名称>_HOMEPAGE_URL 指定项目的主页URL
    • PROJECT_NAME,用于获取当前最近项目的名称;
    • CMAKE_PROJECT_NAME,用于获取顶层项目的名称。
  • 代码注入

    • 通过设置几个CMake变量的值为CMake脚本程序或模块程序的路径,就可以实现向CMake项目定义的前后注入代码
    • CMAKE_PROJECT_INCLUDE_BEFORE:将CMake程序注入到所有项目定义前
    • CMAKE_PROJECT_INCLUDE:将CMake程序注入到所有项目定义后
    • CMAKE_PROJECT_<项目名称>_INCLUDE_BEFORE:将CMake程序注入到指定项目定义前
    • CMAKE_PROJECT_<项目名称>_INCLUDE:将CMake程序注入到指定项目定义后

属性

全局属性

  • 全局属性(global property)即CMake进程所具有的属性,通常用于获取一些全局状态

  • 获取全局属性

    • 两个命令均可以获取<全局属性>,并将值结果存入<结果变量>
    • 当指定属性不存在时,第一个命令中的结果变量会被赋值为NOTFOUND,而第二个命令中的结果变量会被赋空值
    • get_property命令支持对各种属性的获取
scss 复制代码
get_cmake_property(<结果变量> <全局属性>)
get_property(<结果变量> GLOBAL PROPERTY <全局属性> 
       [SET|DEFINED|BRIEF_DOCS|FULL_DOCS])
  • 设置全局属性

    • 该命令用于将<全局属性>的值设为<属性值>,多个<属性值>会被视为一个列表
    • 指定APPEND参数后,<属性值>列表中的每个非空元素将依次被追加到指定属性原值列表的末尾
css 复制代码
set_property(GLOBAL [APPEND] [APPEND_STRING] 
      PROPERTY <全局属性> [<属性值>...])
  • 示例
scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(global_property)
get_cmake_property(res GENERATOR_IS_MULTI_CONFIG)
message("GENERATOR_IS_MULTI_CONFIG: ${res}")
get_property(res GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG SET)
message("GENERATOR_IS_MULTI_CONFIG is SET: ${res}")

目录属性

  • 目录属性(directory property)即作用于目录(包括子目录)的属性

  • 通常用于为目录中的构建目标统一设置编译选项等构建要求,或者获取目录信息

  • 获取目录属性

    • 这两个命令均用于获取指定<目录><目录属性>,并将其值存入<结果变量>
    • 支持绝对路径或相对于当前源文件目录的相对路径
    • <目录>参数为可选参数,省略它则默认获取当前目录的目录属性
xml 复制代码
get_directory_property(<结果变量> [DIRECTORY <目录>] <目录属性>)
get_property(<结果变量> DIRECTORY [<目录>] PROPERTY <目录属性> 
      [SET|DEFINED|BRIEF_DOCS|FULL_DOCS])
  • 设置目录属性

    • 这两个命令均用于将<目录属性>的值设为<属性值>。第一个命令仅用于设置当前目录的若干目录属性;第二个命令用于设置指定<目录>的一个目录属性,且其中的多个<属性值>会被视为一个列表
xml 复制代码
set_directory_properties(PROPERTIES <目录属性> <属性值> 
      [<目录属性> <属性值>]...)
set_property(DIRECTORY [<目录>] [APPEND] [APPEND_STRING] 
      PROPERTY <目录属性> [<属性值>...])
  • 示例

/CMakeLIsts.txt

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(directory_property)
add_subdirectory(a)
add_subdirectory(b)
# 获取目录属性SUBDIRECTORIES的值到res
get_directory_property(res SUBDIRECTORIES)
message("SUBDIRECTORIES: ${res}")

/main.c

arduino 复制代码
#include <stdio.h>
int main() 
{
    printf("DIR: %s\n", DIR);
    return 0;
}

/a/CMakeLists.txt

css 复制代码
# 设置目录属性COMPILE_DEFINITIONS的值为DIR="a"
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS DIR="a")
add_executable(a ../main.c)

/b/CMakeLists.txt

css 复制代码
# 设置目录属性COMPILE_DEFINITIONS的值为DIR="b"
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS DIR="b")
add_executable(b ../main.c)

目标属性

  • 目标属性(target property)即作用于构建目标的属性,通常用于为构建目标设置构建要求或使用要求,也可以用于获取构建目标信息等

  • 获取目标属性

    • 这两个命令均用于获取关联到<构建目标><目标属性>,并将其值存入<结果变量>
    • 当指定的目标属性未被定义,且该目标属性不具有INHERITED特性(继承特性)时,第一个命令的结果变量会被赋值为"<结果变量>-NOTFOUND",而第二个命令的结果变量会被赋空值.若其具有继承特性,CMake会继续向上层作用域查找同名属性的定义;若仍未找到该属性的定义,则将结果变量赋为空值
xml 复制代码
get_target_property(<结果变量> <构建目标> <目标属性>)
get_property(<结果变量> TARGET [<构建目标>] PROPERTY <目标属性> 
    [SET|DEFINED|BRIEF_DOCS|FULL_DOCS])
  • 设置目标属性

    • 这两个命令均用于将关联到若干<构建目标><目标属性>的值设置为 <属性值>,其中第二个命令中多个<属性值>会被视为一个列表。这些构建目标可以是在不同目录程序中定义的,但不可以是别名目标,因为别名目标是只读的构建目标
xml 复制代码
set_target_properties(<构建目标>...
     PROPERTIES <目标属性> <属性值> [<目标属性> <属性值>]...)
set_property(TARGET [<构建目标>...] [APPEND] [APPEND_STRING] 
   PROPERTY <目标属性> [<属性值>...])
  • 示例

/liba/include/liba.h

arduino 复制代码
const char *fa();

/liba/include/liba.c

arduino 复制代码
#include <liba.h>
const char *fa() { return "liba"; }

/liba/CMakeLists.txt

bash 复制代码
add_library(a STATIC src/liba.c)
# 设置a的目标属性
set_property(TARGET a PROPERTY 
     # 构建要求:将include加入头文件搜索目录
     INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 设置a的目标属性
set_property(TARGET a PROPERTY 
     # 使用要求:将include加入(引用者的)头文件搜索目录
     INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include)

/include/f.h

arduino 复制代码
const char *f() { return "main"; }

/main.c

arduino 复制代码
#include <f.h>
#include <liba.h>
#include <stdio.h>
int main() 
{
     printf("fa: %s\n", fa());
     printf("f: %s\n", f());
     return 0;
}

/CMakeLists.txt

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(target_property)
add_executable(main main.c)
# 设置main的目标属性
set_target_properties(main PROPERTIES 
     # 构建要求:将include加入头文件搜索目录
     INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include
     # 构建要求:链接a库(即传递a库的使用要求到main中作为main的构建要求)
      LINK_LIBRARIES a
    )
add_subdirectory(liba)

源文件属性

  • 源文件属性即作用于源文件的属性,通常用于对源文件构建进行配置,也可以用于获取源文件信息等

  • 获取源文件属性

    • 这两个命令均用于获取位于<文件路径>的源文件的<源文件属性>,并将其值存入<结果变量>
    • 源文件属性的作用域是由源文件路径和某个特定目录共同决定的。在不指定DIRECTORYTARGET_DIRECTORY可选参数的情况下,该命令默认在当前目录中获取源文件属性
xml 复制代码
get_source_file_property(<结果变量> <文件路径> 
     [DIRECTORY <目录>|TARGET_DIRECTORY <构建目标>]
     <源文件属性>)
get_property(<结果变量> SOURCE <文件路径> 
     [DIRECTORY <目录>|TARGET_DIRECTORY <构建目标>] 
     PROPERTY <源文件属性> 
     [SET|DEFINED|BRIEF_DOCS|FULL_DOCS])
  • 设置源文件属性

    • 这两个命令均用于将位于若干<文件路径>的源文件的<源文件属性>的值设置为<属性值>,其中第二个命令中的多个<属性值>会被视为一个列表
xml 复制代码
set_source_files_properties(<文件路径>...
      [DIRECTORY <目录>...]
      [TARGET_DIRECTORY <构建目标>...]
      PROPERTIES <源文件属性> <属性值> [<源文件属性> <属性值>]...)
set_property(SOURCE [<文件路径>...]
     [DIRECTORY <目录>...]
     [TARGET_DIRECTORY <构建目标>...]
     [APPEND] [APPEND_STRING] 
     PROPERTY <源文件属性> [<属性值>...])
  • 示例

/main.c

arduino 复制代码
#include <stdio.h>
int main() 
{
    printf("VERSION: %s\n", VERSION);
    return 0;
}

/a/CMakeLists.txt

css 复制代码
add_executable(a ../main.c)

/b/CMakeLists.txt

css 复制代码
add_executable(b ../main.c)

/CMakeLists.txt

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(source_file_property)
add_subdirectory(a)
add_subdirectory(b)
set_source_files_properties(main.c DIRECTORY a
     PROPERTIES COMPILE_DEFINITIONS VERSION="0.1")
set_source_files_properties(main.c DIRECTORY b
     PROPERTIES COMPILE_DEFINITIONS VERSION="0.2")

缓存变量属性

  • 缓存变量属性即作用于缓存变量的属性。这类属性很少用到,一般用于获取缓存变量的描述文本、类型、枚举值、是否为高级配置等信息

  • 获取缓存变量属性

    • 该命令用于获取与<缓存变量>关联的名为<缓存变量属性>的属性,并将其值存入<结果变量>。若指定属性不存在,则结果变量为空值
xml 复制代码
get_property(<结果变量> CACHE <缓存变量>
        PROPERTY <缓存变量属性> 
        [SET|DEFINED|BRIEF_DOCS|FULL_DOCS])
  • 设置缓存变量属性

    • 该命令用于设置若干<缓存变量><缓存变量属性><属性值>,其中多个<属性值>会被视为一个列表。
css 复制代码
set_property(CACHE [<缓存变量>...]
        [APPEND] [APPEND_STRING] 
        PROPERTY <缓存变量属性> [<属性值>...])
  • 示例

/CMakeLists.txt

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(cache_entry_property)
set(VERSION "" CACHE STRING "")
set_property(CACHE VERSION PROPERTY STRINGS 1.0 2.0)

构建中常用的属性

  • 宏定义构建要求 COMPILE_DEFINITIONS

    • COMPILE_DEFINITIONS属性用于定义编译(预处理)C和C++程序时所用到的宏。
    • 该属性值为列表字符串,每一个元素都代表一个宏定义。其元素格式为<宏名称>或<宏名称>=<值>。
    • COMPILE_DEFINITIONS 属性中定义的宏,在C/C++编译时会被当作预处理器定义处理,代码中可以直接使用这些宏
  • 宏定义使用要求 INTERFACE_COMPILE_DEFINITIONS

    • INTERFACE_COMPILE_DEFINITIONS属性用于定义库目标对使用者的宏定义要求的属性
    • 该属性作为使用要求,会被自动传递给依赖者的构建要求
ini 复制代码
# 设置库目标的 INTERFACE 宏定义
target_compile_definitions(myLib INTERFACE MY_LIB_DEFINE=1)

# 等价于直接设置属性
set_target_properties(myLib PROPERTIES 
    INTERFACE_COMPILE_DEFINITIONS "MY_LIB_DEFINE=1"
)

实际应用场景

ini 复制代码
# 库的版本号传递
add_library(myLib SHARED myLib.cpp)
target_compile_definitions(myLib INTERFACE MYLIB_VERSION=2.0.0)
# 链接到 myLib 的目标会自动获得 MYLIB_VERSION 宏。
  • 编译参数构建要求 COMPILE_OPTIONS

    • COMPILE_OPTIONS属性用于定义作为构建要求的编译参数
    • 该属性值是一个列表字符串
    • 建目标最终的编译参数不仅仅由构建目标自身的COMPILE_OPTIONS属性决定,而是由 CMAKE_<编程语言>_FLAGS变量值、COMPILE_OPTIONS属性值及被依赖库的INTERFACE_ COMPILE_OPTIONS属性传递过来的值共同组成
  • 编译参数使用要求 INTERFACE_COMPILE_OPTIONS

    • NTERFACE_COMPILE_OPTIONS属性用于定义库目标对使用者编译参数的要求
    • 任何构建目标,若链接到具有该属性的库目标,都会在构建时指定相应的编译参数。该属性作为使用要求,会被自动传递给依赖者的构建要求
  • 编译特性构建要求 COMPILE_FEATURES

    • COMPILE_FEATURES属性用于定义作为构建要求的编译特性,常用于指定C和C++语言标准的版本,也可以用于细粒度控制要求的编译特性,如C++的constexpr特性等
  • 编译特性使用要求 INTERFACE_COMPILE_FEATURES

    • INTERFACE_COMPILE_FEATURES属性用于定义库目标对使用者编译特性的要求
  • 头文件目录构建要求 INCLUDE_DIRECTORIES

    • INCLUDE_DIRECTORIES属性用于定义头文件的搜索目录,这些目录必须是绝对路径
  • 头文件目录使用要求 INTERFACE_INCLUDE_DIRECTORIES

    • INTERFACE_INCLUDE_DIRECTORIES属性用于定义库目标对使用者头文件搜索目录的要求
  • 链接库构建要求 LINK_LIBRARIES

    • LINK_LIBRARIES属性用于定义作为构建要求的将要链接的库文件或库目标。如果设置当前构建目标的LINK_LIBRARIES属性值为某个库目标,CMake会将被链接的库目标及其依赖的使用要求递归传递到当前构建目标的构建要求中。同时,若链接的是一般库目标,还会使用链接器链接它构建后生成的库文件到当前构建目标。该属性仅作为目标属性使用
  • 链接库使用要求 INTERFACE_LINK_LIBRARIES

    • INTERFACE_LINK_LIBRARIES属性用于定义库目标对链接库的使用要求
  • 链接目录构建要求 LINK_DIRECTORIES

    • LINK_DIRECTORIES属性用于定义构建时链接器的搜索目录,通常用于设置部分第三方库二进制文件的所在目录
  • 链接目录使用要求 INTERFACE_LINK_DIRECTORIES

    • INTERFACE_LINK_DIRECTORIES属性用于定义库目标对使用者链接器搜索目录的要求
  • 链接参数构建要求 LINK_OPTIONS

    • LINK_OPTIONS属性用于定义链接器参数,作为动态库目标、模块库目标及可执行文件目标的构建要求
  • 链接参数使用要求 INTERFACE_LINK_OPTIONS

    • INTERFACE_LINK_OPTIONS属性用于定义库目标对使用者链接器参数的要求
  • 源文件构建要求 SOURCES

    • SOURCES属性用于定义构建目标需要编译的源文件列表
  • 源文件使用要求 INTERFACE_SOURCES

    • INTERFACE_SOURCES属性用于定义当前构建目标使用者需要编译的源文件

自定义属性:define_property

  • CMake允许通过define_property命令自定义应用于不同作用域的属性,并为其添加文档说明

    • 第一个参数用于指定属性的作用域

      • GLOBAL,即全局作用域;
      • DIRECTORY,即目录作用域;'
      • TARGET,即构建目标作用域;
      • SOURCE,即源文件作用域;
      • CACHED_VARIABLES,即缓存变量作用域;
      • TEST,即测试作用域;
      • VARIABLE,该参数仅用于为变量添加文档说明。
    • NHERITED可选参数用于指定自定义的属性是否会在未被显式设置时,继承上层作用域同一属性的值

javascript 复制代码
define_property(<GLOBAL|DIRECTORY|TARGET|SOURCE|
        CACHED_VARIABLES|TEST|VARIABLE>
        PROPERTY <属性名称> [INHERITED]
        BRIEF_DOCS <简介文档>
        FULL_DOCS <完整文档>)

属性相关命令

  • 上一小节中介绍了很多在构建时常用的属性。这些属性可以针对宏定义、编译选项等各方各面进行配置,而且对于每一方面,都分别会有针对构建要求和使用要求的两种属性
  • 为了简化操作,CMake为大多数常用的属性提供了专门的配置命令
  • 该命令会将对<库文件|库目标>的链接过程作为<构建目标>构建要求使用要求,而具体是作为构建要求还是使用要求,取决于PRIVATE、INTERFACE和PUBLIC参数

    • PRIVATE参数表示将紧跟其后的<库文件|库目标>仅设置为<构建目标>的构建要求。

      • 就像函数内部调用了一个辅助函数,但外部调用者完全不知道这个辅助函数的存在
    • INTERFACE参数表示将紧跟其后的<库文件|库目标>仅设置为<构建目标>的使用要求

      • 就像函数签名中暴露了一个参数类型,但函数体内部并不使用它
    • PUBLIC参数表示将紧跟其后的<库文件|库目标>同时设置为<构建目标>的构建要求和使用要求

      • 就像函数签名和函数体都使用了同一个类型,外部调用者必须知道这个类型
    • 忽略该参数(即使用第一种命令形式)时,默认采用PUBLIC参数的方式

vbnet 复制代码
target_link_libraries(<构建目标> <库文件|库目标>...)
target_link_libraries(<构建目标> 
     <PRIVATE|INTERFACE|PUBLIC> <库文件|库目标>...
     [<PRIVATE|INTERFACE|PUBLIC> <库文件|库目标>...]...
)
  • 示例
scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(a)
# 函数功能 获取目标的链接库和对链接库的使用要求 并打印在终端上
function(print_link_libraries tgt)
     #  获取目标的链接的库目标
     get_target_property(res ${tgt} LINK_LIBRARIES)
     message("${tgt} LINK_LIBRARIES: ${res}")
    # 获取库目标对链接库的使用要求
     get_target_property(res ${tgt} INTERFACE_LINK_LIBRARIES)
     message("${tgt} INTERFACE_LINK_LIBRARIES: ${res}")
endfunction()
# 分别建立两个动态库a b
add_library(a SHARED a.c)
add_library(b SHARED b.c)
# 这里通过MY_LINK_OPTION被设置成为不同的类型 来观察链接后的作用域的不同
target_link_libraries(b ${MY_LINK_OPTION} a)
print_link_libraries(b)
add_executable(c c.c)
# 将b库目标链接到c可执行目标上 并不对外暴露
target_link_libraries(c PRIVATE b)
print_link_libraries(c)

设置宏定义:add_compile_definitions

  • 该命令仅对当前目录及其子目录中的构建目标生效,用于设置编译源文件时的<宏定义>
  • <宏定义>会被加入到当前目录程序的COMPILE_DEFINITIONS目录属性,以及当前目录程序中定义的构建目标的COMPILE_DEFINITIONS目标属性
  • <宏定义>不支持带参数的宏,其格式可为<宏名称><宏名称>=<值>
scss 复制代码
add_compile_definitions(<宏定义>...)

设置目标宏定义:target_compile_definitions

  • 该命令用于设置编译指定<构建目标>的源文件时的<宏定义>
  • 调用该命令后,<构建目标>COMPILE_DEFINITIONS属性中会加入PRIVATEPUBLIC参数后的<宏定义>
  • <构建目标>INTERFACE_COMPILE_DEFINITIONS属性中会加入PUBLICINTERFACE参数后的<宏定义>
vbnet 复制代码
target_compile_definitions(<构建目标> 
  <PRIVATE|INTERFACE|PUBLIC> <宏定义>...
    [<PRIVATE|INTERFACE|PUBLIC> <宏定义>...]...
)

设置编译参数:add_compile_options

  • 该命令仅对当前目录及其子目录中的构建目标生效,用于设置编译源文件时的<编译参数>
  • 调用该命令后,<编译参数>会被加入到当前目录程序的COMPILE_OPTIONS目录属性,以及当前目录程序中定义的构建目标的COMPILE_OPTIONS目标属性中
scss 复制代码
add_compile_options(<编译参数>...)

设置目标编译参数:target_compile_options

  • 该命令用于设置编译指定<构建目标>的源文件时的<编译参数>
  • 调用该命令后,<构建目标>COMPILE_OPTIONS属性中会加入PRIVATEPUBLIC参数后的<编译参数><构建目标>INTERFACE_COMPILE_OPTIONS属性中会加入PUBLICINTERFACE参数后的<编译参数>
vbnet 复制代码
target_compile_options(<构建目标> 
     <PRIVATE|INTERFACE|PUBLIC> <编译参数>...
     [<PRIVATE|INTERFACE|PUBLIC> <编译参数>...]...
)

设置目标编译特性:target_compile_features

  • 该命令用于设置编译指定<构建目标>的源文件时所需的<编译特性>,CMake会自动设置相应的编译参数
  • <编译特性>必须是在CMake变量CMAKE_<编程语言>_COMPILE_ FEATURES中存在的值
  • 调用该命令后,<构建目标>COMPILE_FEATURES属性中会加入PRIVATEPUBLIC参数后的<编译特性><构建目标>INTERFACE_COMPILE_FEATURES属性中会加入PUBLICINTERFACE参数后的<编译特性>
vbnet 复制代码
target_compile_features(<构建目标> 
     <PRIVATE|INTERFACE|PUBLIC> <编译特性>...
     [<PRIVATE|INTERFACE|PUBLIC> <编译特性>...]...
)

设置头文件目录:include_directories

  • 该命令仅对当前目录及其子目录中的构建目标生效,用于将<目录>设置为构建目标的头文件搜索目录。<目录>可以是绝对路径或相对于当前源文件目录的相对路径
  • 调用该命令后,<目录>会被同时加入到当前目录程序的INLCUDE_DIRECTORIES目录属性,以及当前目录程序中定义的构建目标的INLCUDE_DIRECTORIES目标属性中
css 复制代码
include_directories([AFTER|BEFORE] [SYSTEM] <目录>...)

设置目标头文件目录: target_include_directories

  • 该命令用于将<目录>加入到<构建目标>的头文件搜索目录列表中。<目录>可以是绝对路径或相对于当前源文件目录的相对路径。其他参数与include_directories命令中的对应参数功能一致
  • 调用该命令后,<构建目标>INCLUDE_DIRECTORIES属性中会加入PRIVATEPUBLIC参数后的<目录><构建目标>INTERFACE_INCLUDE_DIRECTORIES属性中会加入PUBLICINTERFACE参数后的<目录>
vbnet 复制代码
target_include_directories(<构建目标> 
     [SYSTEM] [AFTER|BEFORE]
     <PRIVATE|INTERFACE|PUBLIC> <目录>...
     [<PRIVATE|INTERFACE|PUBLIC> <目录>...]...
)
  • 该命令仅对当前目录及其子目录中的构建目标生效,用于将<库文件或库目标>链接到构建目标中。只有在该命令调用之后创建的构建目标会被设置链接库属性。
scss 复制代码
link_libraries([库文件或库目标]...)
  • 该命令仅对当前目录及其子目录中的构建目标生效,用于将<目录>设置为构建目标链接库搜索目录<目录>可以是绝对路径或相对于当前源文件目录的相对路径
  • 调用该命令后,<目录>会被加入当前目录程序的LINK_DIRECTORIES目录属性,以及当前目录程序中定义的构建目标的LINK_DIRECTORIES目标属性
scss 复制代码
link_directories([AFTER|BEFORE] <目录>...)
  • 该命令用于将<目录>设置为<构建目标>的链接库搜索目录。<目录>可以是绝对路径或相对于当前源文件目录的相对路径
  • 调用该命令后,<构建目标>LINK_DIRECTORIES属性中会加入PRIVATEPUBLIC参数后的<目录><构建目标>INTERFACE_LINK_DIRECTORIES属性中会加入PUBLICINTERFACE参数后的<目录>
vbnet 复制代码
target_link_directories(<构建目标> 
     [BEFORE]
     <PRIVATE|INTERFACE|PUBLIC> <目录>...
     [<PRIVATE|INTERFACE|PUBLIC> <目录>...]...
)
  • 该命令仅对当前目录及其子目录中的构建目标生效,用于设置链接构建目标时所需的<链接参数>
  • 调用该命令后,<链接参数>会被加入当前目录程序的LINK_OPTIONS目录属性及当前目录程序中定义的构建目标的LINK_OPTIONS目标属性
scss 复制代码
add_link_options(<链接参数>...)
  • 该命令用于设置链接指定的<构建目标>时所需的<链接参数>
  • 调用该命令后,<构建目标>LINK_OPTIONS属性中会加入PRIVATEPUBLIC参数后的<链接参数><构建目标>INTERFACE_LINK_OPTIONS属性中会加入PUBLICINTERFACE参数后的<链接参数>
vbnet 复制代码
target_link_options(<构建目标> [BEFORE]
     <PRIVATE|INTERFACE|PUBLIC> <链接参数>...
     [<PRIVATE|INTERFACE|PUBLIC> <链接参数>...]...
)

设置目标源文件:target_sources

  • 该命令用于设置构建指定的<构建目标>时所需的<源文件><源文件>可以是绝对路径或相对于当前源文件目录的相对路径
  • 调用该命令后,<构建目标>SOURCES属性中会加入PRIVATEPUBLIC参数后的 <链接参数><构建目标>INTERFACE_SOURCES属性中会加入PUBLICINTERFACE参数后的<链接参数>
vbnet 复制代码
target_sources(<构建目标>
     <PRIVATE|INTERFACE|PUBLIC> <源文件>...
     [<PRIVATE|INTERFACE|PUBLIC> <源文件>...]...
)

无须递归传递的例程

/CMakeLists.txt

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(no-transition)
add_library(A STATIC "../../ch002/无须传递/liba/a.cpp")
target_include_directories(A PUBLIC "../../ch002/无须传递/liba")
add_library(B STATIC "../../ch002/无须传递/libb/b.cpp")
target_include_directories(B PUBLIC "../../ch002/无须传递/libb")
target_link_libraries(B PRIVATE A)
add_executable(main "../../ch002/无须传递/main.cpp")
target_link_libraries(main PRIVATE B)

存在间接引用的例程

/CMakeLists.txt

scss 复制代码
cmake_minimum_required(VERSION 3.20)
project(transitive-usage-for-indirect-reference)
add_library(A STATIC "../../ch002/间接引用/liba/a.cpp")
target_include_directories(A PUBLIC "../../ch002/间接引用/liba")
add_library(B STATIC "../../ch002/间接引用/libb/b.cpp")
target_include_directories(B PUBLIC "../../ch002/间接引用/libb")
target_link_libraries(B PUBLIC A)
add_executable(main "../../ch002/间接引用/main.cpp")
target_link_libraries(main PRIVATE B)

自定义构建规则:add_custom_command

生成文件

css 复制代码
add_custom_command(OUTPUT <生成文件1> [<生成文件2>...]
        COMMAND <命令1> [ARGS] [<命令行参数>...]
        [COMMAND <命令2> [ARGS] [<命令行参数>...] ...]
        [BYPRODUCTS [<副产品文件>...]]
        [MAIN_DEPENDENCY <主依赖文件>]
        [DEPENDS [<依赖文件>...]]
        [IMPLICIT_DEPENDS <编程语言1> <隐式依赖文件>            [<编程语言2> <隐式依赖文件>] ...]
        [DEPFILE <依赖清单文件>]
        [WORKING_DIRECTORY <工作目录>]
        [COMMENT <注释>]
        [VERBATIM] [APPEND] 
        [COMMAND_EXPAND_LISTS]
        [JOB_POOL <Ninja构建工具的Job Pool>]
        [USES_TERMINAL]
    )
  • 该命令用于将一系列<命令>定义为生成若干<生成文件>的生成规则。当<生成文件>已存在,且<依赖文件>未发生改变时,该生成规则不会重复执行
  • 该命令仅用于定义如何生成文件,但生成规则对应的命令并不会立即执行,因此文件也不会在命令调用后立即生成

响应构建事件

css 复制代码
add_custom_command(TARGET <构建目标>
	    PRE_BUILD | PRE_LINK | POST_BUILD
	    COMMAND <命令1> [ARGS] [<命令行参数>...]
	    [COMMAND <命令2> [ARGS] [<命令行参数>...] ...]
	    [BYPRODUCTS [<副产品文件>...]]
	    [WORKING_DIRECTORY <工作目录>]
	    [COMMENT <注释>]
	    [VERBATIM] [COMMAND_EXPAND_LISTS]
	    [USES_TERMINAL]    
	)
  • 该命令用于将一系列<命令>声明为当<构建目标>的指定构建事件触发时需要执行的自定义构建规则

    • PRE_BUILD事件,即构建前事件。对于Visual Studio构建系统生成器而言,它会在任何<构建目标>的构建规则执行前触发;对于其他生成器而言,它会在源文件编译之后、PRE_LINK事件之前触发
    • PRE_LINK事件,即链接前事件。它会在源文件编译之后,目标文件被链接之前(对于静态库而言则是归档打包工具被调用之前)触发。该事件要求<构建目标>不能是自定义构建目标
    • POST_BUILD事件,即构建后事件。它会在<构建目标>的所有构建规则执行后触发
  • 示例

bash 复制代码
cmake_minimum_required(VERSION 3.20)
project(PRE_BUILD_vs_PRE_LINK)
add_executable(main main.c)
# 在main目标开始构建前 
# 使用 CMake 内置命令 echo 打印字符串 "run PRE_BUILD" 到构建日志
add_custom_command(TARGET main PRE_BUILD
	  COMMAND ${CMAKE_COMMAND} -E echo "run PRE_BUILD"
	  VERBATIM)
# 在 main 目标的所有源文件编译完成之后、链接器开始之前执行
# 使用 CMake 的 echo 命令输出提示信息
add_custom_command(TARGET main PRE_LINK
	  COMMAND ${CMAKE_COMMAND} -E echo "run PRE_LINK"
	  VERBATIM)
# 在 main 目标构建完成(包括链接成功)后执行
# 使用 CMake 的 echo 命令输出提示信息
add_custom_command(TARGET main POST_BUILD
	  COMMAND ${CMAKE_COMMAND} -E echo "run POST_BUILD"
	  VERBATIM)

自定义构建目标:add_custom_target

css 复制代码
add_custom_target(<构建目标名称> [ALL]
	    [<命令1> [<命令行参数>...]]
	    [COMMAND <命令2> [<命令行参数>...] ...]
	    [DEPENDS [<依赖文件>...]]
	    [BYPRODUCTS [<副产品文件>...]]
	    [WORKING_DIRECTORY <工作目录>]
	    [COMMENT <注释>]
	    [JOB_POOL <Ninja构建工具的Job Pool>]
	    [VERBATIM] [COMMAND_EXPAND_LISTS]
	    [USES_TERMINAL]    
	    [SOURCES <源文件>...]
	)

设置依赖关系:add_dependencies

scss 复制代码
add_dependencies(<构建目标> [<依赖的构建目标>]...)

小结

  • 现代CMake,开创了面向目标的构建配置时代,面向目标的属性设置能够将构建目标的配置相互解耦,是现代CMake推崇的做法

本文使用 markdown.com.cn 排版

相关推荐
用户3979678611163 天前
CMake常用指令-string
cmake
Shadow(⊙o⊙)19 天前
CMake实现客户端、服务端交流
组件·cmake
繁重的秋春22 天前
Qt6.0下的CMake的部署配置
qt·cmake
Lee_jerome1 个月前
《C/C++编译全家桶:g++四阶段、.a与.so区别、CMake构建、ARM交叉编译,这篇全讲透了》
c语言·开发语言·arm开发·c++·编译·cmake·cmakelists
开发者联盟league1 个月前
cmake 创建和使用静态库(极简版)
c++·cmake·cpp
开发者联盟league1 个月前
CMake 实战:创建并使用静态库(MinGW + clangd)
cmake·cpp
Javenwww1 个月前
CMake入门——基本语法规则
cmake
郝学胜-神的一滴1 个月前
中级OpenGL教程 023:Assimp模型加载全解——从源码到架构的骈文探秘
c++·unity·游戏引擎·cmake·unreal engine·opengl
橙色阳光五月天1 个月前
CMake 构建配置文件解析
c++·cmake·plugin