A QtQuick / QML preprocessor, the Q’n’D and KISS way

Lately, I have been porting the existing code base of QuasarMX to Qt 5. While the C++ core was migrated in almost zero time, porting the QtQuick 1 code to QtQuick 2 was not as straight forward. In a small experiment I decided to change “import QtQuick 1.1” to “import QtQuick 2.1” in all of the 600 files, to see how far that gets me. To my amusement it actually worked for almost all files. I only had to make some QtQuick 2 specific changes due to changed behavior in the new engine.
I hate to duplicate my code bases, especially if I still have to support platforms that only support Qt 4.7+ and QtQuick 1. It puzzles me how there is no preprocessor / macro functionality available in QML, especially if – like in my case – 99% of the QtQuick 1 code runs on QtQuick 2 without any modification other than changing the import section. Talk about detachment from reality…

So, here is a very quick-and-dirty preprocessor that will rewrite the import section and also apply line changes based on defines. It is inspired by the very simplistic notation of the preprocessor used in Octatask (paper), but the implementation is new. The preprocessor does not change the line count of the preprocessed files (read: debuggable) and preprocessing is reversible as no information is lost (read: unused lines are commented).

It is available here:
https://svn.katastrophos.net/kcl/trunk/qmlpp/ (yes, that is a Subversion repository)

The preprocessor is contained in the sub-folder “src”. It is comprised of the qmlpp.pri file for easy inclusion in your .pro file and qmlpp.sh, which is the Bash script doing the fancy substitution work (read: sed running humongous regexps). If you are using this on Windows, make sure to install Cygwin and put the location of bash.exe into your system’s PATH variable.

The sub-folder “test” contains an example project, that will compile and work on both Qt 4 / QtQuick 1 and Qt 5 / QtQuick 2. Check it out to get a feeling what you need to change to make your code work on both versions of the toolkit.

In your application’s .pro file you basically add the following code:

include(qmlpp/src/qmlpp.pri)

target_qtquick1: qmlPreprocessFolder(yourQmlDirectoryHere, @QtQuick1, 1.0)
target_qtquick2: qmlPreprocessFolder(yourQmlDirectoryHere, @QtQuick2, 2.0)

The exported function is defined as:

qmlPreprocessFolder(folders, defines, rewriteVersion)
  • folders: One or more folders relative to the $$PWD, separated by space. Example: qml js
  • defines: One or more defines to set, separated by space. Defines always start with @. Example: @QtQuick2 @Android
  • rewriteVersion: This defines the version to rewrite the line “import QtQuick x.y” to. Example: 2.1

In your code you can now set the defines as marker comments in the style of //@DEFINENAME:

@QtQuick1 define set:

     someFunctionSpecificToQtQuick1(); //@QtQuick1
     //someFunctionSpecificToQtQuick2(); //@QtQuick2

@QtQuick2 define set:

     //someFunctionSpecificToQtQuick1(); //@QtQuick1
     someFunctionSpecificToQtQuick2(); //@QtQuick2

This works for both .qml and .js files.

You can also stop the preprocessor from rewriting the line containing “import QtQuick” by adding the “//!noRewrite” marker comment to it:

import QtQuick 1.1 //@QtQuick1 //!noRewrite
//import QtQuick 2.0 //@QtQuick2 //!noRewrite

This is useful in those situations where you need more fine-grained control of the rewrite process.

Once you run qmake again, the QML preprocessor will rewrite your files. Make sure to save any pending changes before. I am working on a more generic and safe method of including the preprocessor to possibly rewrite the files during deployment-time or run-time.

The tool is also available via command line. This is useful for when you are rolling your own deployment scripts or if you are using CMake.

usage: ./qmlpp.sh [options] <filename JS or QML or directoryname>

OPTIONS:
   -h                Show this message.
   -q <major.minor>  The version of QtQuick to rewrite to. Example: -q 2.1
   -d <defines>      The defines to set, separated by |. Example: -d "@QtQuick1|@Meego|@Debug"
   -i                Modify file in-place instead of dumping to stdout.

QuasarMX, Qt, QML and the experiments

Some of you following my micro-blogging on Twitter might already know about it: In the last few months I have been busy developing a new application. What initially just started out as a re-write of the user interface of Quasar Media Player has turned into a completely new application called QuasarMX. It is based on the very core of Quasar Media Player but stripped of all the legacy and third-party code. The completely new UI is based on Qt Quick / QML, a new technology which simplicity and elegance I fell in love with over the past year. For me QuasarMX also marks the start of two experiments:

1. How many platforms and operating systems can I port my app to?
2. Is there any commercial viability at all in publishing the software to various app stores (Nokia Ovi store, Android Market etc.) ?

The first experiment is important to me. Now that Nokia put the Qt project into open governance, the Qt community has the unique opportunity to extend primary platform support to Android, iOS and possibly even Windows Phone 7, thus extending the possible market cap and audience for mobile apps based on Qt dramatically. This experiment is largely about experiencing first hand what changes are required to get my application running on these platforms and as a matter of fact what needs to be done in projects like Necessitas (Qt on Android) to make that goal happen.

As for the second experiment I am actually just interested in getting to know the mechanics of the various app stores. I am pretty confident that there is little to no money to be made from yet another music player app. Yet, it is still interesting to see what happens, what works and what not, especially in regards to future apps.

In July Nokia accepted my application and invited me into the Qt Ambassador program. As part of this program Nokia loaned a N950 developer device to me, which I extensively used to develop and test the Harmattan UI for QuasarMX. The new user interface is very much inspired by the simplicity of the MeeGo Harmattan Swipe UI.

As of yesterday the open Beta 1 of QuasarMX is available for the Nokia N9 and N950. Releases for other platforms will follow shortly.