How to distribute code with qt? - c++

I've got C++ application which is using Qt, I'm building it with cmake and linking using following:
target_link_libraries(myApp Qt5::Widgets Qt5::OpenGL ...)
it works fine but when I'm trying to distribute it it's failing because of missing qt libraries.
I was trying to get libraries from my local qt installation Qt/5.4/clang_64/lib/QtCore.Framework/Versions/5/QtCore and other with similar paths and put them near myApp but it doesn't help. It's still trying to get those libraries from local qt installation and if I rename folder of local qt installation it fails.
How to distribute my applications which is using qt libraries on OSX/Mac?

Qt 5.5 comes with a helper tool called macdeployqt. You can run it from the command line, it will produce a valid .app (or even a .dmg containing the app if you ask for it). It's fairly straightforward to use, there are only a few options (like creating a dmg output, using debug libraries...) if you depend only on Qt.
If you have more dependancies that are dynamically linked, then you'll probably need to embed them as well in the application bundle (the .app folder).

From the QT site:
But when you are deploying the application, your users may not have the Qt frameworks installed in the specified location. For that reason, you must either provide the frameworks in an agreed upon location, or store the frameworks in the bundle itself. Regardless of which solution you choose, you must make sure that the frameworks return the proper identification name for themselves, and that the application will look for these names. Luckily we can control this with the install_name_tool command-line tool.
The install_name_tool works in two modes, -id and -change. The -id mode is for libraries and frameworks, and allows us to specify a new identification name. We use the -change mode to change the paths in the application.
Let's test this out by copying the Qt frameworks into the Plug & Paint bundle. Looking at otool's output for the bundle, we can see that we must copy both the QtCore and QtGui frameworks into the bundle. We will assume that we are in the directory where we built the bundle.
mkdir plugandpaint.app/Contents/Frameworks cp -R
/path/to/Qt/lib/QtCore.framework
plugandpaint.app/Contents/Frameworks cp -R /path/to/Qt/lib/QtGui.framework
plugandpaint.app/Contents/Frameworks
First we create a Frameworks directory inside the bundle. This follows the Mac OS X application convention. We then copy the frameworks into the new directory. Since frameworks contain symbolic links, and we want to preserve them, we use the -R option.
install_name_tool -id
#executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/Frameworks/QtCore.framework/Versions/4.0/QtCore
install_name_tool -id
#executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui
plugandpaint.app/Contents/Frameworks/QtGui.framework/Versions/4.0/QtGui
Then we run install_name_tool to set the identification names for the frameworks. The first argument after -id is the new name, and the second argument is the framework which identification we wish to change. The text #executable_path is a special dyld variable telling dyld to start looking where the executable is located. The new names specifies that these frameworks will be located "one directory up and over" in the Frameworks directory.
install_name_tool -change
path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore
#executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/MacOs/plugandpaint install_name_tool -change path/to/qt/lib/QtGui.framework/Versions/4.0/QtGui
#executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui
plugandpaint.app/Contents/MacOs/plugandpaint
Now, the dynamic linker knows where to look for QtCore and QtGui. Then we must make the application aware of the library locations as well using install_name_tool's -change mode. This basically comes down to string replacement, to match the identification names that we set for the frameworks.
Finally, since the QtGui framework depends on QtCore, we must remember to change the reference for QtGui:
install_name_tool -change
path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore
#executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/Frameworks/QtGui.framework/Versions/4.0/QtGui
After all this we can run otool again and see that the application
will look in the right locations.

Related

How to make macdeployqt change the library names inside QtWebEngineProcess.app when it copies over QtWebEngineCore framework

Edit: using Qt 5.9.1, installed with Homebrew
Following a comment in this other question, we are trying to use macdeployqt to prepare the distribution of the OSX bundle of a Qt based application.
This tool is correctly copying over all Qt dependencies (and even some non Qt libraries) inside the bundle. It also changes the dependent library names inside both the application itself and the copied libraries.
It also successfully copies the QtWebEngineProcess.app contained in the Helpers folder of QtWebEngineCore.framework.
 Problem
A problem is that it is not changing the dependent library names inside QtWebEngineProcess, so the bundle is not relocatable (as QtWebEngineProcess refers to its dependent library by absolute paths that are only valid on the development machine).
Trying to run the app on a "client" machine, one would thus get the error:
dyld: Library not loaded:
/usr/local/Cellar/qt/5.9.1/lib/QtWebEngineCore.framework/Versions/5/QtWebEngineCore
Referenced from: Business.app/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess
We tried to manually fix this application, by editing QtWebEngineProcess to replace the absolute part of the path to the Qt libraries by #loader_path/../../../../../../../../Frameworks/.
This only moves the problem forward: now QtWebEngineProcess seems to correctly load its dependent libraries, but the dependent libraries themselves cannot load their dependent libraries anymore, since their install name is starting with #executable_path, and QtWebEngineProcess executable lives in a different folder from Business executable. Hence the error:
dyld: Library not loaded:
#executable_path/../Frameworks/QtQuick.framework/Versions/5/QtQuick
Referenced from: Business.app/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/QtWebEngineCore
Is macdeployqt broken when it comes to an application using Qt's web engine?
Is there a way to have it work without manually re-changing all Qt install names in the bundle?
Following one of Dmitry's comment on the original question, I finally took the time to test with an official distribution of Qt 5.9.1 for OS X, and it turns out he was right. The macdeployqtdistributed with the official binaries of Qt is handling QtWebEngineProcess.app just fine.
The version of this utility distributed with homebrew installation's of Qt is bogus, at least in 5.9.1
So far macdeployqt works fine for me on local machine.
On CI you might encounter problem that artifacts are broken in somewhat way like this.
That happens if you copy your application with cp -r or zip it without -y option then all symlinks will be resolved inside the app and it won't work because there will be many copies of QtWebEngineProcess. Also it will break your signature if you sign app.
Solution is to use cp -a and zip -r -y instead - the last one worked for me instead of using plugin for artifacts.
I've crafted a following script, which fixes these paths with install_name_tool:
#!/bin/bash
set -x -e
pushd MyApp.app/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/MacOS
for LIB in QtGui QtCore QtWebEngineCore QtQuick QtWebChannel QtQml QtNetwork QtPositioning
do
OLD_PATH=`otool -L QtWebEngineProcess | grep ${LIB} | cut -f 1 -d ' '`
NEW_PATH="#loader_path/../../../../../../../${LIB}.framework/${LIB}"
install_name_tool -change ${OLD_PATH} ${NEW_PATH} QtWebEngineProcess
done
popd
Note the lack of quotes around ${OLD_PATH} when used as an install_name_tool argument. This is because value of OLD_PATH has some extra white spaces around the path, which shouldn't be preserved. The lack of quotes is typically harmless if Qt has been installed via Homebrew, as these paths do not contain white spaces. However, this could be improved.
Important: This fix is enough to make Simple Browser (from Qt examples) running. But in more advanced projects, more fiddling may be required.

How can I bundle a dylib within a Mac app bundle in Qt?

I have an application and a dylib that it links against. It is my understanding that the application bundle should contain the dylib. Is that correct?
Is there a way to copy the dylib to the application bundle when built?
Thanks,
Alan
Is there a way to copy the dylib to the application bundle when built?
Well, sure -- with a little shell scripting, anything is possible. :)
In your build script, after you've called macdeployqt, but before you've done any code-signing, you'd copy the .dylib file over into the Contents/Frameworks inside your .app folder.
The trick after that is convincing the executable to look for the library in its new location rather than the location it was in when you linked the executable. (If you do a "otool -L ./MyProgram.app/Contents/MacOS/MyProgram" you will see the places where the program is looking for shared libraries, and you will see that it is not looking for your .dylib file in the Frameworks folder, yet)
To do that you use the install_name_tool command, e.g.:
install_name_tool -change /the/old/path/to/the_library_name.dylib "#executable_path/../Frameworks/the_library_name.dylib" ./MyProgram.app/Contents/MacOS/MyProgram
After doing that you can run "otool -L" on your executable again to verify to your satisfaction that the executable is now set to look for the .dylib file inside the Frameworks folder.

How to change the Library path in CMake MAC OSX?

What I have tried
I am working sample application using Caffe model in MAC OSX. I downloaded Caffe source from https://github.com/BVLC/caffe.
Steps:
1.Installed dependencies packages for caffe as said in Caffe Link. Some packages is in /usr/local/lib and some packages /opt/local/lib
2.Builded shared library(SO) file using CMakeLists.txt in CMake GUI.
3.Created sample c++ application
4.Linked all dependencies and caffe library files with c++ application.
Built the application
After I executed the application, It needed library files of dependencies package from /usr/local/lib
What I want
After I installed caffe dependencies, I copied all library files in another location.
How to link specified location of the library files in Cmake.?
In Mac osx, when the application run, It takes the library form /usr/local/lib not in the current folder(application folder).
How do I set the application to take library files from current folder.?
Here is a command from a makefile that I use to make an OSX application (the names have been changed to protect the innocent):
install_name_tool -id "#executable_path/../Resources/libMyLib.dylib" ./libMyLib.dylib
You can also just run install_name_tool on the command line.
The first argument after -id is the destination you want (including the name of the library) and the second is the current path to the library -- ie the binary itself. In this example the library is in the same folder as the makefile.
Then, after I build the app (the linker will include this new path to the desired library in the binary you build) I copy the library to the Resources folder:
cp ./libMyLib.dylib App.app/Contents/Resources/
In OSX-land #executable_path is whatever directory your actual binary is in. The way apps are packaged is like this:
App.app/Contents/MacOS/App. There is also a folder in App.app/Contents called Resources, and this is generally where I stash dependencies if I don't want the user to have to install them him/herself. Thus, in this case, #executable_path/../Resources is a relative path from the app's binary to where the dylib is going to be.
You can used install_name_tool to put the library anywhere you want. I just feel like this is a good place to do it.
By the way, you can check what the current id of the library is with otool -L. Probably, if you run
> otool -L caffe.dylib
it will return something like /usr/local/lib, even if you move it somewhere else. Try to change the id with install_name_tool -id and then run otool -L again, and see what happens. It should make sense at that point.

How do I get Mac to look for plugins in the Program.App/Contents/Plugins directory?

I have a Qt application which links to libqjpeg.dylib. On the development machine, the libraries are in /Applications/Qt/Desktop/Qt/4.8.0/gcc/plugins/imageformats. I placed the libraries in the Program.App/Contents/Plugins directory on the target machine.
However, when I run it through strace (dtruss on mac) I can see that the application only stats
/Applications/Qt/Desktop/Qt/4.8.0/gcc/plugins/imageformats/libqjpeg.dylib
/Users/USER/lib/libqjpeg.dylib
/usr/local/lib/libqjpeg.dylib
/usr/lib/libqjpeg.dylib
How can I get it to look for the plugin in the Program.App/Contents/Plugins directory?
First of all, I think it's more standard to put dylibs in Contents/Frameworks rather than Contents/Plugins.
Second, the install path for the dylib is recorded in the dylib itself. You can change that path using the install_name_tool command, like so:
install_name_tool -id #loader_path/../Frameworks/libqjpeg.dylib libqjpeg.dylib
Then link your app against that modified copy of libqjpeg.dylib.

How can I link a dynamic library in Xcode?

I am currently developing a program in Qt and it uses the library libqextserialport.1.dylib.
I build it and run in x-code and it spits back:
dyld: Library not loaded: libqextserialport.1.dylib
Referenced from: /Users/samuelreh/Desktop/QtOpenCV/build/Debug/QtOpenCV.app/Contents/MacOS/QtOpenCV
Reason: image not found
The library is located in /Users/samuelreh/Desktop/QtOpenCV/qextserialport/build/.
I can run my program by changing to the executable folder /Users/samuelreh/Desktop/QtOpenCV/build/Debug/QtOpenCV.app/Contents/MacOS/ and entering:
install_name_tool -change libqextserialport.1.dylib /Users/samuelreh/Desktop/QtOpenCV/qextserialport/build/libqextserialport.1.dylib QtOpenCV
I know there is probably many solutions besides this. Anybody know the best / most-elegant / easiest to do from x-code?
I've just done exactly this, with a Module Bundle project. This was being included into a larger project with a separate executable.
I added a "Copy dylibs to frameworks" step, which copied the dylibs to /Foobar.bundle/Contents/Frameworks/Foobar/. Then I added a Run Script Phase to run as the final step, to fix the install names of the dylibs in the executable:
install_name_tool -change libBobDylan.dylib #executable_path/../Plugins/Foobar.bundle/Contents/Frameworks/Foobar/libBobDylan.dylib path/to/build/product/Foobar.Bundle/Contents/MacOS/Foobar
Of course, libBobDylan.dylib also linked to libBillyIdol.dylib. So I had to add another Run Script Phase at the very start of the Target to fix the install names here:
install_name_tool -change libBillyIdol.dylib #executable_path/../Plugins/FooBar.bundle/Contents/Frameworks/Foobar/libBillyIdol.dylib local/path/to/libBobDylan.dylib
I had over a dozen of these to link against; I had to persuade the supplier of the dylibs to pad their header to accommodate my many install_name changes ...
If I understand your problem correctly, your app is building fine, no errors when linking, but when you try to launch it the library cannot be found.
That's not surprising, since the dylib file is in some arbitrary directory not on the system path. You either need to copy it into /usr/lib (probably not a good idea) or include it in the application bundle. The latter is probably the better approach.
I've never tried it, but apparently you need to use a Copy Files Build Phase to put the dylib inside your bundle and then configure Xcode so that your executable will know where to find it.
You just drag the library from the directory onto your Xcode project, best into resources but it does not matter (I believe). I tried with and without checking the 'copy files into bundle' and it work in both cases, not sure if you need to include it in bundle for deployment.
I've tested this with sqlite3 and cocoa application (cocoa-touch).