Talk:Deploying a Qt5 Application Linux

From Qt Wiki
Revision as of 16:23, 18 September 2016 by Probono (talk | contribs) (Move personal experience to the discussion page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The original title of this page as of 15:16, 14 January 2015 was "Creating installation package of Qt5 application for Linux/X11: Personal Experience", and the article as of 13:21, 23 August 2015 still contained mostly a writeup of a user's experience packaging mflqta (My First Linux Qt App), so I have moved this to the discussion page, where I think this is more appropriate.

Creating installation package of Qt5 application for Linux/X11: Personal Experience

by arunkumaraymuo

Creating a DEB package

I'm quite new in Linux, but recently got a special interest in building apps for this platform using Qt5, as I had already experience in using this framework in Windows. The first issue I was interesting in while moving my projects onto the Linux platform was how do I distribute my apps to others. By "others" I mean people who are not geeks in Linux: they probably more familiar with Windows than Linux and expect an app to be installed in few clicks and appear in their applications menu. So, I formulated my task as

create a simple application for Linux/X11 and package its binaries properly, so that others could download it, easily install and run from the applications menu without any programming/scripting knowledge.

I knew that the variety of Linux distribution makes my task a bit challenging, so I decided to start with the simplest case: my app should be installable and runnable at least on the same Linux distro as mine. Mine was Kubuntu 12.10 (with KDE).

First of all, I have downloaded and installed the recent Qt SDK version 5.0.1 directly from the qt-project.org. The installation went successfully, and I launched Qt Creator. Since I was just testing how to deploy my apps rather than what can I develop for Linux, I created a simple GUI application with QMainWindow, a menu and a toolbar. And then I was quite surprised to found out that Qt Creator cannot find any compiler to build the project – Qt Create was complaining that gcc is not found, so I had to install it manually via Kubuntu's package manager. Now the Qt Creator was able to detect the compiler automatically, and I finally built the app. My app's name was mflqta (My First Linux Qt App :) ), and the output folder was ~/projects/mflqta/pkg/.

Creating a DEB package

Next, I googled how can I create an installation package for my app. I quickly found that I have to create a DEB package, and there are DEB package creators with GUI, so I downloaded and installed Debreate (the first item in the search results list I got from Google). It contained a link (page Information) to the YouTube video of using this app. The video helped me a lot, and I got an idea what I have to write into the Debreate's fields. Debreate can create a shortcut for the desktop menu (.desktop file) automatically (page Menu), but I preferred to create it manually, since I wished to be in control of how to name this file, and add the value GenericName: Debreate does not add it if you use its wizard, but then Kubuntu does not show the application's description in its menu.

Then I started to search how to find the dependencies of my app. I found that I have to use the ldd tool. It lists all the libs loaded by an app. My application depended on many libs. However, in the Debreate's tutorial the author was building a package for his Qt4 application and he included only a few dependencies. I wished to include all, but in further tests I learned that I cannot simply list names of libs: these should be package name, not lib's. So I left only libc6, skipping many libs (packages related to MESA, X11, and others). Of course, I wished to include the Qt5 package, but there was no such thing as qt5 in the Kubuntu repository! Only qt4, but this one does not suit, as I learned from building Qt5 apps in Windows. So I listed the following libs form /path/to/Qt/libs/ in the "Files" section: libQt5Core.so.5, libQt5Gui.so.5, libQt5Widgets.so.5, libicuuc.so.49, libicui18n.so.49, libicudata.so.49 (exactly this way, as I found in my tests: not .so, nor .so.5., but .so.5). Later I spent many hours finding out why my installed app cannot start: I had to include the folder platforms and the file libqxcb.so from the /path/to/Qt/plugins/ into the DEB package. And in addition I had to install the library libQt5DBus.so.5, since libqxcb.so requires it.

Next I spent many hours finding where to place the libs and how to point to my app where they are. Finally, I found the way: I installed the libQt5*, libicu* and platforms into the /usr/lib/i386-linux-gnu/qt5 folder (or maybe /usr/lib/i386-linux-gnu/mflqta is a better choice?), compiled my app with the following line going first in the main() function in main.cpp: QApplication::addLibraryPath("/usr/lib/i386-linux-gnu/qt5"); and created the mflqta.sh file:

 #!/bin/sh
 appname=`basename $0 | sed s,\.sh$,,`
  
 dirname=`dirname $0`
 tmp="${dirname#?}"
  
 if [ "${dirname%$tmp}" != "/" ]; then
 dirname=$PWD/$dirname
 fi
 LD_LIBRARY_PATH=/usr/lib/i386-linux-gnu/qt5:${LD_LIBRARY_PATH}
 export LD_LIBRARY_PATH
 $dirname/$appname "$@"

(I adapted the code found at http://doc.qt.io/qt-5/linux-deployment.html) to be launched from the mflqta.desktop, which is the following:

 [Desktop Entry]
 GenericName=My First Linux Qt App
 Name=mflqta
 Version=1.0
 Exec=bash /usr/bin/mflqta.sh
 Comment=Just testing Qt on Linux
 Icon=mflqta.png
 Type=Application
 Terminal=false
 StartupNotify=false
 Encoding=UTF-8
 Categories=Utility;

I left empty the pages Scripts, Changelog and Copyright in Debreate and saved my Debreate project into a mflqta.dbp file. Here its content:

 [DEBREATE-0.7.7]
 <<CTRL>>
 Package: mflqta
 Version: 1.0.0
 Section: utils
 Maintainer: lexasss <lexasss@domain.com>
 Homepage: [Project Website]
 Priority: optional
 Architecture: i386
 Depends: libc6
 Description: My first testing Qt app on Linux. Just testing
 
 <</CTRL>>
 <<FILES>>
 1
 /path/to/mflqta/pkg/mflqta.png -> mflqta.png -> /usr/share/pixmaps
 /path/to/mflqta/pkg/mflqta.desktop -> mflqta.desktop -> /usr/share/applications
 /path/to/mflqta/pkg/mflqta* -> mflqta -> /usr/bin
 /path/to/mflqta/pkg/mflqta.sh -> mflqta.sh -> /usr/bin
 /path/to/Qt/lib/libQt5Widgets.so.5* -> libQt5Widgets.so.5 -> /usr/lib/i386-linux-gnu/qt5
 /path/to/Qt/lib/libQt5Gui.so.5* -> libQt5Gui.so.5 -> /usr/lib/i386-linux-gnu/qt5
 /path/to/Qt/lib/libQt5Core.so.5* -> libQt5Core.so.5 -> /usr/lib/i386-linux-gnu/qt5
 /path/to/Qt/lib/libQt5DBus.so.5* -> libQt5DBus.so.5 -> /usr/lib/i386-linux-gnu/qt5
 /path/to/Qt/lib/libicuuc.so.49* -> libicuuc.so.49 -> /usr/lib/i386-linux-gnu/qt5
 /path/to/Qt/lib/libicui18n.so.49* -> libicui18n.so.49 -> /usr/lib/i386-linux-gnu/qt5
 /path/to/Qt/lib/libicudata.so.49* -> libicudata.so.49 -> /usr/lib/i386-linux-gnu/qt5
 /path/to/Qt/plugins/platforms/libqxcb.so* -> libqxcb.so -> /usr/lib/i386-linux-gnu/qt5/platforms
 <</FILES>>
 <[removed]>
 <<PREINST>>
 0
 <</PREINST>>
 <<POSTINST>>
 0
 <</POSTINST>>
 <<PRERM>>
 0
 <</PRERM>>
 <<POSTRM>>
 0
 <</POSTRM>>
 <[removed]>
 <<CHANGELOG>>
 <<DEST>>DEFAULT<</DEST>>
 <</CHANGELOG>>
 <<COPYRIGHT>>
 <</COPYRIGHT>>
 <<MENU>>
 0
 <</MENU>>
 <<BUILD>>
 0
 1
 1
 <</BUILD>>

After building the package with Debreate I got the file mflqta-1.0.0-1.i386.deb (named it manually: Debreate suggested the name mflqta_1.0.0_i386.deb), installed it and found my application in the Kubuntu's menu Utilities.

Creating RPM package

Well, I got the DEB package, but this one seems to be usable in Debian-based Linux distros only. Most of other relatively known distros use RPM packages. So my next goal was packaging the RPM. I thought this task will be easier, since I knew what libs must be distributes, but it was not truly so. First of all, I searched for some GUI app that does same as the Decreate, but with .rpm file as the output. I found few, but could not install it into my Kubuntu: they all were packaged as RPMs. So I had to go to another distro. I selected Fedora 18 with Xfce.

I installed several tools with GUI, like Easy RPM Builder, but did find how to fill the necessary fields. It seemed that packaging RPMs is quite different task than packaging DEB. I decided to stay with a non-GUI tool called rpmbuild. Thanks to some web resources, I learned the steps I have to complete while building my RPM package. So, first I had to run these two commands in the terminal:

 $ mkdir -p ~/rpm/{RPMS,SRPMS,SPECS,SOURCES,BUILD}
 $ echo '%_topdir /home/[user]/rpm' > ~/.rpmmacros
 You would need to place [user] with your account name. Also, you are free defining any path instead of 'rpm' that I used in my case. Also note, that all manuals recommend building RPMs under non-root account.

Interestingly, the rpmbuild _requires 5 folders to have in its working folder (which is _~/rpm in my example). The folder SOURCES must contain the distributed files (in all example these files were in a tar.gz archive), the folder SPECS must contain the file .spec, which is similar to the Debreate's .dbp file I used to create my DEB package. The folder BUILD is used to extract the files from the archive located in SOURCES. Folders SRPMS and RPMS are used to place the RPMs with sources and binaries accordingly. The rpmbuild also creates one more folder, BUILDROOT, and places there the files from_ BUILD_ according to the rules you specify in .spec, thus simulating the structure of the files and folders to be installed.

So first I had to transfer my distribution files into Fedora. In Kubuntu I created a folder mflqta-1.0.0-1 and copied all the files I used to create the Deb package into it (I did not used any subfolders). I found one peculiarity here: I had to copy all 4 files for libQt5{Gui, Core, DBus, Widgets}, since only the libs with .so.5.0.1 ending were the real files, others were only links to the corresponding libs. I have no idea why using .so.5 files worked fine for DEB package… So I got 16 libQt5* files in my archive. I archived the folder itself, not the files inside it. The archive name was mflqta-1.0.0-1.tar.gz.

Now as my mflqta-1.0.0-1.tar.gz was placed into the ~/rmp/SOURCES in Fedora, I created the file mflqta.spec and saved it into the ~/rpm/SPECS. I used gedit for editing – it knows how to highlight .spec file's content, and this is very convenient (Fedora with Xfce misses gedit, I installed it separately). Here is my .spec file:

 Name: mflqta
 Version: 1.0.0
 Release: 1
 Summary: My first testing Qt app on Linux
 Group: Applications/Productivity
 License: GPL v3
 Source0: %{name}-%{version}-%(release).tar.gz
 BuildArch: i386
 BuildRoot: %{_tmppath}/%{name}-root
 Packager: lexasss <lexasss@domain.com>
 Url: [Project Website]
 Vendor: My Company Name
 #Requires: libc6
  
 % description
 Just testing
  
 %prep
 %setup -q -n %{name}-%{version}-%(release)
 
 %build
  
 %install
 mkdir -p $RPM_BUILD_ROOT/usr/{bin,share}
 mkdir -p $RPM_BUILD_ROOT/usr/bin/%{name}
 mkdir -p $RPM_BUILD_ROOT/usr/bin/%{name}/platforms
 mkdir -p $RPM_BUILD_ROOT/usr/share/{applications,pixmaps}
 install %{name} $RPM_BUILD_ROOT/usr/bin/%{name}
 install %{name}.sh $RPM_BUILD_ROOT/usr/bin/%{name}
 install libQt5*.so.5 $RPM_BUILD_ROOT/usr/bin/%{name}
 install libicu*.so.49 $RPM_BUILD_ROOT/usr/bin/%{name}
 install libq* $RPM_BUILD_ROOT/usr/bin/%{name}/platforms
 install %{name}.desktop $RPM_BUILD_ROOT/usr/share/applications
 install %{name}.png $RPM_BUILD_ROOT/usr/share/pixmaps
 
 %files
 % defattr(-,root,root)
 %{_bindir}/%{name}/%{name}
 %{_bindir}/%{name}/%{name}.sh
 %{_bindir}/%{name}/libicu*
 %{_bindir}/%{name}/libQt5*
 %{_bindir}/%{name}/platforms/libq*
 %{_datadir}/applications/%{name}.desktop
 %{_datadir}/pixmaps/%{name}.png
  
 %clean
 rm -rf $RPM_BUILD_ROOT
 rm -rf $RPM_BUILD_DIR
  
 %changelog
  * Sat Apr 13 2013 lexasss
  - Initial build

Note that I used macros here where I could. If you wonder what is %{_datadir}, go to http://www.rpm.org/wiki/PackagerDocs/Macros#MacroAnaloguesofAutoconfVariables [rpm.org] .

Other note: on the very first installation trial the package manager in Fedors (Yum) was complaining about the unresolved dependency for libc6, so I commented the dependency line.

One may see that the beginning of the .spec file is quite similar to the .dbp file. Then it defines the source preparation step, described in the %prep section (I think, it only extract files from .tar.gz into the BUILD folder). The %build section is empty (no source in my package to build). In the %install section my files (already extracted into the ~/rmp/BUILD) are placed into the correct folders (note that you have to create these folders first) inside of the $RPM_BUILD_ROOT (= ~/rpm/BUILDROOT) folder. Some tutorials suggest that you better create the proper folder tree in the archive already, then the %install section would have only a single line: cp -rvf $RPM_BUILD_DIR/${name}-${version}-%{release} $RPM_BUILD_ROOT In the %files section I did quite similar work: specified what files goes to what locations during installation. Some tutorials recommend using just ./ , but I have not testing this. In the section %clear are the command to clear the working folders, ~/rpm/BUILD and ~/rpm/BUILDROOT. The %post<…> sections is optional and offers to run post-[un]installation scripts (similar to the analogue section in .dbp file). Finally, the %changelog section comes – every tutorial recommends filling it in the way that is shown here.

Now everything was ready to build the RPM. I opened the terminal in the ~/rpm/SPECS folder and typed rpmbuild –bb mflqta.spec I wished to have a .rpm file with my binaries only, that's why the parameters is –bb. With the –ba parameter it also creates .src.rpm file and places it into the SRPMS folder. My mflqta-1.0.0-1.i386.rpm file has appeared in the RPMS folder.

I installed the package and found my app's icon in the "Application menu > Accessories" menu in Fedora.