Doxygen and C Preprocessor Directives

by Clint on March 4, 2009

I’ve been using a tool called Doxygen to document my Objective-C code; it’s comparable to javadoc in the Java world, but can be used with a much wider variety of languages (PHP, C#, and Java, to name a few). In a nutshell, you use a few special symbols when writing normal code comments and Doxygen can then generate HTML, class diagrams, etc., from your source code. (You can see a previous post of mine with some sample code for an example.)

One nice feature of Doxygen is that it can handle C preprocessor directives just like an actual build tool (e.g., gcc), and even generate documentation for them. For example, you might code your own “logging” macro that maps to the standard NSLog() function as follows:

/**
  Custom logging macro blah blah blah...
 */
#define MYLOG(...) NSLog(__VA_ARGS__)

Doxygen will actually include define statements like this in the HTML documentation it generates, including your code comments, as long as you have  ENABLE_PREPROCESSING=YES in your doxygen.config file.

While this is a great feature, you can run into trouble if you forget that conditional statements like #if will also be processed. For example, let’s say we only wanted to include some code if we’re building for the iPhone. Although it’s a bit contrived, we might rewrite the above macro as follows:

#if TARGET_OS_IPHONE

/**
  Custom logging macro blah blah blah...
 */
#define MYLOG(...) NSLog(__VA_ARGS__)

#endif

TARGET_OS_IPHONE is a standard variable that is automatically defined when you do regular builds in Xcode. But when you run Doxygen you’re not doing a “regular build” via Xcode and TARGET_OS_IPHONE isn’t defined. Assuming Doxygen’s preprocessor is enabled, the above #if statement would be false and the contained code (i.e., the MYLOG() definition) wouldn’t be included in your HTML documentation.

The trick, as I found recently, is to use the PREDEFINED setting in the doxygen.config file. This lets you define preprossor variables to mimic the Xcode build environment. Continuing the example, you would put the following in doxygen.config:

PREDEFINED = TARGET_OS_IPHONE=1

The result is that the TARGET_OS_IPHONE variable will not only be defined, but set to “1″, and MYLOG() will be processed by Doxygen.

Clint Harris is an independent software consultant living in Brooklyn, New York. He can be contacted directly at ten.sirrahtnilc@tnilc.
  • bvasyb

    thanks for your clue, for me worked only if I put “TARGET_OS_IPHONE” in the predefined tab