Mardy (Entratas super meego)http://mardy.it/ia/categories/meego.atom2024-02-02T20:11:06ZAlberto MardeganNikolaQbs and code coverage reportshttp://mardy.it/ia/blog/2019/07/qbs-and-code-coverage-reports.html2019-07-01T16:42:20+03:002019-07-01T16:42:20+03:00Alberto Mardegan<p>You know that I'm not an <em>early adopter</em>. That's why it was only a couple of
weeks ago when I decided to give <a href="https://doc.qt.io/qbs/">Qbs</a> a try, by using
the good old <a href="https://www.mardy.it/mappero/">Mappero</a> (and its spin-off,
<a href="https://www.mardy.it/mappero-geotagger/">Mappero Geotagger</a>) as a test bench.
Yes, I know that the Qt company is not going to maintain Qbs anymore in the
future, but the little I knew about Qbs was enough to convince me that it's a
project worth supporting. So, better late than never -- and hopefully the
community (me included) will do a good job in keeping Qbs thriving.</p>
<p>Having Mappero build with Qbs was the simplest thing ever. The only issue I met
was in building the unit tests, because I'm used to set the <code>rpath</code> on test
executables in order to make it easy to run them uninstalled, and with <code>qmake</code>
I achieved that with this:</p>
<div class="code"><pre class="code literal-block"><span class="nv">QMAKE_RPATHDIR</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$$</span><span class="o">{</span>QMAKE_LIBDIR<span class="o">}</span>
</pre></div>
<p>In turns out that with Qbs you can do it in almost the same way, but for some
reason I couldn't figure it out and I even <a href="https://bugreports.qt.io/browse/QBS-1455">reported a
bug</a> to which I got some nice
suggestions, before eventually settling on this:</p>
<div class="code"><pre class="code literal-block"><span class="kr">import</span> <span class="nx">qbs</span> <span class="mf">1.0</span>
<span class="nx">Test</span> <span class="p">{</span>
<span class="k">name:</span> <span class="s2">"path-test"</span>
<span class="k">files:</span> <span class="p">[</span>
<span class="s2">"path-test.cpp"</span><span class="p">,</span>
<span class="s2">"path-test.h"</span><span class="p">,</span>
<span class="s2">"paths.qrc"</span><span class="p">,</span>
<span class="p">]</span>
<span class="nx">Depends</span> <span class="p">{</span> <span class="k">name:</span> <span class="s2">"Mappero"</span> <span class="p">}</span>
<span class="k">cpp.rpaths:</span> <span class="nx">cpp</span><span class="p">.</span><span class="nx">libraryPaths</span> <span class="c1">// <-- this does the trick!</span>
<span class="p">}</span>
</pre></div>
<p>It's surprisingly similar to how it's done in qmake, so it's not clear even to
me why I didn't guess that immediately. Anyway, that was literally my only
problem, and you can see the whole set of Qbs files I wrote by having a look at
<a href="https://gitlab.com/mardy/mappero/commit/2a19b59f018cd0517d37eadd24d1aa1780140e4b">this
commit</a>.</p>
<p>Given how easy the migration was, I thought I should also try to add a code
coverage report; that's not something I had in my qmake build either, but it's
something I really want to have in all my newer projects.</p>
<h3>Teaching Qbs to make a code coverage report</h3>
<p>Unfortunately, my search for examples on how to have Qbs prepare a coverage
report was mostly insuccessful, but thanks to some amazing help from Christian
in the #qbs IRC channel, this was not hard to achieve. So, I hope to be of some
help myself too, by sharing how this works.</p>
<p>First of all, it must be said that Qbs doesn't know anything about code
coverage, at all. However, it's possible (and often easy) to extend Qbs by
adding your own <code>Product</code> with its own set of build rules, so here's the
<code>CoverageReport</code> item for Mappero (though, it should be general enough to be
reusable in your own project):</p>
<div class="code"><pre class="code literal-block"><span class="kr">import</span> <span class="nx">qbs</span>
<span class="nx">Product</span> <span class="p">{</span>
<span class="k">name:</span> <span class="s2">"coverage"</span>
<span class="nx">property</span> <span class="nx">string</span> <span class="k">outputDirectory:</span> <span class="s2">"coverage-html"</span>
<span class="nx">property</span> <span class="nx">stringList</span> <span class="k">extractPatterns:</span> <span class="p">[]</span>
<span class="k">builtByDefault:</span> <span class="kc">false</span>
<span class="k">files:</span> <span class="p">[</span><span class="s2">"**"</span><span class="p">]</span>
<span class="k">type:</span> <span class="p">[</span><span class="s2">"coverage.html"</span><span class="p">]</span>
<span class="nx">Depends</span> <span class="p">{</span> <span class="k">productTypes:</span> <span class="p">[</span><span class="s2">"autotest-result"</span><span class="p">]</span> <span class="p">}</span>
<span class="nx">Rule</span> <span class="p">{</span>
<span class="k">multiplex:</span> <span class="kc">true</span>
<span class="k">explicitlyDependsOnFromDependencies:</span> <span class="p">[</span><span class="s2">"autotest-result"</span><span class="p">]</span>
<span class="k">outputFileTags:</span> <span class="s2">"coverage.html"</span>
<span class="k">requiresInputs:</span> <span class="kc">false</span>
<span class="k">prepare:</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">commands</span> <span class="o">=</span> <span class="p">[]</span>
<span class="kd">var</span> <span class="nx">captureCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"lcov"</span><span class="p">,</span> <span class="p">[</span>
<span class="s2">"--directory"</span><span class="p">,</span> <span class="nx">project</span><span class="p">.</span><span class="nx">sourceDirectory</span><span class="p">,</span>
<span class="s2">"--capture"</span><span class="p">,</span>
<span class="s2">"--output-file"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span>
<span class="s2">"--no-checksum"</span><span class="p">,</span>
<span class="s2">"--compat-libtool"</span><span class="p">,</span>
<span class="p">]);</span>
<span class="nx">captureCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Collecting coverage data"</span><span class="p">;</span>
<span class="nx">captureCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">captureCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">captureCmd</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">extractArgs</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">product</span><span class="p">.</span><span class="nx">extractPatterns</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"--extract"</span><span class="p">);</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"coverage.info"</span><span class="p">);</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">product</span><span class="p">.</span><span class="nx">extractPatterns</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">product</span><span class="p">.</span><span class="nx">extractPatterns</span><span class="p">.</span><span class="nx">length</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"-o"</span><span class="p">);</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"coverage.info"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">extractCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"lcov"</span><span class="p">,</span> <span class="nx">extractArgs</span><span class="p">);</span>
<span class="nx">extractCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Extracting coverage data"</span><span class="p">;</span>
<span class="nx">extractCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">extractCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">extractCmd</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">filterCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"lcov"</span><span class="p">,</span> <span class="p">[</span>
<span class="s2">"--remove"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span> <span class="s1">'moc_*.cpp'</span><span class="p">,</span>
<span class="s2">"--remove"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span> <span class="s1">'qrc_*.cpp'</span><span class="p">,</span>
<span class="s2">"--remove"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span> <span class="s1">'*/tests/*'</span><span class="p">,</span>
<span class="s2">"-o"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span>
<span class="p">]);</span>
<span class="nx">filterCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Filtering coverage data"</span><span class="p">;</span>
<span class="nx">filterCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">filterCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">filterCmd</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">genhtmlCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"genhtml"</span><span class="p">,</span> <span class="p">[</span>
<span class="s2">"--prefix"</span><span class="p">,</span> <span class="nx">project</span><span class="p">.</span><span class="nx">sourceDirectory</span><span class="p">,</span>
<span class="s2">"--output-directory"</span><span class="p">,</span> <span class="nx">product</span><span class="p">.</span><span class="nx">outputDirectory</span><span class="p">,</span>
<span class="s2">"--title"</span><span class="p">,</span> <span class="s2">"Code coverage"</span><span class="p">,</span>
<span class="s2">"--legend"</span><span class="p">,</span>
<span class="s2">"--show-details"</span><span class="p">,</span>
<span class="s2">"coverage.info"</span><span class="p">,</span>
<span class="p">]);</span>
<span class="nx">genhtmlCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Generate HTML coverage report"</span><span class="p">;</span>
<span class="nx">genhtmlCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">genhtmlCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">genhtmlCmd</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">commands</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>The most important thing here are the references to the <code>autotest-result</code> tag:
this is the tag used by the <code>AutotestRunner</code> Qbs item, which is responsible for
running the unit tests. Referencing its product's tag in the <code>Depends</code> item and
in the <code>explicitlyDependsOnFromDependencies</code> properties ensures that "building"
our product will cause the unit tests to run. Other needed bits are the
<code>requiresInputs: false</code> property, which means that our rule doesn't have any
required inputs, and the <code>builtByDefault: false</code> property, which says that our
coverage report should not be generated when just typing <code>qbs</code>. Instead, to run
the tests and get the code coverage report one will have to request it
explicitly, by typing</p>
<div class="code"><pre class="code literal-block">qbs -p coverage
</pre></div>
<p>The <code>prepare</code> property of the <code>Rule</code> is where the commands to generate the code
coverage report are defined. Here we can use the <code>Command</code> item to invoke
external programs, and we return a list of such items, so that the commands
will be executed in sequence. Note that here I'm using <code>lcov</code> and expecting to
find the coverage data produced by <code>gcov</code>, so this is probably not portable
outside of Linux/gcc.</p>
<p>Using the <code>CoverageReport</code> item is quite easy: you just need to declare it, and
specify which paths contain the coverage data that you are interested in
(otherwise, lcov will collect data from all object files that it find under the
build directory, which might not be what you desire):</p>
<div class="code"><pre class="code literal-block"> <span class="nx">CoverageReport</span> <span class="p">{</span>
<span class="k">condition:</span> <span class="nx">project</span><span class="p">.</span><span class="nx">enableCoverage</span>
<span class="k">extractPatterns:</span> <span class="p">[</span> <span class="s1">'*/src/*.cpp'</span><span class="p">,</span> <span class="s1">'*/lib/*.cpp'</span> <span class="p">]</span>
<span class="p">}</span>
</pre></div>
<p>There's little more than that to be done. Of course, you need to find a way to
pass the <code>--coverage</code> option to gcc when building your products, and for this I
created a small <code>buildconfig</code> module in
<code>qbs/modules/buildconfig/BuildConfig.qbs</code> which I depend on in all products
which I wish to build with coverage enabled:</p>
<div class="code"><pre class="code literal-block"><span class="kr">import</span> <span class="nx">qbs</span>
<span class="nx">Module</span> <span class="p">{</span>
<span class="k">cpp.cxxFlags:</span> <span class="nx">project</span><span class="p">.</span><span class="nx">enableCoverage</span> <span class="o">?</span> <span class="p">[</span><span class="s2">"--coverage"</span><span class="p">]</span> <span class="o">:</span> <span class="kc">undefined</span>
<span class="k">cpp.dynamicLibraries:</span> <span class="nx">project</span><span class="p">.</span><span class="nx">enableCoverage</span> <span class="o">?</span> <span class="p">[</span><span class="s2">"gcov"</span><span class="p">]</span> <span class="o">:</span> <span class="kc">undefined</span>
<span class="nx">Depends</span> <span class="p">{</span> <span class="k">name:</span> <span class="s2">"cpp"</span> <span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>If all this looks scary, you should probably have a look at the diff which
shows <a href="https://gitlab.com/mardy/mappero/commit/ee04f6a453a935db653095c45bfe57af6a0ce508">how I added code coverage reporting to
qbs</a>:
hopefully you'll find that it's not that complex, after all.</p>
<p>I hope that Qbs users will find this interesting, and possibly improving my
setup. Ideally we should try to get something like this part of Qbs itself, but
portability outside of Linux / gcc is going to be an issue.</p><p>You know that I'm not an <em>early adopter</em>. That's why it was only a couple of
weeks ago when I decided to give <a href="https://doc.qt.io/qbs/">Qbs</a> a try, by using
the good old <a href="https://www.mardy.it/mappero/">Mappero</a> (and its spin-off,
<a href="https://www.mardy.it/mappero-geotagger/">Mappero Geotagger</a>) as a test bench.
Yes, I know that the Qt company is not going to maintain Qbs anymore in the
future, but the little I knew about Qbs was enough to convince me that it's a
project worth supporting. So, better late than never -- and hopefully the
community (me included) will do a good job in keeping Qbs thriving.</p>
<p>Having Mappero build with Qbs was the simplest thing ever. The only issue I met
was in building the unit tests, because I'm used to set the <code>rpath</code> on test
executables in order to make it easy to run them uninstalled, and with <code>qmake</code>
I achieved that with this:</p>
<div class="code"><pre class="code literal-block"><span class="nv">QMAKE_RPATHDIR</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$$</span><span class="o">{</span>QMAKE_LIBDIR<span class="o">}</span>
</pre></div>
<p>In turns out that with Qbs you can do it in almost the same way, but for some
reason I couldn't figure it out and I even <a href="https://bugreports.qt.io/browse/QBS-1455">reported a
bug</a> to which I got some nice
suggestions, before eventually settling on this:</p>
<div class="code"><pre class="code literal-block"><span class="kr">import</span> <span class="nx">qbs</span> <span class="mf">1.0</span>
<span class="nx">Test</span> <span class="p">{</span>
<span class="k">name:</span> <span class="s2">"path-test"</span>
<span class="k">files:</span> <span class="p">[</span>
<span class="s2">"path-test.cpp"</span><span class="p">,</span>
<span class="s2">"path-test.h"</span><span class="p">,</span>
<span class="s2">"paths.qrc"</span><span class="p">,</span>
<span class="p">]</span>
<span class="nx">Depends</span> <span class="p">{</span> <span class="k">name:</span> <span class="s2">"Mappero"</span> <span class="p">}</span>
<span class="k">cpp.rpaths:</span> <span class="nx">cpp</span><span class="p">.</span><span class="nx">libraryPaths</span> <span class="c1">// <-- this does the trick!</span>
<span class="p">}</span>
</pre></div>
<p>It's surprisingly similar to how it's done in qmake, so it's not clear even to
me why I didn't guess that immediately. Anyway, that was literally my only
problem, and you can see the whole set of Qbs files I wrote by having a look at
<a href="https://gitlab.com/mardy/mappero/commit/2a19b59f018cd0517d37eadd24d1aa1780140e4b">this
commit</a>.</p>
<p>Given how easy the migration was, I thought I should also try to add a code
coverage report; that's not something I had in my qmake build either, but it's
something I really want to have in all my newer projects.</p>
<h3>Teaching Qbs to make a code coverage report</h3>
<p>Unfortunately, my search for examples on how to have Qbs prepare a coverage
report was mostly insuccessful, but thanks to some amazing help from Christian
in the #qbs IRC channel, this was not hard to achieve. So, I hope to be of some
help myself too, by sharing how this works.</p>
<p>First of all, it must be said that Qbs doesn't know anything about code
coverage, at all. However, it's possible (and often easy) to extend Qbs by
adding your own <code>Product</code> with its own set of build rules, so here's the
<code>CoverageReport</code> item for Mappero (though, it should be general enough to be
reusable in your own project):</p>
<div class="code"><pre class="code literal-block"><span class="kr">import</span> <span class="nx">qbs</span>
<span class="nx">Product</span> <span class="p">{</span>
<span class="k">name:</span> <span class="s2">"coverage"</span>
<span class="nx">property</span> <span class="nx">string</span> <span class="k">outputDirectory:</span> <span class="s2">"coverage-html"</span>
<span class="nx">property</span> <span class="nx">stringList</span> <span class="k">extractPatterns:</span> <span class="p">[]</span>
<span class="k">builtByDefault:</span> <span class="kc">false</span>
<span class="k">files:</span> <span class="p">[</span><span class="s2">"**"</span><span class="p">]</span>
<span class="k">type:</span> <span class="p">[</span><span class="s2">"coverage.html"</span><span class="p">]</span>
<span class="nx">Depends</span> <span class="p">{</span> <span class="k">productTypes:</span> <span class="p">[</span><span class="s2">"autotest-result"</span><span class="p">]</span> <span class="p">}</span>
<span class="nx">Rule</span> <span class="p">{</span>
<span class="k">multiplex:</span> <span class="kc">true</span>
<span class="k">explicitlyDependsOnFromDependencies:</span> <span class="p">[</span><span class="s2">"autotest-result"</span><span class="p">]</span>
<span class="k">outputFileTags:</span> <span class="s2">"coverage.html"</span>
<span class="k">requiresInputs:</span> <span class="kc">false</span>
<span class="k">prepare:</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">commands</span> <span class="o">=</span> <span class="p">[]</span>
<span class="kd">var</span> <span class="nx">captureCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"lcov"</span><span class="p">,</span> <span class="p">[</span>
<span class="s2">"--directory"</span><span class="p">,</span> <span class="nx">project</span><span class="p">.</span><span class="nx">sourceDirectory</span><span class="p">,</span>
<span class="s2">"--capture"</span><span class="p">,</span>
<span class="s2">"--output-file"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span>
<span class="s2">"--no-checksum"</span><span class="p">,</span>
<span class="s2">"--compat-libtool"</span><span class="p">,</span>
<span class="p">]);</span>
<span class="nx">captureCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Collecting coverage data"</span><span class="p">;</span>
<span class="nx">captureCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">captureCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">captureCmd</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">extractArgs</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">product</span><span class="p">.</span><span class="nx">extractPatterns</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"--extract"</span><span class="p">);</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"coverage.info"</span><span class="p">);</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">product</span><span class="p">.</span><span class="nx">extractPatterns</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">product</span><span class="p">.</span><span class="nx">extractPatterns</span><span class="p">.</span><span class="nx">length</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"-o"</span><span class="p">);</span>
<span class="nx">extractArgs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">"coverage.info"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">extractCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"lcov"</span><span class="p">,</span> <span class="nx">extractArgs</span><span class="p">);</span>
<span class="nx">extractCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Extracting coverage data"</span><span class="p">;</span>
<span class="nx">extractCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">extractCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">extractCmd</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">filterCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"lcov"</span><span class="p">,</span> <span class="p">[</span>
<span class="s2">"--remove"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span> <span class="s1">'moc_*.cpp'</span><span class="p">,</span>
<span class="s2">"--remove"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span> <span class="s1">'qrc_*.cpp'</span><span class="p">,</span>
<span class="s2">"--remove"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span> <span class="s1">'*/tests/*'</span><span class="p">,</span>
<span class="s2">"-o"</span><span class="p">,</span> <span class="s2">"coverage.info"</span><span class="p">,</span>
<span class="p">]);</span>
<span class="nx">filterCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Filtering coverage data"</span><span class="p">;</span>
<span class="nx">filterCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">filterCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">filterCmd</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">genhtmlCmd</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Command</span><span class="p">(</span><span class="s2">"genhtml"</span><span class="p">,</span> <span class="p">[</span>
<span class="s2">"--prefix"</span><span class="p">,</span> <span class="nx">project</span><span class="p">.</span><span class="nx">sourceDirectory</span><span class="p">,</span>
<span class="s2">"--output-directory"</span><span class="p">,</span> <span class="nx">product</span><span class="p">.</span><span class="nx">outputDirectory</span><span class="p">,</span>
<span class="s2">"--title"</span><span class="p">,</span> <span class="s2">"Code coverage"</span><span class="p">,</span>
<span class="s2">"--legend"</span><span class="p">,</span>
<span class="s2">"--show-details"</span><span class="p">,</span>
<span class="s2">"coverage.info"</span><span class="p">,</span>
<span class="p">]);</span>
<span class="nx">genhtmlCmd</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="s2">"Generate HTML coverage report"</span><span class="p">;</span>
<span class="nx">genhtmlCmd</span><span class="p">.</span><span class="nx">highlight</span> <span class="o">=</span> <span class="s2">"coverage"</span><span class="p">;</span>
<span class="nx">genhtmlCmd</span><span class="p">.</span><span class="nx">silent</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">commands</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">genhtmlCmd</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">commands</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>The most important thing here are the references to the <code>autotest-result</code> tag:
this is the tag used by the <code>AutotestRunner</code> Qbs item, which is responsible for
running the unit tests. Referencing its product's tag in the <code>Depends</code> item and
in the <code>explicitlyDependsOnFromDependencies</code> properties ensures that "building"
our product will cause the unit tests to run. Other needed bits are the
<code>requiresInputs: false</code> property, which means that our rule doesn't have any
required inputs, and the <code>builtByDefault: false</code> property, which says that our
coverage report should not be generated when just typing <code>qbs</code>. Instead, to run
the tests and get the code coverage report one will have to request it
explicitly, by typing</p>
<div class="code"><pre class="code literal-block">qbs -p coverage
</pre></div>
<p>The <code>prepare</code> property of the <code>Rule</code> is where the commands to generate the code
coverage report are defined. Here we can use the <code>Command</code> item to invoke
external programs, and we return a list of such items, so that the commands
will be executed in sequence. Note that here I'm using <code>lcov</code> and expecting to
find the coverage data produced by <code>gcov</code>, so this is probably not portable
outside of Linux/gcc.</p>
<p>Using the <code>CoverageReport</code> item is quite easy: you just need to declare it, and
specify which paths contain the coverage data that you are interested in
(otherwise, lcov will collect data from all object files that it find under the
build directory, which might not be what you desire):</p>
<div class="code"><pre class="code literal-block"> <span class="nx">CoverageReport</span> <span class="p">{</span>
<span class="k">condition:</span> <span class="nx">project</span><span class="p">.</span><span class="nx">enableCoverage</span>
<span class="k">extractPatterns:</span> <span class="p">[</span> <span class="s1">'*/src/*.cpp'</span><span class="p">,</span> <span class="s1">'*/lib/*.cpp'</span> <span class="p">]</span>
<span class="p">}</span>
</pre></div>
<p>There's little more than that to be done. Of course, you need to find a way to
pass the <code>--coverage</code> option to gcc when building your products, and for this I
created a small <code>buildconfig</code> module in
<code>qbs/modules/buildconfig/BuildConfig.qbs</code> which I depend on in all products
which I wish to build with coverage enabled:</p>
<div class="code"><pre class="code literal-block"><span class="kr">import</span> <span class="nx">qbs</span>
<span class="nx">Module</span> <span class="p">{</span>
<span class="k">cpp.cxxFlags:</span> <span class="nx">project</span><span class="p">.</span><span class="nx">enableCoverage</span> <span class="o">?</span> <span class="p">[</span><span class="s2">"--coverage"</span><span class="p">]</span> <span class="o">:</span> <span class="kc">undefined</span>
<span class="k">cpp.dynamicLibraries:</span> <span class="nx">project</span><span class="p">.</span><span class="nx">enableCoverage</span> <span class="o">?</span> <span class="p">[</span><span class="s2">"gcov"</span><span class="p">]</span> <span class="o">:</span> <span class="kc">undefined</span>
<span class="nx">Depends</span> <span class="p">{</span> <span class="k">name:</span> <span class="s2">"cpp"</span> <span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>If all this looks scary, you should probably have a look at the diff which
shows <a href="https://gitlab.com/mardy/mappero/commit/ee04f6a453a935db653095c45bfe57af6a0ce508">how I added code coverage reporting to
qbs</a>:
hopefully you'll find that it's not that complex, after all.</p>
<p>I hope that Qbs users will find this interesting, and possibly improving my
setup. Ideally we should try to get something like this part of Qbs itself, but
portability outside of Linux / gcc is going to be an issue.</p>Introducing Mappero Geotaggerhttp://mardy.it/ia/blog/2012/08/introducing-mappero-geotagger.html2012-08-23T19:05:00+04:002012-08-23T19:05:00+04:00Alberto Mardegan<div style="float: right;"><a href="http://www.mardy.it/archivos/imagines/mappero-geotagger.png"><img src="http://www.mardy.it/archivos/imagines/mappero-geotagger.png" width="400px"></a></div>
<p>A few days ago I published the first version of <a href="http://www.mardy.it/mappero-geotagger">Mappero Geotagger</a>, an open source geotagging application for Linux, Mac OS X and Windows.</p>
<p><em>Geotagging</em> a file means assigning a geographic location to it, and it's an action typically performed on picture files, so that the geotagged photo can be shown on a map at the location where it was taken. Many photo-sharing websites, like for instance <a href="http://flickr.com">flickr</a> and <a href="http://picasa.google.com/">picasa</a>, will show your pictures on a map, as you can see for example <a href="http://www.flickr.com/photos/mardytardi/2915024236/in/set-72157607758948089">in this picture</a> of mine. Some services also allow you to view all photos taken in a certain area; for instance, here are some nice pictures taken in <a href="http://www.flickr.com/map?&fLat=60.3834&fLon=25.6776&zl=13">Porvoo</a>, Finland.</p>
<p>Mappero Geotagger is a simple graphical tool to geotag your photos. You can manually move pictures on the map to set or change their location (removing the geotag is also possible) or, if you happened to record your way with a GPS while you took the pictures, the program can automatically correlate the time of the photos with the time of the GPS signal, and therefore establish with a certain precision where the pictures where taken. Since the time of the GPS receiver and the time of your camera might differ (and usually they do, especially if you travel to a different time-zone and forget to update the time in one of your devices), Mappero Geotagger has some controls to allow you to smoothly adjust the time difference and provides you an immediate feedback as you'll see the images being laid out on the map along the GPS track. The video below should give you a better idea of how this works:</p>
<div style="text-align: center; margin: 0 auto;"><iframe width="560" height="315" src="http://www.youtube.com/embed/b1J84dISuNk?rel=0" frameborder="0" allowfullscreen></iframe><br><small>See Mappero Geotagger in action</small></div>
<p>I'm selling the application <a href="http://www.mardy.it/mappero-geotagger">from my website</a> for 15€, but if you hurry up and either <a href="http://twitter.com">tweet</a> or mention it in <a href="http://plus.google.com">Google+</a> you can have it for 5€ only — and <b>if you blog about it, you'll get it for free</b>. Detailed info on the promotion are <a href="http://www.mardy.it/mappero-geotagger#promotions">here</a></p>
<p>Incidentally, if you own a Nokia N9 or N900, the source code which you will get when purchasing Mappero Geotagger can also be compiled for these phones, into an application (still under heavy development, but already usable) which lets you record a GPX track usable with Mappero Geotagger.</p><div style="float: right;"><a href="http://www.mardy.it/archivos/imagines/mappero-geotagger.png"><img src="http://www.mardy.it/archivos/imagines/mappero-geotagger.png" width="400px"></a></div>
<p>A few days ago I published the first version of <a href="http://www.mardy.it/mappero-geotagger">Mappero Geotagger</a>, an open source geotagging application for Linux, Mac OS X and Windows.</p>
<p><em>Geotagging</em> a file means assigning a geographic location to it, and it's an action typically performed on picture files, so that the geotagged photo can be shown on a map at the location where it was taken. Many photo-sharing websites, like for instance <a href="http://flickr.com">flickr</a> and <a href="http://picasa.google.com/">picasa</a>, will show your pictures on a map, as you can see for example <a href="http://www.flickr.com/photos/mardytardi/2915024236/in/set-72157607758948089">in this picture</a> of mine. Some services also allow you to view all photos taken in a certain area; for instance, here are some nice pictures taken in <a href="http://www.flickr.com/map?&fLat=60.3834&fLon=25.6776&zl=13">Porvoo</a>, Finland.</p>
<p>Mappero Geotagger is a simple graphical tool to geotag your photos. You can manually move pictures on the map to set or change their location (removing the geotag is also possible) or, if you happened to record your way with a GPS while you took the pictures, the program can automatically correlate the time of the photos with the time of the GPS signal, and therefore establish with a certain precision where the pictures where taken. Since the time of the GPS receiver and the time of your camera might differ (and usually they do, especially if you travel to a different time-zone and forget to update the time in one of your devices), Mappero Geotagger has some controls to allow you to smoothly adjust the time difference and provides you an immediate feedback as you'll see the images being laid out on the map along the GPS track. The video below should give you a better idea of how this works:</p>
<div style="text-align: center; margin: 0 auto;"><iframe width="560" height="315" src="http://www.youtube.com/embed/b1J84dISuNk?rel=0" frameborder="0" allowfullscreen></iframe><br><small>See Mappero Geotagger in action</small></div>
<p>I'm selling the application <a href="http://www.mardy.it/mappero-geotagger">from my website</a> for 15€, but if you hurry up and either <a href="http://twitter.com">tweet</a> or mention it in <a href="http://plus.google.com">Google+</a> you can have it for 5€ only — and <b>if you blog about it, you'll get it for free</b>. Detailed info on the promotion are <a href="http://www.mardy.it/mappero-geotagger#promotions">here</a></p>
<p>Incidentally, if you own a Nokia N9 or N900, the source code which you will get when purchasing Mappero Geotagger can also be compiled for these phones, into an application (still under heavy development, but already usable) which lets you record a GPX track usable with Mappero Geotagger.</p>The Meego Accounts & Single Sign On projecthttp://mardy.it/ia/blog/2011/08/meego-accounts-single-sign-on-project.html2011-08-14T21:20:00+04:002011-08-14T21:20:00+04:00Alberto Mardegan<p>One of the new features of the upcoming <a href="http://swipe.nokia.com/">Nokia N9</a> is the unified accounts UI and the Single Sign On (SSO) framework. The developer website hosts a <a href="http://www.developer.nokia.com/swipe/ux/pages/Accounts.html">page describing this feature</a> in high level terms — a highly recommended reading — and offers a picture of the account creation flow (the orange circle representing the finger tapping is a bit off target in the pictures, bummer):</p>
<div style="margin: 0 auto; width: 626px;">
<img src="http://www.developer.nokia.com/swipe/ux/images/integrate_your_app/addaccount-thumb.png">
</div>
<p>The UX designers of the N9 decided to create a centralized place where all the user’s accounts would be managed: all the account providers will be listed in this application, and once the user chooses the provider and enters his credentials (only once), he’ll be prompted to select which services he intends to use on this account, in case the provider account gives access to more than one. Once this is done, all the applications interested in the selected services which have been enabled for the new account will start using it.</p>
<p>This design removes the need to implement account management in every application, because all the accounts and their settings are handled in the accounts UI application. Of course, applications can invoke the accounts UI when an account needs to be created, and they can directly embed the account configuration plugin when a specific account needs to be edited.<br>
The accounts UI provides most of its functionality through <b>account plugins</b>. There are <em>provider</em> plugins, whose task is to create the account and handle those settings that are shared through all the account’s services (such as a global option to enable or disable the account), and <em>service</em> plugins, which add support for a specific service within the account and provide a configuration for its settings (at the very least, a toggle switch to enable or disable the service). This plugin based way of operate on accounts brings the possibility to extend the support for new online services, with plugins possibly coming from different sources, from the OS platform vendor to a user’s community, as well as from a hardware vendor, third party software companies and application stores. And all the new services can be directly accessible from the same applications the user is already familiar with, instead of requiring the installation of one additional stand-alone application.</p>
<p>On the other hand, the <b>Single Sign On</b> framework is mostly transparent to the user (indeed, that’s the goal of any SSO implementation): once the password for one service has been entered, all applications operating on the same account should be able to login without bothering the user with password dialogs. And if the password turns out to be wrong, the SSO framework will ask for it <em>only once</em>, no matter how many applications are using the account.<br>
Security is also tightly bound to the MeeGo SSO implementation: at account creation time it’s possible to specify which applications are allowed to use a certain account (or, more technically, which resource tokens an application must possess in order to be granted access to a certain account’s credentials), and what authentication methods are allowed. For instance, for security reasons we might not want to allow an account to be used with any authentication method where the password is disclosed in plain text to the applications or sent over the network.<br>
Last but not least, the account passwords are stored on the device in encrypted form, and the encrypted storage can be dynamically activated/deactivated by providing/removing a master key, which could be configured to be the user password, or a fingerprint or other biometric checksum, some hardware key (such as a SIM card), or pretty much anything else. When the encrypted password storage is unavailable and some applications need to login to a service, the user will be prompted for the needed service password (again not more than once per account); all this being totally transparent to the application.</p>
<h4>Using the framework in other devices/platforms</h4>
<p>The UI design for the Accounts & SSO components described above is just one of the possible ways of integrating the MeeGo Accounts & SSO framework into a user device. The framework itself has been written to be flexible and support any UI workflow we could think of, without compromises to performance or security. The deployment of the Accounts & SSO framework in other devices and platforms has also been taken into consideration during the architecture and implementation development and several actions were taken towards this goal.</p>
<p>The core software driving the Accounts & SSO frameworks is all open source (LGPL), and can be found in <a href="https://gitorious.org/accounts-sso">gitorious.org</a>. The most interesting components are:
</p><ul>
<li><a href="http://gitorious.org/accounts-sso/accounts-glib">libaccounts-glib</a>: <a href="http://en.wikipedia.org/wiki/GLib">Glib</a>-based API for account management. Depends on <a href="http://www.sqlite.org/">SQLite</a> and <a href="http://www.freedesktop.org/wiki/Software/dbus">D-Bus</a>.</li>
<li><a href="http://gitorious.org/accounts-sso/accounts-qt">libaccounts-qt</a>: Qt API for account management. Thin wrapper of libaccounts-glib APIs.</li>
<li><a href="http://gitorious.org/accounts-sso/signon">signon</a>: the SSO daemon (written with Qt), which provides a D-Bus API. In the same source tree there’s also the libsignon-qt library, for Qt-based clients.</li>
<li href="http://gitorious.org/accounts-sso/signon-glib">libsignon-glib: Glib-based client API for SSO.</li>
</ul>
<p>Most of the other repositories in the project are probably not very useful outside of MeeGo, and they are not that interesting anyway. What might be more interesting to know, is the list of components that are missing from the gitorious project (because they are not open source), and that another platform/vendor would have to reimplement in order to provide all the functionalities described above. Luckily, there’s not much in this list. Apart from a couple of SSO authentication plugins for specific services, the rest is all about UI parts (because of Nokia’s general policy of not releasing the source code of its UI applications): we <i>don’t</i> have the account application and the account plugins, and the SSO UI daemon serving the password dialogs. This is not a big loss because in the UI would have had to be rewritten anyways to run in other platforms (and for MeeGo as well, as the MeeGoTouch UI library which is used in these Nokia UIs has been deprecated).</p>
<p>Having spent quite a considerable amount of (pleasant) time and energy on this project, I feel rather bound to it even though my employment in Nokia has ended. Therefore, I’m willing to continue to dedicate some of my free time (which, I have to say, is not much) to contribute to it and help its deployment in MeeGo and other Linux based platforms. Don’t hesitate to <a href="mailto:mardy@users.sourceforge.net">contact me</a> if you are interested in getting the Accounts & SSO powering the OS you love and would like to know more about it.</p><p>One of the new features of the upcoming <a href="http://swipe.nokia.com/">Nokia N9</a> is the unified accounts UI and the Single Sign On (SSO) framework. The developer website hosts a <a href="http://www.developer.nokia.com/swipe/ux/pages/Accounts.html">page describing this feature</a> in high level terms — a highly recommended reading — and offers a picture of the account creation flow (the orange circle representing the finger tapping is a bit off target in the pictures, bummer):</p>
<div style="margin: 0 auto; width: 626px;">
<img src="http://www.developer.nokia.com/swipe/ux/images/integrate_your_app/addaccount-thumb.png">
</div>
<p>The UX designers of the N9 decided to create a centralized place where all the user’s accounts would be managed: all the account providers will be listed in this application, and once the user chooses the provider and enters his credentials (only once), he’ll be prompted to select which services he intends to use on this account, in case the provider account gives access to more than one. Once this is done, all the applications interested in the selected services which have been enabled for the new account will start using it.</p>
<p>This design removes the need to implement account management in every application, because all the accounts and their settings are handled in the accounts UI application. Of course, applications can invoke the accounts UI when an account needs to be created, and they can directly embed the account configuration plugin when a specific account needs to be edited.<br>
The accounts UI provides most of its functionality through <b>account plugins</b>. There are <em>provider</em> plugins, whose task is to create the account and handle those settings that are shared through all the account’s services (such as a global option to enable or disable the account), and <em>service</em> plugins, which add support for a specific service within the account and provide a configuration for its settings (at the very least, a toggle switch to enable or disable the service). This plugin based way of operate on accounts brings the possibility to extend the support for new online services, with plugins possibly coming from different sources, from the OS platform vendor to a user’s community, as well as from a hardware vendor, third party software companies and application stores. And all the new services can be directly accessible from the same applications the user is already familiar with, instead of requiring the installation of one additional stand-alone application.</p>
<p>On the other hand, the <b>Single Sign On</b> framework is mostly transparent to the user (indeed, that’s the goal of any SSO implementation): once the password for one service has been entered, all applications operating on the same account should be able to login without bothering the user with password dialogs. And if the password turns out to be wrong, the SSO framework will ask for it <em>only once</em>, no matter how many applications are using the account.<br>
Security is also tightly bound to the MeeGo SSO implementation: at account creation time it’s possible to specify which applications are allowed to use a certain account (or, more technically, which resource tokens an application must possess in order to be granted access to a certain account’s credentials), and what authentication methods are allowed. For instance, for security reasons we might not want to allow an account to be used with any authentication method where the password is disclosed in plain text to the applications or sent over the network.<br>
Last but not least, the account passwords are stored on the device in encrypted form, and the encrypted storage can be dynamically activated/deactivated by providing/removing a master key, which could be configured to be the user password, or a fingerprint or other biometric checksum, some hardware key (such as a SIM card), or pretty much anything else. When the encrypted password storage is unavailable and some applications need to login to a service, the user will be prompted for the needed service password (again not more than once per account); all this being totally transparent to the application.</p>
<h4>Using the framework in other devices/platforms</h4>
<p>The UI design for the Accounts & SSO components described above is just one of the possible ways of integrating the MeeGo Accounts & SSO framework into a user device. The framework itself has been written to be flexible and support any UI workflow we could think of, without compromises to performance or security. The deployment of the Accounts & SSO framework in other devices and platforms has also been taken into consideration during the architecture and implementation development and several actions were taken towards this goal.</p>
<p>The core software driving the Accounts & SSO frameworks is all open source (LGPL), and can be found in <a href="https://gitorious.org/accounts-sso">gitorious.org</a>. The most interesting components are:
</p><ul>
<li><a href="http://gitorious.org/accounts-sso/accounts-glib">libaccounts-glib</a>: <a href="http://en.wikipedia.org/wiki/GLib">Glib</a>-based API for account management. Depends on <a href="http://www.sqlite.org/">SQLite</a> and <a href="http://www.freedesktop.org/wiki/Software/dbus">D-Bus</a>.</li>
<li><a href="http://gitorious.org/accounts-sso/accounts-qt">libaccounts-qt</a>: Qt API for account management. Thin wrapper of libaccounts-glib APIs.</li>
<li><a href="http://gitorious.org/accounts-sso/signon">signon</a>: the SSO daemon (written with Qt), which provides a D-Bus API. In the same source tree there’s also the libsignon-qt library, for Qt-based clients.</li>
<li href="http://gitorious.org/accounts-sso/signon-glib">libsignon-glib: Glib-based client API for SSO.</li>
</ul>
<p>Most of the other repositories in the project are probably not very useful outside of MeeGo, and they are not that interesting anyway. What might be more interesting to know, is the list of components that are missing from the gitorious project (because they are not open source), and that another platform/vendor would have to reimplement in order to provide all the functionalities described above. Luckily, there’s not much in this list. Apart from a couple of SSO authentication plugins for specific services, the rest is all about UI parts (because of Nokia’s general policy of not releasing the source code of its UI applications): we <i>don’t</i> have the account application and the account plugins, and the SSO UI daemon serving the password dialogs. This is not a big loss because in the UI would have had to be rewritten anyways to run in other platforms (and for MeeGo as well, as the MeeGoTouch UI library which is used in these Nokia UIs has been deprecated).</p>
<p>Having spent quite a considerable amount of (pleasant) time and energy on this project, I feel rather bound to it even though my employment in Nokia has ended. Therefore, I’m willing to continue to dedicate some of my free time (which, I have to say, is not much) to contribute to it and help its deployment in MeeGo and other Linux based platforms. Don’t hesitate to <a href="mailto:mardy@users.sourceforge.net">contact me</a> if you are interested in getting the Accounts & SSO powering the OS you love and would like to know more about it.</p>Committed to Linuxhttp://mardy.it/ia/blog/2011/02/committed-to-linux.html2011-02-12T13:02:00+03:002011-02-12T13:02:00+03:00Alberto Mardegan<p>
As a Nokia employee working on MeeGo, I feel that my career is going to be deeply affected by the recently announced <a href="http://www.nokia.com/press/press-releases/showpressrelease?newsid=1488004">Nokia strategy</a>. I'm not going to comment on the value of the business decisions; of course I have my opinions about that too, but what I feel more important now is the future of MeeGo, and Linux-based platforms in general, inside Nokia.<br>
The announcement mentions MeeGo only marginally, as a <em>“longer-term market exploration”</em>, and ends the paragraph with <em>“Nokia <b>still</b> plans to ship a MeeGo-related product later this year”</em>. This sounds to me like: we won't market any MeeGo devices in parallel with Windows Phone ones, not to hinder the latter's success, but we'll release the MeeGo product we're currently working on before downscaling MeeGo back into the R&D division.</p>
<p>No matter how wrong my interpretation might be, let's try to collect a few facts:
</p><ul>
<li><b>MeeGo is ready, it's not an R&D project</b>: a MeeGo phone <em>will</em> be released
</li><li>Nokia's primary platform for the middle/long term is Windows Mobile</li>
<li>Meego will become, within Nokia, an R&D project at best
</li></ul>
<p>I feel confused and fooled. Business logics are floating several metres above my head, and I cannot understand what's the point of saying that MeeGo will be an R&D project when it's not. To me, it's like taking a teenager and trying to squeeze him into a baby's cradle. Either you kill him more or less explicitly, or you take him out, let him grow and give him a chance to be a hero. We are going to have the best phone in the market this year. It might not be perfect, as the N900 clearly wasn't, but all the premises for a brilliant future are there. And people will love it, as they loved the N900 despite it being far away from the quality standards we aspired to achieve and despite a zero-digits investment on marketing. Maemo won, against all odds.</p>
<p>
One thing is clear: Nokia is not committed to Linux and the platform for the future Nokia devices is not going to be Linux-based, at least in the middle term. Because if it were, I can't see a reason why it couldn't be MeeGo.</p>
<p>
If I try to imagine my future career in Nokia, I see myself either working on applications or R&D projects on top of Windows Mobile, or trying to squeeze the teenager into the cradle, and maybe put him into the refrigerator and periodically check that he doesn't die, because hey, he's our far future hope.<br>
So, what do I have against Windows Mobile? Simple: it's closed source software. And it doesn't matter if the software I develop is open-source; on the contrary, <b>if I had to choose between developing open-source software on top of a closed platform and developing closed-source software on top of an open-source platform, I'd much rather go for the second option</b>: because with the first one I'd still be using and promoting a closed platform, and unless you are developing a cross-platform application or framework, you'd be doing very little good to the open source world.</p>
<p>
Summing up. I really wish that Nokia took one step back and revised their plan. Not because I ask it of course, but because the loyal customers, the developers and the investors are asking it. And give a clear statement on what is the future of MeeGo, Linux and open-source within Nokia. Meanwhile, <b><a href="http://www.mardy.it/archivos/AlbertoMardeganCVe.pdf">my CV has been updated</a></b> and has appeared on this blog's navigation menu for anyone to read. Companies committed to Linux are welcome to <a href="mailto:mardy.tardi@gmail.com">contact me</a> for proposing a job or even a shorter term collaboration. It would be wonderful if I was given a chance to continue working on MeeGo, <a href="http://old.nabble.com/%22DBus-Embedded%22---a-clean-break-tt30716929.html#a30810229">work on some cool ideas</a>, work to introduce the Accounts & SingleSignOn framework to more Linux distributions and open-source OSes. Or actually, anything where I would be given a chance to develop on top of open-source software.<br>
Proposals from Nokia are welcome as well.</p>
<p style="padding-top: 1em"><i>Note: in this blog post I'm talking only about MeeGo because it's what's affecting me most, and I wanted to keep the post to the point. It doesn't mean that I don't care about Symbian's future and Qt deployment.</i></p><p>
As a Nokia employee working on MeeGo, I feel that my career is going to be deeply affected by the recently announced <a href="http://www.nokia.com/press/press-releases/showpressrelease?newsid=1488004">Nokia strategy</a>. I'm not going to comment on the value of the business decisions; of course I have my opinions about that too, but what I feel more important now is the future of MeeGo, and Linux-based platforms in general, inside Nokia.<br>
The announcement mentions MeeGo only marginally, as a <em>“longer-term market exploration”</em>, and ends the paragraph with <em>“Nokia <b>still</b> plans to ship a MeeGo-related product later this year”</em>. This sounds to me like: we won't market any MeeGo devices in parallel with Windows Phone ones, not to hinder the latter's success, but we'll release the MeeGo product we're currently working on before downscaling MeeGo back into the R&D division.</p>
<p>No matter how wrong my interpretation might be, let's try to collect a few facts:
</p><ul>
<li><b>MeeGo is ready, it's not an R&D project</b>: a MeeGo phone <em>will</em> be released
</li><li>Nokia's primary platform for the middle/long term is Windows Mobile</li>
<li>Meego will become, within Nokia, an R&D project at best
</li></ul>
<p>I feel confused and fooled. Business logics are floating several metres above my head, and I cannot understand what's the point of saying that MeeGo will be an R&D project when it's not. To me, it's like taking a teenager and trying to squeeze him into a baby's cradle. Either you kill him more or less explicitly, or you take him out, let him grow and give him a chance to be a hero. We are going to have the best phone in the market this year. It might not be perfect, as the N900 clearly wasn't, but all the premises for a brilliant future are there. And people will love it, as they loved the N900 despite it being far away from the quality standards we aspired to achieve and despite a zero-digits investment on marketing. Maemo won, against all odds.</p>
<p>
One thing is clear: Nokia is not committed to Linux and the platform for the future Nokia devices is not going to be Linux-based, at least in the middle term. Because if it were, I can't see a reason why it couldn't be MeeGo.</p>
<p>
If I try to imagine my future career in Nokia, I see myself either working on applications or R&D projects on top of Windows Mobile, or trying to squeeze the teenager into the cradle, and maybe put him into the refrigerator and periodically check that he doesn't die, because hey, he's our far future hope.<br>
So, what do I have against Windows Mobile? Simple: it's closed source software. And it doesn't matter if the software I develop is open-source; on the contrary, <b>if I had to choose between developing open-source software on top of a closed platform and developing closed-source software on top of an open-source platform, I'd much rather go for the second option</b>: because with the first one I'd still be using and promoting a closed platform, and unless you are developing a cross-platform application or framework, you'd be doing very little good to the open source world.</p>
<p>
Summing up. I really wish that Nokia took one step back and revised their plan. Not because I ask it of course, but because the loyal customers, the developers and the investors are asking it. And give a clear statement on what is the future of MeeGo, Linux and open-source within Nokia. Meanwhile, <b><a href="http://www.mardy.it/archivos/AlbertoMardeganCVe.pdf">my CV has been updated</a></b> and has appeared on this blog's navigation menu for anyone to read. Companies committed to Linux are welcome to <a href="mailto:mardy.tardi@gmail.com">contact me</a> for proposing a job or even a shorter term collaboration. It would be wonderful if I was given a chance to continue working on MeeGo, <a href="http://old.nabble.com/%22DBus-Embedded%22---a-clean-break-tt30716929.html#a30810229">work on some cool ideas</a>, work to introduce the Accounts & SingleSignOn framework to more Linux distributions and open-source OSes. Or actually, anything where I would be given a chance to develop on top of open-source software.<br>
Proposals from Nokia are welcome as well.</p>
<p style="padding-top: 1em"><i>Note: in this blog post I'm talking only about MeeGo because it's what's affecting me most, and I wanted to keep the post to the point. It doesn't mean that I don't care about Symbian's future and Qt deployment.</i></p>