Sign in to save

Bookmark this page so you can find it later.

Sign in to save

Bookmark this page so you can find it later.

Makefiles help programmers automate compiling, testing, cleaning, and packaging software projects. This cheat sheet covers the structure and rules used by make, so students can build programs reliably without typing long commands each time. It is especially useful for C, C++, Java, and mixed projects where files depend on each other.

Key Facts

  • A basic rule has the form target: prerequisites followed by an indented recipe command on the next line.
  • The command lines in a Makefile recipe must begin with a tab character, not ordinary spaces.
  • make target runs the recipe for target only if the target file is missing or older than one of its prerequisites.
  • The variable assignment CC = gcc stores a compiler command, and it can be used later as $(CC).
  • Automatic variable @meansthetargetname,@ means the target name, < means the first prerequisite, and $^ means all prerequisites.
  • A pattern rule such as %.o: %.c can describe how to build any .o file from a matching .c file.
  • .PHONY: clean tells make that clean is a command name, not a real file that should be checked for timestamps.
  • The command make -j4 allows up to 4 jobs to run in parallel when dependency order permits it.

Vocabulary

Makefile
A Makefile is a text file that tells the make program how to build, rebuild, test, or clean a project.
Target
A target is the file or task name that make tries to create or update, such as app, main.o, or clean.
Prerequisite
A prerequisite is a file or target that must exist or be up to date before another target can be built.
Recipe
A recipe is the list of shell commands that make runs to build a target.
Variable
A variable stores reusable text such as a compiler name, flags, file list, or output directory.
Phony Target
A phony target is a task name that does not represent a real file, such as clean, test, or install.

Common Mistakes to Avoid

  • Using spaces before recipe commands is wrong because make usually requires a tab character at the start of each command line.
  • Forgetting prerequisites is wrong because make may skip rebuilding a file even when a source file or header has changed.
  • Naming a real file clean is a problem if clean is not marked .PHONY, because make may think the target is already up to date.
  • Putting shell variables and make variables in the wrong form causes errors, because (CC)isamakevariablewhile(CC) is a make variable while $PATH passes a shell variable.
  • Assuming make always rebuilds everything is wrong because make uses timestamps to rebuild only missing or outdated targets.

Practice Questions

  1. 1 A file program depends on main.o and utils.o. Write the Makefile rule header for building program from those two object files.
  2. 2 If main.o was last modified at 10:00 and main.c was last modified at 10:15, will make rebuild main.o? Explain using timestamps.
  3. 3 In the rule %.o: %.c, what do @and@ and < represent when building math.o from math.c?
  4. 4 Why should a build system describe dependencies instead of only listing commands in the order a programmer usually runs them?

Understanding Makefile and Build Automation Reference

Make works by treating a project as a dependency graph. Each file or named task is a point in that graph. An executable may depend on several object files, while each object file depends on one source file and perhaps shared header files.

Before building anything, make walks backward through these links. It finds the pieces that must exist first, then runs only the needed steps in the right order.

This is why a well-designed Makefile saves time on large projects. Changing one source file should rebuild its object file and the final program, not every file in the project.

Timestamps are the usual test for whether a result is current. When a source file is edited, its modification time becomes newer than the compiled object file. Make notices that relationship and marks the object file as out of date.

Header files need careful attention because many source files can include the same header. If a header changes but is not listed as a dependency, make may leave old object files in place. The program can then compile successfully while using code that no longer matches its declarations.

Compiler tools can often generate header dependency files automatically. This is common in C and C++ projects.

A build usually has separate compilation and linking stages. Compilation turns each source file into an object file. Linking combines object files with libraries to create an executable.

Keeping these stages separate is important because it allows small rebuilds. A Makefile can store repeated settings such as compiler flags, include folders, warning options, and library names in variables. Students should use warnings early.

Warnings often point to type mistakes, unused values, missing return paths, or unsafe conversions before they become harder bugs. A debug build commonly keeps extra information for a debugger, while a release build may use optimization settings.

Some targets do not create a file at all. Cleaning generated files, running tests, formatting code, and installing a program are examples. These tasks must be marked as phony so a file with the same name does not prevent the command from running.

Parallel builds add another concern. Independent object files can compile at the same time, but a final link step must wait until all required objects are ready. Missing dependencies may appear harmless in a one-job build yet cause random failures when several jobs run together.

When troubleshooting, read the printed commands, check tabs in recipes, confirm file names, and inspect the dependency chain. A reliable Makefile describes the real structure of the project rather than relying on lucky build order.