Overview
In a project real life circle, its developer may face such kind of a challenge: the project has a common code base (we call it $CODE_BASE here); the project introduced some new features into the code before it goes into production (we call it $CODE_PROJECT here); after consideration, the decision is, the new features will be put into the $CODE_BASE, but it will be enclosed by a customer macro like below, after merging the code will be $CODE_MERGED.
#ifdef CUSTOMER_X
NEW_FEATURE code goes here
#else
OLD_FEATURE code goes here
#endif
The problem here is, the merging is a green room for introducing defects. The code maintainer will be uncertain about the quality of $CODE_MERGED without fully regression tests. And it is a common case that after customer project ended, customer platform can not be recreated; and the customer code can not be tested on a real environment.
Defects
There are several common cases here of the defects:
Case 1: Misuse #if instead of #ifdef, and the macro CUSTOMER_X is defined like below:
#define CUSTOMER_X
#if CUSTOMER_X
NEW_FEATURE code goes here
#else
OLD_FEATURE code goes here
#endif
The result is, everytime the OLD_FEATURE will take effect even CUSTOMER_X is defined like above example.
Case 2: Casually, #ifndef is used instead of #ifdef like below as a typo:
#ifndef CUSTOMER_X
NEW_FEATURE code goes here
#else
OLD_FEATURE code goes here
#endif
There may be compiling error, may be not.
Case 3: There may be some lines of code should be enclosed in NEW_FEATURE block but actually not, there may be some lines of code should not be enclosed in NEW_FEATURE block but actually be; the two defects also apply to OLD_FEATURE block.
Solution
Ideally, the $CODE_MERGED building output shall be identical to $CODE_BASE with CUSTOMER_X turned off; and it shall be the same with $CODE_PROJECT with CUSTOMER_X turned on. But the maintainer shall never expect that the compare of building output executables will make any senses.
There is a preprocessing in C/C++ code compiling (GCC, some other compiler products may also have similar function), after preprocessing, the macros will be replaced by its definition and the #ifdef/#else/#endif switch will be substituted with the choosen code block; every line of code (LOC) is the real beef. The compare of preprocessed code will make sense.
To get the preprocessed code, please add --save-temps option into CFLAGS, *.c code will have *.i preprocessed internal file, *.cpp code will have *.ii file.
But wait for a moment, things are not done yet. Some features of modern C/C++ compiler will ruin everything we planed.
Feature 1: In the preprocessed code, there are a lot of lines like '# 12 "/path/to/some/file" 1 2 3' exist. And it's line No. related. It's safe to remove these lines with sed command:
# sed -i '/^# [0-9]* .*$/d' file.i
Feature 2: The C/C++ built-in magic built-in variable __LINE__ is a source of mismatch in comparing preprocessed code, please add -D__LINE__=0 into CFLAGS to remove the mismatch. With -D__LINE__=0 compiler will complain __LINE__ redefined, it can be ignored safely.
By the way, it would be better that the two copies of code that will be compared shall be compiled in the exactly the same path, i.e.
$CODE_BASE has a directory /opt/my_project_base, and $CODE_MERGED has a directory /opt/my_project_merged; It is recommended that user rename /opt/my_project_base into /opt/my_project and build it, and the same needs to be done to /opt/my_project_merged.
As the last step, all of the temporary files (*.i from *.c; *.ii from *.cpp) can be compared from the $CODE_BASE and $CODE_MERGED. (Empty lines and space characters can be ignored to get clearer view)