Build systems are used to automate the process of building software. The ‘make’ command is a build system that has been around for a long time, and is still widely used today. Makefiles are scripts that can be run by the ‘make’ command. A makefile is composed of a set of rules. Each rule defines a target file, a set of dependencies, and the commands needed to create the target file. The dependencies are other files that must be created before the target can be created. When a makefile is run, it evaluates each rule and decides if the commands need to be run. In general, the commands are run if the target does not exist, or any of the dependencies are newer than the target.
The most common use for makefiles is to build C and C++ applications. In these languages, source files are compiled into object files, and then the object files are linked into a final library or executable. A common scenario is when a single source file needs work in a very large codebase. In this scenario, the most efficient way to re-build the project is to re-compile just that one source file, and then link the new object file with all of the existing ones. A makefile with proper dependencies defined will do this automatically. When the makefile runs, it will detect that one of the C files is newer than its object file and re-build the object file. It will then detect that one of the object files is newer than the binary and re-link the binary.
The following is a minimal example of a makefile:
# Build all .c files in the current directory and generate .o files with the
# same name.
# $@ – Name of the target. For these rules, it will be the .o file.
# $^ – Names of all prerequisites. For these rules, it will just be the name
# of the .c file.
$(CC) $(CFLAGS) -c -o $@ $^
# Build an executable ‘foo’ that depends on specific object files. For this
# rule, $@ will be ‘foo’ and $^ will be ‘apple.o banana.o grapes.o’.
foo: apple.o banana.o grapes.o
$(CC) $(LDFLAGS) -o $@ $^