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"?

1
  • Comments are not for extended discussion; this conversation has been moved to chat.
    – Chenmunka
    Jul 3, 2021 at 17:39

3 Answers 3

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.)

15
  • 7
    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? Jul 1, 2021 at 23:59
  • 13
    @JörgWMittag doesn't actually stop MS from changing things in even the most user-facing of things.
    – muru
    Jul 2, 2021 at 2: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.
    – Lorraine
    Jul 2, 2021 at 9:59
  • 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.
    – Lorraine
    Jul 2, 2021 at 12:12
  • 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. Jul 2, 2021 at 20:13
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
4
  • 4
    This is true, but it does not answer the question. This would be a great answer on Unix & Linux :)
    – gerrit
    Jul 2, 2021 at 8:28
  • 3
    @gerrit and unsurprisingly it’s already been covered there ;-) Jul 2, 2021 at 8: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. Jul 2, 2021 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" youtube.com/watch?v=BKorP55Aqvg
    – Zombo
    Jul 3, 2021 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.

5
  • 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 2, 2021 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 2, 2021 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 2, 2021 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 2, 2021 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... Jul 2, 2021 at 21:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .