What is a Makefile

An introduction to Makefiles

This file is intended to provide an introduction to creating Makefiles. It is intended for people who know what the make program does and what it is used for, but have never created a makefile themselves. The examples focus on makefiles for C programs.

I should also say that there are several make programs whose makefiles are not 100% compatible. I'm referring to the GNUmake here. Especially all things that have to do with patterns can be slightly different depending on the make program used.

Targets, rules and dependencies

A Makefile is there to tell the program make, What it should do (this is the "target"), and how it should do it (this is the "rule" associated with the target). Furthermore, you can specify for each target which other targets or files it depends on.

This is best seen in a simple example. Let us assume that we have a small C program called with an associated header file, which we normally use

translate. So our goal is to create the file. This obviously depends on and. In the Makefile it looks like this:

prog: prog.c prog.h gcc -o prog prog.c

In the first line we tell make that there is a target called "prog" and that it depends on and. In the second line we tell make which rule (i.e. how) it can use to create this target. If make is now called, it checks whether one of the two files is newer than the target or not. In this case it runs the second line and creates a new executable file.

A common trap for beginners is that the second line must start with a, not a space.

Many targets cannot be created with a single command, as is the case here, but several are necessary. In this case, the line with the dependencies is simply followed by several lines, all of which begin with. The dependencies for a target can also be distributed over several lines.

Note that in our example "" is a name for a target and a file at the same time. make sees no difference in this. The two files and are also nothing more than targets for make. These targets do not depend on anything, so they are always up-to-date, and there are no rules for creating them. If, for example, the file did not exist, make would find that it cannot generate the target that is required for. The error message is accordingly:

make: *** No rule to make target `prog.h '. Stop.

The line with the rule () is called by make in a shell. It is therefore possible to use wildcards or command substitutions (with `...`).

The default target

When calling make you can specify the target to be generated:

make prog

If, on the other hand, you call make without any arguments, the first target found in the Makefile is created. In many Makefiles, the first target is therefore a name that depends on a number of other targets. The goal is usually to get a finished, executable program by calling make without arguments. In particular, all object files and the executable files must be generated for this.

Pattern in rules

In practice, programming projects consist of so many files and dependencies that it is impractical to include a new rule in the Makefile for each individual object file and to change this with each new one. This is why there is the option of defining rules using a kind of wildcard pattern:

% .o:% .c gcc -Wall -g -c $ <

This rule states that each file depends on the corresponding file and how it can be created with the help of the compiler.

In order to be able to access the name of the target and the dependencies within the rule, there are some variables predefined by make. Here are the most important in my opinion (of many):

the first addiction
Name of the target
a list of all dependencies
a list of all dependencies, although duplicate dependencies have been eliminated.

Note that when specifying the dependencies, the dependency of the object files on various header files has been neglected. I will discuss one way of getting this back in the section on dependencies as a target.

The way of creating rules using patterns presented here is just one of many possible. Unfortunately, it has to be said that patterns and pattern substitutions in makefiles are among the most confusing and confusing things. In addition, they may differ from one another with different make versions.

Rules that were generated by patterns are a special case of so-called "implicit rules", i.e. rules that make has not explicitly specified, but that are deduced by make by applying certain rules.

Suffix rules

Another type of implicit rules that you see often are so-called suffix rules. The above dependency of files on the corresponding files would be expressed with their help as follows:

.c.o gcc -Wall -g -c $ <

There are several types of suffix rules. However, suffix rules should be viewed as obsolete, and it is better to use the more readable rules generated by patterns.

Variables in Makefiles

It is possible to define and use variables in Makefiles. Usually one uses capital letters. For example, the following variables are common:

The compiler
Compiler options
Linker options

The content of these variables is then accessed with or back.

A simple Makefile of a program, which is to be linked from a number of object files, could look like this:

VERSION = 3.02 CC = / usr / bin / gcc CFLAGS = -Wall -g -D_REENTRANT -DVERSION = \ "$ (VERSION) \" LDFLAGS = -lm -lpthread `gtk-config --cflags`` gtk-config - libs` -lgthread OBJ = file1.o file2.o file3.o file4.o file5.o prog: $ (OBJ) $ (CC) $ (CFLAGS) -o prog $ (OBJ) $ (LDFLAGS)% .o: % .c $ (CC) $ (CFLAGS) -c $ <

The default target here is the executable program. This depends on all object files. When linking, the math, pthread, various GTK libraries, and the gdk-threads-library are linked.

This example also shows that one must not forget that a shell is executing the commands: without the backslash in, it would remove the quotation marks. The version number should be passed to the C preprocessor as a constant character string. You can also see that a program called "" is called when linking, which outputs the libraries required for GTK on the standard output. Command substitution is carried out here.

Since, as has been seen in the last examples, the dollar sign is used in makefiles to identify variables, if a dollar sign actually appears in a rule, it must be masked: is a dollar sign. (See the example in the section on recursive make!)

Comments in Makefiles

Just like the shell, make treats lines beginning with a "#" as comments:

# commented out line.

Phony targets

With normal targets, make checks whether they are more up-to-date than all targets on which they depend. If this is not the case, they are regenerated. Targets that do not depend on any other targets are therefore always up-to-date and are never created. This usually applies to the sources of a program, for example.

Some targets do not correspond to a physical file. A typical target of this type is the "clean" target, which deletes all files that have been created. It doesn't depend on anything. Problems can arise if there is a file called "clean" in the directory. Make now determines that the target "clean" has already been created and does not depend on anything and is therefore up-to-date.

So there are targets that should always (!) Be created anew, regardless of which other targets they are dependent on. These are called phony targets:

OBJ = file1.o file2.o file3.o file4.o file5.o BIN = prog .PHONY: clean clean: rm -rf $ (BIN) $ (OBJ)

If you enter "", the command is always carried out, regardless of whether a file named "clean" exists or not.

Phony targets, like all normal targets, can depend on others. In general there is no need to declare any targets as phony. But keep this in mind.

Pattern substitution

Just as you could create rules with the help of patterns, you can also create variables:

OBJ = file1.o file2.o file3.o file4.o file5.o SRC = $ (OBJ:%. O =%. C) HDR = $ (OBJ:%. O =%. H) config.h

Here have the variables and then the values

file1.c file2.c file3.c file4.c file5.c file1.h file2.h file3.h file4.h file5.h config.h

Just as with the creation of rules with the help of patterns, it is also true here that the possibilities of creating variables through such substitutions are so varied that one cannot really know all of them. If in doubt, we recommend reading the make instructions. (See the section on more information!)

Dependencies as target ()

If implicit rules or rules generated by patterns are used to generate object files, the dependencies of these on the header files do not apply. To get around this problem without having to manually build in the dependencies of each object file into the Makefile, you usually define a target called "" for "dependencies":

SRC = file1.c file2.c file3.c file4.c file5.c CC = / usr / bin / gcc DEPENDFILE = .depend dep: $ (SRC) $ (CC) -MM $ (SRC)> $ (DEPENDFILE) -include $ (DEPENDFILE)

The option lets the precompiler look for directives in the source code. It outputs the corresponding dependencies on the standard output in exactly the format in which make needs them. Here, however, they are diverted to a file called, which is then inserted into the Makefile using a. The minus sign in front of the tells make that it should not abort with an error message if the file does not exist - after all, it must first be created by calling "". (The command is a specialty of the GNUmake program. I don't know how to achieve a similar effect with other make programs.)

Such a target is particularly important if there are header files that are generated dynamically and then incorporated into source code files.

Recursive make

In large projects, the source files are scattered over many directories on several levels. For reasons of clarity and effectiveness, it is common in such cases to give each directory its own Makefile. You can then e.g. put a "" in a directory without having to recompile the entire directory tree afterwards.

In the top-level directory there is only one Makefile which recursively calls make in all subdirectories. In addition, a file with general rules could be stored there, which is included in the subdirectories of all Makefiles.

There is no standard way of doing all of this. It depends too much on the specific conditions of the project. Here is an extremely rudimentary example for the sake of clarity:

# Makefile in the top-level directory DIRS = dir1 dir2 dir3 compile: for i in $ (DIRS); do make -c $$ i; done

This will be evaluated by make. The shell then references the value of the variable. The option given to make causes make to change to the specified directory before starting.

# Makefile.rules in the top-level directory CC = / usr / bin / gcc CFLAGS = -Wall -g% .o:%. C $ (CC) $ (CFLAGS) -c $ <

In this file, rules and variables are defined that are to apply in all subdirectories. What is still missing are the makefiles in the individual subdirectories:

# Makefile in a subdirectory include ../Makefile.rules OBJ = file1.o file2.o file3.o file4.o file5.o all: $ (OBJ)

Only the object files to be created in this directory are listed here and the default target is set so that they are created without arguments when make is called.

Typical problems with make

There are some things that make is not particularly good at, and which therefore often cause problems that are difficult to solve. Most of them stem from the fact that determining dependencies is too complicated and insecure (see the section on dependencies!)

Dependencies in recursive make

Let us assume that we have distributed our sources over several directories to which we descend recursively (as described in the previous section). There are also two directories and. In there is a target, which depends on a file in the directory. And this in turn is generated from a file that is also located in the directory.

If a is now called in the directory, it cannot determine whether the file is up-to-date, since it has no rules and no dependencies for this, because these are in the directory's Makefile. And what's worse, it can't create the file either if it's missing. In particular, in this case you have to ensure that the recursive one calls in first and then calls in. And that doesn't help either if the reverse case also occurs at the same time.

The only thing you can do about it is to structure the sources so cleanly that all dependencies of a target are in the same directory or in a subdirectory, but not in a neighboring or higher one. In the case of a recursive one, the must first be carried out in the subdirectories and then in the directory itself.

Recompile when compiler flags change

Another problem is that it cannot determine if, for example, some compiler flags have been changed in the makefile. This may mean that some files have to be recompiled. Since this does not determine alone, there is nothing left but one. Alternatively, you could make all targets dependent on the makefile and possibly accept unnecessary recompilations.

Recompile when moving files

If old files are retrieved with, the date of the last change also remains old. cannot determine that recompilation is actually necessary. Only one can help here.

additional Information

you get through

info make

The info pages of the GNU Makes are written in a very understandable way and I can recommend them with a clear conscience to anyone who wants to know more about writing Makefiles.

This documentation is also available in other formats (html, ascii, dvi, ps and texinfo). They can be downloaded from the GNU online manual pages.

By the way, I am always grateful for additions, corrections or comments.