安装文件¶
软件通常安装到一个独立于源代码和构建树的目录中。通过这种方式,软件可以以一种干净的形式进行分发,使用户远离构建过程的细节。CMake 提供了 install
命令来指定项目的安装方式。该命令由 CMakeLists 文件中的项目调用,并告知 CMake 如何生成安装脚本。这些脚本在安装时执行,以执行文件的实际安装。对于 Makefile 生成器 (UNIX、NMake、MinGW 等),用户只需运行 make install
(或 nmake install
),而 make 工具将调用 CMake 的安装模块。在基于 GUI 的系统 (Visual Studio、Xcode 等) 中,用户只需构建名为 INSTALL
的目标即可。
对 install
命令的每次调用都会定义一些安装规则。在单个 CMakeLists 文件(源代码目录)中,这些规则将按照相应命令调用的顺序来评估。CMake 3.14 中已更改多个目录中的顺序。
install
命令有几个签名,专为常见的安装用例而设计。命令的特定调用将签名指定为第一个参数。这些签名是 TARGETS
、FILES
或 PROGRAMS
、DIRECTORY
、SCRIPT
、CODE
和 EXPORT
。
- install(TARGETS …)
安装项目内部构建的目标对应的二进制文件。
- install(FILES …)
通用的文件安装,通常用于软件所需的标题文件、文档和数据文件。
- install(PROGRAMS …)
安装不是由项目构建的可执行文件,例如 shell 脚本。此参数与
install(FILES)
相同,不同之处在于已安装文件的默认权限包含可执行位。`- install(DIRECTORY …)
此参数安装整个目录树。可用于安装具有资源的目录,如图标和图像。
- install(SCRIPT …)
指定在安装过程中要执行的用户提供的 CMake 脚本文件。通常用于为其他规则定义安装前或安装后操作。
- install(CODE …)
指定要在安装过程中执行的用户提供的 CMake 代码。这类似于
install (SCRIPT)
,但代码在调用中作为字符串内联提供。- install(EXPORT …)
生成并安装一个 CMake 文件,其中包含从安装树中导入目标到另一个项目的代码。
TARGETS
、FILES
、PROGRAMS
和 DIRECTORY
签名都用于为文件创建安装规则。需要安装的目标、文件或目录紧跟在签名名称参数之后。可以利用关键字参数及其相应值指定其他详细信息。以下列出了这些签名中大多数提供的关键字参数。
- DESTINATION
该参数指定安装规则将放置文件的位置,后面必须是一个指示位置的目录路径。如果目录指定为绝对路径,则安装时将被评估为一个绝对路径。如果目录指定为相对路径,则安装时将相对于安装前缀进行评估。前缀可以通过缓存变量
CMAKE_INSTALL_PREFIX
由用户设置。特定于平台的默认值由 CMake 提供:UNIX 下为/usr/local
,而 Windows 下为“<SystemDrive>/Program Files
/<ProjectName>”,其中 SystemDrive 类似于C:
,而 ProjectName 是赋予最顶层project
命令的名称。
- PERMISSIONS
该参数指定要对已安装的文件设置的文件权限。只有当需要覆盖特定
install
命令签名选定的默认权限时,才需要此选项。有效的权限包括OWNER_READ
、OWNER_WRITE
、OWNER_EXECUTE
、GROUP_READ
、GROUP_WRITE
、GROUP_EXECUTE
、WORLD_READ
、WORLD_WRITE
、WORLD_EXECUTE
、SETUID
和SETGID
。某些平台不支持所有这些权限;在那些平台上,这些权限名称将被忽略。
- CONFIGURATIONS
该参数指定安装规则所适用的构建配置列表(例如 Debug、Release 等)。对于 Makefile 生成器,构建配置由
CMAKE_BUILD_TYPE
缓存变量指定。对于 Visual Studio 和 Xcode 生成器,在构建install
目标时选择配置。只有当当前安装配置与向该参数提供的列表中的条目匹配时才会评估安装规则。配置名称比较不区分大小写。
- COMPONENT
此参数指定应用安装规则的安装组件。某些项目将其安装划分成多个组件,以便单独打包。例如,项目可能定义一个
Runtime
组件,其中包含运行工具所需的文件;一个Development
组件,其中包含生成工具扩展所需的文件;一个Documentation
组件,其中包含手册页和其他帮助文件。然后,该项目可针对每个组件单独进行打包,且每次仅安装一个组件。默认情况下,将安装所有组件。特定于组件的安装是一种高级功能,旨在供软件包维护者使用。它要求使用一个参数手动调用安装脚本,该参数定义COMPONENT
变量,以指定所需的组件。请注意,组件名称不是由 CMake 定义的。每个项目都可以定义自己的组件集合。
- 可选
此参数指定,如果要安装的输入文件不存在,则它不会导致错误。如果输入文件存在,则将按照请求安装它。如果它不存在,则将静默地不安装它。
安装目标¶
项目通常会安装其生成过程中创建的一些库和可执行文件。install
命令提供 TARGETS
签名以实现此目的。
紧跟在 TARGETS
关键字后面的是使用 add_executable
或 add_library
创建的目标列表,这些目标将被安装。与每个目标相应的一个或多个文件都将被安装。
使用此签名安装的文件可以分为以下几类:ARCHIVE
、LIBRARY
或 RUNTIME
。这些类别旨在按典型的安装目标对目标文件进行分组。相应的关键字参数是可选的,但如果存在,则指定后面跟在其后的其他参数仅适用于该类型的目标文件。对目标文件分类如下
- 可执行文件 -
RUNTIME
由
add_executable
创建(在 Windows 上为 .exe,在 UNIX 上没有扩展名)- 可加载模块 -
LIBRARY
由
add_library
创建(在 Windows 上为 .dll,在 UNIX 上为 .so),并带有MODULE
选项- 共享库 -
LIBRARY
使用
add_library
命令在类 UNIX 平台上使用SHARED
选项创建(.so 在大多数 UNIX 上,.dylib 在 Mac 上)- 动态链接库 -
RUNTIME
使用
add_library
命令在 Windows 平台上使用SHARED
选项创建(.dll)- 导入库 -
ARCHIVE
可链接文件,由导出符号的动态链接库创建(.lib 在大多数 Windows 上,.dll.a 在 Cygwin 和 MinGW 上)
- 静态库 -
ARCHIVE
使用
add_library
命令使用STATIC
选项创建(.lib 在 Windows 上,.a 在 UNIX、Cygwin 和 MinGW 上)
考虑一个定义可执行文件 myExecutable
的项目,它链接到共享库 mySharedLib
。它还提供了一个静态库 myStaticLib
和一个名为 myPlugin
的可执行文件插件模块,该模块也链接到共享库。可以使用以下命令单独安装可执行文件、静态库和插件文件
install(TARGETS myExecutable DESTINATION bin)
install(TARGETS myStaticLib DESTINATION lib/myproject)
install(TARGETS myPlugin DESTINATION lib)
只有当所链接的共享库也安装时,可执行文件才能在已安装的位置运行。为了支持所有平台,库的安装需要更小心一些。必须将库安装在每个平台的动态链接程序搜索的位置。在类 UNIX 平台上,库通常被安装到 lib
中,而在 Windows 上,应该将它放在 bin
中可执行文件的旁边。另一个挑战是,Windows 上与共享库关联的导入库应该像静态库一样处理,并安装到 lib/myproject
中。换句话说,我们有三个不同种类的文件,它们使用一个单一的 target 名称创建,必须安装到三个不同的目标!幸运的是,可以使用 category 关键字参数解决此问题。可以使用以下命令安装共享库
install(TARGETS mySharedLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/myproject)
这告诉 CMake,RUNTIME
文件(.dll)应安装到 bin
,LIBRARY
文件(.so)应安装到 lib
,ARCHIVE
(.lib)文件应安装到 lib/myproject
。在 UNIX 上,将安装 LIBRARY
文件;在 Windows 上,将安装 RUNTIME
和 ARCHIVE
文件。
如果要将上述示例项目打包到单独的运行时和开发组件中,则我们必须为每个已安装的目标文件分配适当的组件。为了运行应用程序,需要可执行文件、共享库和插件,因此它们属于 Runtime
组件。同时,导入库(对应于 Windows 上的共享库)和静态库仅需要开发应用程序扩展,因此属于 Development
组件。
可以通过将 COMPONENT
参数添加到上述每个命令来指定组件分配。您还可以将所有安装规则合并到一个命令调用中,这相当于上述所有命令都添加了组件。使用它们各自类别的规则安装每个目标生成的文件。
install(TARGETS myExecutable mySharedLib myStaticLib myPlugin
RUNTIME DESTINATION bin COMPONENT Runtime
LIBRARY DESTINATION lib COMPONENT Runtime
ARCHIVE DESTINATION lib/myproject COMPONENT Development)
可以指定 NAMELINK_ONLY
或 NAMELINK_SKIP
作为 LIBRARY
选项。在某些平台上,版本化的共享库具有如下符号链接:
lib<name>.so -> lib<name>.so.1
其中 lib<name>.so.1
是库的 soname,lib<name>.so
是在给定 -l<name>
时帮助链接器找到库的“名称链接”。当安装库目标时,NAMELINK_ONLY
选项仅导致安装名称链接。当安装库目标时,NAMELINK_SKIP
选项导致安装除名称链接以外的库文件。当未给出任何选项时,安装这两部分。在版本化的共享库没有名称链接或库未经过版本化的平台上,NAMELINK_SKIP
选项安装库,NAMELINK_ONLY
选项不安装任何内容。有关创建版本化共享库的详细信息,请参阅 VERSION
和 SOVERSION
目标属性。
安装文件¶
项目可能会安装除了使用 add_executable
或 add_library
之外创建的文件,例如头文件或文档。使用 FILES
标志指定通用的文件安装。
紧跟在 FILES
关键字后面的是要安装的文件列表。相对路径是相对于当前源目录评估的。文件将被安装到给定的 DESTINATION
目录。例如,命令
install(FILES my-api.h ${CMAKE_CURRENT_BINARY_DIR}/my-config.h
DESTINATION include)
从源代码目录中安装文件 my-api.h
,从构建目录中安装文件 my-config.h
到安装前缀下的 include 目录。默认情况下,被安装的文件会赋予权限 OWNER_WRITE
、OWNER_READ
、GROUP_READ
和 WORLD_READ
,但这可以通过指定 PERMISSIONS
选项来覆盖。考虑想要在 UNIX 系统上安装一个只有其所有者(例如 root)可读的全局配置文件的情况。我们使用命令执行此操作
install(FILES my-rc DESTINATION /etc
PERMISSIONS OWNER_WRITE OWNER_READ)
它以所有者读/写权限将文件 my-rc
安装到绝对路径 /etc
。
RENAME
参数为已安装文件指定一个可能与原始文件不同的名称。仅当命令安装单个文件时才允许重命名。例如,命令
install(FILES version.h DESTINATION include RENAME my-version.h)
将把文件 version.h
从源目录安装到安装前缀下的 include/my-version.h
。
安装程序¶
项目还可以安装帮助程序,例如实际上未作为目标编译的外壳脚本或 Python 脚本。可以使用 FILES
标志和 PERMISSIONS
选项添加执行权限,使用这两个选项来安装这些程序。但是,很多情况下需要一种更简单的界面。CMake 为此提供了 PROGRAMS
标志。
紧跟在 PROGRAMS
关键字后面的是要安装的脚本列表。此命令与 FILES
标志相同,除了默认权限之外还包括 OWNER_EXECUTE
、GROUP_EXECUTE
和 WORLD_EXECUTE
。例如,我们可以使用以下命令安装一个 Python 实用程序:
install(PROGRAMS my-util.py DESTINATION bin)
该命令将 my-util.py
安装到安装前缀的 bin
目录,并赋予其所有者、组和全世界读/执行权限,以及所有者写入权限。
安装目录¶
项目还可以提供一个包含大量资源文件的目录,例如图标或 html 文档。可以使用 DIRECTORY
签名安装整个目录。
DIRECTORY
关键字紧跟要安装的目录列表。相对路径根据当前源目录进行评估。每个指定的目录都安装到目标目录。在复制目录时,每个输入目录名称的最后一个组件都会附加到目标目录。例如,此命令
install(DIRECTORY data/icons DESTINATION share/myproject)
将 data/icons
目录从源目录安装到安装前缀下的 share/myproject/icons
中。尾部斜杠将使最后一个组件为空,并将输入目录的内容安装到目标目录。此命令
install(DIRECTORY doc/html/ DESTINATION doc/myproject)
将 doc/html
的内容从源目录安装到安装前缀下的 doc/myproject
中。如果没有给出输入目录名称,如
install(DIRECTORY DESTINATION share/myproject/user)
则将创建目标目录,但不会向其中安装任何内容。
通过 DIRECTORY
签名安装的文件与 FILES
签名具有相同的默认权限。通过 DIRECTORY
签名安装的目录与 PROGRAMS
签名具有相同的默认权限。FILE_PERMISSIONS
和 DIRECTORY_PERMISSIONS
选项可用于覆盖这些默认值。考虑一个案例,其中包含大量示例 shell 脚本的目录要安装到所有者和组都可写的目录中。我们可以使用此命令
install(DIRECTORY data/scripts DESTINATION share/myproject
FILE_PERMISSIONS
OWNER_READ OWNER_EXECUTE OWNER_WRITE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
DIRECTORY_PERMISSIONS
OWNER_READ OWNER_EXECUTE OWNER_WRITE
GROUP_READ GROUP_EXECUTE GROUP_WRITE
WORLD_READ WORLD_EXECUTE
)
将目录 data/scripts
安装到 share/myproject/scripts
中,并设置所需的权限。在某些情况下,项目创建的已完全准备好的输入目录可能已经设置了所需的权限。USE_SOURCE_PERMISSIONS
选项指示 CMake 在安装期间使用输入目录中的文件和目录权限。如果在前一个示例中,输入目录已经使用正确的权限进行了准备,则可以使用以下命令代替
install(DIRECTORY data/scripts DESTINATION share/myproject
USE_SOURCE_PERMISSIONS)
如果要安装的输入目录在源管理下,输入中可能存在你不想安装的额外子目录。还有可能存在不应该安装或要以不同权限安装的特定文件,而大多数文件将获得默认值。此时可以使用 PATTERN
和 REGEX
选项。 PATTERN
选项首先跟随一个通配模式,然后跟随一个 EXCLUDE
或 PERMISSIONS
选项。 REGEX
选项首先跟随一个正则表达式,然后跟随 EXCLUDE
或 PERMISSIONS
。 EXCLUDE
选项跳过匹配前一个模式或表达式的那些文件或目录的安装,而 PERMISSIONS
选项为它们分配特定的权限。
每个输入文件和目录将针对包含正斜杠的完整路径进行模式或正则表达式测试。一个模式只会匹配在完整路径末尾出现的完整文件或目录名称,而一个正则表达式可以匹配任何部分。例如,模式 foo*
将匹配 .../foo.txt
但不匹配 .../myfoo.txt
或 .../foo/bar.txt;
而正则表达式 foo
将匹配它们全部。
回到上面安装 icons 目录的示例,设想一个输入目录由 git 管理,而且还包含我们不希望安装的一些额外文本文件。此命令
install(DIRECTORY data/icons DESTINATION share/myproject
PATTERN ".git" EXCLUDE
PATTERN "*.txt" EXCLUDE)
在安装 icons 目录的同时忽略其中包含的任何 .git 目录或文本文件。使用 REGEX
选项的等效命令是
install(DIRECTORY data/icons DESTINATION share/myproject
REGEX "/.git$" EXCLUDE
REGEX "/[^/]*.txt$" EXCLUDE)
它使用“/”和“$”以与模式相同的方式约束匹配。考虑一个类似的情况,其中输入目录包含壳脚本和文本文件,我们希望将它们与其他文件一起安装,但使用不同的权限。此命令
install(DIRECTORY data/other/ DESTINATION share/myproject
PATTERN ".git" EXCLUDE
PATTERN "*.txt"
PERMISSIONS OWNER_READ OWNER_WRITE
PATTERN "*.sh"
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
将 source 目录中的 data/other
内容安装到 share/myproject
,同时忽略 .git 目录,并向 .txt
和 .sh
文件授予特定权限。
安装脚本¶
项目安装可能需要执行除在安装目录中放置文件之外的其他任务。第三方软件包可能提供其自己的机制来注册新的插件,这些插件必须在项目安装期间调用。 SCRIPT
签名就是为此目的提供的。
SCRIPT
关键字紧跟着一个 CMake 脚本的名称。CMake 将在安装期间执行此脚本。如果给定的文件名是相对路径,它将针对当前源目录进行评估。一个简单的用例是在安装期间打印一个消息。我们首先编写一个包含以下代码的 message.cmake
文件
message("Installing My Project")
然后使用以下命令引用此脚本
install(SCRIPT message.cmake)
主 CMakeLists 文件处理过程中不执行自定义安装脚本;它们在安装过程本身执行。在包含 install (SCRIPT)
调用的代码中定义的变量和宏从脚本中不可访问。但在脚本执行过程中定义了一些变量,可用来了解安装信息。变量 CMAKE_INSTALL_PREFIX
设置为实际的安装前缀。它可能与相应的缓存变量值不同,因为安装脚本可能由使用不同前缀的打包工具执行。环境变量 ENV{DESTDIR}
可由用户或打包工具设置。它的值在前缀和绝对安装路径中添加前缀,以确定安装文件的位置。为了引用磁盘上的安装位置,自定义脚本可以使用 $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}
作为路径的顶层部分。变量 CMAKE_INSTALL_CONFIG_NAME
设置为当前正在安装的构建配置的名称(比如 Debug、Release 等)。在特定组件的安装过程中,变量 CMAKE_INSTALL_COMPONENT
设置为当前组件的名称。