CMake系列讲解(进阶篇)2.1 进阶命令CMake-install()


基础命令install()

〓〓〓〓〓〓〓〓踏实学CMake总目录〓〓〓〓〓〓〓〓〓〓


1、install命令

该条命令的主要作用是安装项目生成的文件到指定位置,比如项目编译过程中生成的库文件和可编译程序,配置文件等。

该命令根据安装文件类型的不同可以分为以下多种,在本文中我们只介绍最常用的几种。

install(TARGETS <target>... [...])
install(IMPORTED_RUNTIME_ARTIFACTS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
install(PACKAGE_INFO <package-name> [...])
install(RUNTIME_DEPENDENCY_SET <set-name> [...])

1.1 安装TARGETS到指定目录(如编译生成的库或可执行文件)

install(TARGETS <target>... [...])

注:不想看理论可以直接跳到1.1.3小节,直接查看实例仿照使用即可

1.1.1 命令格式

install(TARGETS <target>... [EXPORT <export-name>]
        [RUNTIME_DEPENDENCIES <arg>...|RUNTIME_DEPENDENCY_SET <set-name>]
        [<artifact-option>...]
        [<artifact-kind> <artifact-option>...]...
        [INCLUDES DESTINATION [<dir> ...]]
        )

1.1.2 命令参数

  • TARGETS: 表示要安装的目标文件。在CMake中,每个add_executable或者add_library会产生一个目标(target)。目标一般就是项目中需要编译生成的可执行文件或者库文件等。不明白的可以看一下之前章节对于add_executable和add_library命令的介绍。

  • EXPORT: 该参数用于将TARGETS进行导出,从而其他项目可以链接使用。

  • RUNTIME_DEPENDENCIES: 该参数用于指定目标的运行时依赖关系。使用RUNTIME_DEPENDENCIES可以在安装目标时一起安装其运行时依赖:

    install(TARGETS myexe
    RUNTIME_DEPENDENCIES mylib
    ...)
    

    这样就表示myexe的运行时依赖是mylib库,安装myexe时也会一并安装mylib库。

  • RUNTIME_DEPENDENCY_SET: 该参数用于指定目标使用依赖项集合。

    target_link_libraries(exe1 lib1 lib2)
    target_link_libraries(exe2 lib1 lib2)
    add_dependencies(CommonDeps lib1 lib2)
    这样CommonDeps就是一个依赖项集合,包含lib1和lib2两个库。
    然后在install命令中使用:
    install(TARGETS exe1 exe2
    RUNTIME_DEPENDENCY_SET CommonDeps
    ...)
    这样就将exe1和exe2目标都关联到了CommonDeps这个依赖项集合。
    
  • 其中<artifact-option>...可能包含:

    • [DESTINATION <dir>] : 指定安装文件/文件夹的目标位置
      <dir>可以为绝对路径也可以为相对路径,写绝对路径时,该路径直接作为安装路径。写相对路径时,会把CMAKE_INSTALL_PREFIX与相对路径拼接成最终的安装路径。

      注意:在之后介绍的几种install形式的命令中,也会用到DESTINATION参数,最终安装路径的策略也是这样。

    • [PERMISSIONS <permission>…] 指定安装文件/文件夹的访问权限,如OWNER_WRITE,GROUP_READ等

    • [CONFIGURATIONS <config>…] 指定用于那些配置(Debug/Release等)安装文件

    • [COMPONENT <component>] 指定安装文件属于哪个组件

    • [NAMELINK_COMPONENT ] 指定符号链接文件的组件

    • [OPTIONAL] [EXCLUDE_FROM_ALL] 仅创建符号链接,不复制文件

    • [NAMELINK_ONLY|NAMELINK_SKIP] 不创建符号链接,仅复制文件

  • artifact-kind指的是需要安装的文件类型。一般常用的类型有:

    • ARCHIVE:包括静态库、windows中的.lib
    • LIBRARY:动态库(不包括dll)
    • RUNTIME:可执行文件,windows下的dll

1.1.3 使用实例——安装编译生成的可执行文件或者库文件到指定目录

最简单最常用的使用方式,就是将编译生成的可执行文件或者库文件安装到某个目录下。
其使用方式如下:

// 使用相对路径,最终的安装路径是 ```${CMAKE_INSTALL_PREFIX}/lib
install(TARGETS cmake_inst_hello 
    DESTINATION lib
)
# 使用绝对路径。最终的安装路径就是/usr/mtj/
install(TARGETS cmake_inst_hello 
    DESTINATION /usr/mtj/
)

具体Demo项目示例:

项目文件目录:
.
├── cmake_example.conf
├── CMakeLists.txt
├── include
│ └── Hello.h
└── src
├── Hello.cpp
└── main.cpp

cmake_minimum_required(VERSION 3.5)
project(cmake_install_inst_bin)
############################################################
# Create a library
############################################################
#Generate the shared library from the library sources
add_library(cmake_inst_hello_lib SHARED 
    src/Hello.cpp
)

target_include_directories(cmake_inst_hello_lib
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
set(SOURCES 
    src/main.cpp
)
add_executable(cmake_install_inst_bin ${SOURCES})

target_include_directories(cmake_install_inst_bin
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)

# link the new hello_library target with the hello_binary target
target_link_libraries(cmake_install_inst_bin
    PRIVATE
    cmake_inst_hello_lib
)

############################################################
# Install TARGETS
############################################################

# 输出默认相对路径拼接前缀
message("default install path: " ${CMAKE_INSTALL_PREFIX})

# install binaries
install(TARGETS cmake_install_inst_bin
    DESTINATION bin
)

# install lib
install(TARGETS cmake_inst_hello_lib
    LIBRARY DESTINATION lib
)

执行cmake命令,我们可以看到控制台打印默认使用相对路径时CMAKE_INSTALL_PREFIX的值为:/usr/local
在这里插入图片描述

之后执行make,生成一个可执行文件cmake_install_inst_bin以及一个动态库libcmake_inst_hello_lib.so
在这里插入图片描述
最后执行 make install,install命令会将可执行程序安装到${CMAKE_INSTALL_PREFIX}/bin,将库安装到${CMAKE_INSTALL_PREFIX}/lib,即/usr/local/bin/usr/local/lib. 由于我们安装的目录是/usr下,因此执行make install需要root权限,使用sudo执行。
在这里插入图片描述
在这里插入图片描述

1.2 安装FILES/PROGRAMS到指定目录

install(FILES <file>... [...])
install(PROGRAMS <program>... [...])

注:不想看理论可以直接跳到1.1.3小节,直接查看实例仿照使用即可

1.2.1 命令格式

install(<FILES|PROGRAMS> <file>...
        TYPE <type> | DESTINATION <dir>
        [PERMISSIONS <permission>...]
        [CONFIGURATIONS <config>...]
        [COMPONENT <component>]
        [RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])

注意:如果使用FILES形式的命令来安装头文件,考虑使用target_sources(FILE_SET)定义的文件集合命令来替代。关联目标(TARGET)的文件集合,会作为目标的一部分安装。

1.2.2 命令参数

  • FILES: FILES形式的命令规定了一种在项目中安装文件的规则。要安装文件的名称如果以相对路径的形式给出,则FILES的路径就是以当前资源路径为参考的相对路径。且如果命令中没有指定Permission参数,被安装的文件的默认权限会是OWNER_WRITE, OWNER_READ, GROUP_READ, and WORLD_READ.

  • PROGRAM形式的命令与FILES形式除了被安装的文件的默认权限不同之外,其他并没有什么不同。PROGRAMS形式的命令,其被安装的文件的默认权限还包括了OWNER_EXECUTE, GROUP_EXECUTE, and WORLD_EXECUTE 这种格式的命令是用来安装除targets之外的可执行程序,例如shell脚本等。

    使用FILES形式或者PROGRAMS形式的命令时,<file>…文件列表可以使用生成器表达式。然而,任意一项文件使用了生成器表达式,都会被看做是完整路径来计算。

  • 可选参数RENAME <name> 用来规定一个不同于原始文件名称的其他名字。重命名命令仅在安装单个文件时才能使用。

  • TYPE和DESINATION参数必须提供一个,但并不需要同时存在。TYPE参数指明了被安装文件的类型,如果DESTINATION参数没有被设置,DESTINATION会被根据被安装文件的TYPE,从对应的GNUInstallDirs中获取,下表是支持的文件类型和对应的 GNUInstallDirs变量值以及对应的默认值。

    Type参数GNUInstallDirs Variable内置默认值
    BIN${CMAKE_INSTALL_BINDIR}bin
    SBIN${CMAKE_INSTALL_SBINDIR}sbin
    LIB${CMAKE_INSTALL_LIBDIR}lib
    INCLUDE${CMAKE_INSTALL_INCLUDEDIR}include
    SYSCONF${CMAKE_INSTALL_SYSCONFDIR} etc
    SHAREDSTATE${CMAKE_INSTALL_SHARESTATEDIR}com
    LOCALSTATE${CMAKE_INSTALL_LOCALSTATEDIR}var
    RUNSTATE${CMAKE_INSTALL_RUNSTATEDIR}<LOCALSTATE dir>/run
    DATA${CMAKE_INSTALL_DATADIR}<DATAROOT dir>
    INFO${CMAKE_INSTALL_INFODIR}<DATAROOT dir>/info
    LOCALE${CMAKE_INSTALL_LOCALEDIR}<DATAROOT dir>/locale
    MAN${CMAKE_INSTALL_MANDIR}<DATAROOT dir>/man
    DOC${CMAKE_INSTALL_DOCDIR}<DATAROOT dir>/doc
    LIBEXEC${CMAKE_INSTALL_LIBEXECDIR}libexec

    请注意,一些类型的内置默认值使用DATAROOT目录作为前缀。DATAROOT前缀的计算方式与类型类似,其中CMAKE_INSTALL_DATAROOTDIR作为变量,share作为内置默认值。您不能将DATAROOT用作TYPE参数;请改用DATA。

    为了使包符合分发文件系统布局策略,如果项目必须指定DESTINATION,强烈建议他们使用以相应的相对GNUInstallDirs变量开头的路径。这允许包维护者通过设置适当的缓存变量来控制安装目标。

    include(GNUInstallDirs)
    install(FILES logo.png
            DESTINATION ${CMAKE_INSTALL_DOCDIR}/myproj
    )
    

1.2.3 使用实例——安装文件或可执行程序(除targets之外)

最简单最常用的使用方式,就是将某个单个文件安装到某个目录下。在本实例中我安装了项目中会经常使用到的几种文件:

  • 头文件 (头文件安装更推荐使用target_sources(FILE_SET),能够关联target,作为target的一部分安装)
  • xml配置文件
  • shell脚本
  • md文档

代码目录结构

.
├── CMakeLists.txt
├── config.xml
├── include
│ └── Hello.h
├── readMe.md
├── run.sh
└── src
├── Hello.cpp
└── main.cpp

CMake文件

在上一小节的实例的基础之上又添加对于单个文件的安装

cmake_minimum_required(VERSION 3.5)
project(cmake_install_install_file)
include(GNUInstallDirs)
############################################################
# Create a library
############################################################
#Generate the shared library from the library sources
add_library(cmake_install_file_lib SHARED 
    src/Hello.cpp
)

target_include_directories(cmake_install_file_lib
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
set(SOURCES 
    src/main.cpp
)
add_executable(cmake_install_install_file ${SOURCES})

target_include_directories(cmake_install_install_file
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)

# link the new hello_library target with the hello_binary target
target_link_libraries(cmake_install_install_file
    PRIVATE
    cmake_install_file_lib
)

############################################################
# Install File
############################################################

message("default install path: " ${CMAKE_INSTALL_PREFIX})

# install binaries
install(TARGETS cmake_install_install_file
    DESTINATION bin
)

# install lib
install(TARGETS cmake_install_file_lib
    LIBRARY DESTINATION lib
)

# install a header file to /usr/local/include
install(FILES "${CMAKE_SOURCE_DIR}/include/Hello.h"
    DESTINATION "/usr/local/header"
    PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ
)

# install a xml config file to /usr/local/config and specify the destination
install(FILES "${CMAKE_SOURCE_DIR}/config.xml"
    DESTINATION "/usr/local/script"
)

# install run.sh and specify a destinaton
# in this example, we usr instal command to intall run,sh to /usr/local/run.sh
install(PROGRAMS "${CMAKE_SOURCE_DIR}/run.sh"
    DESTINATION "/usr/local/script"
)
message("default CMAKE_INSTALL_DOCDIR path: " ${CMAKE_INSTALL_DOCDIR})
# install readme.doc to default dir
install(FILES "${CMAKE_SOURCE_DIR}/readMe.md"
    TYPE DOC
    PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ
)

执行截图
在这里插入图片描述

  • 头文件Hello.h被安装进了/usr/local/header/中
    在这里插入图片描述
  • xml配置文件安装进/usr/local/script文件夹中
  • shell脚本则安装在了/usr/local/script/文件夹中
    可以看到,在都没有PERMISSIONS参数情况下,使用FILES安装的文件只有读写权限,而使用
    PROGRAMS安装的文件有可执行权限
    在这里插入图片描述

1.3 安装目录文件夹到指定目录

install(DIRECTORY <dir>... [...])

该命令用于安装一个或多个文件夹内的内容到指定目录。

注意:如果要安装的是只含有头文件的目录,推荐使用target_sources(FILE_SET) 定义的文件集。文件集不仅保留目录结构,还会将头文件与TARGETS关联起来作为TARGETS的一部分进行安装

注:不想看理论可以直接跳到1.3.3小节,直接查看实例仿照使用即可

1.3.1 命令格式

install(DIRECTORY dirs...
        TYPE <type> | DESTINATION <dir>
        [FILE_PERMISSIONS <permission>...]
        [DIRECTORY_PERMISSIONS <permission>...]
        [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
        [CONFIGURATIONS <config>...]
        [COMPONENT <component>] [EXCLUDE_FROM_ALL]
        [FILES_MATCHING]
        [[PATTERN <pattern> | REGEX <regex>]
         [EXCLUDE] [PERMISSIONS <permission>...]] [...])

1.3.2 命令参数

  • DIRECTORY:表示要安装的是一个或多个目录。
  • DESTINATION: 表示要被安装的一个或多个目录的安装路径,<dir>可以为绝对路径也可以为相对路径,写绝对路径时,该路径直接作为安装路径。写相对路径时,会把CMAKE_INSTALL_PREFIX与相对路径拼接成最终的安装路径。
  • FILE_PERMISSIONS:用来指定文件的权限
  • DIRECTORY_PERMISSIONS:用来指定目录的权限
  • USE_SOURCE_PERMISSIONS:用来指定文件夹的权限

使用DIRECTORY安装一个或者多个目录的内容到指定的目标路径,目录结构会被逐字节拷贝到目标路径。每个目录名的最后一部分(即目录名中最后一个/后边的内容)会被拼接到目标路径。但可以使用末尾添加一个/来避免这种情况,因为/后边为空,表示目录最后一部分为空。如果目录名称是相对路径,那么该目录会解析成相对于当前目录的路径。如果没有给出输入目录名,则将创建目标目录,但不会安装任何内容。如果只指定了USE_SOURCE_PERMISSIONS而没有指定FILE_PERMISSIONS,则文件权限将从源目录结构中复制。如果没有指定权限,则文件将被赋予FILES形式中指定的默认权限644,目录将会被赋予PROGRAMS形式命令的默认权限755。

在 3.1 版本中新增: 可以使用MESSAGE_NEVER来控制安装过程中是否打印安装文件或者目录过程中的消息。

可以使用PATTERN或REGEX选项以精细的粒度控制目录的安装。这些选项会使用通配符或者正则表达式来匹配输入目录的文件和目录。他们可以用来将某些选项(见下文)应用于遇到的目录和文件子集。与表达式匹配的是每个输入文件或目录的全路径。PATTERN仅仅匹配文件名,模式匹配发生在全路径最后文件名部分(文件名前需要带/)。REGEX则会匹配全路径的任何一个部分,但可以使用/或$来模拟REGEX的行为。
默认情况下所有文件和目录都会被安装,FILES_MATCHING选项必须出现在第一个匹配选项之前,以禁止安装未被任何表达式匹配的文件(不包括目录)。例如:

install(DIRECTORY src/ DESTINATION doc/myproj
        FILES_MATCHING PATTERN "*.png")

将从源文件树中提取png文件并安装。

某些选项可能遵循通配符匹配或者正则表达式,并且仅应用于与之匹配的文件或目录。EXCLUDE选项会跳过匹配的文件或者目录。
PERMISSIONS选项则会重写匹配的文件或者目录的权限,例如下列代码:

install(DIRECTORY icons scripts/ DESTINATION share/myproj
        PATTERN "CVS" EXCLUDE
        PATTERN "scripts/*"
        PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
                    GROUP_EXECUTE GROUP_READ)

icons目录将会被安装到share/myproj/icons,scripts/目录将会被安装在share/myproj,即每个目录的最后一部分会被追加到安装目录后边,share/myproj/icons最后一部分为icons,追加之后为share/myproj/icons,scripts/最后一部分为空,追加之后为share/myproj。icons会以默认权限安装,scripts会以给定的权限安装, 任何包含CSV的文件都不会被安装。

TYPE和DESTINATION这两个选项必须提供一个,且只能提供一个,不能两个同时存在,TYPE参数指定所列安装目录中文件的通用文件类型。然后,通过从GNUInstallDirs中获取相应的变量,或者如果未定义该变量,则使用内置的默认值,自动设置目标。请参阅下表,了解支持的文件类型及其相应的变量和内置默认值。如果项目希望显式定义安装目标,则可以提供DESTINATION参数提供具体的安装路径而不是文件类型。下表中是不同Type所对应的GNUInstallDirs变量名以及这些变量的默认值。

Type参数GNUInstallDirs Variable内置默认值
BIN${CMAKE_INSTALL_BINDIR}bin
SBIN${CMAKE_INSTALL_SBINDIR}sbin
LIB${CMAKE_INSTALL_LIBDIR}lib
INCLUDE${CMAKE_INSTALL_INCLUDEDIR}include
SYSCONF${CMAKE_INSTALL_SYSCONFDIR} etc
SHAREDSTATE${CMAKE_INSTALL_SHARESTATEDIR}com
LOCALSTATE${CMAKE_INSTALL_LOCALSTATEDIR}var
RUNSTATE${CMAKE_INSTALL_RUNSTATEDIR}<LOCALSTATE dir>/run
DATA${CMAKE_INSTALL_DATADIR}<DATAROOT dir>
INFO${CMAKE_INSTALL_INFODIR}<DATAROOT dir>/info
LOCALE${CMAKE_INSTALL_LOCALEDIR}<DATAROOT dir>/locale
MAN${CMAKE_INSTALL_MANDIR}<DATAROOT dir>/man
DOC${CMAKE_INSTALL_DOCDIR}<DATAROOT dir>/doc
LIBEXEC${CMAKE_INSTALL_LIBEXECDIR}libexec

请注意,一些类型的内置默认值使用DATAROOT目录作为前缀。DATAROOT前缀的计算方式与类型类似,其中CMAKE_INSTALL_DATAROOTDIR作为变量,share作为内置默认值。您不能将DATAROOT用作TYPE参数;应该使用DATA。

为了使包符合分发文件系统布局策略,如果项目必须指定DESTINATION,强烈建议使用以相应的相对GNUInstallDirs变量开头的路径。这允许包维护者通过设置适当的缓存变量来控制安装目标。

在版本3.4中新增:作为destination参数给出的安装目标可以使用语法为$<…>的“生成器表达式”。
在3.5版本中新增:给DIRECTORY的dirs...也可以使用“生成器表达式”。
在版本3.31中新增:TYPE参数支持LIBEXEC类型。

1.3.4 使用实例——安装项目头文件文件夹到指定目录

在本例中我将项目的头文件所在的文件夹安装到指定目录。

代码结构:

.
├── api1
│   └── Test1.h
├── api2
│   └── Test2.h
├── CMakeLists.txt
└── src
    ├── main.cpp
    ├── Test1.cpp
    └── Test2.cpp

代码中有两个头文件文件夹,分别为api1和api2,将这两个文件夹中的内容安装到/usr/local目录下

CMake文件:

cmake_minimum_required(VERSION 3.5)
project(cmake_install_install_file)
include(GNUInstallDirs)
############################################################
# Create an executable
############################################################
set(SOURCES 
    src/main.cpp
    src/Test1.cpp
    src/Test2.cpp
)
add_executable(cmake_install_install_file ${SOURCES})

target_include_directories(cmake_install_install_file
    PUBLIC
        ${PROJECT_SOURCE_DIR}/api1
        ${PROJECT_SOURCE_DIR}/api2
)

############################################################
# Install File
############################################################

message("default install path: " ${CMAKE_INSTALL_PREFIX})

# install binaries
install(TARGETS cmake_install_install_file
    DESTINATION bin
)

############################################################
# Install Directory
############################################################
install(DIRECTORY "${CMAKE_SOURCE_DIR}/api1"
    DESTINATION "/usr/local"
)

install(DIRECTORY "${CMAKE_SOURCE_DIR}/api2/"
    DESTINATION "/usr/local"
)

执行截图:在这里插入图片描述
安装结果:
在这里插入图片描述
在安装时,我们的两条安装命令为:

install(DIRECTORY "${CMAKE_SOURCE_DIR}/api1"
    DESTINATION "/usr/local"
)

install(DIRECTORY "${CMAKE_SOURCE_DIR}/api2/"
    DESTINATION "/usr/local"
)

api1后边没有/,则api1待安装目录的最后一部分为api1,拼接到目标路径后就是/usr/local/api1,而api2待安装目录的最后一部分为空,拼接到目标路径后就是/usr/local/,因此最终安装之后,api1文件夹被安装到/usr/local/下,而api2中的Test2.h被安装到/usr/local/下。

1.4 安装脚本文件或者代码文件到指定目录

install(SCRIPT <file> [...])
install(CODE <code> [...])

注:不想看理论可以直接跳到1.4.3小节,直接查看实例仿照使用即可

1.4.1 命令格式

install([[SCRIPT <file>] [CODE <code>]]
        [ALL_COMPONENTS | COMPONENT <component>]
        [EXCLUDE_FROM_ALL] [...])

1.4.2 命令参数

  • SCRIPT: 表示会在安装期间调用指定的cmake脚本。如果脚本文件名是相对路径,则将根据当前源目录进行解释。
  • CODE:CODE形式将在安装过程中调用给定的CMake代码,代码是通过双引号包含的一串字符串。例如
install(CODE "MESSAGE(\"Sample install message.\")")

会在安装期间打印一条消息。

3.21新增: 当命令中包含ALL_COMPONENTS参数时,将为特定于组件安装的每个组件执行自定义安装脚本。这个选项与COMPONENT选项互斥。
3.14新增: file或者code可以使用生成器表达式$<...> ,在这种情况下,引用的是他们的文件名而不是文件内容。

1.4.3 使用实例

本小节的实例中我们一起看看install CODE 或者install Script究竟应该如何使用。

代码目录结构:

.
├── cmake
│   └── install_script.cmake
├── CMakeLists.txt
└── src
    └── main.cpp

这次的代码结构非常简单,仅有一个源文件以及一个cmake脚本文件(install_script.cmake)。

代码CMake文件

cmake_minimum_required(VERSION 3.5)
project(cmake_install_script_code)
############################################################
# Create an executable
############################################################
set(SOURCES 
    src/main.cpp
)
add_executable(cmake_install_script_code ${SOURCES})

install(SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/install_script.cmake")
install(CODE "message(\"This is an example of installing CODE\")")

代码中install_script.cmake的文件

message("This is an example of installing script")

执行程序并运行make make install

执行与运行结果结果:
在这里插入图片描述

注意:关于对CMkae中ExampleScriqt.cmake脚本和include(ExampleScript),二者都设计到第三方的cmake脚本,但是:

  • 对于install(SCRIPT path),是在执行make install命令时运行。适用于在安装过程中执行相应的操作,例如配置文件的处理或安装后设置。
  • 对于include(…),是在CMake配置阶段立即执行的,在项目构建的时候引入其他CMake文件的内容。可以用来重用代码和设置构建规则。

1.5 安装导出文件EXPORT

install(EXPORT <export-name> [...])

为依赖项目安装CMake文件导出设置(export settings). 使得其他项目可以通过 find_package 来找到和使用这些目标。这通常用于库项目,以便其他项目能够链接到这些库。

1.5.1 命令格式

install(EXPORT <export-name> DESTINATION <dir>
        [NAMESPACE <namespace>] [FILE <name>.cmake]
        [PERMISSIONS <permission>...]
        [CONFIGURATIONS <config>...]
        [CXX_MODULES_DIRECTORY <directory>]
        [EXPORT_LINK_INTERFACE_LIBRARIES]
        [COMPONENT <component>]
        [EXCLUDE_FROM_ALL]
        [EXPORT_PACKAGE_DEPENDENCIES])
install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...])

EXPORT生成并安装一个CMake文件,其中包含将目标从安装树导入另一个项目的代码。

1.5.2 命令参数

  • <export-name>:是之前在使用install(TARGET)安装目标命令时EXPORT选项指定的名称。例如:

    如果目标使用 install(TARGETS MyLibrary EXPORT MyLibraryTargets),则这里的 <export-name> 将是 MyLibraryTargets。

  • DESTINATION <dir>: 指定导出文件的安装目标目录。

  • NAMESPACE: 在将目标名称写入导入文件时,NAMESPACE选项将在目标名称前添加<NAMESPACE>

  • FILE: 指定生成的CMake文件的名称,默认是<export-name>.cmake,FILE选项指定的名称必须是以.cmake为后缀的文件名。

  • CONFIGURATIONS: 指定导出安装的配置(如 Debug、Release)例如,CONFIGURATIONS Debug Release 表示只在这些配置下安装导出。

  • EXPORT_LINK_INTERFACE_LIBRARIES:匹配(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)?属性的内容会被导出。

    注意:已安装的<export-name>.cmake文件可能附带了额外的配置的
    <export-name>-*.cmake文件,这些文件通过globbing进行加载。不要在安装<package name>-config.cmake文件时使用与包名称相同的导出名称,否则glob可能会错误地匹配并加载后者。

  • COMPONENT: 当为一个组件指定component选项后,该组件<component>隐式地依赖于导出集中提到的其他所有组件。导出的<name>.cmake文件要求每个导出的组件都存在,以便正确地构建项目。例如,一个项目可能定义了开发组件和运行时组件,动态库进入运行时组件,静态库和头文件进入开发组件。导出集通常也是在开发组件中。但它将从运行时和开发组件导出目标,因此,如果安装了开发组件,则需要安装运行时组件,但反之则不然。如果安装开发组件时没有安装运行时组件,则尝试与之链接的依赖项目将出现构建错误。包管理器(如APT和RPM)通常通过在包元数据中将运行时组件列为开发组件的依赖项来处理此问题,确保如果存在头文件和CMake导出文件,则始终安装库。

    注:在 CMake 中,组件是一种将目标(如库或可执行文件)分组的方式,便于管理安装过程和依赖关系。
    导出集:当使用 install(EXPORT …) 指令时,可以定义一个导出集,这个集合包含了需要导出的目标和它们的相关信息。

  • CXX_MODULES_DIRECTORY: 3.28中新增,指定一个子目录来存储导出集中Target的模块信息,此目录将填充文件,这些文件将必要的目标属性信息添加到相关目标中。请注意,如果没有此信息,作为导出集中目标的一部分的C++模块都不支持在消费目标中导入。

EXPORT有助于外部项目使用当前项目构建和安装的目标。例如,以下代码

install(TARGETS myexe EXPORT myproj DESTINATION bin)
install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj)
install(EXPORT_ANDROID_MK myproj DESTINATION share/ndk-modules)

将可执行文件myxe安装到<prefix>/bin,并安装导入代码到文件<prefix>/lib/myproj/myproj.cmake<prefix>/share/ndk-modules/Android.mk。外部项目可以使用include命令加载此文件,并使用导入的目标名称mp_myexe从安装树引用myxe可执行文件,就像目标是在自己的树中构建的一样。

1.5.3 使用实例——在ProjectA中安装动态库及其导出目标使之能在ProjectB中使用

  在本小节的实例中我们使用两个项目来介绍如何使用export命令将项目A生成的动态库导出给项目B使用。在ProjectA中,我生成了一个动态库,并将这个动态库及其导出Target安装到对应目录下,最终在ProjectB中使用这个动态库。

1.5.3.1 两个项目的文件结构**

ProjectA:

.
├── CMakeLists.txt
├── HelloAConfig.cmake.in
├── include
│   └── HelloA.h
└── src
    ├── HelloA.cpp
    └── main.cpp

ProjectB:

.
├── CMakeLists.txt
└── main.cpp
1.5.3.2 ProjectA CMake文件详解
  1. 创建动态库
    # 1. Create an shared lib
    add_library(HelloA SHARED 
        src/HelloA.cpp
    )
    target_include_directories(HelloA
        PRIVATE
            ${PROJECT_SOURCE_DIR}/include
    )
    

    首先创建一个动态库,使用的命令在之前的章节中都已讲解过。

  2. 安装动态库
    # 2. install library
    install(TARGETS HelloA
        EXPORT HelloATarget
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib
        RUNTIME DESTINATION bin
    )
    

    使用install命令将创建的动态库安装到${CMAKE_INSTALL_PREFIX}/lib目录下,对此命令不熟悉的可以温习一下前边的章节。注意一定要添加EXPORT参数来指定export-name

  3. 安装动态库导出目标
    # 3. install export file 
    install(EXPORT HelloATarget
        FILE HelloATarget.cmake
        NAMESPACE MyHello::
        DESTINATION lib/cmake/MyHello
    )
    

    这里安装时使用的export-name必须与安装库时的export-name保持一致,即要使用HelloATarget。FILE参数指定了生成的.cmake文件的文件名。namespace参数指定了生成库的命名空间。DESTINATION指定了该导出目标将会被安装到lib/cmake/MyHello目录下。

  4. 安装配置文件
    # 4. install config file
    include(CMakePackageConfigHelpers)
    # Generate HelloATargetConfig.cmake file using template HelloATargetConfig.cmake.in file
    configure_package_config_file(
        HelloATargetConfig.cmake.in
        HelloATargetConfig.cmake
        INSTALL_DESTINATION lib/cmake/MyHello)
    # write package version info so other project can check version when they use the lib
    write_basic_package_version_file(
        "${CMAKE_CURRENT_BINARY_DIR}/HelloATargetConfigVersion.cmake"
        VERSION 1.0
        COMPATIBILITY AnyNewerVersion
    )
    # install config file
    install(FILES
        "${CMAKE_CURRENT_BINARY_DIR}/HelloATargetConfigVersion.cmake"
        "${CMAKE_CURRENT_BINARY_DIR}/HelloATargetConfig.cmake"
        DESTINATION lib/cmake/MyHello
    )
    
    • 首先使用我们需要引入CMakePackageConfigHelpers模块,这样就能够使用其提供的一些工具来生成和管理CMake包的配置文件。
    • 然后使用configure_package_config_file根据模板文件生成最终的配置文件以保证所有的路径和依赖关系都被正确设置
    • 使用write_basic_package_version_file来生成一个版本文件,指示库的版本信息,以便其他项目可以检查库的版本和兼容性策略。
    • 最后将生成的配置文件安装到指定目录下
  5. 安装项目头文件
    # 5. install header files
    install(DIRECTORY include/
        DESTINATION include/
    )
    

    最后安装库的头文件

1.5.3.3 HelloATargetConfig.cmake.in文件解析
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
include_directories("${CMAKE_INSTALL_PREFIX}/include")
# 查找目标
include(CMakePackageConfigHelpers)
include("${CMAKE_CURRENT_LIST_DIR}/HelloATarget.cmake")
  • include(CMakeFindDependencyMacro)
    这行代码包含了 CMake 提供的查找依赖项的宏。这些宏用于在项目中查找并处理依赖库,确保在构建时能够找到所需的库。
  • include_directories("${CMAKE_INSTALL_PREFIX}/include")这一行指定了额外的包含目录。在构建时,编译器会在这个目录(${CMAKE_INSTALL_PREFIX}/include)中查找头文件。
  • include(CMakePackageConfigHelpers)这一行包含了 CMake 提供的包配置帮助宏,它提供了一些工具来生成和处理包配置文件,简化了包的查找和使用。
  • 这一行代码包含了名为 HelloATarget.cmake 的文件。这个文件通常定义了项目的具体构建目标,例如库或可执行文件的属性、依赖关系等。
1.5.4 ProjectA执行

在这里插入图片描述
可以看出库文件和配置文件以及头文件都被安装到了指定目录下。

1.5.3.5 ProjectB 项目

cmake_minimum_required(VERSION 3.20)
project(cmake_install_export_B)
message("default CMAKE_PREFIX_PATH path: " ${CMAKE_PREFIX_PATH})
set(CMAKE_PREFIX_PATH "/usr/local/lib/cmake/MyHello" ${CMAKE_PREFIX_PATH})

find_package(HelloATarget REQUIRED)

set(SOURCES 
    main.cpp
)
add_executable(cmake_install_export_B ${SOURCES})
target_link_libraries(cmake_install_export_B MyHello::HelloA)

使用set命令来设置查找CMake模块和包的前缀路径,并且保留了之前的路径,确保 CMake 能够找到所需的库和包
e m s p ; emsp; emsp;nmsp 之后使用find_package命令来查找名为HellpATarget的包,REQUIRED 表明如果找不到这个包,CMake 配置将失败。
在这里插入图片描述

1.6 安装Package

install(PACKAGE_INFO <package-name> [...])
注意:在3.31版本才可用

1.6.1 命令介绍

install(PACKAGE_INFO <package-name> EXPORT <export-name>
        [APPENDIX <appendix-name>]
        [DESTINATION <dir>]
        [LOWER_CASE_FILE]
        [VERSION <version>
         [COMPAT_VERSION <version>]
         [VERSION_SCHEMA <string>]]
        [DEFAULT_TARGETS <target>...]
        [DEFAULT_CONFIGURATIONS <config>...]
        [PERMISSIONS <permission>...]
        [CONFIGURATIONS <config>...]
        [COMPONENT <component>]
        [EXCLUDE_FROM_ALL])

PACKAGE_INFO形式生成并安装一个通用包规范文件,该文件描述了已安装目标,以便它们可以被另一个项目使用。目标安装与导出 <export-name> 相关联,使用 install(TARGETS) 签名中的 EXPORT 选项。与 install(EXPORT) 不同,这些信息不以 CMake 代码的形式表达,可以被 CMake 以外的工具使用。当导入到另一个 CMake 项目中时,导入的目标将以 <package-name>::为前缀。默认情况下,生成的文件将被命名为 <package-name>[-<appendix-name>].cps。如果提供了 LOWER_CASE_FILE 选项,则包名在磁盘上的显示(包括文件名和安装目的地)将首先转换为小写。

如果未指定DESTINATION,则根据平台选择特定的默认值。

如果指定了 APPENDIX,则生成的不是顶级包规范,而是将指定的目标作为package的附录导出。附录可以用来将不常用的目标(及其外部依赖)与包的其余部分分开。这样,使用该包的消费者可以忽略他们不使用的目标的传递依赖,从而简化依赖管理。通过使用附录,多个构建树生成的工件可以组合成一个逻辑上的“包”。这意味着一个包可以由多个来源的目标组成,增强了模块化和重用性。

附录不能更改包的基本元数据。这意味着在使用 APPENDIX 时,以下参数不能与之一起指定:VERSION、COMPAT_VERSION、VERSION_SCHEMA、DEFAULT_TARGETS或DEFAULT_CONFIGURATIONS。此外,强烈建议在主要包和任何附录之间保持 LOWER_CASE_FILE 的使用一致性。也就是说,如果主包的文件名被转换为小写,附录的文件名也应遵循相同的规则。这有助于保持文件系统中的一致性,避免混淆。

1.7 安装运行时依赖

install(RUNTIME_DEPENDENCY_SET <set-name> [...])

注意:在3.21中新增

用于管理和安装运行时依赖关系的命令。它可以确保在安装期间正确处理这些依赖关系

1.7.1 命令格式

install(RUNTIME_DEPENDENCY_SET <set-name>
        [[LIBRARY|RUNTIME|FRAMEWORK]
         [DESTINATION <dir>]
         [PERMISSIONS <permission>...]
         [CONFIGURATIONS <config>...]
         [COMPONENT <component>]
         [NAMELINK_COMPONENT <component>]
         [OPTIONAL] [EXCLUDE_FROM_ALL]
        ] [...]
        [PRE_INCLUDE_REGEXES <regex>...]
        [PRE_EXCLUDE_REGEXES <regex>...]
        [POST_INCLUDE_REGEXES <regex>...]
        [POST_EXCLUDE_REGEXES <regex>...]
        [POST_INCLUDE_FILES <file>...]
        [POST_EXCLUDE_FILES <file>...]
        [DIRECTORIES <dir>...]
        )

安装之前通过一个或多个 install(TARGETS) 或 install(IMPORTED_RUNTIME_ARTIFACTS) 命令创建的运行时的依赖关系集。在 DLL 平台上,属于运行时依赖关系集的目标的依赖项将安装在 RUNTIME 目标目录和组件中;在非 DLL 平台上,则安装在 LIBRARY 目标目录和组件中。macOS 框架将安装在 FRAMEWORK 目标目录和组件中。构建树中构建的目标将不会作为运行时依赖项被安装,也不会安装它们自身的依赖项,除非这些目标本身是通过 install(TARGETS) 命令安装的。

生成的安装脚本在构建树文件上调用 file(GET_RUNTIME_DEPENDENCIES) 来计算运行时依赖关系。构建树中的可执行文件作为 EXECUTABLES 参数传递,构建树中的共享库作为 LIBRARIES 参数传递,构建树中的模块作为 MODULES 参数传递。在 macOS 上,如果其中一个可执行文件是 MACOSX_BUNDLE,该可执行文件将作为 BUNDLE_EXECUTABLE 参数传递。macOS 上的运行时依赖关系集中最多只能包含一个这样的捆绑可执行文件。MACOSX_BUNDLE 属性对其他平台没有影响。请注意,file(GET_RUNTIME_DEPENDENCIES) 仅支持收集 Windows、Linux 和 macOS 平台的运行时依赖关系,因此 install(RUNTIME_DEPENDENCY_SET) 也有相同的限制。

以下子参数作为相应的参数转发到文件(GET_RUNTIME_DEPENDENCIES)(对于提供非空目录、正则表达式或文件列表的子参数)。它们都支持生成器表达式。

  • DIRECTORIES <dir>...
  • PRE_INCLUDE_REGEXES <regex>...
  • PRE_EXCLUDE_REGEXES <regex>...
  • POST_INCLUDE_REGEXES <regex>...
  • POST_EXCLUDE_REGEXES <regex>...
  • POST_INCLUDE_FILES <file>...
  • POST_EXCLUDE_FILES <file>...

附:返回总目录的传送门如下:

〓〓〓〓〓〓〓〓踏实学CMake总目录〓〓〓〓〓〓〓〓〓〓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在下马农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值