> in some instances, it is necessary to type extra instances of "make clean", because the dependency analysis of make does not understand that dependencies have changed.

The only sense in which Make will analyze a dependency is that it will run rules for which the target does not exist or has a less recent mtime than any of its dependencies. When what you describe happens, it's not a failure of Make's dependency analysis, but your failure to specify the dependency. This is easier said than done with the Make+CC combo, but I attribute that problem to quirks of C and CC, not to Make's dead simple and basically infallible dependency analysis.

The only way to cause Make's dependency analysis to "fail" is to modify the mtime of an already built target to be more recent than that of a dependency that would otherwise be more recent. That's only a failure in the sense that it might not desirable, it's still well defined behavior of Make.

> say you build a program which includes /usr/include/ncurses.h. Then, you do a system update which replaces your ncurses library. Or, you do a git pull which changes that header. Make will not rebuild your binary without "make clean".

If this happens, it's because you have a dependency on /usr/include/ncurses.h that you have not specified for the target. Again, easier said than done to specify the location of system wide header files that are maybe only resolved using pkg-config because you have no idea of their location on the users' systems, but that's not a problem that Make supposes to solve for C and the ecosystem of applications that exist to patch up its deficiencies.

Even worse is for example if you've specified your dependency on the ncurses header, update the system and end up with a new ncurses header that has new includes of its own that you have not specified. The only way you'll ever know is by reading ncurses header file. If you don't, the next system upgrade might update those unspecified indirect dependencies and your Makefile will shrug because you haven't specified those as dependencies. Broken, but really not on the Make end.

> files appearing or disappearing along the include path can change the result of a "make clean;make all", but are not detected by make's dependecy analysis. Here an explanation of D.J. Bernstein: http://cr.yp.to/redo/honest-nonfile.html

If the developer in the scenario described had simply added "vis.h" as a dependency to the rule that depends on it, it would not have happened. Creating "vis.h" and adding it and running make again would have solved the problem.

> doing atomically rebuilds: http://cr.yp.to/redo/atomic.html, so that a new dependency is either there, and complete, or not there

target: generate_something
./generate_something -o target.tmp && mv target.tmp target

> parallel builds

GNU Make for example does parallel builds fine. Yes, it's necessary to have complete dependency information (which in Make is achieved only by specifying it) but that's a necessity of any build system that supposes to reliably track dependencies in every case.

In general, your criticism seems aimed specifically against Make+CC. In that sense I completely agree that it's not a great combination and that there are probably solutions much better tailored for building C code that address its quirks specifically. Make works well however when you know what and where your dependencies are. C, when used to build software for n>1 frequently updated operating systems presents a header and library labyrinth that makes that a non-trivial problem.