Does make have any sort of way to see if a .h file changed, that all C/C++ source files that directly or indirectly include it need to be recompiled, but others don't need to?
Make does not do this directly. Typically you generate a set of dependencies using gcc. gcc can be told to output a rule suitable for use in make that describes the dependencies of a source file. See the -M option and friends.
Another commenter already pointed out the `-M` flag on GCC (and Clang), but I thought I'd share the rule I use to provide automatic dependencies. It's a bit simpler than the one suggested in the GNU Make Manual:
I suggest just building with "-MMD -MP", using "-MT" if for some reason you want .d files to reside elsewhere. The "-MP" option prevents build errors when a file is removed. No need for crazy redirects or an actual rule to create the .d files.
`-MP` makes sense, but I don't see how Make could work out when to regenerate the dependency files themselves without the `-MT` value as in my rule, or the `sed` expression given in the GNU Make Manual. Maybe I'm missing something about `-MMD` - I don't understand everything said about it in the manpage of GCC.
Generally it's my style to prefer I/O redirection in the shell to programs taking output parameters and managing their own files. Thus, using `> $@` rather than `-MF` or `-MD`.
You seem to be invoking the compiler separately for the sole purpose of building .d files (with a %.d: %.c rule). Don't do it that way. With the -MMD option, you create the .d file as a side-effect of the real compilation. Any change that would require the .d file to be rebuilt has to involve a change to the old dependencies as listed in the old .d file. So the source file will be recompiled. In order for a new dependency on a new header file to be added, an existing and remaining dependency needs to change to add '#include ...'. So the .c file will get recompiled because that file has changed and then you have a new .d file.
For C/C++, I have to subvert GNU make's attempts to rebuild .d files by using $(wildcard) on them and inserting a /./ in the middle of the path.
Note that there are other cases where I do have a rule for building .d files and have them rebuilt. In particular for Fortran 90 modules. This occurs wherever you need to build the .d files first for the initial first build to be in the correct order. This is only an issue for C/C++ if you are invoking a program to generate C sources (e.g. rpcgen). In practice, that is often best handled with a few dependencies listed explicitly in your Makefile.
If your rule that builds .o files from .c/.cpp files uses the one of the dependency-generating flags to GCC (options beginning with -M), GCC will create an alternate or additional (depending on the flag) make-compatible file that describes what files were included during the build. If you then include these files in your Makefile with the 'include' directive, then you'll get the behavior you're looking for.
Implementing it correctly can be a little tricky, but once you understand how to do it it's not too bad. As I recall there used to be some subtle issues that required some post-processing of the GCC-generated dependency files to avoid a problem when header files were renamed or removed (foo.c included foo.h, so a dependency was generated; later foo.c was modified to not require foo.h, and foo.h was removed, but the build still thinks it's required to build) -- it looks like GCC now has a -MP option to add phony empty rules for all dependencies, allowing make to power through these.
Make only understands rules such as "X depends on Y", but gcc can output these dependency rules (-M* switches). So yes, it is possible to do this but make itself is source code agnostic.
Depends on which version of "make" you're using. GNU make does not, but automatic dependency detection is just one of many enhancements in Electric Make, a drop-in replacement for GNU make.
The result of my grep/sed gives me just the header dependencies alone, which is really all I need:
$ cat obj/value.i
src/memory.h src/value.h
So I might consider replacing the grep/sed with gcc -M, and strip off the first two entries, which would give me the equivalent result, but it doesn't strike me as a "must do" right now. Again, I don't use make, just /bin/sh, so I don't need a make rule per se.
Thanks for the advice about the ".i" suffix. I don't think there's a conflict though, because all my intermediate files go into a completely separate "obj" directory, and no pre-processed source will be going there. However, I will consider choosing a different suffix just for the heck of it. I'm sure anything I choose (such as ".dep" or ".inc") will mean something to somebody somewhere, but since I'm in the entirely orthogonal context of the "obj" directory I don't think it matters.