Plus, 'set a flag for just this file' is another thing that's appallingly broken. GNU Make has an extension to do that --- but if you use it, it makes your builds non-deterministic. (Flags propagate down the build chain; builds are not in a defined order; each file is built once regardless of the flags for that file; so if a file is reachable via more than one rule with different flags settings, it's pure luck which setting wins...)
This has the significant advantage that you can not just build 2 or 3 or however many different configurations you would think important enough to hard code in the build system from one source tree, but as many as anyone who builds it happens to need.
Incidentally this is one thing that GNU autotools get right.
It is true that if you use target specific variables you have to ensure yourself that you set them in such a way that no non-deterministic behavior can occur; it would be great if make reported a warning in such cases. Another problem with them is that you can't invoke make with a single object file target any more if that requires a variable set on and inherited from the corresponding link target to compile.
The problem is that there is no way to generate multiple files from one rule. If you write:
$(OBJDIR)/foo.o: src/foo.c
...you get one rule, which will build one object file. If you want to generate two object files with two different settings, you have to have two rules. $(OBJDIR1)/foo.o: src/foo.c
$(OBJDIR2)/foo.o: src/foo.c
There is no way round this. Target-specific variables look like they solve this, but they basically don't work for anything useful. There are only three options here: (1) hardcode multiple rules; (2) use make macros to generate multiple rules; (3) invoke the makefile multiple times with different settings. All of these options are horrible.This is what I mean, more or less. Your build directory contains a generated configuration state (generated by GNU autoconf, for example), and a wrapper Makefile that reads the configuration and builds everything (maybe by invoking a "real" Makefile in the source directory if you're lucky, maybe by having a bunch of ugly Makefiles generated in the build directory too, as GNU automake does).
And (provided you avoid automake) I don't find it horrible at all.
An example is C++, where often standard libraries don't allow mixing debug and release code.
https://github.com/HearthSim/extract-scripts/blob/master/Mak...
Turns out, Make kind of just assumes you're building a C project and makes a lot of further assumptions which made writing this Makefile a living hell. In the end, I didn't even manage to do proper checks for built files.
Make seems like a really poorly execution of a good concept. I think a large part of why autotools suck also comes from that.