views:

833

answers:

5

I'm required to write documentation for my current project that lists all .c files and for each one lists every .h file which is directly or indirectly included by that file.

This is a large project, and although we have Makefiles which theoretically have this information, those Makefiles are sometimes incorrect (we inherited this project from another company). We've often had to do a make clean ; make for our changes to actually be reflected in the recompilation, so I don't want to rely on these Makefiles.

So is there a tool which lets us give it the name of a .c file and an include path and have it tell us all of the .h files which are directly or indirectly included by the .c file? We don't have anything weird like

#define my_include "some_file.h"
#include my_include

so the tool doesn't need to be perfect. Anything that searched .c and .h files in an include path for regular includes would be good enough.

+5  A: 

"gcc -M file.c" does what you need.

+9  A: 

What I do in my Makefile is

SRCS=$(wildcard *.c)

depend: $(SRCS)
    gcc -M $(CFLAGS) $(SRCS) >depend

include depend

This means that if any of the source files are updated, the depend rule will run, and use gcc -M to update the file called depend. This is then included in the makefile to provide the dependency rules for all the source files.

Make will check that a file is up to date before including it, so this depend rule will run if necessary whenever you run make without you needing to do a "make depend".

This will run any time any file has changed. I've never found this a problem, but if you had a huge number of files in the directory you might find it took too long, in which case you could try having one dependency file per source file, like this:

SRCS=$(wildcard *.c)
DEPS=$(SRCS:.c=.dep)

%.dep : %.c
    gcc -M $(CFLAGS) $< >$@

include $(DEPS)

Note that you can use -MM instead of -M to not include system headers.

Mark Baker
You could change a header to include an extra header, thereby changing the dependencies of all the source files, but your dependency list would not get updated. Getting it completely right can be quite fiddly IIRC, but a good start is to include all your own headers in the dependencies of depend.
Steve Jessop
Oh, and for performance note that this re-processes all files if any file changes. So you might want to have multiple depend files (perhaps even one per source file). Finally, note that you can usually use -MM, which skips dependencies on system headers, since you won't be changing those.
Steve Jessop
Actually I lied - I always use -MM myself :)
Mark Baker
I didn't know you could include multiple files, but I've just checked and you can, so yes, one depend file per source file is a good option if you find performance a problem with the way I do it - personally I don't, I hardly notice it's doing anything.
Mark Baker
Thanks for your comments, I've edited the post.
Mark Baker
+1  A: 

In MSVC (2005 and 2008 at least, possibly other versions as well but not VC6) you can get the compiler to tell you all the files that were included during compilation. The output is quite verbose, but complete and fairly easy to parse with human eyes.

In Project Settings, go to the C/C++>Advanced tab, and toggle "Show Includes," then rebuild your project from the ground up.

John Dibling
Or from the command line/makefile: -showIncludes
Michael Burr
+2  A: 

An alternative to gcc -M is fastdep. Fastdep's author reports fastdep to be ten times faster than gcc's -M. If the project takes a while to build, fastdep may be worth a look.

Jonathan Wright
+2  A: 

Use SCons

$ scons --tree=all
scons: Reading SConscript files ...

scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
+-.
  +-SConstruct
  +-app
  | +-test.o
  | | +-test.c
  | | +-/include/PCI_1149_1.h
  | | +-/include/Pci.h
  | | +-/usr/bin/gcc
  | +-/usr/bin/gcc
  | +-/lib/libpci1149_64.a
  ...
humble_guru
Nice! I actually looked through the SCons API since I figured I'd have to muck around with its internals to get it to do this. I'll keep this command line switch in mind in the future.
Eli Courtwright