[Development] Qt 4.x and Qt 5 frameworks should use @rpath (QTBUG-31814)

Jake Petroules jake.petroules at petroules.com
Fri Aug 1 23:22:29 CEST 2014


On 2014-08-01, at 03:01 PM, Adam Strzelecki <ono at java.pl> wrote:

>>> All we need to do is build all Qt dylibs with install_name = @rpath/pathrelative_to_lib_folder/some.dylib and make sure qmake adds following linker flags to all apps linking to Qt:
>> 
>> Just @rpath/QtModuleName.framework/Versions/A/QtModuleName and @rpath/qtsomethingorother.dylib
> 
> I am sorry, you are right, we don't have umbrella frameworks in Qt so this should be sufficient.
> 
>> No - don't include the last one. macdeployqt (nor any other tool) should NOT modify the binaries at all (modifying is bad for codesigning too). This is why we have DYLD_LIBRARY_PATH (see below).
> 
> I believe we had this discussion in the bug report thread. The problem with DYLD_LIBRARY_PATH is that you need to inject it somehow to process launching the app:
> 
> (1) if we put it into Info.plist it won't work for bundle less apps (i.e. for simple testing)

Actually you CAN add Info.plist to bundle-less apps using the linker arguments: `-sectcreate __TEXT __info_plist Info.plist` which will embed it in the binary. All OS X APIs handle this transparently. Though, LSEnvironment is not a good solution for the problem we are trying to solve here.

> (2) if we put it into user's .bash_profile then it won't work in apps launched via Finder, as launchd does not run/respect .bash_profile
> (3) if we use launched setenv it will work just for single UI session
> (4) if we put it /etc/launchd-user.conf then we need admin rights + we may likely break other apps

Why would you do any of these things?

> So altogether this is safest solution. Please note that if we gonna deploy Qt frameworks via macdeployqt we need to resign application anyway because app bundle gonna be changed.

"Inject"? DYLD_LIBRARY_PATH is an environment variable. You simply set it in the process's environment before spawning. This is the correct solution and is neither difficult nor error prone. It is something you do only during development time (i.e. in Creator or qbs); once the app is deployed with macdeployqt, the frameworks will be copied into the bundle and setting DYLD_LIBRARY_PATH is no longer necessary.

In a terminal session, simply:

$ export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/path/to/Qt/Frameworks
$ open /apps/MyApp.app

In Creator, just click run and this will be handled automatically. In qbs just type `qbs run -p foo` and this will be handled automatically.

@rpath and DYLD_LIBRARY_PATH exist for good reasons, we must use them properly, not a halfway solution. ANY solution that involves placing absolute paths in ANY file inside the MyApp.app/ directory tree is a wrong solution. Note that Qt Creator already does the logical equivalent of what I described on Windows; the Qt libraries path is added to the PATH when the process is launched. Launching directly in Explorer doesn't work, and launching directly from Finder doesn't need to work either.

However, if we add a QMAKE_EMBEDDED_FRAMEWORKS variable, essentially rolling macdeployqt functionality directly into qmake, then it would work when launched in Finder. This is closer to how the native tools do things anyways, but DYLD_LIBRARY_PATH should still be set by Creator/qbs regardless of whether that is added.

>> No, QMAKE_INSTALLNAMEPREFIX, which is set to "@rpath" (as opposed to an absolute path as is done currently).
> 
> You're right, this is much simpler. Of course my previous solution was considering umbrella frameworks, or frameworks having plugins inside of them, that's why prefix matching.

No need for prefix matching even if you do this. Plugins embedded in their respective frameworks would actually make a lot of sense. Just add more rpaths to the Qt plugins, like @loader_path/../../../Frameworks or however many levels it is.

>>> Once this is done qtbase Qmake project should define QMAKE_RPATHPREFIX to be Qt SDK install_prefix/lib during build process.
>> 
>> No replacement needs to be done. (…)
> 
> I meant changes that has to be done in Qt SDK build process itself (which is defined in quake projects in qtbase repo). In this case each Qt module should set QMAKE_INSTALLNAMEPREFIX=@rpath on Mac.
> 
>> export QT_INSTALL_LIBS=$(qmake -query QT_INSTALL_LIBS)
>> export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$QT_INSTALL_LIBS
>> /apps/MyApp.app/Contents/MacOS/MyApp
> 
> This solution will work only if you are going to launch the app in terminal or your tool (i.e. Qt Creator) will be able to figure out it needs to set DYLD_LIBRARY_PATH which is far to complicated.

I'm a bit confused by your sentence structure. Are you saying it's complicated for Qt Creator to figure out if it needs to set DYLD_LIBRARY_PATH? It isn't. The value is `qmake -query QT_INSTALL_LIBS`. As I said above, there is no requirement that the application be launchable directly from Finder (doesn't work on Windows from Explorer either) at development time.

>> What might be good, too, is a QMAKE_EMBEDDED_FRAMEWORKS or somesuch which copies the Qt frameworks into the application bundle at build time (though you'd still set DYLD_LIBRARY_PATH because not everyone would necessarily use QMAKE_EMBEDDED_FRAMEWORKS). No reason to postpone that step to macdeployqt; this is how the native Apple tools work anyways. Ossi will disagree with me here and say that anything to do with creating bundle structures is a deployment step only, though. :)
> 
> Well, to be frank there is a simple workaround for that :) Why not symlink Qt frameworks at build time, then replace symlinks with real copies on deployment step?
> 
> So the easiest solution would be to symlink Qt frameworks when your create app bundle otherwise add /full/path/to/qt when you use bundle-less app. No need to mess with DYLD_LIBRARY_PATH.

Symlinks make no sense. Just copy the full frameworks, there is no disadvantage to this. This is the standard workflow for development on Apple platforms anyways and has been since NeXTSTEP. I'm sure someone will bring up "performance!!1" but that is a poor argument;

(1) It can be optimized; you're not going to copy the entire frameworks every single time you press build.
(2) In the NeXTSTEP days, computers were much slower and no one had SSDs, despite this;
(3) Of all the OS X applications that exist, very few use Qt and I have NEVER seen a single complaint about the supposed performance implications of copying frameworks at build time like is done for the vast majority of OS X applications built with Xcode. Let's focus on REAL bottlenecks that actually cause performance problems, instead of an extremely minor one that actually solves the major problem we are concerned with.
(4) No one's forced - simply copy the frameworks at build time for normal people, and premature optimizing performance pedants can use the DYLD_LIBRARY_PATH based solution. This is why I proposed a QMAKE_EMBEDDED_FRAMEWORKS, pedants need not set it. Everyone's happy.

>> Also, I'd add @executable_path/../Libraries and @loader_path/../Libraries to the search path as well. While less commonly known/used (and found in at least some official Apple documentation), some people do like to put frameworks in $CONTENTS_FOLDER_PATH/Frameworks and dylibs in $CONTENTS_FOLDER_PATH/Libraries.
> 
> I don't think you should add these by default since Qt doesn't need them, so unlikely your app need them. If you use some 2rd party library you are free to extend QMAKE_RPATH list yourself.

It might need them. Depends if we change macdeployqt to place a dylibs (non-frameworks) build of Qt libraries inside Libraries instead of Frameworks. Not a major part of the discussion anyways, just an idea.

> Cheers,
> -- 
> Adam


Finally, I want to stress: OS X and iOS are not like other platforms. They are not Windows, or Android, or Linux, or BlackBerry, or FreeBSD. They have a unique set of standards and workflows that tend to be very different from others and it's futile to attempt to shoehorn ideas from other platforms into how things "should" work. It results in a poor developer (and user) experience and makes Qt unnecessarily difficult to work with on OS X at the present time.

Let's use standard solutions instead of strange contraptions. Standard solutions involve @rpath along with DYLD_LIBRARY_PATH, copying frameworks at build time, or both (preferably both). Not symlinks, not LSEnvironment or other global configurations, not modifying Qt libraries at any point after installation, and not embedding absolute paths in ANY files that are part of the application bundle.
-- 
Jake Petroules - jake.petroules at petroules.com
Chief Technology Officer - Petroules Corporation
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20140801/e6cd8180/attachment.html>


More information about the Development mailing list