37

The syntax for Makefiles requires that indented lines start with a tab, and not a space. So far as I can tell, this has been the case even for very early implementations of make. But even modern-day make (I've checked my current computer, which has make 4.19 installed) rejects any Makefile using spaces for indentation.

The Unix Haters Handbook laments how a bugfix was mailed in to Berkeley, and that this bugfix never made it in to the "trunk", or "master", or whatever it was called then, so that the next release of BSD still absolutely requires the \t. I recall it's tripped me up a few times in my early days, and even now, newbies need to be explicitly told about this. So evidently, allowing any kind of whitespace here would be useful.

Is this behaviour intentional? Is the reason for this historical, being rooted in some kind of arcane teletype situation or something I'm not aware of? Or is it just a case of "not broke, won't fix"?

tofro
  • 34,832
  • 4
  • 89
  • 170
Omar and Lorraine
  • 38,883
  • 14
  • 134
  • 274

3 Answers3

48

This goes back to early versions of Make, and isn’t specific to GNU’s implementation; as explained by the author of the original Make, Stuart Feldman:

Why the tab in column 1? Yacc was new, Lex was brand new. I hadn't tried either, so I figured this would be a good excuse to learn. After getting myself snarled up with my first stab at Lex, I just did something simple with the pattern newline-tab. It worked, it stayed. And then a few weeks later I had a user population of about a dozen, most of them friends, and I didn't want to screw up my embedded base. The rest, sadly, is history.

This is also recounted in an email from Stuart Feldman in this blog post on tabs and Makefiles, which includes the following choice quotes:

So even though I knew that "tab in column 1" was a bad idea, I didn't want to disrupt my user base.

So instead I wrought havoc on tens of millions.

I have used that example in software engineering lectures.

As for why changing from “newline-tab” to something else is problematic, presumably there was already enough variation in Makefile contents at the time for other approaches to cause issues: for example, spaces could be used to make macro definitions clearer.

(Established usage and changes in Make since then make this decision even more entrenched now: conditional constructs in some implementations of Make mean that many Makefiles use leading spaces to clarify blocks intended for Make itself, not as recipes. Allowing space to introduce recipes would break all that. Makefile authors can in some cases choose to write Makefiles with a different recipe introductor, see elarivie’s answer for details; but that’s an internally-requested change, not something that Make could impose on all Makefiles.)

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
  • 8
    I often bring this example up when people say something like "Microsoft are so stupid, why don't they just change X and it would be so much better". Well, "just change it" didn't even work with ~10 users, how is it going to work with 1.5 billion? – Jörg W Mittag Jul 01 '21 at 23:59
  • 13
    @JörgWMittag doesn't actually stop MS from changing things in even the most user-facing of things. – muru Jul 02 '21 at 02:21
  • 2
    I'm not sure I understand why permitting spaces would screw anything up. Take Python for example, a tab-only indented file is just as valid as a space-only indented file or a mixed one. – Omar and Lorraine Jul 02 '21 at 09:59
  • 1
    @OmarL: What do you mean by "screw everything up"? The parser or the users? If you are talking about the parser, the author explains the reasoning: he didn't know much about parsers, and doing it this way was the simplest way possible he could figure out. If you are talking about the users, the author also explains that: by the time he had realized it was a mistake, and had figured out a way to fix it, he already had about a dozen users who had already written makefiles, and changing the syntax would have broken all their code, therefore it was no longer possible to make any changes. – Jörg W Mittag Jul 02 '21 at 10:26
  • Note that the state of parsing technology in 1976 when make was written was very different from the state of parsing technology in 1990 when CPython was written. 14 years of progress in software engineering, especially back then, is like going from the Ford Model T to a 2022 Ford F150 Lightning. – Jörg W Mittag Jul 02 '21 at 10:33
  • 7
    I mean, for the user. As in the quote, "and I didn't want to screw up my embedded base". How could anyone using tabs have possibly been screwed over by something that accepts both tabs and spaces, like anything sane? I don't see an explanation for this anywhere. – Omar and Lorraine Jul 02 '21 at 12:12
  • @JörgWMittag, not really; Consider C, which also accepts either space and tab for "indentation" (Okay, the whitespace is not as significant in C, but). I am sure that even in 1976 checking for a space character was not difficult. – Omar and Lorraine Jul 02 '21 at 12:15
  • 1
    @OmarL I tried to explain that in my edit: users could use (and do use, nowadays) spaces for other purposes. Changing the significance of space is a backwards-incompatible change. – Stephen Kitt Jul 02 '21 at 12:15
  • 5
    @OmarL: This answer quotes the author of make, who couldn't figure it out. You can argue that "it was not difficult", but the fact of the matter is that the author of make himself has publicly stated that it was. – Jörg W Mittag Jul 02 '21 at 20:13
  • 1
    @OmarL: But in C, leading spaces (or tabs) don't have any special meaning. But people who wrote makefiles might well have been using spaces for formatting. If you now give that space in the first column a special meaning, your nicely-formatted-with-spaces makefile no longer works. – jamesqf Jul 02 '21 at 21:25
  • 1
    I don't quite get why getting (typically a once-in-a-lifetime learning and you know it) used to "newline-tab" is such a difficult thing and catastrophic failure ;) – tofro Jul 03 '21 at 10:03
  • 1
    @tofro I don’t think it causes much difficulty for people who are familiar with Make; the problem is for people who aren’t... Think of people unaware of the tab rule, copying a Makefile from a book or magazine back in the day, or editing one with an editor that doesn’t distinguish tabs. – Stephen Kitt Jul 03 '21 at 10:56
  • If the book or magazine targets beginners and doesn't mention the "tab rule", it's crap. If it does indeed and the user doesn't read it, well... – tofro Jul 03 '21 at 11:54
  • @tofro you’re assuming all readers carefully read all the text leading up to the snippet they’re interested in ;-). (I agree with you that more fuss is made over this than is deserved, but there are circumstances where it trips people up.) – Stephen Kitt Jul 03 '21 at 12:14
  • That‘ll teach them ;) – tofro Jul 03 '21 at 12:43
17

GNUMake does not only accepts tab,

If you don't like tab as prefix, it is editable with the use of the special variable .RECIPEPREFIX

See: https://www.gnu.org/software/make/manual/html_node/Special-Variables.html

The first character of the value of this variable is used as the character make assumes is introducing a recipe line. If the variable is empty (as it is by default) that character is the standard tab character.

For example, this is a valid makefile:

.RECIPEPREFIX = >
all:
> @echo Hello, world
elarivie
  • 271
  • 3
  • 4
    This is true, but it does not answer the question. This would be a great answer on [Unix.SE] :) – gerrit Jul 02 '21 at 08:28
  • 3
    @gerrit and unsurprisingly it’s already been covered there ;-) – Stephen Kitt Jul 02 '21 at 08:40
  • 4
    Although not an answer, it is still very valuable and relevant. I don't think answers need to be so laser focused to be good and useful. – Wayne Conrad Jul 02 '21 at 10:12
  • To @gerrit and people saying this is not an answer, it is. It is the askers fault that they didnt ask a valid question. It would be like someone asking, why are Roses green? Then someone answers "they arent green", and commenters come in a say "you didnt answer the question" https://youtube.com/watch?v=BKorP55Aqvg – Zombo Jul 03 '21 at 16:57
6

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:

this: that
# 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.

Kaz
  • 1,660
  • 1
  • 7
  • 14
  • I'm not sure why one would need to check for exactly eight spaces, and not just one. Yes, of course it would be incompatible with how it is now. Also, how would the empty line create ambiguities if the rule bodies could be on lines with just spaces instead of tabs? Either the intervening line is empty, in which case you ignore it like the empty line between the two tab-lines is ignored now; or it has some whitespace, in which case it'd be part of the rule body. – ilkkachu Jul 02 '21 at 19:38
  • @ilkkachu I'm thinkin of a rule which is followed by an empty line, where it is intended to end, and then followed by space-indented material which is not a recipe line. Though Makefiles often have empty lines between rules, that's not actually the terminating signal ... I guess is my point. – Kaz Jul 02 '21 at 20:50
  • @ilkkachu My eight space example is based on the idea that it would be reasonably rare to have that much indentation as a false positive, and also that that is precisely the amount of indentation that is likely occur when tabs are expanded to spaces. E.g. someone copies and pastes a dump from a terminal window without tabs being preserved or whatnot. – Kaz Jul 02 '21 at 20:52
  • ^ This problem occurs in practice, so if we were concerned with solving real problems, making make recognize an 8 space indentation as a Tab alternative would make sense. – Kaz Jul 02 '21 at 20:59
  • As a quick counterexample, Automake produces Makefiles with eight (and more) leading spaces. Arguably one could fix Automake to suit whatever change was made in Make, but that still leaves all the existing Makefile.in files out there... – Stephen Kitt Jul 02 '21 at 21:36