A Fundamental Guide to make and Makefile
make is a command-line utility, while Makefile (alternatively lowercase makefile) is a configuration file that defines how to build target executables or run other operations. Executing the make command in the current directory will invoke the local Makefile.
Makefile Structure
Dependency Rule
A line like mybin: mytest.c defines a dependency rule: the target mybin depends on the source file mytest.c. The list after the colon is the set of dependency files, which can include multiple files required to build the target.
Recipe
The line immediately following the dependency rule is the recipe—this is the concrete command to execute to build the target. A tab character must precede the recipe (this is a strict syntax requirement). For example, gcc -o mybin mytest.c uses the GCC compiler to compile mytest.c into the executable mybin.
Using make
make [target]
- By default,
makescans the Makefile from top to bottom and builds the first target it encounters. makeonly generates one target per invocation unless specified otherwise.
Defining Multiple Targets
You can add additional targets, such as a clean target that has no dependencies. For example:
clean:
rm -f mybin
The rm -f mybin command deletes the mybin executable. The -f flag ensures no error is thrown if the file does not exist (it forces deletion without prompting). To run this target, execute make clean.
Phony Targets
Marking a target as phony with .PHONY: target_name ensures its recipe runs every time the target is invoked, regardless of file timestamps. Unlike regular targets (which only rebuild if dependencies are newer), phony targets ignore timestamp checks. This is particularly useful for clean operations, as you don’t want to skip cleaning if a file named clean happens to exist in the directory.
Example of a phony clean target:
.PHONY: clean
clean:
rm -f mybin
Using Variables and Automatic Variables
You can define variables to simplify Makefile maintenance, reducing redundancy and making updates easier. Automatic variables provide shortcuts for common values like the target name or dependency list:
CC = gcc
OUTPUT_FLAG = -o
mybin: test.c
$(CC) $(OUTPUT_FLAG) $@ $^
.PHONY: clean
clean:
rm -f mybin
- User-defined variables (e.g.,
CC,OUTPUT_FLAG) are referenced using the syntax$(VAR_NAME). - Automatic variables:
$@: Expands to the name of the current target (in this case,mybin).$^: Expands to the full list of dependencies for the current target (here,test.c).