einpoklum
einpoklum

Reputation: 131525

How can I determine which preprocessor macros in foo.c originate in bar.h?

I have two C language files: foo.c and bar.h (and possibly many other files). I want a list of all preprocessor macros which are used in foo.c and defined in bar.h.

Alternatively, if that's too difficult, even a list of all preprocessor macros (but not any identifier, word or piece of text) which appear in both foo.c and bar.h.

How can I obtain that?

Upvotes: 2

Views: 107

Answers (3)

rici
rici

Reputation: 241701

You can piece this information together from the output of gcc's preprocessing options.

To get the list of macros actually used by a file, you can use the -E -dU options, which preprocesses the file and also includes #define commands at the first use of any macro. (It also produces #undef commands for undefined names tested with #ifdef or #if defined(...).) (You must use the -E option -- only preprocess -- for -dU to be handled properly.)

Since -dU does not suppress the preprocessed output, you need to filter it by looking only at the #define directives. For some applications, you might also want to further filter it by looking only at the uses actually in the file in question, since the report includes uses of macros by included files as well. But in this case, the intersection with the macros actually defined in the header file is probably sufficient.

So to get a list of used macros in file.c:

gcc -E -dU file.c | grep -Eo '^#define [_A-Za-z][_A-Za-z0-9]*'

(The grep -Eo removes the definition of the macro.)

You can approximate a list of macros actually defined in a header file using a slightly more generous grep invocation, something like this:

grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' header.h

That will pick up macros defined in conditional sections even if the conditional fails, and it will pick up lines in comments which look like #define directives. Usually, neither of those will cause many problems.

You could use gcc's -E -dM or -E -dD options to get a list of all defines in a header, but both of those will also insert the macros defined by headers included by the header. (-dM also includes the predefined macros.) So you would really need to do some more work to focus on the macros actually defined by the header file, unless you are interested in all the macros defined as a result of including the header file.

Then you just need to find the intersection of the two lists. One way is to exract the macro names (awk '{print $2}'), sort -u both lists independently and then merge them, and finally pass them both through uniq -d to only look at the entries in both lists. (Both the following define the shell function used_and_defined which you would invoke used_and_defined foo.c bar.h)

used() {
  gcc -E -dU "$1" |
  grep -Eo '^#define [_A-Za-z][_A-Za-z0-9]*' |
  cut -f2 -d' ' |
  sort -u
}

defined() {
  grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' "$1" |
  awk '{ print $2 }' |
  sort -u
}

used_and_defined() {
  cat <(used "$1") <(defined "$2") | sort | uniq -d
}

Or you could do the whole operation with awk

used_and_defined() {
  awk '/^[[:space:]]*#[[:space:]]*define/ {
         gsub(/[ (].*/, "", $2);
         if (NR == FNR) ++macros[$2];
         else if (macros[$2]) print $2;
       }' \
       <(grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' "$2") \
       <(gcc -E -dU "$1")
}

Upvotes: 2

Virgile
Virgile

Reputation: 10148

A gcc-specific (but clang has the same option with the same semantics) is to use -dD in addition to -E:

gcc -E -dD -o foo.i [other options] foo.c

will keep #define lines in the output, together with # nnn "/path/to/file.h" directives, so that you should be able to tell which macro belongs to which file. If you want to extract the macros that come from bar.h, Depending on the number of macros you expect to find in bar.h, the search command of your favorite editor might be sufficient, or a small awk/perl/python/... script would help.

Upvotes: 0

Jens
Jens

Reputation: 72639

One strategy (admittedly cumbersome) could be:

foreach macro identifier SOME_MACRO in bar.h, run

gcc -E -DSOME_MACRO=recognizable_value foo.c | grep recognizable_value

i.e. preprocess the source and detect if an expansion happened. Note that this does not work for macros only used in #if directives and such.

Upvotes: 1

Related Questions