简体   繁体   English

不能使用 fixup_bundle() 来创建带有 Qt 的可移植包

[英]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.有两种方法可以做到这一点。

  1. Furious escaping within the quotes...引述中的愤怒逃跑...
     install(CODE " include(BundleUtilities) fixup_bundle(\\"\\$\\{CMAKE_INSTALL_PREFIX\\}/MyApp.app\\" \\"\\" \\"\\") " COMPONENT Runtime)
  2. (Much easier:) Use a bracket argument ... (容易得多:) 使用括号参数...
     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...)产生了您想要的结果。

Destination path目的地路径

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 the Prefix .所有路径都相对于Prefix [...] On macOS, the Prefix is relative to the Contents 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 the plugins directory (see How to Create Qt Plugins for details).请注意,插件需要放在plugins目录下的特定子目录中(详见如何创建 Qt 插件)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM