15 May, 2013 (published)
15 September, 2014 (last modified)

Debugging qmake

  Categories: Bugs, Linux, Mac, MS Windows, Qt, Qt, Qt-tips
  Tags:  , ,

qtcreatorFor developers the Qt framework is of great value. The tool chains allows for building applications using the same source C++ base for Windows, Mac, Linux and for some mobile platforms. Qt extends standard C++ with much needed functionality. We appreciate tremendously the consistent, transparent Unicode support and the support for all kinds of smart pointers.

Developers using Qt build their projects through Qt Creator, the  GUI interface to the make facility qmake. In this post we want to share some tips to avoid pitfalls with qmake. The program qmake can fail, a.o. because of bugs in our scripts, but - and that is worse - when it fails it does so frequently silently. In the following we will show how to force qmake to become more verbose.

Debug print

Developers cannot afford to learn all the details of debuggers supplied with the computer languages they use for their coding. However, whether or not it is bash, Perl,  PHP or cmd.exe a very useful debugging aid with no learning curve is using a form of debug print. That is to say the developer has intermediate results being printed, usually piped to stdout and echoed on the screen. At a later stage of the development cycle these debug statements can be commented out. What we often do is we define a function like decho(), identical to echo() if a certain debug variable is set to true and the function does nothing if the debug variable is not true.

Happily qmake also has a form of debug printing. With qmake the function is called warning(). Suppose you define a new local variable in a pri or pro file, like

MY_SOURCEPATH=$${PWD}/$$SOURCE

Chances are substantial that something will have gone wrong, either a misspelling or $$SOURCE might not yet have been defined. To catch these problems you could add to your qmake script, right after de definition of MY_SOURCEPATH:

warning(MY_SOURCEPATH = $$MY_SOURCEPATH)

When you now run qmake the warning will be shown explicitly to you, and $$MY_SOURCEPATH will  have been interpolated. As a result you immediately can check the correctness of the value of the variable $$MY_SOURCEPATH. So, use warning() often. We mean very often.

No "../" in your path, please

Even if you do not plan to develop for Windows your qmake scripts should be Windows-proof. And Windows cannot handle this go up one dir in the dir tree, if   "../" is located in the middle of a path. Windows cannot even handle the situation if it would be "..\"  In addition we find the occurrences of many "../" in a script not very good for readability. A simple way to go up in the dir tree is using $$dirname. For instance:

PWD_UP = $$dirname(PWD)
PWD_UPUP = $$dirname(PWD_UP)

Guard against multiple inclusion

The qmake pri inclusion files are like C/C++ header files. In C and C++ developers put up guards against double inclusion of header files. In these sophisticated languages double inclusion is not necessarily a bug, as long as the one-definition rule is not violated, but double inclusion amounts there at least always to unnecessary overhead. Double inclusion of pri files is invariably a bug. So we will make our own guard, by putting the following snippet on top of all our pri files:

# warn when multiply included
contains (INCLUDED_FILES, NameOfPriFile) {
warning (multiple inclusion of NameOfPriFile)
}
INCLUDED_FILES += NameOfPriFile

Any multiple inclusion of a pri file will now be flagged as a warning.

Make scripts talk

Whenever you have to use a system command try to make the command talk. System commands are often silenced to prevent the command from waiting for user input. But apart from that, verbose output is highly appreciated. When you use the system command to run a script (bat, bash, ..) try to make the script to write to stdout when the first line is interpreted and - even better -  also have it write to stdout when the script is exiting. If you have a bug, or a failed build, the qmake output can tell you immediately where it happened and whether or not the bug is in the system script.

Make include files talk

If you are desperate in finding the cause of certain errors, you can also put a tracer in the relevant qmake pri files. Let the script talk by putting at the beginning of the pri file:

warning(entering NameOfPriFile)

and at the end of the pri file you put:

warning (leaving NameOfPriFile)

The produced warnings will tell you exacly where a particular system call failure, or qmake failure, occurred. We found regularly that these tracers exposed surprising bugs.

Pitfalls using system()

The program qmake has its limitations, but fortunately it gives access to the underlying operating system through the system() function. This is a great escape route, although it breaks the independence of the operating system. For  Windows using system() means that you should never feed the operating system a "../" type path. Secondly, for Windows before you feed a path to the system replace all "/" by "\\", the escape for "\\" is essential. A quick way is:

WINDIR ~= s,/,\\,g

or the other way around:

UNIXDIR ~= s,\\\\,/,g

Windows system() calls need first the Windows command shell. So copying with Windows system() is done through "cmd /k copy [..]".

Prefer system() over QMAKE_POST_LINK

The nice feature of system() calls is that they are immediately run by qmake and do not have to wait for a completed build. This shortcut makes debugging and checking faster and more transparent as the qmake output is much less entangled. Even if you need the system call to be done after the builds, and you really need QMAKE_POST_LINK, get the system command first working with system(), before you turn it into a post-build procedure. We think however the best solution is to fully separate, on the pro file level, separating developing from deploying. In such a case you never need QMAKE_POST_LINK anymore.

Quoting

Different operating systems and different shells treat their arguments differently. The problem is in the dashes and spaces. Some shells parse them as the end of the argument and that leads to disaster. The solution is quoting them. If you send a quoted command to system() you will have to escape the quote: \" . QMake provides the $$quote() function. We found however that \" is safer when ealing with paths having spaces in their names. A pretty bad solution is to put all your build tools behind well-behaving directories with underscores etc. And this is bad, because you might ship a script to your client that breaks because of lacking quotes. So make life harsh on yourself: use paths that have to be quoted. If you solve this complication on your own computer your clients will not be surprised by a malfunctioning script. Use quotes, over and over again for paths.

Happy QMaking.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Write a reaction

By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution.