Makefile

A Makefile is a text file that describes procedures executed by make, a common build tool on UNIX-like operating systems.

Overview

make is a build tool that automatically performs a sequence of steps, such as compilation and linking, to produce an executable from source code. It has long been used as a standard tool on UNIX-like operating systems such as Linux.

Developers describe the required procedures in a Makefile. When the make command reads the file, it executes the commands in sequence and completes the build.

What Is a Makefile?

A Makefile describes the compiler, source files, outputs, build order, and dependencies. Running make is enough to perform the build, and reading the file shows how the program is built. make also determines which files need to be updated, minimizing compilation work and shortening development time.

The GNU Make home page describes Make as follows.

  • GNU Make is a tool which controls the generation of executables and other non-source files of a program from the program’s source files.

This provides the following benefits.

  • End users can build and install a package without knowing the detailed procedure.
  • make identifies files that need updates from the changed source files and automatically determines the correct order.
  • Make supports compiled languages other than C.

Make - GNU Project - Free Software Foundation

Basic Makefile Syntax

make is useful when building C/C++ projects that use many libraries. To use make, prepare a Makefile.

The basic syntax consists of a Target: SourceFile line and a command line beginning with a tab. You can replace a line break with a semicolon to place the rule on one line. You can also define variables in the form name=value and use them for file names and other values.

Target : [Prerequisites]
	Recipe
  • Target: the name of a file generated by the program.
  • Prerequisites: input files used to generate the target. You can specify multiple files or omit them.
  • Recipe: an action executed by make. It may span multiple lines by using \, and each line must begin with a tab.
Target: SourceFile
	Command

For example:

hello: hello.c
	gcc -o hello hello.c

You can also add commands to delete or copy generated files.

clean:
	rm -f *~ hello 
install: hello 
	install -s hello.exe Path

If you run make without arguments, the first target is executed. To run clean or install, specify it as an argument.

make clean
make install

To use a file other than Makefile, specify it with -f.

make -f sample.mk

You can also use variables.

CXX            = g++
OPTIMIZE       = -O3
CFLAGS         = -IC:/Users/include \
	-IC:/Python/include 
DEST           = C:/Users/Local
LDFLAGS        = -LC:/Users/Local/libs
LIBS           = -lpython
OBJS           = hello

all: clean $(PROGRAM) install
$(PROGRAM): $(OBJS)
	$(CXX) -o $(OBJS) $(OBJS).cpp $(CFLAGS) $(LDFLAGS) $(LIBS)
clean: rm -f *~ $(OBJS)
install: $(PROGRAM)
	install -s $(OBJS).exe $(DEST)

Makefile Variables

Makefile variables include implicit variables and variables you define yourself. Implicit variables are predefined variables used by implicit rules.

What Are Implicit Rules?

Some rules and variables are predefined. For example, the recipe for compiling a C source file executes $(CC) -c $(CFLAGS) $(CPPFLAGS). The CC, CFLAGS, and CPPFLAGS variables are predefined, and you can override them to change the recipe.

Implicit Variable List

Variable Description Default
AR Archive-maintenance program ar
AS Assembly program as
CC C compiler cc
CXX C++ compiler g++
CO RCS checkout program co
CPP C preprocessor $(CC) -E
FC Fortran and Ratfor compiler or preprocessor f77
GET SCCS extraction program get
LEX Converts Lex grammar to C or Ratfor lex
PC Pascal compiler pc
YACC Converts Yacc grammar to C or Ratfor yacc
YACCR Converts Yacc grammar to Ratfor yacc -r
MAKEINFO Converts Texinfo source to Info makeinfo
TEX Creates TeX DVI from TeX source tex
TEXI2DVI Creates TeX DVI from Texinfo source texi2dvi
WEAVE Translates Web to TeX weave
CWEAVE Translates C Web to TeX cweave
TANGLE Translates Web to Pascal tangle
CTANGLE Translates C Web to C ctangle
RM File-removal command rm -f
ARFLAGS Flags for the archive-maintenance program rv
ASFLAGS Assembler flags none
CFLAGS C compiler flags none
CXXFLAGS C++ compiler flags none
COFLAGS RCS co flags none
CPPFLAGS C preprocessor and compiler flags none
FFLAG Fortran compiler flags none
GFLAG SCCS get flags none
LDFLAGS Compiler flags used when invoking the linker none
LFLAGS Lex flags none
PFLAGS Pascal compiler flags none
RFLAGS Fortran compiler flags for Ratfor none
YFLAGS Yacc flags none

https://www.gnu.org/software/make/manual/make.html#Implicit-Variables

Defining Variables

There are two ways to define variables.

Symbol Details
= Recursively expanded variable
:= Simply expanded variable
foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
  echo $(foo) # => Huh?

Referencing Variables

Use $(name) or ${name}.

objects = program.o foo.o utils.o
program : $(objects)
	cc -o program $(objects) 

$(objects) : defs.h

Automatic Variables Used in Rules

Automatic variables provide shorthand references to targets and prerequisites.

Variable Function
$@ Target name
$< First prerequisite name
$^ All prerequisite names without duplicates
$? Prerequisites newer than the target
$+ All prerequisite names, including duplicates
$* Part matched by the target pattern

$@

$@ is the current target name. $(D) and $(F) suffixes select its directory and file name.

aaa/bbb/foo:
	echo $@     # => aaa/bbb/foo
	echo $(@D)  # => aaa/bbb
	echo $(@F)  # => foo

$<

$< is the first prerequisite name.

output/foo: input/bar input/baz
	echo $<     # => input/bar
	echo $(<D)  # => input
	echo $(<F)  # => bar

$^

$^ contains all prerequisite names without duplicates.

output/foo: input/bar input/baz
	echo $^     # => input/bar input/baz
	echo $(^D)  # => input input
	echo $(^F)  # => bar baz

$?

$? contains prerequisite names with timestamps newer than the target.

output/foo: input/bar input/baz
	echo $?     # => input/bar
	echo $(?D)  # => input
	echo $(?F)  # => bar

$+

$+ contains all prerequisite names, including duplicates. $^ is more commonly used.

output/foo: input/baz input/baz input/baz
	echo $+     # => input/baz input/baz input/baz
	echo $(+D)  # => input input input
	echo $(+F)  # => baz baz baz

$*

$* is the part matched by the target pattern. This is useful when generating related files.

Makefile Functions

Make provides functions for string processing and conditional branching in Makefiles.

Functions (GNU make)

Text Transformation Functions

Call functions with $(function arguments) or ${function arguments}.

Reference: https://www.gnu.org/software/make/manual/make.html#Text-Functions

shell

Runs a shell command.

files := $(shell echo *.c)
INCLUDE := $(shell find $(INCDIRS) -type d)
SRCDIR = ./srcs
SRCS := $(shell find $(SRCDIR) -name *.c)
all:
	echo $(files) # => hoge.c foo.c
	echo $(INCLUDE) # => include
	echo $(SRCDIR) # => ./srcs
	echo $(SRCS) # => hoge.c foo.c
RESULT = $(shell seq 1 10)
all:
	echo $(RESULT)  # => 1 2 3 4 5 6 7 8 9 10

addprefix

Adds a prefix. This is useful for options such as -l.

FILES := foo bar
all:
	echo $(addprefix src/,$(FILES))  # => src/foo src/bar

dir

Extracts directory parts from file names.

FILES := src/hoge.c src/hoge.h index.html
all:
	echo $(dir $(FILES))  # => src/ src/ ./

notdir

Extracts file names.

FILES := ./dir/hoge.txt
all:
	echo $(notdir $(FILES))  # => hoge.txt

Substitution References

Substitution references replace matching suffixes in variable values. They are shorthand for patsubst.

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
bar2 := $(patsubst %.o,%.c,$(foo))

realpath, suffix, and wildcard

"$(realpath ./dir/hoge.txt)"//C:users/hoge/bin/hoge.txt
"$(suffix ./dir/hoge.txt)"//.txt
"$(wildcard ./dir/*.txt)"//hoge.txt hogehoge.txt

Conditional Functions

"$(if $(VAR1),$(exist),$(none))"

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif

Debugging a Makefile

GNU Make’s -n option prints commands without executing them.

CC = gcc
OBJ = hoge.c
CFLAGS = -c $(OBJ)
$ make -n
gcc -c hoge.c -o hoge

The warning function prints content while make runs.

$(warning MAKE = $(MAKE))
$(warning CC = $(CC))
$(warning CFLAGS = $(CFLAGS))

Installing g++, make, and cmake

Installing on Windows

On Windows, use MinGW for g++/gcc, GnuWin for make, and the CMake installer for cmake.

To install from a command-line interface:

choco install mingw
choco install make

Configure the paths as environment variables.

C:\Program Files (x86)\MinGW\bin
C:\Program Files (x86)\GnuWin32\bin

Installing on Linux

Install all required packages together:

sudo apt install build-essential

Or install them individually:

sudo apt install g++
sudo apt install make

Summary

This article summarized how to write a Makefile. It will continue to be updated as a reference.