CMake实用知识点之一

1 Q&A

  • Q : 在C++代码工程中,有**.h.in后缀的文件,有什么用?里面有这样的代码:#cmakedefine CROSS_COMPILE又是有什么作用?

  • A :

    在C++代码工程中,带有 .h.in 后缀的文件通常是 CMake 配置文件模板,用于生成配置头文件。这些文件通常包含一些占位符或宏定义,这些占位符或宏定义会在配置过程中被替换为实际的值。

    • .h.in 文件的用途

      • 模板文件:.h.in 文件是模板文件,其中包含占位符或预定义的宏。这些占位符将在 CMake 配置过程中被替换为实际的值,从而生成实际的头文件(通常是 .h 文件)。
      • 配置时间定义:这些文件允许在 CMake 配置时根据当前的配置选项、系统属性等生成特定的宏定义。这使得生成的头文件能够反映配置过程中的条件和选项。
    • cmakedefine 的作用

      • 宏替换:#cmakedefine 是 CMake 特定的指令,用于在配置过程中生成特定的宏定义。CMake 会根据 CMakeLists.txt 中的 configure_file 命令将这些指令替换为实际的宏定义或注释掉。
    • 示例解释

      假设有一个 config.h.in 文件:

      /* config.h.in - template for config.h */
      
      #cmakedefine CROSS_COMPILE
      #cmakedefine HAVE_LIBJPEG
      #cmakedefine VERSION "@PROJECT_VERSION@"
      

      在 CMakeLists.txt 中,你可能有以下配置:

      # 设置一些选项或检查一些条件
      set(CROSS_COMPILE 1)
      set(HAVE_LIBJPEG 1)
      set(PROJECT_VERSION "1.0.0")
      
      # 生成 config.h 文件
      configure_file(
        "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
        "${CMAKE_CURRENT_BINARY_DIR}/config.h"
        @ONLY
      )
      

      在 configure_file 命令执行后,CMake 会将 config.h.in 中的 #cmakedefine 指令替换为相应的宏定义,并替换占位符为实际的值,生成的 config.h 文件可能是这样的

      /* config.h - generated from config.h.in by CMake */
      
      #define CROSS_COMPILE 1
      #define HAVE_LIBJPEG 1
      #define VERSION "1.0.0"
      
    • 更详细的解释

      • #cmakedefine:在配置过程中,如果 CROSS_COMPILE 被定义了(即在 CMakeLists.txt 中调用了 set(CROSS_COMPILE 1)),那么 #cmakedefine CROSS_COMPILE 将被替换为 #define CROSS_COMPILE 1。如果 CROSS_COMPILE 没有被定义,则 #cmakedefine CROSS_COMPILE 行会被注释掉。

      • @PROJECT_VERSION@:在 configure_file 中,@PROJECT_VERSION@ 会被替换为实际的项目版本字符串 1.0.0。

    • 配置示例

      假设有以下 CMakeLists.txt 文件:

      cmake_minimum_required(VERSION 3.10)
      
      project(MyProject VERSION 1.0.0)
      
      # 设置一些选项
      option(CROSS_COMPILE "Enable cross compiling" ON)
      option(HAVE_LIBJPEG "Enable JPEG support" ON)
      
      # 生成 config.h 文件
      configure_file(
        "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
        "${CMAKE_CURRENT_BINARY_DIR}/config.h"
        @ONLY
      )
      

      配置并生成项目后,CMake 会将这些指令替换为实际的宏定义,从而生成最终的 config.h 文件。

    • 总结

      带有 .h.in 后缀的文件是 CMake 用来生成配置头文件的模板文件。它们允许根据项目配置和系统属性生成特定的宏定义。#cmakedefine 指令用于在配置过程中生成这些宏定义,并根据条件将其替换为实际的值或注释掉。通过这种方式,CMake 可以灵活地管理配置时间的条件和选项,从而生成合适的头文件

  • Q:CMAKE_FIND_ROOT_PATH有什么用?

  • A:

    在交叉编译中,只有设置了该宏,才可以让gdb自动到该目录下去寻找相关的g++g--,之后只需要CAMKE_BUILD_TYPECMAKE_CXX_FLAGS_RELEASE就可实现同一份代码不同的平台下的编译;

    CMAKE_FIND_ROOT_PATH 是一个 CMake 变量,用于设置根路径(root paths),这些路径将被用于搜索依赖项、库、头文件和可执行文件。它通常用于交叉编译环境中,帮助指定搜索路径的优先级和位置。

    • 主要用途

      • 交叉编译:在交叉编译环境中,目标平台的库和头文件通常位于不同的目录中。使用 CMAKE_FIND_ROOT_PATH 可以帮助 CMake 在这些特定路径中查找依赖项,而不是在主机系统的默认路径中查找。
      • 控制查找顺序:通过配置 CMAKE_FIND_ROOT_PATH_MODE_* 变量(如 CMAKE_FIND_ROOT_PATH_MODE_LIBRARY,CMAKE_FIND_ROOT_PATH_MODE_INCLUDE,CMAKE_FIND_ROOT_PATH_MODE_PROGRAM),可以控制在查找库、头文件和可执行文件时是否使用 CMAKE_FIND_ROOT_PATH 变量。
    • 使用方法

      设置 CMAKE_FIND_ROOT_PATH

      可以在命令行或 CMakeLists.txt 文件中设置 CMAKE_FIND_ROOT_PATH:

      cmake -DCMAKE_FIND_ROOT_PATH=/path/to/sysroot ..
      

      或者在 CMakeLists.txt 文件中:

      set(CMAKE_FIND_ROOT_PATH /path/to/sysroot)
      
    • 控制查找模式

      可以通过设置 CMAKE_FIND_ROOT_PATH_MODE_* 变量来控制查找行为。这些变量的值可以是 ONLY,NEVER,或 BOTH。

      • ONLY:仅在 CMAKE_FIND_ROOT_PATH 中查找。
      • NEVER:不在 CMAKE_FIND_ROOT_PATH 中查找,只在默认路径中查找。
      • BOTH:在 CMAKE_FIND_ROOT_PATH 和默认路径中查找。
    • 示例

      # 仅在 CMAKE_FIND_ROOT_PATH 中查找库
      set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
      
      # 在 CMAKE_FIND_ROOT_PATH 和默认路径中查找头文件
      set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
      
      # 不在 CMAKE_FIND_ROOT_PATH 中查找可执行文件,只在默认路径中查找
      set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
      
  • Q:如何在交叉编译时指定编译器呢?

  • A:

    设置如下的宏变量可以指定交叉编译时所要使用的编译器:

    • CMAKE_C_COMPILER: 指定C编译器
    • CMAKE_CXX_COMPILER: 指定C++编译器

    而对于交叉编译,尤其重要的是指定平台,以ARM为例,需要指定如下:

    • CMAKE_SYSTEM_NAME: set(CMAKE_SYSTEM_NAME Linux)
    • CMAKE_SYSTEM_PROCESSOER: set(CMAKE_SYSTEM_PROCESSOR arm)

2 Knowledge point

  • **.cmake: 称为工具链文件

  • CMAKE_CXX_COMPILER_ARG1:是 CMake 的一个内部变量,用于指定传递给 C++ 编译器的第一个命令行参数。

    在大多数情况下,不会直接使用或设置这个变量。它主要用于特殊情况下,尤其是涉及某些编译器或工具链时,CMake 可能需要为编译器命令行添加特定的参数。

    通常情况下,CMAKE_CXX_COMPILER_ARG1 不是用户直接配置的变量。更可能会通过工具链文件或高级构建脚本来间接设置它,如下示例:

    • 示例

      假设你需要为你的 C++ 编译器添加一个特殊的参数,例如 -foo,你可以在你的工具链文件(toolchain.cmake)中设置这个变量:

      # toolchain.cmake,当前工具链的名字
      
      # 设置 C++ 编译器
      set(CMAKE_CXX_COMPILER /usr/bin/g++)
      
      # 设置第一个参数
      set(CMAKE_CXX_COMPILER_ARG1 -foo) # 该变量是在工具链中所使用的
      

      然后在命令行中使用该工具链文件:

      cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake ..

    • 注意事项

      • 慎重使用:CMAKE_CXX_COMPILER_ARG1 是一个内部变量,通常仅用于特定的高级配置或工具链文件。直接修改或使用它可能会导致意想不到的行为或构建错误。
      • 工具链文件:在工具链文件中设置该变量是最常见的用法,因为工具链文件通常用于配置交叉编译环境或其他复杂的编译设置。
      • 调试和验证:如果你必须使用这个变量,确保通过 CMake 输出(例如,cmake --trace 或检查生成的构建文件)验证编译器命令行参数的正确性。
    • 结论:

      CMAKE_CXX_COMPILER_ARG1 是一个用于传递给 C++ 编译器的第一个命令行参数的内部变量。它主要用于特殊配置或工具链文件中,帮助确保编译器以特定方式运行。对于大多数用户和常规项目配置,不需要直接使用或修改这个变量。如果确实需要,请谨慎操作,并在工具链文件中进行适当的设置。

  • 条件判断语句

    if (condition)
      ...
    else()
      ...
    endif()
    
  • set解释

    SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE)
    
    • 设置构建类型:
      SET(CMAKE_BUILD_TYPE Release ...) 这部分代码将CMAKE_BUILD_TYPE变量设置为Release。这意味着当项目被构建时,默认会生成优化过的二进制文件,以便在生产环境中使用,而不是包含调试信息的版本。

    • CACHE STRING
      表明CMAKE_BUILD_TYPE变量被添加到CMake的缓存中,并且它的类型是字符串。这意味着一旦设置,它将在CMake的缓存中保留其值,直到显式更改。这对于在多次运行CMake时保持一致的构建配置非常有用。

    • FORCE
      关键字表示即使CMAKE_BUILD_TYPE变量已经在CMake缓存中有了值,也会强制使用这里指定的新值(即Release)。这在你想要确保构建类型总是被设置为特定值,而不受之前CMake运行的影响时非常有用。

  • set_target_properties:设置目标的一些属性来改变它们构建的方式,如设置输出的名称,对于动态库,还可以用来指定动态库版本和API版本。我能够使用任何你想要的属性/值对,并且在随后的代码中调用get_target_propertyget_target命令取出属性的值。

  • add_library: 用于生成动态库或静态库

    • 静态库:静态库没有版本号

      目标是hello_static, 源代码是${LIBHELLO_SRC}

      add_library(hello_static STATIC ${LIBHELLO_SRC})

    • 动态库:按照规定,动态库包含版本号

      • VERSION:指代动态库版本,

      • SOVERSION:指代API版本

      • 示例:

        # 要注意到这里的使用带s的set_target_properties, 那么参数中也要使用properties,是一一对应关系
        set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
        
  • get_target_property: 获取某一个目标文件的属性信息,如下所示

    # 将当前的目标文件hello_static,生成时修改为自己想要的名字hello,这里的关键key是OUTPUT_NAME
    # 还有就是,原先的hello_static名字对应库文件或可执行文件会被CMake清理,而只保留一个hello对应的可执行文件
    set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
    
    # 获取目标文件hello_static的输出文件名,这里的OUTPUT_VALUE是一个变量
    get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME)
    
  • install:需要将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用

    1. 将hello的共享库安装到<prefix>/lib目录;
    2. hello.h安装到<prefix>/include/hello目录;

    示例代码如下所示,注意这里是TARGETSS,表示有多个目标文件,目标文件和输出文件是两个不同的概念,注意体会。还有就是FILES是指非可执行文件,就是普通的源代码。而DESTINATION后边跟着的是目的地文件夹。

    • LIBRARY:表示so动态库文件;

    • ARCHIVE: 表示lib静态库文件;

      install(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
      install(FILES hello.h DESTINATION include/hello)
      
  • 可以指定so生成的位置,有两种方法

    1. 在主工程文件CMakeLists.txt中修改add_subdirectory(lib)指令来指定一个编译输出位置;
    2. 或者在lib/CMakeLists.txt中添加set(LIBRARY_OUTPUT_PATH <路径>)来指定一个新的位置;
  • 整体的CMakeLists.txt如下所示:

    SET (LIBHELLO_SRC hello.cpp)
    # SET (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    # 添加动态库,关键词为shared,不需要写全称libhello.so,
    # 只需要填写hello即可,cmake系统会自动为你生成 libhello.X
    
    ADD_LIBRARY (hello SHARED ${LIBHELLO_SRC})
    
    # 添加静态库,关键词为static,ADD_LIBRARY (hello STATIC ${LIBHELLO_SRC})
    # 仍然用hello作为target名时,是不能成功创建所需的静态库的,
    # 因为hello作为一个target是不能重名的, 故把上面的hello修改为hello_static
    # 同理,我不需要写全libhello_static.a
    # 只需要填写hello即可,cmake系统会自动为我生成 libhello_static.X
    ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC})
    
    # 按照一般的习惯,静态库名字跟动态库名字应该是一致的,只是扩展名不同;
    # 即:静态库名为 libhello.a; 动态库名为libhello.so ;
    # 所以,希望 "hello_static" 在输出时,不是"hello_static",而是以"hello"的名字显示,故设置如下:
    SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")
    GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME)
    MESSAGE (STATUS "This is the hello_static OUTPUT_NAME: " ${OUTPUT_VALUE})
    
    # cmake在构建一个新的target时,会尝试清理掉其他使用这个名字的库,
    # 因此,在构建libhello.a时,就会清理掉libhello.so.
    # 为了回避这个问题,比如再次使用SET_TARGET_PROPERTIES定义 CLEAN_DIRECT_OUTPUT属性。
    SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    
    # 按照规则,动态库是应该包含一个版本号的,
    # VERSION指代动态库版本,SOVERSION指代API版本。
    
    SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)
    
    # 我们需要将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用,
    # 在本例中我们将hello的共享库安装到<prefix>/lib目录;
    # 将hello.h安装<prefix>/include/hello目录。
    
    INSTALL (TARGETS hello hello_static LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib)
    INSTALL (FILES hello.h DESTINATION include/hello)
    
  • set_target_properties有哪些key?

    是 CMake 中的一个命令,用于设置特定目标(例如可执行文件、库等)的属性。以下是一些常见的 key 属性及其含义:

    4 + 2 + 2 + 2 + 1

    • ARCHIVE_OUTPUT_DIRECTORY:指定目标的归档文件(静态库)的输出目录。

    • LIBRARY_OUTPUT_DIRECTORY: 指定目标的库文件(共享库、模块库等)的输出目录。

    • RUNTIME_OUTPUT_DIRECTORY: 指定目标的运行时文件(可执行文件)的输出目录。

    • OUTPUT_NAME:指定目标生成的文件的名称,而不是使用默认的目标名称。

    • VERSION:设置目标的版本号(通常用于共享库)。

    • SOVERSION:设置共享库的接口版本号。

    • COMPILE_FLAGS:设置编译器标志(flags)。如:-std=c++0x是指C++11,是针对编译器而言的设置,是控制编译的不同行为的开关

    • COMPILE_DEFINITIONS: 为目标添加编译器定义(如宏定义)

    • LINK_FLAGS:设置链接器标志(flags)。比如

      • LINK_FLAGS "-Wl,-zdefs"
        • -Wl:告诉编译器将后面的选项传递给链接器,是GCC和Clang常用前缀,用于将选项传递给链接器
        • -z defs:表示连接器在链接时检查所有符号,确保所有符号在链接时得到解析。若有任何未定义的符号,链接过程将失败并报告错误
    • INCLUDE_DIRECTORIES: 为目标添加包含目录。

    • INSTALL_RPATH: 设置目标在安装后的运行时搜索库的路径(即 RPATH)。

    • CLEAR_DIRECT_OUTPUT: 设置为 1 时,CMake 会在生成新的目标输出文件之前清除以前生成的同名文件。这对于避免冲突和确保生成的文件是最新的非常有用。

    以下是一个使用 set_target_properties 的简单示例:

    add_executable(myapp main.cpp)
    set_target_properties(myapp PROPERTIES
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
        OUTPUT_NAME "my_application"
        VERSION "1.0.0"
        SOVERSION "1"
        COMPILE_FLAGS "-Wall"
    )
    
    • 详细介绍
      • COMPILE_DEFINITIONS: 为目标添加编译器定义(如宏定义),就是在开始编译时,源代码中添加相应的宏。这样在源代码中,就可以使用预处理命令#ifdef #endif,来选择性的打开某些我们关心的代码,示例如下:

        以下是一个简单的示例 CMakeLists.txt 文件,展示了如何使用 COMPILE_DEFINITIONS 属性:

        cmake_minimum_required(VERSION 3.0)
        project(MyProject)
        
        # 添加一个可执行文件
        add_executable(myapp main.cpp)
        
        # 设置编译器定义
        target_compile_definitions(myapp PRIVATE
            DEBUG_MODE                # 定义一个名为 DEBUG_MODE 的宏
            VERSION_NUMBER=123        # 定义一个名为 VERSION_NUMBER 的宏,并设定其值为 123
            USE_FEATURE_X            # 定义一个名为 USE_FEATURE_X 的宏,没有赋值,相当于 #define USE_FEATURE_X
        )
        
        # 另一种设置方式,直接使用 set_target_properties,注意它们之间使用分号隔开
        # set_target_properties(myapp PROPERTIES
        #     # 就是对应Visual Studio中项目属性页中配置属性/C/C++/预处理器中的预处理器定义,由于可以有很多,所有使用S结尾
        #     COMPILE_DEFINITIONS "DEBUG_MODE;VERSION_NUMBER=123;USE_FEATURE_X" 
        # )
        
      • INSTALL_RPATH: 设置目标在安装后的运行时搜索库的路径(即 RPATH)。

        用途: 当我安装一个目标(例如共享库或可执行文件)时,RPATH用于指定该目标在运行时如何找到它所依赖的共享库。

        假设有一个目标 myapp,它依赖于某些安装在特定目录(例如 /usr/local/lib)中的库。我就可以使用 INSTALL_RPATH 属性来指定安装后的运行路径

        add_executable(myapp main.cpp)
        set_target_properties(myapp PROPERTIES
          INSTALL_RPATH "/usr/local/lib"
        )
        
  • CMAKE_INSTALL_PREFIX:宏展开之后为/usr/local,该目录通常存放本地安装的软件和文件


  • INSTALL: 该函数用于指定项目安装的目标和安装路径,其中

    • RUNTIME:指定可执行文件的安装路径。与其他关键字(如 LIBRARYARCHIVE)一起使用,可以分别为可执行文件、共享库和静态库指定安装路径。

      • 若value指定为绝对路径,就会用使用到/usr/local
      • 若value指定为相对路径,就会使用了${CMAKE_INSTALL_PREFIX}, 即安装在/usr/local
    • LIBRARY:指定共享库文件安装路径。

    • ARCHIVE:指定静态库指定安装路径。

    • EXPORT: 导出目标及其属性,以便其它项目能够使用 find_package 导入这些目标,注意这里是导入库了,库中可以有很多静态库或动态库

      以上三种二进制文件都是目标文件,所以统一用TARGETS来表示

    INSTALL 中的 EXPORT 属性

    • 作用: 导出目标及其属性,以便其他项目能够使用 find_package 导入这些目标。

    • 用途: 使项目的构建配置可以被其他项目重用,简化依赖管理和集成过程。

    • 使用示例

      假设我有一个库项目,并希望其它项目能够使用这个库,可以这样设置

        cmake_minimum_required(VERSION 3.10)
        project(MyLibraryProject)
      
        # 创建一个库
        add_library(mylib mylib.cpp)
      
        # 定义安装目标
        install(TARGETS mylib
            EXPORT MyLibraryTargets
            LIBRARY DESTINATION lib
            ARCHIVE DESTINATION lib
            RUNTIME DESTINATION bin
            INCLUDES DESTINATION include
        )
      
        # 安装头文件
        install(FILES mylib.h DESTINATION include)
      
        # 安装导出文件
        install(EXPORT MyLibraryTargets
            FILE MyLibraryTargets.cmake
            DESTINATION lib/cmake/MyLibrary
        )
      

      在这个示例中:

      • add_library(mylib mylib.cpp): 创建了一个库 mylib。
      • install(TARGETS mylib ... EXPORT MyLibraryTargets ...): 指定安装目标,并将mylib导出为 MyLibraryTargets。
      • install(EXPORT MyLibraryTargets ...): 安装导出文件 MyLibraryTargets.cmake,这个文件包含了mylib的安装信息。

      生成导出文件

      生成的 MyLibraryTargets.cmake 文件包含了关于 mylib 的安装路径和其他相关信息,使得其他项目可以通过 find_package 轻松导入 mylib。

      在其他项目中使用导出的目标

      假设有一个新的项目想要使用导出的 mylib 库,可以这样写:

        cmake_minimum_required(VERSION 3.10)
        project(MyAppProject)
      
        # 查找安装的库,注意:安装的库和链接导入的库是两个不同的概念,安装的库通过`install(... export MyLib)`来完成,并生成对象的MyLib.cmake文件,find_package通过该文件找到安装的库名,并找到该库名下的静态库文件
        find_package(MyLibrary REQUIRED PATHS /path/to/installed/lib/cmake/MyLibrary)
      
        # 创建可执行文件,并链接导入的库
        add_executable(myapp main.cpp)
        target_link_libraries(myapp PRIVATE mylib)
      

      在这个示例中:

      • find_package(MyLibrary REQUIRED PATHS /path/to/installed/lib/cmake/MyLibrary): 查找并导入 MyLibrary。
      • target_link_libraries(myapp PRIVATE mylib): 链接导入的 mylib 库。

      优势:

      • 简化依赖管理: 通过导出和导入目标,可以轻松管理项目之间的依赖关系,而不需要手动指定库的路径和其他细节。
      • 提高可重用性: 项目的构建配置可以被其他项目重用,减少重复工作。
      • 增强集成性: 使用 EXPORT 属性,可以更好地集成多个项目,特别是当这些项目需要共享库或其他目标时。

      总结:

      INSTALL 命令中的 EXPORT 属性用于导出目标的安装信息。

      导出的信息可以生成一个 CMake 脚本文件,使其他项目可以通过 find_package 导入这些目标。

      这种机制简化了依赖管理,提高了项目的可重用性和集成性


使用 RUNTIME 关键字的示例

假设我有一个可执行文件 myapp 和一个库 mylib,我希望在安装过程中将它们分别安装到不同的目录中,如下所示:

  cmake_minimum_required(VERSION 3.10)
  project(MyProject)

  # 创建库
  add_library(mylib SHARED mylib.cpp)

  # 创建可执行文件
  add_executable(myapp main.cpp)

  # 链接库
  target_link_libraries(myapp PRIVATE mylib)

  # 安装库文件
  install(TARGETS mylib
      LIBRARY DESTINATION lib
  )

  # 安装可执行文件
  install(TARGETS myapp
      RUNTIME DESTINATION bin
  )
  • 文件说明:

    • RUNTIME: 指定安装可执行文件(如 .exe、.bin、.dll 文件)的路径。主要用于安装可运行的二进制文件。
    • LIBRARY: 指定安装动态库(如 .so 文件)的路径。主要用于安装动态链接库。
    • ARCHIVE: 指定安装静态库(如 .a 文件)的路径。主要用于安装静态链接库。

    以下是一个综合的例子:

    cmake_minimum_required(VERSION 3.10)
    project(MyProject)
    
    # 创建静态库
    add_library(mylib_static STATIC mylib_static.cpp)
    
    # 创建动态库
    add_library(mylib_shared SHARED mylib_shared.cpp)
    
    # 创建可执行文件
    add_executable(myapp main.cpp)
    
    # 链接动态库
    target_link_libraries(myapp PRIVATE mylib_shared)
    
    # 安装静态库
    install(TARGETS mylib_static
        ARCHIVE DESTINATION lib/static
    )
    
    # 安装动态库
    install(TARGETS mylib_shared
        LIBRARY DESTINATION lib/shared
    )
    
    # 安装可执行文件
    install(TARGETS myapp
        RUNTIME DESTINATION bin
    )
    
  • $ENV{...}

    在 CMake 中,$ENV{...} 用于访问环境变量。通过这种方式,我可以在 CMake 脚本中使用操作系统环境变量的值。例如,$ENV{ASDF} 将访问名为 ASDF 的环境变量的值。

    • 使用示例
      假设我有一个环境变量 ASDF,并希望在 CMake 脚本中使用它的值,可以这样做:

      设置环境变量,在命令行中设置环境变量 ASDF,如下所示:

      export ASDF=/path/to/some/directory

    • 在 CMake 脚本中使用环境变量
      在我的 CMakeLists.txt 文件中,可以使用 $ENV{ASDF} 来访问 ASDF 环境变量的值:

      cmake_minimum_required(VERSION 3.10)
      project(MyProject)
      
      # 获取环境变量 ASDF 的值
      set(MY_ASDF_PATH $ENV{ASDF})
      
      # 打印环境变量的值
      message(STATUS "ASDF is set to ${MY_ASDF_PATH}")
      
      # 使用环境变量的值
      include_directories(${MY_ASDF_PATH}/include)
      link_directories(${MY_ASDF_PATH}/lib)
      
      add_executable(myapp main.cpp)
      target_link_libraries(myapp mylib)
      

      在这个示例中:

      set(MY_ASDF_PATH $ENV{ASDF}) 将环境变量 ASDF 的值赋给 CMake 变量 MY_ASDF_PATH。
      message(STATUS "ASDF is set to ${MY_ASDF_PATH}") 打印环境变量 ASDF 的值。
      include_directories(${MY_ASDF_PATH}/include) 使用环境变量 ASDF 的值指定包含目录。
      link_directories(${MY_ASDF_PATH}/lib) 使用环境变量 ASDF 的值指定库目录。
      

      适用场景

      • 配置依赖路径: 例如,您可以将第三方库的路径设置为环境变量,并在 CMake 脚本中引用它,以便在不同的开发环境中灵活配置。
      • 条件编译: 根据环境变量的值来启用或禁用某些功能或编译选项。
      • 跨平台构建: 使用环境变量来处理不同平台上的路径或设置差异。

      注意事项

      确保在运行 CMake 之前环境变量已被设置。

      如果环境变量未设置或为空,CMake 中对应的变量也将为空,可能会导致构建错误。因此,可以使用 CMake 的 if 语句来检查环境变量是否设置。

      if(NOT DEFINED ENV{ASDF})
          message(FATAL_ERROR "Environment variable ASDF is not set")
      endif()
      

      通过这种方式,您可以在构建过程中灵活地使用和管理环境变量,从而更好地适应各种构建需求和环境。

  • INCLUDE_DIRECTORIES

    在 CMake 中,INCLUDE_DIRECTORIES 指令用于指定编译器在编译源文件时搜索头文件的目录。INCLUDE_DIRECTORIES 还可以使用 BEFORE 关键字来控制包含目录的优先级。

    多个BEFORE,后面指定的BEFORE目录的优先级更高

    使用示例

    include_directories(BEFORE ${PROJ_SOURCE_DIR}/src/)
    

    解释

    • ${PROJ_SOURCE_DIR}/src/: 这是一个包含目录的路径,它通常是项目源代码目录的子目录。在这个示例中,它表示在 ${PROJ_SOURCE_DIR} 下的 src 目录。

    • BEFORE: 这个关键字用于确保指定的包含目录在包含搜索路径列表的前面(即具有更高的优先级)。
      含义

    通过使用 BEFORE 关键字,您可以确保 ${PROJ_SOURCE_DIR}/src/ 目录中的头文件比其他任何通过 include_directories 指定的目录中的头文件更优先被找到和使用。

    详细解释:

    • 优先级控制:

      当编译器查找头文件时,它会按照包含目录的顺序进行搜索。使用 BEFORE 关键字,可以将 ${PROJ_SOURCE_DIR}/src/ 放在搜索路径的前面,确保该目录中的头文件首先被找到。

    • 避免命名冲突:

      如果项目中有多个包含目录,且这些目录中有同名的头文件,使用 BEFORE 可以确保指定目录中的头文件优先被使用,避免不必要的命名冲突。

    • 示例场景

      假设我的项目结构如下:

      MyProject/
      ├── CMakeLists.txt
      ├── src/
      │   └── myheader.h
      └── include/
          └── myheader.h
      

      在 CMakeLists.txt 中:

      cmake_minimum_required(VERSION 3.10)
      project(MyProject)
      
      # 指定源文件
      add_executable(myapp src/main.cpp)
      
      # 指定包含目录,使用 BEFORE 确保 src 目录优先
      include_directories(BEFORE ${PROJ_SOURCE_DIR}/src/)
      include_directories(${PROJ_SOURCE_DIR}/include/)
      

      在这个例子中,如果 src/main.cpp 文件包含了 #include "myheader.h",那么编译器会首先在 src/ 目录中查找 myheader.h,因为 src/ 目录被指定为优先目录。只有在 src/ 目录中找不到 myheader.h 时,编译器才会去 include/ 目录中查找。

      通过这种方式,可以更精细地控制包含目录的搜索顺序,以满足项目的特定需求。

  • PROJ_SOURCE_DIR: 表示当前工程的根目录,获取方式为${PROJ_SOURCE_DIR}

  • $<TARGET_FILE:ABC>: 是生成器表达式,用于获取可执行文件ABC的完整文件路径

posted @ 2024-07-18 17:43  绍荣  阅读(3)  评论(0编辑  收藏  举报