Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt Mac Application Failed to Create Self-contained App Bundle (Qt Creator Build)

I'm using Qt Creator 3.6.1 with Qt 5.6.0 (Clang 7.0 (Apple), 64 bit), and I encountered some problems while trying to create an app bundle for deployment.

Note: the app name is called bibi

  1. Qt Creator successfully generated bibi.app under build-bibi-Desktop_Qt_5_6_0_clang_64bit-Release/ folder
  2. This bibi.app failed to link Qt successfully on the other Mac
  3. macdeployqt doesn't solve the problem

Followings are the details:

Error screenshot when running bibi.app on the other Mac:

otool

> otool -L build-bibi-Desktop_Qt_5_6_0_clang_64bit-Release/bibi.app/Contents/MacOS/bibi
build-bibi-Desktop_Qt_5_6_0_clang_64bit-Release/bibi.app/Contents/MacOS/bibi:
    @rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.6.0, current version 5.6.0)
    @rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.6.0, current version 5.6.0)
    @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.6.0, current version 5.6.0)
    /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

macdeployqt

> which macdeployqt
/Users/<myusername>/Qt/5.6/clang_64/bin/macdeployqt
> macdeployqt bibi.app
> otool -L bibi.app/Contents/MacOS/bibi
bibi.app/Contents/MacOS/bibi:
    @rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.6.0, current version 5.6.0)
    @rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.6.0, current version 5.6.0)
    @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.6.0, current version 5.6.0)
    /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

tree bibi.app

bibi.app
├── Contents
│   ├── Frameworks
│   │   ├── QtCore.framework
│   │   │   ├── QtCore -> Versions/Current/QtCore
│   │   │   ├── Resources -> Versions/Current/Resources
│   │   │   └── Versions
│   │   │       ├── 5
│   │   │       │   ├── QtCore
│   │   │       │   └── Resources
│   │   │       │       └── Info.plist
│   │   │       └── Current -> 5
│   │   ├── QtDBus.framework
│   │   │   ├── QtDBus -> Versions/Current/QtDBus
│   │   │   ├── Resources -> Versions/Current/Resources
│   │   │   └── Versions
│   │   │       ├── 5
│   │   │       │   ├── QtDBus
│   │   │       │   └── Resources
│   │   │       │       └── Info.plist
│   │   │       └── Current -> 5
│   │   ├── QtGui.framework
│   │   │   ├── QtGui -> Versions/Current/QtGui
│   │   │   ├── Resources -> Versions/Current/Resources
│   │   │   └── Versions
│   │   │       ├── 5
│   │   │       │   ├── QtGui
│   │   │       │   └── Resources
│   │   │       │       └── Info.plist
│   │   │       └── Current -> 5
│   │   ├── QtPrintSupport.framework
│   │   │   ├── QtPrintSupport -> Versions/Current/QtPrintSupport
│   │   │   ├── Resources -> Versions/Current/Resources
│   │   │   └── Versions
│   │   │       ├── 5
│   │   │       │   ├── QtPrintSupport
│   │   │       │   └── Resources
│   │   │       │       └── Info.plist
│   │   │       └── Current -> 5
│   │   └── QtWidgets.framework
│   │       ├── QtWidgets -> Versions/Current/QtWidgets
│   │       ├── Resources -> Versions/Current/Resources
│   │       └── Versions
│   │           ├── 5
│   │           │   ├── QtWidgets
│   │           │   └── Resources
│   │           │       └── Info.plist
│   │           └── Current -> 5
│   ├── Info.plist
│   ├── MacOS
│   │   └── bibi
│   ├── PkgInfo
│   ├── PlugIns
│   │   ├── imageformats
│   │   │   ├── libqdds.dylib
│   │   │   ├── libqgif.dylib
│   │   │   ├── libqicns.dylib
│   │   │   ├── libqico.dylib
│   │   │   ├── libqjpeg.dylib
│   │   │   ├── libqtga.dylib
│   │   │   ├── libqtiff.dylib
│   │   │   ├── libqwbmp.dylib
│   │   │   └── libqwebp.dylib
│   │   ├── platforms
│   │   │   └── libqcocoa.dylib
│   │   └── printsupport
│   │       └── libcocoaprintersupport.dylib
│   └── Resources
│       ├── empty.lproj
│       └── qt.conf
└── Icon\r

38 directories, 32 files

Thanks.

Problem Solved

Thanks scottt, and the problem is solved. Here are the reasons I failed to create self-contained app bundle successfully:

  • otool -L didn't resolve @rpath, and I was confused since it always returned me the same output
  • lack of a way to test if the bundle is already self-containing all the frameworks

In short, the problem can be solve by using Scott's otool-rpath, lsof, or setting DYLD_PRINT_LIBRARIES and DYLD_PRINT_TO_FILE. And, I've written a note with details here.

like image 405
Heron Yang Avatar asked Mar 12 '23 11:03

Heron Yang


1 Answers

Assuming you used the qt-unified-max-x64-online.dmg installer and installed Qt into $HOME/Qt. You can build your project with:

cd MY-QT-PROJECT
QT_BIN_DIR=$HOME/Qt/5.6/clang_64/bin
make clean
$QT_BIN_DIR/qmake -config release
make -j$(getconf NPROCESSORS_ONLN)

This creates an app bundle but it would NOT run on regular users' machines.

Look at the RPATH in the Mach-O executable buried in the bundle:

otool-rpath ./*.app/Contents/MacOS/*
/Users/user/Qt/5.6/clang_64/lib

I'm using a small otool-rpath script I wrote myself here for illustrative purposes.

That RPATH plus the install names listed in your otool -L output above make the dynamic linker, dyld, look for Qt frameworks under /Users/user/Qt/5.6/clang_64/lib. Thus, it would NOT work for users who haven't installed Qt in the same location.

To change that, run Qt’s macdeployqt tool:

$QT_BIN_DIR/macdeployqt ./*.app -verbose=3 -always-overwrite -appstore-compliant

See an example macdeployqt log here.

macdeployqt creates self-contained app bundles. Observe how the RPATH in the executable changed from /Users/user/Qt/5.6/clang_64/lib to @executable_path/../Frameworks:

otool-rpath ./*.app/Contents/MacOS/*
@executable_path/../Frameworks

@executable_path does the obvious thing and is expanded by dyld to bibi.app/Contents/MacOS at run time. The RPATH in the executable and the install names together make dynamic linking within the bundle work at run time.

Further Reading

  • You can get a log of how the dynamic linker looks up libraries by setting DYLD_PRINT_LIBRARIES and DYLD_PRINT_TO_FILE. Example log. See dyld(1).

  • You can use lsof -p $PID | grep QtCore to inspect which copy of the framework is actually used by your app at run-time. Example log.

  • Run-Path Dependent Libraries
  • @executable path, @load path and @rpath

References

  • Mac Developer Library: Mach-O Programming Topics
  • OSX ABI Mach-O File Format Reference

Keywords: "RPATH", "INSTALL NAME", "Mach-O dynamic linking".

Note that RPATH in ELF has subtlely different semantics.

like image 160
scottt Avatar answered Apr 29 '23 14:04

scottt