[HowTo] Building your app for desktop platform

Why bother

Building your application for the desktop platform may sound like a waste of time at first glance, but it has several advantages that, in my opinion, outweighs the additional cost :

  • debug experience. Debugging in the phone works, but is not optimal. Debugging code that runs of your computer, however, is usually a far better experience
  • faster “build and test” workflow. Compiling and deploying the app on phone, or
    on emulator, takes around thirty seconds, building the rpm, deploying. And that’s excluding additional compilation time. That’s a looooong time, compared to nearly instant run on desktop
  • better separation of logic / interface. Because you will have to develop two interfaces, you will put more logic where it belongs (in C++)
  • unit testing. Much simpler to run locally than on a remote device
  • qml live bench allows quickly deploy any change in qml. Once the app is working
    for desktop, writing the qml files is easy and quick to test
  • as a bonus, you get a working desktop app !

Prerequisites

  • a linux computer with sailfish sdk installed
  • a Qt development environment (for debian based distro, apt install qttools5-dev). I recommend against using a recent version of Qt if possible, because the version on SFOS is quite old
  • cmake (this should be possible as well using qmake, but i don’t know how)
  • Basic knowledge of C++ and application development

Step-by-step

Creating the project

The first step consists of creating an empty project. This can be done using qtcreator,
or using the sfdk tool:

# go to your project base directory, create an empty directory for it
mkdir Tutorial
cd Tutorial
~/SailfishOS/bin/sfdk init -b cmake Tutorial -t qtquick2app

This creates a CMakeLists.txt file in the current directory, with a demo application.
This file is currently not suitable for desktop compilation. Let’s fix that a bit.

Adapting the CMakeLists file

The first thing is to check whether we are building for SFOS, or for desktop. We could
use a variable, but it’s better to do it automagically. The cmake file contains the
following line:

pkg_search_module(SAILFISH sailfishapp)

This means we can test if we are building for SFOS by testing the SAILFISH_FOUND
variable (see cmake documentation for details on this). So, let’s use this and
complete things a bit:

if (${SAILFISH_FOUND})
    set(BUILD_FOR_SAILFISH ON)
    message("Build for sailfish os")
    add_compile_definitions(SAILFISH_BUILD)
    set(AppName "harbour-tutorial") # we wish to publish on harbour
    set(QML_RESOURCES_FILE)
    set(Qt5Test_FOUND OFF) # no unit tests for sailfish
else()
    set(BUILD_FOR_SAILFISH OFF)
    message("Building for desktop")
    find_package(Qt5 COMPONENTS QuickControls2 REQUIRED) # we want qtquickcontrols for desktop interface
    set(CMAKE_AUTORCC ON) # we'll use a qrc file for qml
    set(QML_RESOURCES_FILE qml_desktop/qml.qrc)
    set(AppName "Tutorial")
    enable_testing() # we want to enable unit testing
    find_package(Qt5Test)
endif()

And modify our target to include the qrc file :

add_executable(Tutorial·src/Tutorial.cpp ${QML_RESOURCE_FILE})

This setup two different type of compilation, and a definition that we can use in
our C++ code to check the target we are building for (SAILFISH_BUILD).

We now need to create a qrc file, and include our desktop qml files. See any QtQuick Controls tutorial for this.

Adapting the main

We need to adapt the main file as well, because currently it will not compile for
a desktop platform, since the sailfish headers are missing. We need to replace:

#include <sailfishapp.h>

by

#if defined(SAILFISH_BUILD)
#include <sailfishapp.h>
#else
#include <QQmlApplicationEngine>
#endif
#include <QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
#include <QtQml>

Which include required qml headers (not all may be required, depending on your app,
but for most app this set will be the minimum).

We need to fix the main() function as well. Replace:

return SailfishApp::main(argc, argv);

by

#if defined(SAILFISH_BUILD)
return SailfishApp::main(argc, argv)
#else
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/Tutorial.qml"));
engine.load(url);
return QCoreApplication::exec();
#endif

Compile and run

Using Qt creator, enable a desktop kit, and compile and run your app. Enjoy fast compile and launch times, and no frustration by waiting for the deploy step.

Develop your app, prototype some desktop interface with QtQuick Controls. Test it. Once you know your C++ code is working, start developing the SFOS interface in qml, using qml live bench (and enjoy fast updates too).

6 Likes

this is very nice tutorial, but it would be better if you could provide a link to
We now need to create a qrc file, and include our desktop qml files. See any QtQuick Controls tutorial for this.

Thank you! I have been thinking about this for some time now, but for Kirigami.
As you say, a big hurdle is porting over logic from js/qml to C++, but i’ll just have to roll up my sleeves and get started when i have some time.

I agree with @lolek that creating a qrc might just as well be some ancient Greek ritual if the only prior Qt experience is SFOS.
Another hurdle on my list is porting from qmake to cmake, particularly with some source code in submodules.

Should this perhaps be made into a wiki?

If anyone is curious to see a build setup that supports many platforms including desktop, take a look at Fahrplan. fahrplan/fahrplan2.pro at master · poetaster/fahrplan · GitHub It just uses qmake, so it might be interesting to study?

Defines are included for desktop. I have to admit I haven’t built it for Desktop, but I’ve very carefully commented it out … I know that desktop builds worked as recently as 2018/19 from third parties.

Deployment logic sits in: fahrplan/deployment.pri at master · poetaster/fahrplan · GitHub

Should this perhaps be made into a wiki?

Sure, it’s there to be improved! I don’t get a clue on how to turn that into a wiki on this forum, though.

For qmake to cmake, i just get the transition done for kontroller, you may have a look on the cmake build (although it’s still in a early state). But i guess it all depends where you start from. CMake is a big beast to learn, but once you have understood its logic, you can make it do whatever you want.

There is no need for that QML works on the desktop, too. The Plasma desktop is the best example for that.

You misunderstand. The Sailfish Silica dialect/widgets/… does not exist for desktop. So i should minimize the logic i have in qml, so that it does not need to be duplicated to the desktop qml i will need to write, because that would be terrible to maintain.

Here’s my example of how to use the same code base for building both Sailfish and Desktop/Android app: piperka / Piperka Client · GitLab

2 Likes

Generic/Cross Platform/$CurrentName, remains the eternal dream, the green light on the end of the dock.

Never truer words!
Generalisation is a generally great idea, in much the same way as the difference between theory and practice is that it theory it will work in practice.

I want to make Android/SF versions until the day that the showstoppers in Sailfish are gone, and I can use my SF phone away from home.

(I am starting to accept that this may be never, as it is looking as if bugs originating in the underlying AOSP may be unfixable in practice - the GPS issue, which appears to still be there in 10 III, was in the XA2 4 years ago)

I prefer to make QML/JS only apps, (as really the apps do very little actual processing, they are mostly just a UI, so JS should be adequte) , as the C++ build time is so painfully long.

I imagine being able to plug a monitor and keyboard straight into the phone and edit the JS/QML files on the phone and develop directly there.

Your post was pretty misunderstandable, though. Many People are unaware that they can use JS files to enhance QML. Since pyotherside sucks this is the method to lower the entry bar for newcomers. Plain JS imported & used by QML files would work on the desktop, too.

I guess you meant that when you need to generalize anyway, you’re going C++ for more performance, too.

Somewhat hard to cater to them in a technical matter such as this. But i’ll try to be more verbose.
And it is not the javascript files so much as the logic inside the qml files themselves, i.e. inline javascript. And the different ways of making said files available at runtime sure isn’t doing the situation any favors.

I did, and i thought it looked somewhat promising. But making it generic has apparently been given up on at least twice. And i seem to recall i met at least soft discouragement from @rinigus, but perhaps my memory is exaggerating.

Then there is the case of how “same” i actually want the frontends to be. I would think that going full-on convergent with Kirigami (but would i?) and still having a same-enough structure for the rather static Silica app will just bring unnecessary pain.

But i won’t know until i try.
Ideas noted - experiences still very much wanted.

I have done such shimming of QML into logic and frontend implementation bits for Pure Maps and OSM Scout Server. Overall, after you get a grip of it, it is very nice. I barely touch frontend (Silica, Kirigami and Ubuntu Touch) code and most work is in the logic bits. Pure Maps is convergent in Kirigami, if the width of the screen permits it. Once in a while you get platform-specific issues, but ability to develop on PC does speed it up a lot.

As for generalizing it, not sure. Couple have tried, but nothing came out of it. Then @piggz has used it for his app with mainly just adopting the idea and tuning some bits where needed, as I recall. Such tuning is needed in “app” part and maybe a bit here and there. While it does feel like major duplication to have it in different apps, nobody had enough time to generalize it. Its also not a very funny work - maybe it is better to focus on your app instead of generalization.

1 Like