MakeFile入门笔记

本文最后更新于:2022年10月18日 上午

MakeFile入门笔记

平时开发和配置环境时有时会需要编译c,就经常遇见这个东西,刚好上课也学这个东西,就稍微学一些MakeFile。因为也是入门,所以可能比较乱。

快速的理解MakeFile+读懂一个MakeFile - 知乎 (zhihu.com)

Makefile入门(超详细一文读懂)_晨曦艾米的博客-CSDN博客_makefile

make命令和makefile文件 - 知乎 (zhihu.com)

makefile介绍 — 跟我一起写Makefile 1.0 文档 (seisman.github.io)

MakeFile是什么

MakeFile是用来编译C/C++的工具,他能通过自身的语法连接C/C++开发过程中写的各类.cpp.h.o文件,最终生成可执行文件。直观来说,是“编译、链接、删除、移动”的整个过程。

MakeFile语法

MakeFile的基本模式如下:

1
2
3
4
target... : prerequisites ...
command1
command2
......

在编译程序时,需要定义目标target以及其依赖文件prerequisite,然后执行的各种命令,表示我们将通过命令利用依赖文件来达成目标。默认MakeFile的第一个target是最终的target,并且命令的缩进是一个Tab而不是4个空格。特殊地,可以指定目标为ALL来指定需要生成的多个目标文件。

MakeFile中的变量和Shell相似,使用$表示取变量,当变量名多余1个字符时要加上(),除此以外也有一些特殊的变量:

1
2
3
4
5
$a
$(abc)
$^ # 所有的依赖文件
$@ # 生成的目标文件
$< # 第一个依赖文件

MakeFile的变量赋值会有一些奇怪,使用等号=和冒号等号:=是不一样的,此外?=表示如果变量未赋值那么赋予一个值,有一种a = 1 if a is None else a这种感觉。

1
2
3
4
5
6
7
8
# 结果VIR_B是"AA B" 因为普通等号是赋予最后的值
VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA
# :=的结果是比较直观的赋值,VIR_B是"A B"
VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA

MakeFile有一些预定义变量,比如CC是指定c编译器的名称,默认是cc

1
CC = gcc

MakeFile函数比较特殊:

1
2
3
4
# wildcard 使用通配符匹配所有.c文件,赋值给SRC变量
SRC = $(wildcard ./*.c)
# patsubst 改$(SRC)变量中的后缀,.c都改成.o
OBJ = $(patsubst %.c, %.o, $(SRC))

还有一个比较费解的伪目标.PHONY,这个会定义一些target,这些target不会与目录下的文件产生关系,所以每次都需要进行构建。比如下面这个例子,将所有.c文件编译成.o文件,然后又把.o编译成.out文件,假如make clean,那就会删除这个.out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))

ALL: hello.out

hello.out: $(OBJ)
gcc $< -o $@

$(OBJ): $(SRC)
gcc -c $< -o $@

clean:
rm -rf $(OBJ) hello.out

.PHONY: clean ALL

一般还经常会指定头文件路径,也就是编译时在gcc中通过-I参数指定某个include目录,在MakeFile中,经常这么写:

1
2
3
CFLAGS=-I /home/develop/include
yourapp:*.c
gcc $(CFLAGS) -o yourapp

那库文件(lib)也是类似的写法,不过是-L参数。

实际使用的时候经常是make cleanmakemake install,那加上参数其实就是特别编译MakeFile中指定的target,如下,install目标就是把编译出来的可执行文件复制到固定的目录。

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
32
33
34
35
36
37
38
39
all: myapp

# Which complier
CC = gcc

# Where to install
INSTDIR = /usr/local/bin

# Where are include files kept
INCLUDE = .

# Options for development
CFLAGS = -g -Wall -ansi

# Options for release
# CFLAGS = -O -Wall -ansi

myapp: main.o 2.o 3.o
$(CC) -o myapp main.o 2.o 3.o
main.o: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c mian.c
2.o: 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o: 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c

clean:
-rm main.o 2.o 3.o

install: myapp
@if [-d $(INSTDIR)];\
then \
cp myapp $(INSTDIR);\
chmod a+x $(INSTDIR)/myapp;\
chmod og-w $(INSTDIR)/myapp;\
echo "Installed in $(INSTDIR)";\
else \
echo "Sorry, $(INSTDIR) dose not exist";\
fi