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:


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>

   -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.