CMake将依赖关系复制到可执行输出路径

时间:2018-07-27 10:30:21

标签: c++ macos cmake shared-libraries dynamic-linking

我有以下简单的CMake项目。它基本上是一个可动态链接到Qt小部件的可执行文件(我以Qt为例)。我要弄清楚的是,是否可以使用CMake将所有链接的库(不仅是当前项目构建的库)复制到可执行输出目录。

cmake_minimum_required(VERSION 3.12)
project(MyProject)

set(CMAKE_CXX_STANDARD 14)

set(QT_CMAKE_DIR "/Users/huser/Qt/5.11.1/clang_64/lib/cmake")
set(CMAKE_PREFIX_PATH ${CMAKE_MODULE_PATH} ${QT_CMAKE_DIR})
find_package(Qt5 REQUIRED COMPONENTS Widgets)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_LIST_DIR}/build)

问题在于输出目录仅包含MyProject可执行文件(这是预期的行为)。但是,如果我将该可执行文件分发给没有安装Qt的人,他们将无法打开它。因此,我只想将必要的库/框架与可执行文件捆绑在一起。

运行otool -L MyProject列出了依赖项:

MyProject:
    @rpath/QtWidgets.framework/Versions/5/QtWidgets
    @rpath/QtGui.framework/Versions/5/QtGui
    @rpath/QtCore.framework/Versions/5/QtCore
    /usr/lib/libc++.1.dylib
    /usr/lib/libSystem.B.dylib

我正在寻找的是通过CMake的一种常见方法,即在构建步骤之后立即将这3个框架复制到输出目录中。这将导致以下目录结构:

build/
    MyProject
    QtWidgets.framework
    QtGui.framework
    QtCore.framework

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

要考虑两个方面:

  • 构建树
  • 安装树

构建树是您作为开发人员所使用的树,安装树是在执行install目标或提取软件包内容之后“创建”的东西。

要重新分发基于Qt5的项目,建议您使用两种工具:

  • CPack:这允许创建可分发给用户的生成程序包或存档。其中包括Windows安装程序,.tar,gz.dmg,...
  • macdeployqt:Qt提供的工具,可用于复制您的应用程序所需的所有库,插件...。

使用BundleUtilities仍然需要您明确标识并安装所有Qt插件。对于具有Qt以外的依赖关系的更复杂的应用程序确实很有帮助,但是对于简单的应用程序,我建议使用下面描述的方法。

您将在示例的修改后的版本下面找到该示例,其中包括有关最佳做法以及CPack和macdeployqt集成的一些建议。

配置并构建项目后,构建Package目标将创建一个MyProject-0.1.1-Darwin.dmg程序包。

请注意,还需要做更多的事情,但这应该是一个很好的起点。

阅读以下内容也可能会有所帮助:https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/RPATH-handling

要配置项目,请考虑传递变量-DQt5_DIR:PATH=/path/to/lib/cmake/Qt5而不是对路径进行硬编码。

假设源文件或项目位于名为src的目录中,则可以使用以下方式配置项目:

mkdir build
cd build
cmake -DQt5_DIR:PATH=/Volumes/Dashboards/Support/Qt5.9.1/5.9.1/clang_64/lib/cmake/Qt5 ../src/

src/CMakeLists.txt

cmake_minimum_required(VERSION 3.12)
project(MyProject)

set(CMAKE_CXX_STANDARD 14)

# Suggestions:
# (1) EXECUTABLE_OUTPUT_PATH is deprecated, consider
#     setting the CMAKE_*_OUTPUT_DIRECTORY variables
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)

set(CMAKE_MACOSX_BUNDLE 1)
set(CMAKE_INSTALL_RPATH "@executable_path/../Frameworks")

# Suggestions:
# (1) Do not hardcode path to Qt installation
# (2) Configure the project specifying -DQt5_DIR
#     See https://blog.kitware.com/cmake-finding-qt5-the-right-way/
# (3) By convention, "REQUIRED" is added at the end
find_package(Qt5 COMPONENTS Widgets REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets)
install(TARGETS ${PROJECT_NAME} DESTINATION . COMPONENT Runtime)

# Get reference to deployqt
get_target_property(uic_location Qt5::uic IMPORTED_LOCATION)
get_filename_component( _dir ${uic_location} DIRECTORY)
set(deployqt "${_dir}/macdeployqt")
if(NOT EXISTS ${deployqt})
  message(FATAL_ERROR "Failed to locate deployqt executable: [${deployqt}]")
endif()

# Execute deployqt during package creation
# See https://doc.qt.io/qt-5/osx-deployment.html#macdeploy
install(CODE "set(deployqt \"${deployqt}\")" COMPONENT Runtime)
install(CODE [===[
execute_process(COMMAND "${deployqt}" "${CMAKE_INSTALL_PREFIX}/MyProject.app")
]===] COMPONENT Runtime)

set(CPACK_GENERATOR "DragNDrop")
include(CPack)