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

Jake Petroules jake.petroules at petroules.com
Tue Aug 12 21:14:23 CEST 2014


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

>> Default behavior: I’m on favor of keeping it opt-in, either via "CONFIG += bundle_frameworks" or “make bundle”
> 
> Me too. However as I said previously stakeholders are to decide. I don't want start over this discussion with Jake.

Please stop saying that to fuel your argument just because I disagree with some of your points; it sounds kind of rude. Also, I am one of these "stakeholders", though I'm not sure what definition you're going off of, because the dictionary's definition "a person with an interest or concern in something" would include you as well, now, wouldn't it? ;)

Also, believe it or not, but I agree with not copying frameworks by default (see below)...

On 2014-08-12, at 10:25 AM, Adam Strzelecki <ono at java.pl> wrote:

> Okay, Phase II.
> 
> (1) Introduce "bundle_frameworks" CONFIG option, and set it default for "rpath" shared builds on iOS & OS X

This falls apart in the face of third party libraries. Who says a particular application is even using Qt with qmake?

> (2) Introduce "bundle" make target, when "bundle_frameworks" CONFIG is set, it is added to "all"
> 
> (3) Make's "bundle" will copy (update if not there) all used Qt frameworks to app's bundle Frameworks and used plugins to Plugins (currently implemented as separate macdeployqt)
> 
> NOTES:
> 
> * Since qmake knows which Qt libraries and plugins are used "bundle" target it will generate following Makefile entries
> 
> 	all: Sample.app bundle
> 
> 	bundle: Sample.app/Frameworks/QtCore.framework
> 	  mkdir -p Sample.app/Frameworks && cp -r $$[QT_INSTALL_LIBS]/QtCore.framework Sample.app/Frameworks
> 	  ...
> Of course this example is simplification, since we don't need to copy headers and we need to take debug versions or release. So there will be more commands in practice.

Yes, cp is a very slow way of copying, and is especially intolerable for incremental builds. Using rsync is much better as you get instantaneous (< 70ms) incremental builds (and makes your speed argument about making copying non-default, totally pointless).

> 
> * One can disable "bundle_frameworks" via CONFIG -= bundle_frameworks, so existing workflow where executable is given rpath pointing to Qt libraries
> 
> * If disabled, one can still do "make bundle" that is equivalent to current "macdeployqt" and "bundle" target will also add "install_name_tool" -rpath replacement steps
> 
> Regards,
> -- 
> Adam
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development


So, this is all wrong; the biggest problem is that it's too Qt centric. Also, it's extremely inflexible (as is macdeployqt). In the real world, people use more libraries than just Qt and may want to customize their bundle layout, the locations of embedded frameworks, etc. So let's summarize what must be done:

(1) Add qmake options QMAKE_EMBEDDED_FRAMEWORKS, QMAKE_EMBEDDED_LIBRARIES, QMAKE_EMBEDDED_PLUGINS, each taking a list of absolute paths and/or objects; applies to any qmake bundle target, app, framework, etc. Does nothing for non-bundle targets. Does nothing outside of OS X and iOS.
(1a) "objects" (don't know what this is called in qmake but INSTALLS works similarly) meaning:

weirdlib.src = /Library/Frameworks/WeirdLib.framework
weirdlib.dst = $$join($$QMAKE_EMBEDDED_FRAMEWORKS_LOCATION, SubFolderForSomeReason)
weirdlib.headers = true
weirdlib.variants = debug release
QMAKE_EMBEDDED_FRAMEWORKS += weirdlib /Library/Frameworks/Sparkle.framework

qmake could set some default objects like embed_qtcore (translates to $$[QT_INSTALL_LIBS]/QtCore.framework + default options). That would be important for Qt plugins which would need to augment the default "dst" to place themselves in a subdirectory of Contents/PlugIns. Items specified as absolute paths simply take the default options for headers, variants or the target-level options specified in (3).

(2) These options can be populated with a default list containing Qt libraries, using qmake's dependency knowledge -- or not, I don't care.
(3) Additional target-level control options: QMAKE_EMBEDDED_FRAMEWORKS_LOCATION, QMAKE_EMBEDDED_LIBRARIES_LOCATION (default to Contents/Frameworks for OS X, Frameworks for iOS, relative to bundle "wrapper path" aka the .app folder), QMAKE_EMBEDDED_PLUGINS_LOCATION (Contents/PlugIns & PlugIns), QMAKE_EMBEDDED_FRAMEWORKS_HEADERS (bool), QMAKE_EMBEDDED_FRAMEWORKS_VARIANTS (list: debug, release, debug+release).
(4) User's OWN responsibility (NOT qmake's) to set QMAKE_RPATHDIR for their application/library/framework to whatever they like - @executable_path/../Frameworks, $$[QT_INSTALL_LIBS], /Library/Application Support/FooBar/Frameworks, etc.
(5) Qt Creator (and `qbs run`) set DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH to appropriate paths ($$[QT_INSTALL_LIBS] + possibly others) as an additional convenience for folks who don't (or forget to) set QMAKE_EMBEDDED_* or QMAKE_RPATHDIR (follows Xcode behaviour, and conceptually follows Qt Creator's behaviour on Windows). Don't want? Go to settings, delete.

My requirements:

(1) NO changes are made to USER targets by qmake, ever. That means qmake MUST NOT set any rpaths in user targets unless EXPLICITLY given by the user with QMAKE_RPATHDIR (currently you are doing this but only as a transitionary measure and it will be removed once support for copying frameworks is added).

Understand that some things belong at the system level (i.e. internal to qmake) and some things belong at the user level. That's just the way it is.

Now, if some people don't like the standard way of doing things... scenario: user doesn't want to copy frameworks (note that your no-default-copy idea falls apart for third party libraries so you'd need to end up adding additional rpaths to your app ANYWAYS):

<code>
CONFIG(debug, debug|release) {
    QMAKE_EMBEDDED_FRAMEWORKS =  # empty
    QMAKE_RPATHDIR = $$[QT_INSTALL_LIBS]
}
<code>

Side benefits
(1) No need for `install_name_tool -delete_rpath` when you want to release.
(2) Doesn't bother anyone else or engineer qmake completely for one single non-standard use case (i.e. yours).

Discuss. Keep in mind that I am very much in favour of solutions that will serve all parties' use cases. Flexibility here is key.
-- 
Jake Petroules - jake.petroules at petroules.com
Chief Technology Officer - Petroules Corporation


More information about the Development mailing list