{"id":641,"date":"2013-09-20T16:17:41","date_gmt":"2013-09-20T14:17:41","guid":{"rendered":"http:\/\/katastrophos.net\/andre\/blog\/?p=641"},"modified":"2013-09-20T16:20:46","modified_gmt":"2013-09-20T14:20:46","slug":"qml-preprocessor-the-qnd-and-kiss-way","status":"publish","type":"post","link":"https:\/\/katastrophos.net\/andre\/blog\/2013\/09\/20\/qml-preprocessor-the-qnd-and-kiss-way\/","title":{"rendered":"A QtQuick \/ QML preprocessor, the Q&#8217;n&#8217;D and KISS way"},"content":{"rendered":"<p>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 &#8220;import QtQuick 1.1&#8221; to &#8220;import QtQuick 2.1&#8221; 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.<br \/>\nI 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 &#8211; like in my case &#8211; 99% of the QtQuick 1 code runs on QtQuick 2 without any modification other than changing the import section. Talk about detachment from reality&#8230;<\/p>\n<p>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 (<a href=\"http:\/\/www.fruct.org\/publications\/fruct11\/files\/Par.pdf\">paper<\/a>), 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).<\/p>\n<p>It is available here:<br \/>\n<a href=\"https:\/\/svn.katastrophos.net\/kcl\/trunk\/qmlpp\/\" target=\"_blank\">https:\/\/svn.katastrophos.net\/kcl\/trunk\/qmlpp\/<\/a> (yes, that is a Subversion repository)<\/p>\n<p>The preprocessor is contained in the sub-folder &#8220;src&#8221;. 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&#8217;s PATH variable.<\/p>\n<p>The sub-folder &#8220;test&#8221; 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.<\/p>\n<p>In your application&#8217;s .pro file you basically add the following code:<\/p>\n<pre name=\"code\" class=\"javascript:nocontrols\">\r\ninclude(qmlpp\/src\/qmlpp.pri)\r\n\r\ntarget_qtquick1: qmlPreprocessFolder(yourQmlDirectoryHere, @QtQuick1, 1.0)\r\ntarget_qtquick2: qmlPreprocessFolder(yourQmlDirectoryHere, @QtQuick2, 2.0)\r\n<\/pre>\n<p>The exported function is defined as:<\/p>\n<pre name=\"code\" class=\"javascript:nocontrols\">\r\nqmlPreprocessFolder(folders, defines, rewriteVersion)\r\n<\/pre>\n<ul>\n<li><strong>folders: <\/strong> One or more folders relative to the $$PWD, separated by space. Example: qml js<\/li>\n<li><strong>defines: <\/strong> One or more defines to set, separated by space. Defines always start with @. Example: @QtQuick2 @Android<\/li>\n<li><strong>rewriteVersion: <\/strong> This defines the version to rewrite the line &#8220;import QtQuick x.y&#8221; to. Example: 2.1<\/li>\n<\/ul>\n<p>In your code you can now set the defines as marker comments in the style of \/\/@DEFINENAME:<\/p>\n<p>@QtQuick1 define set:<\/p>\n<pre name=\"code\" class=\"javascript:nocontrols\">\r\n     someFunctionSpecificToQtQuick1(); \/\/@QtQuick1\r\n     \/\/someFunctionSpecificToQtQuick2(); \/\/@QtQuick2\r\n<\/pre>\n<p>@QtQuick2 define set:<\/p>\n<pre name=\"code\" class=\"javascript:nocontrols\">\r\n     \/\/someFunctionSpecificToQtQuick1(); \/\/@QtQuick1\r\n     someFunctionSpecificToQtQuick2(); \/\/@QtQuick2\r\n<\/pre>\n<p>This works for both .qml and .js files.<\/p>\n<p>You can also stop the preprocessor from rewriting the line containing &#8220;import QtQuick&#8221; by adding the &#8220;\/\/!noRewrite&#8221; marker comment to it:<\/p>\n<pre name=\"code\" class=\"javascript:nocontrols\">\r\nimport QtQuick 1.1 \/\/@QtQuick1 \/\/!noRewrite\r\n\/\/import QtQuick 2.0 \/\/@QtQuick2 \/\/!noRewrite\r\n<\/pre>\n<p>This is useful in those situations where you need more fine-grained control of the rewrite process.<\/p>\n<p>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.<\/p>\n<p>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.<\/p>\n<pre name=\"code\" class=\"bash:nocontrols\">\r\nusage: .\/qmlpp.sh [options] &lt;filename JS or QML or directoryname&gt;\r\n\r\nOPTIONS:\r\n   -h                Show this message.\r\n   -q &lt;major.minor&gt;  The version of QtQuick to rewrite to. Example: -q 2.1\r\n   -d &lt;defines&gt;      The defines to set, separated by |. Example: -d &quot;@QtQuick1|@Meego|@Debug&quot;\r\n   -i                Modify file in-place instead of dumping to stdout.\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>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 &#8220;import QtQuick 1.1&#8221; to &#8220;import QtQuick 2.1&#8221; in all of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[20,19,41,40,49],"class_list":["post-641","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-development","tag-hack","tag-qml","tag-qt","tag-qtquick"],"_links":{"self":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/posts\/641"}],"collection":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/comments?post=641"}],"version-history":[{"count":22,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/posts\/641\/revisions"}],"predecessor-version":[{"id":664,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/posts\/641\/revisions\/664"}],"wp:attachment":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/media?parent=641"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/categories?post=641"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/tags?post=641"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}