Makefile
Install make
- Unix/Linux/macOS:
make
is included in the default environment. - Windows:
- Install scoop
- Install
git
byscoop install git
. - Add bucket
main
byscoop bucket add main
. - Install
make
byscoop install make
Why Makefile?
- You want to avoid hard-coding paths
- You need to build a package on more than one computer
- You want to support multiple compilers
- You want to describe how your program is structured logically, not flags and commands
- …
Compile a program
Now you already know how to build a program with the following structure:
.cpp
├── SimpleMC.h
├── SimpleMC.cpp
├── SimpleMCmain2
├── payoff.cpp
│ ├── PayOff1.h
│ └── PayOff1
└── random.cpp
├── Random1.h └── Random1
What if we changed the PayOff1.h
file? Then the tasks are tedious:
- We have to re-compile Payoff1.cpp.
- We have to re-compile SimpleMC.cpp.
- We have to re-compile SimpleMCmain2.cpp.
Makefile syntax
A Makefile is a text file named Makefile
or makefile
.
targets: dependencies
commands
commands commands
- The
targets
is the target file that we want to generate. - The
dependencies
are the files that the target file depends on. - The
commands
are the commands which generate the target file from the dependencies.
Before the commands, there must be a tab character.
A simple Makefile
You can actually use Makefile to generate any file.
hello.txt:
echo "Hello, World!" > hello.txt
hello.txt
is the target file.- No dependencies.
echo "Hello, World!" > hello.txt
is the command to generate the target file.
Run the Makefile
To run the Makefile, you can type make
in the terminal.
make
will generate the first target file.You can also specify the target file to generate.
make hello.txt
In the example, as long as the target file exists, make
will not generate it again.
- Because there’s no dependencies for
hello.txt
.
Another example
hello2.txt: hello1.txt
cp hello1.txt hello2.txt
hello1.txt:
echo "Hello, World!" > hello1.txt
- If I just type
make
, the first target file is the default target to be built.- If there’s no
hello1.txt
, it will generatehello1.txt
first, then generatehello2.txt
fromhello1.txt
. - If there’s already
hello1.txt
and nohello2.txt
, it will generatehello2.txt
fromhello1.txt
. - If there’s already both
hello1.txt
andhello2.txt
, it will generatehello2.txt
based on ifhello1.txt
is modified.
- If there’s no
Use Makefile to build a program
Unix:
hello: main.cpp
g++ -o hello main.cpp
"Build hello" echo
Windows:
hello: main.cpp
cl main.cpp /EHsc /Fehello
"Build hello" echo
When you run make
,
- The first target file is
hello
, it will be the default target to be built. hello
depends onmain.cpp
.- Make will decide if run the commands if:
- No
hello
executable file exists. - The
main.cpp
is modified. (It’s newer thanhello
executable file.)
- No
Variables in Makefile
Common Variables:
SRC_DIR = ./src
BUILD_DIR = ./build
OBJS = $(BUILD_DIR)/*.o
CC = gcc
CXX = g++
CFLAGS = -std=c++17 -Wall -Werror -Wextra -Wsign-conversion
SRC_DIR = ./src
BUILD_DIR = ./build
OBJS = $(BUILD_DIR)/*.o
CC = cl
CXX = cl
CFLAGS = /EHsc /W4 /std:c++17
CC
often means the compiler for C language.CXX
often means the compiler for C++ language.CFLAGS
are the flags for the compiler.- Use
$(variable_name)
to get the value of a variable.
Use Makefile to build a program
Without using variables:
hello: hello.o main.o
g++ -Wall -std=c++11 -o hello hello.o main.o
hello.o: hello.cpp hello.h
g++ -Wall -std=c++11 -c hello.cpp
main.o: main.cpp hello.h
g++ -Wall -std=c++11 -c main.cpp
.PHONY: clean
clean:
rm hello hello.o main.o
Using variables:
CXX := g++
CXXFLAGS := -Wall -std=c++11
objects := hello.o main.o
hello: $(objects)
$(CXX) $(CXXFLAGS) -o hello $(objects)
hello.o: hello.cpp hello.h
$(CXX) $(CXXFLAGS) -c hello.cpp
main.o: main.cpp hello.h
$(CXX) $(CXXFLAGS) -c main.cpp
.PHONY: clean
clean:
rm hello $(objects)
Phony targets
Phony targets are targets that are not actually files.
The most common phony target is clean
.
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
- If there’s a file named
clean
in the current directory, it will be ignored. - Because
clean
is a phony target,make clean
will always run the commands regardless if there’s a file namedclean
in the current directory.
A more complex Makefile
Now let’s see how to compile what we did in the last lab.
# Compiler and flags
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Werror -Wextra -Wsign-conversion
# Directories
SRC_DIR = ./vanilla_payoff_class
BUILD_DIR = ./build
# Include paths
INCLUDES = -I$(SRC_DIR) -I$(SRC_DIR)/payoff -I$(SRC_DIR)/random
# Object files
OBJ = $(BUILD_DIR)/SimpleMC.o \
$(BUILD_DIR)/SimpleMCmain2.o \
$(BUILD_DIR)/PayOff1.o \
$(BUILD_DIR)/Random1.o
$(BUILD_DIR)/SimpleMCmain2: $(OBJ)
@mkdir -p $(@D) # $(@D) means the directory of the target file
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(BUILD_DIR)/SimpleMCmain2 $(OBJ)
$(BUILD_DIR)/PayOff1.o: $(SRC_DIR)/payoff/PayOff1.cpp $(SRC_DIR)/payoff/PayOff1.h
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/payoff/PayOff1.cpp -o $(BUILD_DIR)/PayOff1.o
$(BUILD_DIR)/Random1.o: $(SRC_DIR)/random/Random1.cpp $(SRC_DIR)/random/Random1.h
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/random/Random1.cpp -o $(BUILD_DIR)/Random1.o
$(BUILD_DIR)/SimpleMC.o: $(SRC_DIR)/SimpleMC.cpp $(SRC_DIR)/SimpleMC.h
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/SimpleMC.cpp -o $(BUILD_DIR)/SimpleMC.o
$(BUILD_DIR)/SimpleMCmain2.o: $(SRC_DIR)/SimpleMCmain2.cpp $(SRC_DIR)/SimpleMC.h
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/SimpleMCmain2.cpp -o $(BUILD_DIR)/SimpleMCmain2.o
clean:
rm -rf $(BUILD_DIR)
.PHONY: clean
Optional: Automatic variables and wildcards
$@
is the target file.$<
is the first dependency.$^
is all the dependencies.*
: Matches zero or more characters in a filename or string.- For example,
*.cpp
matches all the.cpp
files in the current directory. - Like saying “everything that ends with
.cpp
”.
- For example,
%
: Matches zero or more characters in a filename or string.- For example,
%.o
matches each.o
file in the current directory. - Like saying “anything that ends with
.o
” individually.
- For example,
Using automatic variables, the Makefile can be simplified to:
# Compiler and flags
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Werror -Wextra -Wsign-conversion
# Directories
SRC_DIR = src
BUILD_DIR = build
# Include paths
INCLUDES = -I$(SRC_DIR) -I$(SRC_DIR)/payoff -I$(SRC_DIR)/random
# Source files
SRC = $(SRC_DIR)/SimpleMC.cpp \
$(SRC_DIR)/SimpleMCmain2.cpp \
$(SRC_DIR)/payoff/PayOff1.cpp \
$(SRC_DIR)/random/Random1.cpp
# Object files
OBJ = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRC))
# Target executable
TARGET = $(BUILD_DIR)/SimpleMCmain2
# Default target
all: $(TARGET)
# Rule for making the target
$(TARGET): $(OBJ)
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -o $@ $^
# Rule for object files
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
# Clean rule
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
No need to understand it, just take a look.
Assignment
Star Pattern:
Create a cpp file named star_pattern.cpp
that print diffeent star patterns based on the input size n
.
- It will ask user to input the size
n
between 1 and 9. - Based on the input size
n
, the program will print different star patterns.
The star patterns are shown in the next page, you must use loops/nested loops to print them.
For example, if n
is 5, the program will print:
Right Triangle:
*
**
***
****
*****
Left Triangle:
*
**
***
****
*****
Bonus(2pts): Add a pattern that print a Diamond:
Diamond:
*
***
*****
*******
*********
*******
*****
***
*
Assignment 2
Use makefile to build the following program named main
:
.
├── add.cpp
├── add.h
├── main.cpp
├── mul.cpp
└── mul.h
- Please make sure that all the object files are put under current directory or build directory(Either is fine).
- Remember to define a phony target
clean
. - As long as makefile can be correctly run, you will get full credits.
- No need to change the file structure.
- You are free to use any variables, wildcards, and automatic variables or not.
Submission
- Submit star_pattern.cpp for assignment 1.
- Zip the whole folder(including all the files and makefile) and submit it in Canvas.