Makefile

variables1

It should be noted that the usage of site a variable in makefile is different from using it in shell.

In makefile, we use $() to site a variable, which is means that command substitution in shll2

There are three ways to assign for a variable:

  1. =: It allows that the subsquent variable uses the following variable.
  2. :=: It is same with the assignment symbol in programming language.
  3. ?=: If the varible has value, it will do nothing.

Wildcard

The wildcard in makefile is similar with macro in C/C++, it isn’t similar with wildcard in linux shell, so it doesn’t expend automatically.

1
2
3
object1 = *.c  // *.c

object2 = $(wildcard *.cpp) // main.cpp t1.cpp t2.cpp

Automatically generate dependencies

Utilizing the -M and -MM options to get the dependencies of source code from gcc/g++.

These options’ document can be found at Preprocessor-Options. (About why these option in the preprocessor options, it is easy to understand. In the state of preprocessor, the preprocessor will judge the header file, so it knows which files are dependented by current file.)

Hide commands themself

Place @ front of the command, e.g. If you use echo "compiling..." in makefile, you will get the output

1
2
echo "compiling..."
compiling...

If you use @echo "compiling...", you will get the output

1
compiling...

Function

https://cdn.jsdelivr.net/gh/gaohongy/cloudImages@master/202309171043153.png

Automation variables

  • $@ : 匹配目标中模式定义的集合
1
2
%.o: %.c
	$(CC) $(CFLAGS) $< -o $@

上述的 $@ 指代的即为 %.o。不过从上述示例中,并不能看出“集合”这一含义,可以结合 $% 来看。

  • $% : 仅当目标是函数库文件时,表示规则中的目标成员名
1
2
fun(a b c): 
	echo $% $@

$% 将代表 a$@ 将代表 fun。从这个示例中就可以看出 $@ “集合“这一含义了。

不过 $% 的定义中提到“仅当目标是函数库文件“,如何理解这一点?

1
2
fun:
	echo $%

此时 $% 为空,由此即可理解这一变量的用法。

  • $< : 第一个依赖文件的名字

例如,在 main.o: main.c defs.h 中,$< 就是 main.c。

  • $? : 所有比目标新的依赖目标的集合

例如,如果 main.o 依赖于 main.c 和 defs.h,而 defs.h 比 main.o 更新,那么 $? 就是 defs.h。

  • $^ : 所有的依赖目标的集合

例如,在 main.o: main.c defs.h 中,$^ 就是 main.c defs.h

  • $+ : 这个变量很像 $^ ,也是所有依赖目标的集合。只是它不去除重复的依赖目标

例如,在 main.o: main.c defs.h main.c 中,$+ 就是 main.c defs.h main.c

Example

Advanced makefile usage example code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 编译工具信息
CXX = g++
CFLAGS = -std=c++11 -Wall

# 目录信息
SRC_DIR = src
BUILD_DIR = build

# 源文件列表
SRCS = $(wildcard $(SRC_DIR)/*.cpp)

# 生成目标文件列表(目标文件.o和源文件.cpp一一对应)
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))

# 最终目标文件
TARGET = $(BUILD_DIR)/main

.PHONY: all clean

all: $(TARGET)

$(TARGET): $(OBJS)
	@mkdir -p $(dir $@) # $(dir $@) = $(BUILD_DIR)
	@$(CXX) $(CFLAGS) $^ -o $@

$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
	@mkdir -p $(dir $@)
	@$(CXX) $(CFLAGS) -c $< -o $@

clean:
	rm -rf $(BUILD_DIR)

The above makefile can judge the source code which locates in src directory, and output the object file into the build directory. The following image shows the project file structure.

1
2
3
4
5
6
7
8
% tree
.
├── Makefile
└── src
    ├── funcs.cpp
    └── main.cpp

2 directories, 3 files

After compiling, we will get the following file structure.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
% tree
.
├── Makefile
├── build
│   ├── funcs.o
│   ├── main
│   └── main.o
└── src
    ├── funcs.cpp
    └── main.cpp

3 directories, 6 files

There are some noteworthy points.

  1. $^ and $< Using the same file structure and editing the makefile, we can understand the difference between these two automation variables.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 编译工具信息
CXX = g++
CFLAGS = -std=c++11 -Wall

# 目录信息
SRC_DIR = src
BUILD_DIR = build

# 源文件列表
SRCS = $(wildcard $(SRC_DIR)/*.cpp)

# 生成目标文件列表(目标文件.o和源文件.cpp一一对应)
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))

# 最终目标文件
TARGET = $(BUILD_DIR)/main

.PHONY: all clean

all: $(TARGET)

$(TARGET): $(OBJS)
	@echo "\n"
	@echo $^

$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
	@ echo $<

clean:
	rm -rf $(BUILD_DIR)

After we excute the make command, we will get the result.

1
2
3
4
5
src/funcs.cpp
src/main.cpp


build/funcs.o build/main.o

Based on the project file structure, we can understand the difference between them.

Pattern-specific Variable Values

The most obvious and easy example is distinguishing the difference between $< and $^ in Automation variables. i.e. the difference between $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp and $(OBJS): $(SRCS)

Although we know the $< is the name of the first prerequisite and $^ is the names of all the prerequisites, if we don’t know the difference of the above expression of target: prerequisites, we can’t understand the result of the g++ $(CFLAGS) -c $< -o $@.

In short, if we still use the above file structure, when using $(OBJS): $(SRCS), the $(OBJS) is the build/funcs.o build/main.o, they are one expression, but using $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp, the $(BUILD_DIR)/%.o is build/funcs.o and build/main.o, they are two expressions, it looks like a enumation, so we can use this to generate the corresponding .o object file and .c source file.

Reference

0%