Link Search Menu Expand Document

Packaging

Packaging Apps

A Sailfish OS application has to be packaged into an installation package before it can be submitted to Jolla Harbour to make it available in Jolla Store. This article briefly describes the available deployment methods and explains the steps involved in creating an installation package for an application project.

Deployment Methods

Deploying an application to the Sailfish OS emulator or to a device is the process of transferring application binaries and any required resource files (such as QML files, images, the desktop integration file, etc.) to the target execution environment. There are two principal methods for deploying the application: copying binaries directly and creating an RPM installation package containing the files. You can choose the deployment method by using the project kit selector in Sailfish IDE.

QtC_Deployment_Method.png

Both deployment methods work whether the application is to be run on the emulator or on a device. However, each method performs the deployment differently and has different pros and cons.

As the name implies, copying binaries transfers the binary and resource files by simply copying the files over to the correct locations in the target environment. This method is fast, as it requires no other steps and the changes to the environment are minimal. Binary copy deployment is a good choice for day to day development.

With the RPM package deployment method, an RPM package containing the application binaries and resource files is created first. After this, the RPM package is transferred to the target environment and installed using native RPM installation tools.

This method is slightly slower than the binary copy method because it involves intermediate steps but there are several benefits. In addition to simply containing the files to be installed, RPM packages can describe package dependencies. This allows automatically installing any packages your application depends on when your application is installed.

Since there is no emulator or device to install to when submitting an application to Jolla Harbour, RPM deployment is the only available option. Additionally, the RPM deployment method mimics the end user installation experience much more closely than binary copy. Because of this it is good idea to test deploying your application as an RPM package from time to time to ensure any package dependencies are correctly resolved.

Also note that kit selection determines where the package can be deployed. x86 packages run on the emulator, whereas only ARM packages can be installed on a device. Packages destined for Jolla Harbour are obviously intended for installation on real devices and hence must be compiled using the SailfishOS-<version>-armv7hl kit.

The rest of this article illustrates the process that takes place when an RPM installation package is created for your application project.

Installation Package Creation Overview

In addition to the standard Qt project file, various other files play a part in describing the files that go into an RPM package and how the package should behave when it is installed into the target environment. The figure below shows the files involved in the creation of an RPM package.

RPM_Creation.png

If you use the Sailfish OS Qt Quick application template inside the Sailfish IDE to create your project, all of these files are created automatically for you. Most of the files are generated by the new project wizard but some get generated as a part of building or deploying the project. The following sections use a project called harbour-myfirstapp created using the Sailfish OS Qt Quick application template to explain the role of each of these files.

The .pro and .prf Files

After creating a project using the Sailfish IDE new project wizard, the harbour-myfirstapp.pro file looks like this:

TARGET = harbour-myfirstapp

CONFIG += sailfishapp

SOURCES += src/harbour-myfirstapp.cpp

DISTFILES += qml/harbour-myfirstapp.qml \
    qml/cover/CoverPage.qml \
    qml/pages/FirstPage.qml \
    qml/pages/SecondPage.qml \
    rpm/harbour-myfirstapp.changes.in \
    rpm/harbour-myfirstapp.changes.run.in \
    rpm/harbour-myfirstapp.spec \
    translations/*.ts \
    harbour-myfirstapp.desktop

SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172

CONFIG += sailfishapp_i18n

TRANSLATIONS += translations/harbour-myfirstapp-de.ts

The Qt project file lists the files that comprise your application. Naturally, many of the items in the project file, such as the compiled target harbour-myfirstapp and all of the QML files, should become a part of the installation package. But if you’re thinking nothing in the project file itself seems to indicate which files should be installed and where, you’d be right.

Since all projects based on the Sailfish OS Qt Quick application template share an identical structure, the project file variable declarations that direct installation have been separated to a shared Qt feature file.

The CONFIG += sailfishapp declaration instructs qmake to include the declarations from the Qt feature file sailfishapp.prf into this project file.

Qt feature files (.prf files) are similar to project include (.pri files) files in that they allow a shared set of declarations to be included into multiple project files. Qt feature files, as the name suggests, are used to provide modular features to all Qt-based projects, whereas project include files are more commonly used to partition a large project into smaller, more manageable parts. Also note that with Qt features you only need to know the name of the feature you want to use (here we’re stating that our project is a Sailfish OS application, hence we want all the declarations that apply to the feature sailfishapp). The name of a feature is always the stem of the feature file, i.e. everything before the .prf suffix. With project include files you typically give the path and name of the project include file you’re including.

If you’re curious as to the content of the sailfishapp.prf feature file, for the emulator target it can be found in the directory:

# Linux and OSX
~/SailfishOS/mersdk/targets/SailfishOS-i486/usr/share/qt5/mkspecs/features

# Windows
C:\SailfishOS\mersdk\targets\SailfishOS-i486\usr\share\qt5\mkspecs\features
(assuming you installed Sailfish SDK in the default location.)

The file looks like this:

QT += quick qml

target.path = /usr/bin

!sailfishapp_no_deploy_qml {
    qml.files = qml
    qml.path = /usr/share/$${TARGET}

    INSTALLS += qml
}

desktop.files = $${TARGET}.desktop
desktop.path = /usr/share/applications

icon.files = $${TARGET}.png
icon.path = /usr/share/icons/hicolor/86x86/apps

INSTALLS += target desktop icon

CONFIG += link_pkgconfig
PKGCONFIG += sailfishapp
INCLUDEPATH += /usr/include/sailfishapp

QMAKE_RPATHDIR += /usr/share/$${TARGET}/lib

OTHER_FILES += $$files(rpm/*)

These declarations are common to all Sailfish OS Qt Quick applications. The INSTALLS declaration lists the parts of project that are to be installed, here the compiled target, the entire qml directory, the desktop integration file, and the application icon.

The values on the right side of the INSTALLS declaration refer to the built-in target install set and three custom install sets defined above. For example, for the desktop integration file, an install set named desktop is declared:

desktop.files = $${TARGET}.desktop
desktop.path = /usr/share/applications

The files member of the install set lists the files that are to be installed. This can be one or more files or directories in any order. Here the name of the file to install will be myfirstapp.desktop, since myfirstapp.pro defines the name of the target as myfirstapp. The path member of the install target defines the path where files listed by the files member are copied.

For a desktop Qt project, installation is typically performed by running qmake followed by make install. This is true for Sailfish OS projects as well but if you take a look at the Compile Output pane in Sailfish IDE after deploying or running a project, you’ll notice the actual command is make install INSTALL_ROOT=/home/deploy/installroot.

The path /home/deploy/installroot refers to a location in the build engine virtual machine. This is not the final installation location, rather it is the location where the RPM build step looks for the files. The files and directories under the INSTALL_ROOT path, however, are organized exactly as the install sets in the .prf file dictate.

Now that everything is in place for RPM package creation, the .spec file comes into play for the next step of the process.

About Qt Feature File Paths

Note that although you can examine the .prf files under Sailfish SDK installation directory, these are not actually the feature files used when you build your project. As the build is performed by the build engine virtual machine, the .prf files are actually inside the build environment in the virtual machine. The feature files themselves are, however, identical in both locations.

To see this, enter the build environment where you’ll find the feature files in the directory /usr/share/qt5/mkspecs/features.

$ sfdk build-shell ls /usr/share/qt5/mkspecs/features

The RPM SPEC File

To create an RPM package, you need a SPEC file that provides information about the software being packaged. The SPEC file contains instructions for building the project and for packaging the resulting binaries into an RPM package.

With the Sailfish SDK 3.9 (an older), you don’t usually create the SPEC file directly. Instead, a .yaml file is used. The SPEC file itself is generated automatically during the build process from the .yaml file. Starting with the Sailfish SDK 3.10, the new project wizard generetes the SPEC file directly.

The initial SPEC file for the example project looks like this:

Name: harbour-myfirstapp

Summary: My Sailfish OS Application
Version: 0.1
Release:    1
Group:      Qt/Qt
License:    LICENSE
URL:        http://example.org/
Source0:    %{name}-%{version}.tar.bz2
Requires:   sailfishsilica-qt5 >= 0.10.9
BuildRequires:  pkgconfig(sailfishapp) >= 1.0.2
BuildRequires:  pkgconfig(Qt5Core)
BuildRequires:  pkgconfig(Qt5Qml)
BuildRequires:  pkgconfig(Qt5Quick)
BuildRequires:  desktop-file-utils

%description
Short description of my Sailfish OS Application


%prep
%setup -q -n %{name}-%{version}

%build

%qmake5 

%make_build


%install
%qmake5_install

desktop-file-install --delete-original         --dir %{buildroot}%{_datadir}/applications                %{buildroot}%{_datadir}/applications/*.desktop

%files
%defattr(-,root,root,-)
%{_bindir}/%{name}
%{_datadir}/%{name}
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/*/apps/%{name}.png

In addition to the instructions for building the binaries, the SPEC file is used to declare how to resolve both build-time and runtime dependencies for the project.

The most important items in the SPEC file are described next.

Name

The name of your project. Note that this is not the name of the application as it appears to the user. Instead it is the base name used to form file names elsewhere in the SPEC file. For example, the application icon is referred to as %{name}.png. Hence the value for Name should always match the TARGET declaration in the Qt project file.

Summary

A short description of the package.

Version

The version number of the package.

When set to 0 (zero) and the project resides under a Git working directory, the actual version number will be determined programmatically from the name of the latest Git tag on the current branch. Additionally, if the current HEAD, index or working tree differs from the tree denoted by the tag, a suffix composed from the current branch name, time stamp and a commit SHA1 will be added to the package version. If git-state is not clean a git-stash will be created and its SHA1 will be used instead of HEAD.

License

The name of the license the package adheres to.

BuildRequires

This item defines a package required to build your project.

The value can either be an exact package name, or package configuration name. If you are using a package configuration name, you should use the syntax pkgconfig(<package configuration name>). The package configuration name refers to a .pc file. Similarly to Qt feature files, if you know the name of the package configuration file, you don’t need to know the exact name of the package that provides the functionality.

For example, the following two snippets both establish a build-time requirement for the sailfishapp configuration:

BuildRequires:  libsailfishapp-devel >= 1.0.2
BuildRequires:  pkgconfig(sailfishapp) >= 1.0.2

The greater than or equal notation can be used to establish a minimum version requirement. Note that on the first example the comparison is made against the version of the package, whereas with the pkgconfig notation the comparison is made against the version of the package configuration, i.e. the version number specified by the Version field in the .pc file itself.

Where applicable, the use of pkgconfig is preferred, as it insulates your project from possible package naming changes. The configuration can be referred to by the same name even if the package that provides it is renamed.

Requires

The Requires keyword specifies packages that are required by the application at runtime. In case these packages are not already present in the target environment, they get installed automatically when the RPM package for the application is installed.

Typically the packages listed for Requires provide importable QML modules, for example the package sailfishsilica-qt5 enables the application’s QML files to use the Sailfish Silica module via the import Sailfish.Silica 1.0 statement.

Finding out which package provides which QML module may take a little detective work, see the Tips and tricks section for a few hints on how to locate the package you need.

%description

A longer form description of the package.

%build

The %build section contains the commands for building the application. For applications which use QMake, as is the case for the application created by the wizard, macros %qmake5 and %make_build should be used instead of actual commands.

%install

The %install section contains the commands for installing the application to %buildroot - which is a directory structure with the files to be packaged. This is done as part of the build process when creating a package, not when the end-user install the package. For applications which use QMake, macro %qmake5_install should be used for installing the files defined in the %files section.

%files

The %files section lists the files and directories that are copied to the system when the package is installed. Each of the files and paths listed in this section refers to one of the items in the INSTALLS declaration in either the sailfishapp.prf file, or your .pro file.

Note that where the .prf and .pro files use Qt project variables, the SPEC file uses macros. The declarations for the project’s qml directory in the in the .prf file

qml.files = qml
qml.path = /usr/share/$${TARGET}

correspond to the line '%{_datadir}/%{name}/qml' in the SPEC file.

The number of items in the %files section must match the number of items in INSTALLS declarations. If this is not the case, you are either attempting to install something that is not generated in the first place, or forgetting to install something that is generated, i.e. you have more lines in the %files section than items in INSTALLS declarations, or vice versa. Both of these are actually errors, resulting in the error messages when trying to deploy the project.

Note that you should always use the macros, such as %{_bindir} for /usr/bin, to refer to standard platform locations in the SPEC file.

It may seem strange that the path is spelled out in the Qt feature file but referred to with a macro in the SPEC file. But both of these are actually provided by the SDK – the Qt feature file is a part of the SDK and the line in the SPEC file in generated by Sailfish IDE. Hence, if the standard paths ever change, the paths will not get out of sync, as both change when the SDK is updated. Using the macros when you modify a SPEC file ensures your project will continue to work without any modifications if the standards paths are ever changed.

Tips and Tricks

This section shows a few terminal commands that may be useful for tracking down specific packages or names as they should appear in the Requires and BuildRequires lines of the RPM SPEC file. All of the commands should be run under a build environment with sfdk build-shell --maintain.

Note that the zypper package manager is not available in the emulator nor on a real device in their default configurations. The equivalent command in the emulator virtual machine or on a device is pkcon. These two commands have slightly different options and one may not support all the functionality the other has, but both may coexists on one system - zypper can be installed with pkcon install zypper.

List all available package configurations (for use with BuildRequires: pkgconfig()):

$ pkg-config --list-all

List installed package configuration files:

$ ls -l /usr/lib/pkgconfig/

See full details, such as the version, include path, and libraries linked against, e.g. for the sailfishapp package configuration:

$ cat /usr/lib/pkgconfig/sailfishapp.pc

Find out which package provides the Qt5Positioning configuration:

zypper what-provides 'pkgconfig(Qt5Positioning)'

Show all packages (an ‘i’ in the status column indicates the package is currently installed):

$ zypper packages

Show the files contained in the installed package qt5-positioning-devel:

$ rpm -ql qt5-qtpositioning-devel