Pat Browne

Why do commands require tabs in a Makefile?

Mar 3, 2024

When I was first learning how to write a Makefile to compile a C program, one thing that stuck me (and almost everyone who encounters it) as odd was the requirement that each command line start with a tab character ('\t'). Makefiles are used to automate repetitive tasks (such as recompiling a C/C++ program), and have a unique syntax. They are made up of rules, which consist of a “target”, a list of “dependencies”, and a series of “commands”. They look like this:

target: dependencies
	command

For example, suppose I want to automate two different tasks: recompiling my program after editing some of the source code, and deleting all non-source files after editing them. I could write a Makefile that looks like this:

.PHONY: clean

myprogram: myprogram.c
	gcc myprogram.c -o myprogram
	
clean:
	rm *.o *~ myprogram

The .PHONY makes sure that when I run the shell command make clean, I don’t create a file named clean. Instead, I run the command underneath, which removes the files I don’t want in my directory. The other rule has a target filename myprogram, which requires a file named myprogram.c. If it finds this .c file in my directory, it will compile it using the command below. If you want to learn more about makefiles and their syntax, a resource that I highly recommend is Makefile Tutorial.

Notice how each command is indented with a tab character. The UNIX utility make doesn’t allow you to list a command underneath a target/dependency combination, or to indent with a number of spaces. In the “tabs vs. spaces” war, make has definitifely taken a side. Why is this?

The author of make, Stuart Feldman, has explained this debacle a few times, including once to Michael Stillwell. Stuart explained that make was written in a weekend. To tokenize Makefiles, he was using lex, which was in its first version at the time. He got frustrated with trying to write a pattern for commands, and instead used a fixed patter ('\t') to indicate rules. After a while, about a dozen people at Bell Labs were using make. He didn’t want to disrupt his users, and so left the “tab in column 1” rule in the UNIX command to preserve backwards compatibility.

As he says in the email:

> Within a few weeks of writing Make, I already had a dozen friends who were 
> using it.
> 
> 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.

If you don’t want to begin your commands with '\t', different versions of make allow you to specify other characters. For example, GNUMake allows for a special variable, .RECIPEPREFIX, where you can change the character that make assumes is introducing a recipe line. However, from personal experience, this isn’t much help to someone who is trying to make a binary file and keeps getting syntax errors!