[英]Can't use fixup_bundle() to create a portable bundle with Qt
I already searched this issue on other posts, but nothing so far.我已经在其他帖子上搜索过这个问题,但到目前为止还没有。 So here I am.
所以我来了。
I'd like to create a bundle that is portable.我想创建一个可移植的包。 Portable as in "I can run it on any OS X machine, even if my required libs (Qt) are not installed".
便携,如“我可以在任何 OS X 机器上运行它,即使我需要的库(Qt)没有安装”。 Unfortunately, I can't figure out how to use fixup_bundle() (which seems the right tool for it) to achieve this goal.
不幸的是,我不知道如何使用 fixup_bundle()(这似乎是它的正确工具)来实现这个目标。
Here is my minimal CMake generated C++ project :这是我最小的 CMake 生成的 C++ 项目:
main.cpp主程序
#include <QString>
#include <iostream>
int main()
{
QString s("Hello, world!");
std::cout << s.toStdString() << std::endl;
return 0;
}
CMakeLists.txt CMakeLists.txt
cmake_minimum_required(VERSION 2.8.11)
project(test)
# That part because I use a custom build of Qt. That's not the
# relevant part (I guess?)
FILE(GLOB QTROOTS path_to_qt/Qt-4.8.1/osx/bin/qmake)
find_program(QT_QMAKE_EXECUTABLE NAMES qmake PATHS ${QTROOTS})
find_package(Qt4 COMPONENTS QtCore REQUIRED)
include(${QT_USE_FILE})
add_executable(test MACOSX_BUNDLE main.cpp)
target_link_libraries(test ${QT_LIBRARIES})
install(SCRIPT bundle.cmake)
bundle.cmake bundle.cmake
INCLUDE(BundleUtilities)
fixup_bundle(test.app "" "")
Here's the resulting test.app structure这是生成的 test.app 结构
test.app
- Contents
- Info.plist
- Frameworks
- QtCore.framework
- Versions
- 4
- QtCore
- MacOS
- test
Everything that is needed seems to be in the bundle.所需的一切似乎都在捆绑包中。 Everything compiles smoothly, runs well, but when I invoke fixup_bundle, that's what I get :
一切都顺利编译,运行良好,但是当我调用 fixup_bundle 时,这就是我得到的:
vincent@hpcd0016-lion:tmp/test_bundle/ (0) > make install
[100%] Built target test
Install the project...
-- Install configuration: ""
-- fixup_bundle
-- app='test.app'
-- libs=''
-- dirs=''
-- fixup_bundle: preparing...
-- fixup_bundle: copying...
-- 1/4: *NOT* copying '/Users/vincent/tmp/test_bundle/test.app/Contents/MacOS/test'
-- 2/4: copying 'path_to_qt/Qt-4.8.1/osx/lib//QtCore.framework/Versions/4/QtCore'
-- fixup_bundle: fixing...
-- 3/4: fixing up '/Users/vincent/tmp/test_bundle/test.app/Contents/MacOS/test'
exe_dotapp_dir/='test.app/Contents/MacOS/'
item_substring='/Users/vincent/t'
resolved_embedded_item='/Users/vincent/tmp/test_bundle/test.app/Contents/MacOS/test'
Install or copy the item into the bundle before calling fixup_bundle.
Or maybe there's a typo or incorrect path in one of the args to fixup_bundle?
CMake Error at /Applications/CMake 2.8-11.app/Contents/share/cmake-2.8/Modules/BundleUtilities.cmake:568 (message):
cannot fixup an item that is not in the bundle...
Call Stack (most recent call first):
/Applications/CMake 2.8-11.app/Contents/share/cmake-2.8/Modules/BundleUtilities.cmake:656 (fixup_bundle_item)
bundle.cmake:2 (fixup_bundle)
cmake_install.cmake:31 (INCLUDE)
make: *** [install] Error 1
There's the dependencies path (given by otool -L) :有依赖路径(由 otool -L 给出):
vincent@hpcd0016-lion:test.app/Contents/ (0) > otool -L test.app/Contents/MacOS/test
test.app/Contents/MacOS/test:
path_to_qt/Qt-4.8.1/osx/lib//QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.1)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
vincent@hpcd0016-lion:tmp/test_bundle/ (0) > otool -L test.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore
test.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore:
path_to_qt/Qt-4.8.1/osx/lib//QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.1)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 41.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 635.15.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55010.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1094.0.0)
/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 53.0.0)
Obviously, fixup_bundle did not fix up the bundle for the binaries still have their ids set to my machine's paths.显然, fixup_bundle 没有修复二进制文件的包,它们的 id 仍然设置为我机器的路径。
What am I doing wrong on this simple example?在这个简单的例子中我做错了什么?
I added this line at the top of my CMakeLists.txt我在 CMakeLists.txt 的顶部添加了这一行
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR})
And that was it.就是这样。
By default, apparently, CMAKE_INSTALL_PREFIX is set to /usr/local on my machine.显然,默认情况下,CMAKE_INSTALL_PREFIX 在我的机器上设置为 /usr/local。 If changing it to my current working directory solved the issue, that means CMake was trying to perform some operations on /usr/local (which it is not allowed to do).
如果将其更改为我当前的工作目录解决了该问题,则意味着 CMake 试图在 /usr/local 上执行一些操作(这是不允许的)。 So why the error message does not mention such a right access error?
那么为什么错误信息没有提到这样一个正确的访问错误呢?
I don't know if I haven't read enough documentation, or if the documentation needs some precisions...我不知道我是否没有阅读足够的文档,或者文档是否需要一些精度......
In addition, I actually had to be even more explicit about the install path (ie within the .app).此外,我实际上必须更加明确地说明安装路径(即在 .app 中)。
Like this:像这样:
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR})
install(CODE "
include(BundleUtilities)
fixup_bundle(${CMAKE_INSTALL_PREFIX}/MyApp.app \"\" \"\")
" COMPONENT Runtime)
(NB no separate SCRIPT but rather embedded CODE - shouldn't make a difference). (注意没有单独的脚本,而是嵌入的代码 - 应该没有区别)。
To respond to timlukins' answer, if you're going to invoke any CMake variables in the install code then you have to be very careful to escape them against early evaluation.为了回应 timlukins 的回答,如果您要在安装代码中调用任何 CMake 变量,那么您必须非常小心地避免早期评估。
The code you posted will resolve CMAKE_INSTALL_PREFIX
immediately, before it embeds your code into the install script.您发布的代码将立即解析
CMAKE_INSTALL_PREFIX
,然后将您的代码嵌入到安装脚本中。 Which means the cmake_install.cmake
file will contain:这意味着
cmake_install.cmake
文件将包含:
include(BundleUtilities)
fixup_bundle(/usr/local/MyApp.app "" "")
(Or whatever path you had CMAKE_INSTALL_PREFIX
set to when generating the build tree.) That's not what you want, since it makes your build unrelocatable even before it's installed. (或者在生成构建树时将
CMAKE_INSTALL_PREFIX
设置为的任何路径。)这不是您想要的,因为它使您的构建即使在安装之前也无法重定位。
You need to protect the CMAKE_INSTALL_PREFIX
invocation so it makes it into the install script intact, and will use the install-time value.您需要保护
CMAKE_INSTALL_PREFIX
调用,使其完好无损地进入安装脚本,并将使用安装时值。 There's two ways you can do that.有两种方法可以做到这一点。
install(CODE " include(BundleUtilities) fixup_bundle(\\"\\$\\{CMAKE_INSTALL_PREFIX\\}/MyApp.app\\" \\"\\" \\"\\") " COMPONENT Runtime)
install(CODE [[ include(BundleUtilities) fixup_bundle("${CMAKE_INSTALL_PREFIX}/MyApp.app" "" "") ]] COMPONENT Runtime)
No variable names will be recognized or references replaced inside a bracket argument, so it's safe to use them unescaped.不会识别变量名称或替换括号参数内的引用,因此可以安全地使用未转义的变量名。 (Generator expressions will still be processed, which can sometimes be quite handy. Not for this, exactly, but I did just think of one way...)
(生成器表达式仍将进行处理,有时可以说是相当方便的。不是这个,就是这样,但我只是觉得的一种方式......)
To oarfish's comment question:对于 oarfish 的评论问题:
How did you solve the problem that qt platform plugin (libqcocoa.dylib) is not bundled with the rest?
qt平台插件(libqcocoa.dylib)未捆绑的问题你是怎么解决的?
Here's how I might handle that.这是我可以处理的方法。 It's lightly tested, seems to at least be on the right track.
它经过了轻微的测试,似乎至少是在正确的轨道上。 Though I can't help feeling like there must be an easier way to handle the output paths.
虽然我不禁觉得必须有一种更简单的方法来处理输出路径。
install(CODE [[
include(BundleUtilities)
# You could also do this part before the CODE using
# install(TARGETS MyApp BUNDLE DESTINATION foo)
#
file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/foo"
TYPE DIRECTORY FILES "$<TARGET_BUNDLE_DIR:MyApp>")
# This string is crazy-long enough that it's worth folding into a var...
set (_plugin_file "$<TARGET_FILE_NAME:Qt5::QCocoaIntegrationPlugin>")
# Ditto the output paths for our installation
set (_appdir "${CMAKE_INSTALL_PREFIX}/foo/MyApp.app")
set (_outdir "${_appdir}/Contents/plugins/platforms")
file(INSTALL DESTINATION "${_outdir}"
TYPE FILE FILES "$<TARGET_FILE:Qt5::QCocoaIntegrationPlugin>")
fixup_bundle("${_appdir}" "${_outdir}/${_plugin_file}" "")
]] COMPONENT Runtime)
As soon as you generate the build system, you can read the cmake_install.cmake
file that's output into the binary directory for this CMakeLists.txt
file, to see if the install(CODE...)
produced what you intended.一旦生成构建系统,您就可以读取输出到此
CMakeLists.txt
文件的二进制目录中的cmake_install.cmake
文件,以查看install(CODE...)
产生了您想要的结果。
If you're wondering where that ${_outdir}
path came from, that's Word of God.如果您想知道
${_outdir}
路径从何而来,那就是上帝的话语。
From Qt's " Using qt.conf
":从 Qt 的“使用
qt.conf
”:
Absolute paths are used as specified in the
qt.conf
file.使用
qt.conf
文件中指定的绝对路径。 All paths are relative to thePrefix
.所有路径都相对于
Prefix
。 [...] On macOS, thePrefix
is relative to theContents
in the application bundle.[...] 在 macOS 上,
Prefix
与应用程序包中的Contents
。 For example,application.app/Contents/plugins/
is the default location for loading Qt plugins.例如,
application.app/Contents/plugins/
Contents/plugins/ 是加载 Qt 插件的默认位置。 Note that the plugins need to be placed in specific sub-directories under theplugins
directory (see How to Create Qt Plugins for details).请注意,插件需要放在
plugins
目录下的特定子目录中(详见如何创建 Qt 插件)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.