如何配置CMakeLists.txt来安装共享库的公共标头?

问题描述 投票:0回答:3

我想使用cmake来安装我的库

edv
但是当我执行时:

cmake --build . --target install

它安装了,但只创建了

bin/edv.dll
lib/ < empty >
。如何让 cmake 将
EDV_PUBLIC_INCLUDE_DIRECTORIES
安装在
include/...
中?

这是我的

CMakeLists.txt

cmake_minimum_required(VERSION 3.12)

project(edv)

# include PUBLIC directories
set(EDV_PUBLIC_INCLUDE_DIRECTORIES      include/ )

set(EDV_PRIVATE_INCLUDE_DIRECTORIES     src/   )

# Edv source files list
file(GLOB_RECURSE EDV_SOURCE_FILES "src/*.cpp" "src/*.hpp*")


# build the library
add_library(${PROJECT_NAME} SHARED ${EDV_SOURCE_FILES} )

target_include_directories(${PROJECT_NAME} PUBLIC ${EDV_PUBLIC_INCLUDE_DIRECTORIES})
target_include_directories(${PROJECT_NAME} PRIVATE ${EDV_PRIVATE_INCLUDE_DIRECTORIES})

install (TARGETS ${PROJECT_NAME}
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    PUBLIC_HEADER DESTINATION include
)
c++ cmake
3个回答
19
投票

CMake 无法从目标推断出要安装的头文件集。这是有道理的,因为目标可能同时包含私有和公共头文件,但 CMake 不区分这些文件。因此,您必须在

INSTALL(FILES ...)
命令中显式列出头文件:

install(FILES ${MY_HEADER_FILES} DESTINATION include)

CMake 3.23 及更高版本更新:

CMake 版本 3.23 引入了File Sets,它允许以更优雅的方式处理头文件。这样,头文件只需在

CMakeLists.txt
中列出一次即可集成到 IDE 中并正确安装:

cmake_minimum_required(VERSION 3.23)

# example library with sources and headers located at ${MY_LIB_SOURCE_DIR}
add_library(my_lib)

target_sources(my_lib
# source files get added as before
   PUBLIC
     ${MY_LIB_SOURCE_DIR}/my_lib/my_lib.cpp
# header files get added via FILE_SET
# the BASE_DIR should point to the same directory that would be used as
# the target_include_directory; it will be added to the include directories
# of my_lib and be used to determine the relative install path
  PUBLIC FILE_SET HEADERS
  BASE_DIRS ${MY_LIB_SOURCE_DIR} FILES
    ${MY_LIB_SOURCE_DIR}/my_lib/my_lib.hpp
)

# install command needs to reference the file set again
install(TARGETS my_lib FILE_SET HEADERS)

3.23更新结束

您偶然发现的

PUBLIC_HEADER
字段与 OSX 框架机制相关,这是它自己的蠕虫。我建议你远离它,除非你真的想在 OSX 上将你的库部署为
.framework

特别注意 INCLUDES DESTINATION

 命令的 
INSTALL(TARGET ...)
 选项。虽然这实际上并不复制任何文件本身,但它允许将包含目录自动添加到配置包脚本提供的导入目标中。如果您打算向您的用户提供包配置脚本(您可能应该这样做,至少如果您希望您的用户也使用 CMake),您可能需要设置此选项。

由于安装机制总体上相当复杂,我有一个一个小的 github 项目,您可以在其中观察所有正在运行的元素。


10
投票

要安装

./include
文件夹中的所有标头,您需要做的就是:

  • 设置一个包含您要安装的所有头文件的列表(即定义

    EDV_INCLUDE_FILES
    ),

  • 使用所有这些头文件设置目标属性

    PUBLIC_HEADER

  • 使用安装目录设置

    PUBLIC_HEADER
    中的
    install(TARGETS ...)
    参数。

这个方法也是CMake支持macOS框架的基础。

我已经使用设置更新了上面的示例,以将项目的公共标头安装在项目目标输出目录的

./include
中。警告:我还没有亲自测试过项目定义,因此可能需要一些小的调整才能工作。

cmake_minimum_required(VERSION 3.12)

project(edv)

# include PUBLIC directories
set(EDV_PUBLIC_INCLUDE_DIRECTORIES      include/ )

set(EDV_PRIVATE_INCLUDE_DIRECTORIES     src/   )

# Edv source files list
file(GLOB_RECURSE EDV_SOURCE_FILES "src/*.cpp" "src/*.hpp*")

file(GLOB_RECURSE EDV_INCLUDE_FILES "include/*.hpp*")

# build the library
add_library(${PROJECT_NAME} SHARED ${EDV_INCLUDE_FILES} ${EDV_SOURCE_FILES} )

target_include_directories(${PROJECT_NAME} PUBLIC ${EDV_PUBLIC_INCLUDE_DIRECTORIES})
target_include_directories(${PROJECT_NAME} PRIVATE ${EDV_PRIVATE_INCLUDE_DIRECTORIES})

set_target_properties(${PROJECT_NAME}
    PROPERTIES
    PUBLIC_HEADER "${EDV_INCLUDE_FILES}"
)

install (TARGETS ${PROJECT_NAME}
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    PUBLIC_HEADER DESTINATION include
)

2
投票

Cmake 安装文档 与 ComicSansMS 几乎有相同的建议。

以下示例展示了在将标头安装到特定于项目的子目录时如何遵循此建议(在文档前面提到):

include(GNUInstallDirs)
install(FILES mylib.h
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/myproj
)

在此解决方案中,目标不是硬编码的,而是使用 GNUInstallDirs Cmake 模块定义的变量(并在斜杠后添加子目录)。使用变量使包维护者的事情变得更加灵活,并且变量的默认值可能是特定于系统的。 (可能不是特定于

CMAKE_INSTALL_INCLUDEDIR
,但绝对是特定于
CMAKE_INSTALL_LIBDIR
的系统。) 这是 CPPCon 2019 - Deep CMake for Library Authors 的 Craig Scott 的一个很好的解释

或者更好的是,如果

include
中有一个子文件夹,那么也可以安装整个目录:

include(GNUInstallDirs)
install(DIRECTORY include/myproj
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
© www.soinside.com 2019 - 2024. All rights reserved.