Ultimately, there is no air-tight justification for any design choice in a computing language, because alternative designs can easily be defined.
The rationale for
make is that Makefiles are parsed using an ad hoc line-oriented algorithm which classifies whether a line is a rule body or not based on the leading tab.
You can literally implement a parser for it by a loop wrapping around
fgets, doing some trivial classifications like "does this start with tab" and "is there a colon (that isn't commented out by
I think, you can't easily support a more flexible syntax without revising the algorithm as such. Not to mention, threatening backward compatibility.
You might think that, say, at least recognizing eight leading spaces as a facsimile of a tab would be reasonable; that would handle Makefiles that were accidentally pumped through tab expansion.
Yet, that would break some Makefiles out there in the world which have a line with eight spaces. This is valid Makefile syntax:
# My Makefile
# Comment after eight spaces
FOO = bar # assignment after eight spaces
So a naive version* of this could only work as a non-conforming extension: a mode of operation under which some POSIX-ly correct Makefiles either fail, or even suffer a silent change in behavior somehow. Some complications/enhancements in the parsing logic could be put in to reduce the odds of this happening.
Now, we have the mechanism for requesting conformance. The POSIX standard gives us the provision that a Makefile should contain the special target
.POSIX which requests conforming mode. GNU Make observes this, and has some deviations from POSIX in its default mode.
So the question remains why doesn't GNU Make fix that tab design? Probably, the project doesn't want to "go to town" with gross incompatibilities with POSIX in its default mode of operation, so that users would be often forced to use the
.POSIX mechanism. GNU Make does have a way to change the character from tab to something else, and the project likely regards that as a good enough solution: you can have a visible character that will not be eaten by whitespace filtering, be happy with that.
* Improved versions could handle those space uses outside of rule bodies, but probably require a major revision to the parsing algorithm. Under the current status quo, any line with a tab is part of a rule body. These do not have to be consecutive. For instance, this is valid rule syntax:
# commented line; blank line follows
[Tab]@echo updating $@ from $<
[Tab]@foo-utility -o $@ $<
Both commands are part of the
this: that rule, in spite of there being a comment which is not tabbed, and the presence of blank lines. This creates ambiguities, if
[Tab] could be replaced by spaces.