I have a project where I have a substantial amount of conditional defines for making cross platform development easier. However I'm having issues convincing Doxygen to extract all the defines, as it will only pick up ones that only happened to evaluate.
For example in the following snippet, Doxygen will document TARGET_X86_64 but not TARGET_ARM64.
#if defined(_M_ARM64) || defined(__arm64__) || defined(__aarch64__)
/** Build target is ARM64 if defined. */
#define TARGET_ARM64
#else
/** Build target is x86_64 if defined. */
#define TARGET_X86_64
#endif
Enabling EXTRACT_ALL did not help, and disabling preprocessing causes Doxygen to not document anything at all. How do I get doxygen to extract documentation for both cases?
I made a "solution" that's verbose, but works. It's less awkward when you want to have #elseif statements than using just using pure #if statements. Although either would work.
First, define everything and not care about conditional logic.
/** Some define */
#define TARGET_DEFINE
/** Some other define */
#define OTHER_TARGET_DEFINE
Second, take the conditional logic that you originally used to create the defines and transform it to undefine logic.
#if !(ORIGINAL_LOGIC)
#undef TARGET_DEFINE
#endif
Lastly, change the conditional logic so that nothing is undefined when doxygen is doing the preprocessing.
#if !defined(DOXYGEN)
...
Related
Original Question
What I'd like is not a standard C pre-processor, but a variation on it which would accept from somewhere - probably the command line via -DNAME1 and -UNAME2 options - a specification of which macros are defined, and would then eliminate dead code.
It may be easier to understand what I'm after with some examples:
#ifdef NAME1
#define ALBUQUERQUE "ambidextrous"
#else
#define PHANTASMAGORIA "ghostly"
#endif
If the command were run with '-DNAME1', the output would be:
#define ALBUQUERQUE "ambidextrous"
If the command were run with '-UNAME1', the output would be:
#define PHANTASMAGORIA "ghostly"
If the command were run with neither option, the output would be the same as the input.
This is a simple case - I'd be hoping that the code could handle more complex cases too.
To illustrate with a real-world but still simple example:
#ifdef USE_VOID
#ifdef PLATFORM1
#define VOID void
#else
#undef VOID
typedef void VOID;
#endif /* PLATFORM1 */
typedef void * VOIDPTR;
#else
typedef mint VOID;
typedef char * VOIDPTR;
#endif /* USE_VOID */
I'd like to run the command with -DUSE_VOID -UPLATFORM1 and get the output:
#undef VOID
typedef void VOID;
typedef void * VOIDPTR;
Another example:
#ifndef DOUBLEPAD
#if (defined NT) || (defined OLDUNIX)
#define DOUBLEPAD 8
#else
#define DOUBLEPAD 0
#endif /* NT */
#endif /* !DOUBLEPAD */
Ideally, I'd like to run with -UOLDUNIX and get the output:
#ifndef DOUBLEPAD
#if (defined NT)
#define DOUBLEPAD 8
#else
#define DOUBLEPAD 0
#endif /* NT */
#endif /* !DOUBLEPAD */
This may be pushing my luck!
Motivation: large, ancient code base with lots of conditional code. Many of the conditions no longer apply - the OLDUNIX platform, for example, is no longer made and no longer supported, so there is no need to have references to it in the code. Other conditions are always true. For example, features are added with conditional compilation so that a single version of the code can be used for both older versions of the software where the feature is not available and newer versions where it is available (more or less). Eventually, the old versions without the feature are no longer supported - everything uses the feature - so the condition on whether the feature is present or not should be removed, and the 'when feature is absent' code should be removed too. I'd like to have a tool to do the job automatically because it will be faster and more reliable than doing it manually (which is rather critical when the code base includes 21,500 source files).
(A really clever version of the tool might read #include'd files to determine whether the control macros - those specified by -D or -U on the command line - are defined in those files. I'm not sure whether that's truly helpful except as a backup diagnostic. Whatever else it does, though, the pseudo-pre-processor must not expand macros or include files verbatim. The output must be source similar to, but usually simpler than, the input code.)
Status Report (one year later)
After a year of use, I am very happy with 'sunifdef' recommended by the selected answer. It hasn't made a mistake yet, and I don't expect it to. The only quibble I have with it is stylistic. Given an input such as:
#if (defined(A) && defined(B)) || defined(C) || (defined(D) && defined(E))
and run with '-UC' (C is never defined), the output is:
#if defined(A) && defined(B) || defined(D) && defined(E)
This is technically correct because '&&' binds tighter than '||', but it is an open invitation to confusion. I would much prefer it to include parentheses around the sets of '&&' conditions, as in the original:
#if (defined(A) && defined(B)) || (defined(D) && defined(E))
However, given the obscurity of some of the code I have to work with, for that to be the biggest nit-pick is a strong compliment; it is valuable tool to me.
The New Kid on the Block
Having checked the URL for inclusion in the information above, I see that (as predicted) there is an new program called Coan that is the successor to 'sunifdef'. It is available on SourceForge and has been since January 2010. I'll be checking it out...further reports later this year, or maybe next year, or sometime, or never.
I know absolutely nothing about C, but it sounds like you are looking for something like unifdef. Note that it hasn't been updated since 2000, but there is a successor called "Son of unifdef" (sunifdef).
Also you can try this tool http://coan2.sourceforge.net/
something like this will remove ifdef blocks:
coan source -UYOUR_FLAG --filter c,h --recurse YourSourceTree
I used unifdef years ago for just the sort of problem you describe, and it worked fine. Even if it hasn't been updated since 2000, the syntax of preprocessor ifdefs hasn't changed materially since then, so I expect it will still do what you want. I suppose there might be some compile problems, although the packages appear recent.
I've never used sunifdef, so I can't comment on it directly.
Around 2004 I wrote a tool that did exactly what you are looking for. I never got around to distributing the tool, but the code can be found here:
http://casey.dnsalias.org/exifdef-0.2.zip (that's a dsl link)
It's about 1.7k lines and implements enough of the C grammar to parse preprocessor statements, comments, and strings using bison and flex.
If you need something similar to a preprocessor, the flexible solution is Wave (from boost). It's a library designed to build C-preprocessor-like tools (including such things as C++03 and C++0x preprocessors). As it's a library, you can hook into its input and output code.
I'm searching for a preprocessor which allows for partial preprocessing of C source files. What I want to do is to skip certain or all #include and to define some macros on the command line (or a separate file), the preprocessor then should only process what I specified and ignore the rest.
Here is an example of what I want to do:
#define FOO
#ifdef FOO
/* FOO */
#endif
#ifdef BAR
/* BAR */
#endif
should be translated into
/* FOO */
#ifdef BAR
/* BAR */
#endif
Some time ago at my previous job I needed a similar preprocessor and I think that I found a link to a standalone preprocessor here on stackoverflow, however after an hour of searching the web I gave up.
You might be looking for coan, which has the ability to interpret #if and #ifdef directives given a set of definition (or undefinitions) supplied on the command-line.
coan is based on the unifdef utility listed in this related question: Partially processing a file with the preprocessor.
Also see Is there a C preprocessor that eliminates #ifdefs but also evaluates preprocessor macros? which has an example invocation.
I am traversing through linux kernel code. I found a macro defined as #define __async_inline __always_inline. I searched for __always_inline,I found the following statement #define __always_inline inline. My question is why they need to do like this? They can directly use inline instead of this macro's?
The code says this:
#ifdef CONFIG_HAS_DMA
#define __async_inline
#else
#define __async_inline __always_inline
#endif
It is self-explained. __async_inline will be replaced by inline if CONFIG_HAS_DMA is not defined.
This is a common way to parameterize code, to move a decision of some sort (in this case, the use of inline) to a single place, so that should that decision change (for whatever reason: different compilers, different configuration options, different architectures), there is only one single place to change.
I have a lot of code doing conditional compilation based on defines that come from the compiler, not any definitions in the code itself or external headers. For example, quite frequently I have things such as:
#if defined _MSC_VER || defined __ICL
// A lot of freakin code
#elif defined __GNUC__ || defined __ICC
// A bunch more here
#else
#error "Unsupported environment"
#endif
I need to generate documentation for all the code inside the platofm/compiler-specific sections, but I cannot simply set ENABLE_PREPROCESSING to NO because it must be YES for INCLUDE_GRAPH and INCLUDED_GRAPH to work, according to the documentation.
So, how do I do this?
You'd first have to define a macro DOXYGEN or similar in the "predefined" section of the doxygen configuration. Then you'd have to split up the #elif in separate #if and keep track of the fact that you found one valid configuration.
#if defined _MSC_VER || defined __ICL || defined DOXYGEN
#define WE_FOUND_SOME
// A lot of freakin code
#endif
#if defined __GNUC__ || defined __ICC || defined DOXYGEN
#define WE_FOUND_SOME
// A bunch more here
#endif
#ifndef WE_FOUND_SOME
# error "Unsupported environment"
#endif
But beware that if these different parts define the same functions or macros, that doxygen will easily get lost.
In short: I want to generate two different source trees from the current one, based only on one preprocessor macro being defined and another being undefined, with no other changes to the source.
If you are interested, here is my story...
In the beginning, my code was clean. Then we made a new product, and yea, it was better. But the code saw only the same peripheral devices, so we could keep the same code.
Well, almost.
There was one little condition that needed to be changed, so I added:
#if defined(PRODUCT_A)
condition = checkCat();
#elif defined(PRODUCT_B)
condition = checkCat() && checkHat();
#endif
...to one and only one source file. In the general all-source-files-include-this header file, I had:
#if !(defined(PRODUCT_A)||defined(PRODUCT_B))
#error "Don't make me replace you with a small shell script. RTFM."
#endif
...so that people couldn't compile it unless they explicitly defined a product type.
All was well. Oh... except that modifications were made, components changed, and since the new hardware worked better we could significantly re-write the control systems. Now when I look upon the face of the code, there are more than 60 separate areas delineated by either:
#ifdef PRODUCT_A
...
#else
...
#endif
...or the same, but for PRODUCT_B. Or even:
#if defined(PRODUCT_A)
...
#elif defined(PRODUCT_B)
...
#endif
And of course, sometimes sanity took a longer holiday and:
#ifdef PRODUCT_A
...
#endif
#ifdef PRODUCT_B
...
#endif
These conditions wrap anywhere from one to two hundred lines (you'd think that the last one could be done by switching header files, but the function names need to be the same).
This is insane. I would be better off maintaining two separate product-based branches in the source repo and porting any common changes. I realise this now.
Is there something that can generate the two different source trees I need, based only on PRODUCT_A being defined and PRODUCT_B being undefined (and vice-versa), without touching anything else (ie. no header inclusion, no macro expansion, etc)?
I believe Coan will do what you're looking for. From the link:
Given a configuration and some source code, Coan can answer a range of questions about how the source code would appear to the C/C++ preprocessor if that configuration of symbols had been applied in advance.
And also:
Source code re-written by Coan is not preprocessed code as produced by the C preprocessor. It still contains comments, macro-invocations, and #-directives. It is still source code, but simplified in accordance with the chosen configuration.
So you could run it twice, first specifying product A and then product B.