Contents

make and makefile

I am a C/C++ newbie, but occassionally I have to compile or build projects. Since make (and Makefiles) are quite prevalent and sometimes they have to be adjusted due to potential errors, I had to learn more about them and want to share my learning publicly here.

Building Blocks

First of all, what are the involved building blocks of make and Makefiles?

  • make:
    • The GNU make utility. This is the executable that you actually run in order to execute a series of commands in order to modify files, typically compiling programs.
    • If you run the program without specifying an explicit input file with the -f option, make will look for the makefiles GNUmakefile, makefile, and Makefile, in that order.
    • make updates a target if it depends on prerequisite files that have been modified since the target was last modified, or if the target does not exist.
  • makefile:
    • This is the input file for the make utility and consists of a series of instructions how to modify files.
    • A makefile can contain one or more rules.
  • rule:
    • A rule consists of one or more targets, prerequisites and commands.
  • target:
    • A target is a file name and typically, there is only one per rule.
  • prerequisite:
    • The prerequisites are file names, separated by spaces. These files need to exist before the commands for the target are run. These are also called dependencies.
  • command:
    • The commands are a series of steps typically to make the target. They need to start with a tab character, not spaces.

The general format of a Makefile is as follows:

1
2
3
4
targets: prerequisites
	command
	command
	command

Example 1: Starting Simple

A more specific Makefile would be the following file:

1
2
myfirsttarget:
	echo "hi from first target"

This file has one target named myfirsttarget, no prerequisites and only one command, which will execute the echo command:

1
2
3
└─$ make           
echo "hi from first target"
hi from first target

Typically, when a target is run (i.e., when the commands of a target are run), the commands will create a file with the same name as the target.

If we create a file named myfirsttarget in the same folder as the Makefile, the commands will no longer be executed:

1
2
3
4
5
6
7
8
└─$ make           
echo "hi from first target"
hi from first target

└─$ touch myfirsttarget  

└─$ make               
make: 'myfirsttarget' is up to date.

Example 2: Multiple Rules

Let us consider another example Makefile with two rules:

1
2
3
4
5
mytarget1:
	echo "hello from target1"

mytarget2: 
	echo "hello from target2"

The desired rule and target can be specified with make <targetname>. If no target is specified, the first target of the Makefile is chosen automatically:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
└─$ make mytarget1
echo "hello from target1"
hello from target1

└─$ make mytarget2
echo "hello from target2"
hello from target2

└─$ make          
echo "hello from target1"
hello from target1

Furthermore, multiple targets can be specified at once:

1
2
3
4
5
└─$ make mytarget1 mytarget2
echo "hello from target1"
hello from target1
echo "hello from target2"
hello from target2

The target execution can also be carried out in parallel with the -j option:

1
2
3
4
5
└─$ make mytarget1 mytarget2 -j
echo "hello from target1"
echo "hello from target2"
hello from target1
hello from target2

Example 3: Variables

Variables can be used for dynamic input and they are limited to string values. Single or double quotes have no meaning in make :

Variables can be declared either with = or :=, as shown below:

1
2
var1 := myvalue1
var2 = myvalue2

They can be referenced either with ${var}, $(var), or $var:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
x := dude
y = blubber dubber
all:
        echo $(x)
        echo ${x}
        echo $x 

        echo $(y)
        echo ${y}
        echo $y

Example 4: Security Issues

Furthermore, a Makefile may be used for injection attacks, when a user is able to introduce malicious input in a variable. Any variables are expanded before execution, regardless of any used quotes. Therefore, you should never allow unsanitized user input in the files:

Example Makefile:

1
2
test:
	echo "$(var)"

If an attacker is able to call make with the aforementioned Makefile and control the contents of the var variable, they can inject arbitrary commands, as demonstrated below:

1
2
3
4
└─$ make var='"; whoami #'
echo ""; whoami #"

root

Similarly, the following Makefile will exhibit the same behaviour with single quotes:

1
2
test:
	echo '$(var)'
1
2
3
4
└─$ make var="'; whoami #"
echo ''; whoami #'

root