6

Recently, I have successfully migrated my Qt4 application to Qt5. In fact, the application builds and runs on a development machine using the pre-built binary Qt5 libraries from the qt-opensource-linux-x64-5.3.1 distribution.

Because my app needs to be compliant with the LGPL 2.1 license, the linking to Qt5 must be dynamic. (So I can**not** link to Qt statically!)

My problem is deployment of this application. I just can't come up with a package with all the sharable libraries, which would run across existing Linux distributions.

I can tolerate the requirement that the users need to install Qt5 themselves from the qt-opensource-linux-x64-5.3.1 binary. In fact, this would ensure even stricter compliance with LGPL 2.1. So, I can assume that the compatible Qt5 libraries are installed and available on the host machine (although I don't know if I can assume a specific directory for Qt installation)

However, it's not clear to me how to package my application to run on the host machine. Any help would be greatly appreciated!

Miro Samek
  • 1,909
  • 14
  • 19
  • Can't you just create a .tar.gz containing your app, the Qt libs (to dynamically link to), all the Qt plugins needed, and use a small script to set `LD_LIBRARY_PATH` which launches your application? (Or use `ORIGIN`.) That's what's described in the [documentation](http://qt-project.org/doc/qt-5/linux-deployment.html#creating-the-application-package). – peppe Aug 06 '14 at 16:50
  • That's exactly what I'm doing, except I use Install Jammer to package the binary, libraries, and the shell script to launch the app. The problem is that it runs on Ubuntu 12.04, but segmentation-faults on other Linuxes, such as Slackware :-( – Miro Samek Aug 06 '14 at 17:06
  • 1
    This is all explained in the Qt manual: http://qt-project.org/doc/qt-5/linux-deployment.html – MrEricSir Aug 06 '14 at 18:09
  • Thanks MrEricSir. This is the second comment that points to the same Qt documentation, which I already found and based my first attempt of deployment (for Ubuntu 12.04). I have painstakingly copied all dependencies indicated by ldd, for both the application executable and for the platform plugin libqxcb.so. This was tedious and apparently does not work for other Linuxes. On the other hand, the binary distribution of Qt5 itself works, because QtCreator launches correctly. My question is how I can replicate this and perhaps "piggyback" on the already existing Qt5 installation. – Miro Samek Aug 06 '14 at 18:51
  • 4
    I'm voting to close this question as off-topic because **it is about licensing or legal issues**, not programming or software development. [See here](http://meta.stackoverflow.com/a/274964/1402846) for details, and the [help/on-topic] for more. – Kevin Brown-Silva Jun 04 '15 at 23:51

2 Answers2

5

I thought that other people with a similar problem would be interested what I ended up doing. So, I experimented some more with the simplest for me dynamic linking of the standard pre-build binary Qt5 shared libraries. It turned out that I could come up with a distribution that worked on the following Linux distros: CentOS 7 64-bit, Ubuntu 12.04 64-bit, and Slackware 14.1 64-bit with KDE desktop. The trick was not to include all the dependencies shown by the ldd command. Instead, my binary distribution contains only the following files:

+-platforms/
| +-libqxcb.so
+-libicudata.so.52
+-libicui18n.so.52
+-libicuuc.so.52
+-libQt5Core.so.5
+-libQt5DBus.so.5
+-libQt5Gui.so.5
+-libQt5PrintSupport.so.5
+-libQt5Widgets.so.5
+-qm
+-qm.sh

Where, qm is the application executable and qm.sh is the bash script for launching the application. The script looks as follows:

#!/bin/sh
dirname=`dirname $0`
tmp="${dirname#?}"

if [ "${dirname%$tmp}" != "/" ]; then
dirname=$PWD/$dirname
fi
LD_LIBRARY_PATH=$dirname
export LD_LIBRARY_PATH
$dirname/qm "$@"

The application (qm) does not have any plugins and uses only the basic Qt widget library.

I should perhaps add that I was using the binary qt-opensource-linux-x64-5.3.1 distribution:

http://download.qt-project.org/official_releases/qt/5.3/5.3.1/qt-opensource-linux-x64-5.3.1.run.mirrorlist

I hope this is helpful.

Miro Samek
  • 1,909
  • 14
  • 19
4

IANAL

A commonly misunderstood aspect of LGPL is that it requires dynamic linking. It doesn't. It merely requires the ability for the party that got the code to relink it with the LGPL'd libraries that they were able to rebuild from the source that you used and provide to build the Qt that ships with your application.

Dynamic linking takes care of that by definition, as the linking is performed every time on application startup (prelinking is only a cache), and the library's source is available (in the distribution's package).

So, all you need to do is to split your application into two projects:

  1. A static library project (.a) that contains all of your code. This is the closed source part.

  2. An application executable that links the static library with the Qt library, C++ runtime, etc. At this point it's immaterial whether the Qt library is statically or dynamically linked.

To be compliant with LGPL, your users must be able to obtain, per terms of LGPL, all of the files necessary to perform step #2 (in the simplest case just a .pro file!), and the static library (.a) from step #1.

In fact, step #2 makes it very easy to provide a platform-specific way of linking your project with locally installed Qt. For example, if you were targeting RedHat systems, you could use the following script:

#! /bin/bash
# MiroProject.sh
yum install qt5-devel
qmake MiroProject
make install

The project file could look as follows, assuming that the static library resides in the same place as MiroProject.pro and dummy.cpp.

# MiroProject.pro
template = app
LIBS += -L. -lMiroProject
SOURCES += dummy.cpp

You need a to reference at least one symbol in the static library to have it link. This also avoids a different problem peculiar to MSVC. For example:

// dummy.cpp
int main(int argc, char ** argv);
void dummy__reference() {
  main(0, 0);
}

A minimum package requires four files: MiroProject.sh - the script above, MiroProject.a from step #1, dummy.cpp and MiroProject.pro. And of course you must provide the sources for the Qt library that you've built MiroProject.a with.

Ideally, your package should include the whole shebang: Qt sources, your closed-source .a or .lib, the open-source wrapper, and a script that builds it all.

IANAL

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thanks a lot, Kuba. The approach seems very interesting. Could you point me to any existing application deployed that way? Or perhaps, would it be possible to create such a deployment for one of the simpler Qt example applications? I think that this would be a very useful reference to all the poor souls like me. – Miro Samek Aug 07 '14 at 15:46
  • @Miro It's trivial. Just change the template type from `app` to `staticlib`. That's usually all it takes to generate an `.a` (unix) or `.lib` (Windows) static library from your project. – Kuba hasn't forgotten Monica Aug 07 '14 at 15:54
  • Yes, I'm aware that the step #1 from your recipe is relatively straightforward. After all, this is my code that I understand. But the next step is unclear to me. I understand that I would have to re-build Qt statically on Linux-64 (which distro would you recommend?). Then I need to create another project ("MiroProject.pro") that links all this together statically. I could use a hand here. – Miro Samek Aug 07 '14 at 16:02
  • Compiling Qt statically requires you to follow the instructions provided with Qt, just pass the `-static` option to `configure`. I've amended the example above to make it complete. When running `qmake` *on your machine*, make sure that you run the qmake from the static Qt's installation. This takes care of setting everything else, you don't need to tweak your `PATH` environmental variable or anything like that. – Kuba hasn't forgotten Monica Aug 07 '14 at 16:40
  • 1
    Thank you again, Kuba. The steps are becoming a bit clearer now. I need to start with building Qt5 statically. Next, I need to build my closed source application code as a static library (.a) against the static Qt5 libraries. Next, I need to link it all together statically using MiroProject.pro (thanks for the dummy_reference trick). All this should produce a self-contained executable that is easy to install. Finally, I need to prepare a LGPL compliance kit with Qt5 sources, all build scripts, as well as my closed source static library, so that anybody can repeat the static linking step. – Miro Samek Aug 07 '14 at 17:52