Monday 22 November 2010

Subgrouping C code in Doxygen

The Doxygen documentation about grouping concerns itself about C++ code and using the groups with plain C wasn't quite as forward as I thought. I wanted to create a main group (shown as a top-level page) and sub-groups below it in a manner where the files belonging to a sub-group are not shown on the ancestor group pages. E.g. I wanted the structure to be something like

The files wouldn't obviously been shown on the listing, but they would appear in the Main and Module pages' Files section.

After a bit of hacking, this is how I accomplished it:

/** @file main.c
* @brief
* (other Doxygen tags)
* @ingroup main_group

/** @defgroup main_group
This is the main group of the application.

/** @file module.c
* (other tags)
* @ingroup module

/** @defgroup module
* This is the sub group of the application. */
* @ingroup main_group */

/** @file helper.c
* (other tags)
* @ingroup module

So the trick is to define separately the new groups and what group (if any) they belong to. If I tried to have @defgroup inside the file header block, things just didn't seem to work. At the best main.c was not shown under main_group. I think this is because @ingroup only takes into account the previous suitable tag. So if there is a @defgroup before it, @ingroup adds this group to whatever group @ingroup defines, and the file level inclusion is not done.

Saturday 20 November 2010

soundKonverter failing to convert FLAC to AAC

Looks like something was broken in Kubuntu 10.10 because converting FLACs to AACs seems to have stopped working in soundKonverter. I let Dolphin convert my CDs to FLAC and then use soundKonverter to convert them to AAC for iPod. Last time I did this (not too long ago) everything went smoothly, now soundKonverter just told me the conversion had failed.

After some digging I found out that the log was actually in ~/.kde/share/apps/soundkonverter/log (obviously I first looked in /var/log). The logs told me "Unknown encoder 'libfaac'". The package was there, though.

Googling, I came up with this page. I first tried installing ffmpeg, x264 and libx264-dev by hand, but that didn't help. But following the instructions to the letter did (though I use aptitude instead of apt-get). It's just somewhat annoying having to install packages outside of the package manager.

Friday 19 November 2010

Creating sequence charts with Doxygen

I wrote a small Python script called trace2dox that one can use to turn trace logs in message sequence charts into Doxygen. I'll write instructions later, but for now the script and an example log and msc output can be found from Gitorious. You just need to create the logs somehow and then run it through this script to create the charts you need. You can filter, colour etc. the output.

For the project I wrote this script I created a C macro that wraps the message sending function and adds logging capabilities. This way I didn't need to change anything in the actual source code.

I'll write better documentation once I have time.

Thursday 11 November 2010

A way to handle big merges

I had the task of merging two branches with lots of changes. The problem was I wasn't very familiar with the code but I took the task because I have lots of experience in this sort of stuff, although this merge was the biggest I have done. The branches were over a year old and unfortunately it looked like they were differentiated as best as possible. For instance doing refactoring in one branch but not bothering to do it in the other. An extra challenge was that the quality wasn't very good and documentation was somewhat lacking. A typical situation, in other words.

So I thought about this for a while before plunging head in. Quickly I figured out that the best way to figure out what was needed would be a three-way compare where I would see the two branch heads and their common ancestor. This way I would immediately see where each difference was made (and in many cases the same blocks were changed in both branches, but in different ways). I tried a few merge tools but only one really did what I needed: KDiff3. It's by no means the perfect tool (it has some usability issues) but with it I could easily have the ancestor on the left pane, the other branch in the middle and the other - where I wanted to do the merge - in the right pane and I could edit the final product easily in the merge window at the bottom.

The branches were originally in Subversion, but I moved them to Git because it's far quicker and has a much better support for branching (and I knew it better).

In theory I could have configured KDiff3 to work from a single clone, but I figured it was just as well to to have each commit on it's own clone, just to be on the safe side.

So I had three clones: initial, branchA and branchB. And off I went.

At first I tried to do this the thinking man's way, i.e. try to actually merge the code, but it became quickly obvious that this would take me months since most changes were non-trivial. "If I change this, where else does it affect?"

So I turned this into an idiot-proof process: I simply flagged each change something like this:
#if defined(BRANCHA)

I had the following possibilities:
1. Something was changed in A, but not B (B is the same as ancestor),
2. Something was changed in B, but not A,
3. Something was changed in both A and B and in the same way,
4. Something was changed in both A and B but differently.

1 and two would be handled like the example above (unless a very trivial change, like an added logging command), third situation would mean no flagging and the fourth would reguire a if defined... elseif defined...else to keep the original code for later reference.

There were some 650 places I needed to flag but after that the code was in one branch and the cleanup was even possible. Now it is just a case of looking at each change and deciding whether it can be used on both branches or if this really is a differentiation. The latter can then possibly be made into run-time flags which is preferrable because this would mean the binary is the same for both branches. This in turn would mean automated testing would be a lot faster since most of the similar code could be tested just once rather than twice. And considering the changes I think the number of different code segments is well below 100 (rest is mostly refactoring that can be used in both branches).

This was definitely the best way because mistakes basically meant the code wouldn't compile rather than anything more complex. The mistakes I made usually involved putting the #endif directive on the wrong side of a brace or some similar thing and with the right technique even those are relatively easy to find.

After everything was done and the SW was built, tests passed. Like that, and for both branches. So what I spent figuring out the best way was saved many times over during testing and debugging.