Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I strip out inactive #if directives with the gcc/g++ preprocessor?

Tags:

c++

c

gcc

I am using a third party open source project and need to strip out the inactive #ifs, #ifdefs, etc to better understand the code flow.

Is there a way to use make to produce versions of the source files without these directives? I'd like to avoid expanding macros, just remove directives.

I was looking at https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html

and it seems like -dD and -fdirectives-only are good options to start.

Where will these preprocessed files appear? Where do I add these commands for use with a Makefile and "make"? I tried running "make -n" to produce a script and adding options to the g++ and gcc calls in the script after -Wformat among other things, but I dont notice anything.

I'm not sure if this complicates anything, but I am also using avr-gcc and avr-g++.

I have looked at coan, which does not support #included #defines so it would not work for this purpose, and I could not get sunifdef to work. Is there is a way of doing this with the preprocessor.

The defines are scattered among the current file, the included files, and included makefiles that specify -Dfoo=opt options.

like image 555
user221237 Avatar asked Mar 18 '23 21:03

user221237


2 Answers

You're on the right track with your preprocessor options. -D will define a macro with a value of 1, -U will cancel any previous definition (it will become undefined), and -fdirectives-only will suppress macro expansion. In addition to those, you can use the -E flag with gcc to tell it to provide the preprocessor output as separate files for your examination. However, I don't think they're going to be quite what you expect. The CPP (C pre-processor) output may have other things added to it, as suggested by this SO question, and you should check the gnu CPP output manual page. That is what you will get from the CPP.

It sounds like you want to be able to strip this extraneous code once and develop from there. To do that, I would encourage you to give unifdef another try. This is what unifdef was designed to do, while the CPP was designed to prepare code for compilation. They're different tasks, so you should use the right tools for them. It is available as a standalone application at http://dotat.at/prog/unifdef/ and is built into some Linux Shells.

It allows you to specify macros that you want it to consider defined or undefined, and it removes blocks of code where the conditional directive would evaluate to false. For example, you can run it like this:

unifdef -I< path > -DMACRO1 -UMACRO2

It will search through the directory specified by < path > through C/C++ source files, looking for #if, #ifdef, #ifndef, etc. When it encounters them, it will evaluate the conditional expression and selectively remove the code controlled by that expression. Consider an input file with this code:

int i = 0;
#ifdef MACRO1
int j = 0;
#endif /* ifdef MACRO1 */
int k = 0;

int m = 0;
#if (MACRO1 && MACRO2)
int n = 0;
#endif /* if (MACRO1 && MACRO2) */
int p = 0;

int q = 0;
#ifdef MACRO3
int r = 0;
#endif /* ifdef MACRO3 */
int t = 0;

If we call unifdef like my example above, the output will be this:

int i = 0;
int j = 0;
int k = 0;

int m = 0;
int p = 0;

int q = 0;
#ifdef MACRO3
int r = 0;
#endif /* ifdef MACRO3 */
int t = 0;

Notice that the declaration of n has been removed, because it was contained in a preprocessor #if/#endif block whose controlling expression evaluated to false (we told unifdef to consider MACRO2 undefined). The declaration of j remains, but the #ifdef and #endif statements were removed because the controlling expression was known to be true.

The block that depends on MACRO3 is left untouched because its state is unknown.

There is a significant amount of flexibility and control over how this runs, too.

If you decided you do want it to be part of your build process, you can always add it to your makefile.

If you do not have a list of which macros should be defined or undefined available, you can use the "unifdefall" script provided with unifdef and it will use the CPP to discover macro definitions in the source code on its own, and remove/keep code blocks according to the definitions contained in the source code.

TL;DR

Yes you can (sort of) do it with the preprocessor. But unifdef and sunifdef are tools that are made to do exactly this, so you should use them instead.

like image 142
skrrgwasme Avatar answered Apr 26 '23 18:04

skrrgwasme


Assumptions

  1. The aim of the exercise is to produce a body of C/C++ source code with most of the conditional compilation removed, and which compiles to the identical binaries.
  2. This is third party source code, and you are aware of the problems of merging subsequent updates.
  3. This is open source, but you have no intention of ever distributing modified source code.
  4. The programs are arbitrarily complex and are built by arbitrarily complex makefiles or similar tools, with command-line symbol definitions and/or configuration include files.

My strategy is to use a program like unifdef. The first time I did this I wrote my own, and you may have to modify the program to produce the desired results.

The core strategy is:

  1. Identify a single likely defined symbol (experimentation or trial and error required).
  2. Run the code through unifdef.
  3. Optionally, compare before and after source visually to spot obvious problems.
  4. Build the after version to ensure it builds correctly.
  5. Compile the before and after versions to produce pre-processed output using the same makefiles.
  6. Compare pairs of before and after pre-processed source. They should be identical, give or take some white space.
  7. Resolve issues by editing either before or after version as required.
  8. Optionally, remove all references to the symbol from all makefiles. [It should make no difference.]
  9. Repeat, using the after version and a different symbol.

One symbol at a time, testing thoroughly every time. Some symbols may turn out to be too hard, and if you have much more than a million lines of source code and a hundred or so symbols it can all get out of hand.

Final step: if you modify unifdef then feel free to contribute your changes back to the community. This is a seriously challenging task to do well!

like image 32
david.pfx Avatar answered Apr 26 '23 18:04

david.pfx