I would like to know the paths of all code files that went into the compilation of a whole C/C++ project with multiple resulting binaries. I know about https://github.com/rizsotto/Bear et al which you can wrap around your "make" call and which tell you the gcc/g++ calls but they of course show me only the C/C++ files which were compiled - how can I find out which of the headers were needed for the compilation and ended up in one of the resulting binaries?
-
1Compile with coverage output, that should give you the necessary information. (The easiest way may be to actually get the coverage output though...) – Jaffa Mar 06 '20 at 17:50
-
Could run compilation through the pre-processor only and "grep" for "include" – chux - Reinstate Monica Mar 06 '20 at 17:51
-
@chux-ReinstateMonica The preprocessed input won't have any `#include`s in it, although there could be #line directives with the different file names. – 1201ProgramAlarm Mar 06 '20 at 17:54
-
@1201ProgramAlarm Yes, that is the usual case. I recall using this method though because the `include file` was retained as a comment. – chux - Reinstate Monica Mar 06 '20 at 17:56
2 Answers
GCC has the runtime option -M
. From the GCC man page:
-M Instead of outputting the result of preprocessing, output a rule
suitable for make describing the dependencies of the main source
file. The preprocessor outputs one make rule containing the object
file name for that source file, a colon, and the names of all the
included files, including those coming from -include or -imacros
command-line options.
Unless specified explicitly (with -MT or -MQ), the object file name
consists of the name of the source file with any suffix replaced
with object file suffix and with any leading directory parts
removed. If there are many included files then the rule is split
into several lines using \-newline. The rule has no commands.
This option does not suppress the preprocessor's debug output, such
as -dM. To avoid mixing such debug output with the dependency
rules you should explicitly specify the dependency output file with
-MF, or use an environment variable like DEPENDENCIES_OUTPUT.
Debug output is still sent to the regular output stream as normal.
Passing -M to the driver implies -E, and suppresses warnings with
an implicit -w.
Clang supports the very same option

- 159,371
- 13
- 185
- 298
GCC and Clang both support the -H
option (as well as the -M
option and its relatives).
-H
Print the name of each header file used, in addition to other normal activities. Each name is indented to show how deep in the ‘
#include
’ stack it is. Precompiled header files are also printed, even if they are found to be invalid; an invalid precompiled header file is printed with ‘...x
’ and a valid one with ‘...!
’ .
The -H
output is reported on standard error, not standard output.
The -H
option gives more and different information compared with the -M
option. It reports on the nesting levels (one or more leading dots), and shows each time a header is included — even if the content is ignored because of header guard macros:
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
…content of header…
#endif /* HEADER_H_INCLUDED */
This can be useful; having the formatted make
dependency lines (as generated by -M
) can also be useful.
Example output (from compiling one source file from one of my programs — make
generated the compiler command line, of course):
$ gcc -H -g -O3 -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -DSYSTEM=MACOS_X -DSTR_SYSTEM='"(macOS Mojave 10.14.6 - Darwin 18.7.0)"' -I/Users/jonathanleffler/inc -DDEBUG -DHASH_STATISTICS -c main.c
. make.h
.. /Users/jonathanleffler/inc/posixver.h
.. config.h
.. /usr/include/assert.h
... /usr/include/sys/cdefs.h
.... /usr/include/sys/_symbol_aliasing.h
.... /usr/include/sys/_posix_availability.h
.. /usr/include/ctype.h
... /usr/include/_ctype.h
.... /usr/include/runetype.h
..... /usr/include/_types.h
...... /usr/include/sys/_types.h
....... /usr/include/machine/_types.h
........ /usr/include/i386/_types.h
....... /usr/include/sys/_pthread/_pthread_types.h
.. /usr/include/errno.h
... /usr/include/sys/errno.h
.. /usr/include/inttypes.h
... /usr/include/Availability.h
.... /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/AvailabilityInternal.h
... /usr/include/sys/_types/_wchar_t.h
... /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include/stdint.h
.... /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/stdint.h
..... /usr/include/sys/_types/_int8_t.h
..... /usr/include/sys/_types/_int16_t.h
..... /usr/include/sys/_types/_int32_t.h
..... /usr/include/sys/_types/_int64_t.h
..... /usr/include/_types/_uint8_t.h
..... /usr/include/_types/_uint16_t.h
..... /usr/include/_types/_uint32_t.h
..... /usr/include/_types/_uint64_t.h
..... /usr/include/sys/_types/_intptr_t.h
...... /usr/include/machine/types.h
....... /usr/include/i386/types.h
........ /usr/include/sys/_types/_u_int8_t.h
........ /usr/include/sys/_types/_u_int16_t.h
........ /usr/include/sys/_types/_u_int32_t.h
........ /usr/include/sys/_types/_u_int64_t.h
........ /usr/include/sys/_types/_intptr_t.h
........ /usr/include/sys/_types/_uintptr_t.h
..... /usr/include/_types/_intmax_t.h
..... /usr/include/_types/_uintmax_t.h
.. /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/limits.h
... /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/syslimits.h
.... /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/limits.h
..... /usr/include/limits.h
...... /usr/include/machine/limits.h
....... /usr/include/i386/limits.h
........ /usr/include/i386/_limits.h
...... /usr/include/sys/syslimits.h
.. /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include/stdbool.h
.. /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/stdio.h
... /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include/stdarg.h
... /usr/include/_stdio.h
.... /usr/include/sys/_types/_va_list.h
.... /usr/include/sys/_types/_size_t.h
.... /usr/include/sys/_types/_null.h
.... /usr/include/sys/stdio.h
... /usr/include/sys/_types/_off_t.h
... /usr/include/sys/_types/_ssize_t.h
... /usr/include/secure/_stdio.h
.... /usr/include/secure/_common.h
.. /usr/include/stdlib.h
... /usr/include/sys/wait.h
.... /usr/include/sys/_types/_pid_t.h
.... /usr/include/sys/_types/_id_t.h
.... /usr/include/sys/signal.h
..... /usr/include/sys/appleapiopts.h
..... /usr/include/machine/signal.h
...... /usr/include/i386/signal.h
..... /usr/include/machine/_mcontext.h
...... /usr/include/i386/_mcontext.h
....... /usr/include/mach/machine/_structs.h
........ /usr/include/mach/i386/_structs.h
..... /usr/include/sys/_pthread/_pthread_attr_t.h
..... /usr/include/sys/_types/_sigaltstack.h
..... /usr/include/sys/_types/_ucontext.h
...... /usr/include/machine/_mcontext.h
..... /usr/include/sys/_types/_sigset_t.h
..... /usr/include/sys/_types/_uid_t.h
.... /usr/include/sys/resource.h
..... /usr/include/sys/_types/_timeval.h
... /usr/include/sys/_types/_wchar_t.h
... /usr/include/malloc/_malloc.h
.. /usr/include/string.h
... /usr/include/secure/_string.h
.. /usr/include/sys/stat.h
... /usr/include/sys/_types/_timespec.h
... /usr/include/sys/_types/_blkcnt_t.h
... /usr/include/sys/_types/_blksize_t.h
... /usr/include/sys/_types/_dev_t.h
... /usr/include/sys/_types/_ino_t.h
... /usr/include/sys/_types/_mode_t.h
... /usr/include/sys/_types/_nlink_t.h
... /usr/include/sys/_types/_gid_t.h
... /usr/include/sys/_types/_time_t.h
... /usr/include/sys/_types/_s_ifmt.h
.. /usr/include/unistd.h
... /usr/include/sys/unistd.h
.... /usr/include/sys/_types/_posix_vdisable.h
.... /usr/include/sys/_types/_seek_set.h
... /usr/include/sys/_types/_useconds_t.h
.. /Users/jonathanleffler/inc/debug.h
... /Users/jonathanleffler/inc/kludge.h
.. /Users/jonathanleffler/inc/emalloc.h
.. list.h
.. /Users/jonathanleffler/inc/sastrings.h
.. /Users/jonathanleffler/inc/stderr.h
... /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include/stdarg.h
. /Users/jonathanleffler/inc/getopt.h
Multiple include guards may be useful for:
/opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/syslimits.h
/usr/include/assert.h
/usr/include/errno.h
/usr/include/machine/limits.h
/usr/include/secure/_stdio.h
/usr/include/secure/_string.h
/usr/include/sys/_posix_availability.h
/usr/include/sys/_symbol_aliasing.h
/usr/include/sys/_types/_seek_set.h
$
That lists 110 headers; there are just five that are repeated:
2 /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include-fixed/limits.h
2 /opt/gcc/v9.2.0/lib/gcc/x86_64-apple-darwin18.7.0/9.2.0/include/stdarg.h
2 /usr/include/machine/_mcontext.h
2 /usr/include/sys/_types/_intptr_t.h
2 /usr/include/sys/_types/_wchar_t.h
Other projects I work on have many more repeats. Taking one source file, more or less at random (I know it's a big file; I don't think it is the worst), there is a list of headers included 4 or more times — there are many included 2 and 3 times. The total number of header lines from -H
for this file is 592. Project-specific directory and file names have been changed to protect the innocent — and the file names were massaged with realpath(2)
to deal with the idiosyncratic use of ../subdir/header.h
style names in the source code and in the included headers, which -H
expands to names such as:
../incl/../subdir1/../subdir1/../subdir2/../subdir2/header27.h
Counts:
4 /usr/include/errno.h
4 /usr/include/time.h
4 /opt/project/incl/header1.h
4 /opt/project/incl/header2.h
4 /opt/project/subdir/header3.h
4 /opt/project/subdir/header4.h
4 /opt/project/subdir/header5.h
5 /opt/project/incl/header6.h
5 /opt/project/subdir/header7.h
6 /opt/project/subdir/header8.h
6 /opt/project/subdir/header9.h
6 /work5/gcc/v9.2.0/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include-fixed/limits.h
7 /opt/project/subdir/header10.h
14 /usr/include/bits/wordsize.h
14 /opt/project/subdir/header11.h
22 /work5/gcc/v9.2.0/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/stddef.h

- 1
- 1

- 730,956
- 141
- 904
- 1,278