MakeFile

本文记录了MakeFile文件的基本编写规则和语法,参考这篇文章整理

简介和示例

Makefile 文件描述了 Linux 系统下 C/C++ 工程的编译规则,它用来自动化编译 C/C++ 项目。一旦写编写好 Makefile 文件,只需要一个 make 命令,整个工程就开始自动编译,不再需要手动执行 GCC 命令。

一个中大型 C/C++ 工程的源文件有成百上千个,它们按照功能、模块、类型分别放在不同的目录中,Makefile 文件定义了一系列规则,指明了源文件的编译顺序、依赖关系、是否需要重新编译等。

说明一下,文档目录结构是用户目录HOME下有src incl bin lib。

src:源码

incl:头文件

bin:执行码

lib:静态/动态库

这是最常见的Linux编程目录结构,以下代码编译都是依据这个结构。

Makefile代码示例:

1
2
3
4
5
hello:hello.c
gcc -I${HOME}/incl -c hello.c
gcc -o hello hello.o
rm -f hello.o
mv hello ${HOME}/bin

把第一行去掉,这段代码其实就是把编译、链接、删除、移动写成shell脚本执行

注:

linux中变量调用的时候会在变量名前加一个$符号。一般作为规范,环境变量是全部大写的。

在shell中你可以通过echo $PATH来查看变量$PATH的值。

Makefile结构说明

Makefile里主要包含了五个东西:变量定义、显式规则、隐晦规则、文件指示和注释。

1、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

2、显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 刚才写的疑似shell脚本的Makefile全部都是显示规则。

3、隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。

4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样。

5、注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。

复杂一些的Makefile

根据上面的结构说明,我们对Makefile一层一层的改写,首先是隐晦规则:

1
2
3
4
5
6
.SUFFIXES: .cpp .c
.cpp.o:
g++ ${INCL} -c $<

.c.o:
gcc ${INCL} -c $<

这个隐晦规则其实就是告诉大家,后缀为cpp的文件怎么编译成.o,后缀为c的文件怎么编译成.o。

img

相关代码:

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
#隐含规则
INCL=-I${HOME}/incl

.SUFFIXES: .cpp .c
.cpp.o:
g++ ${INCL} -c $<

.c.o:
gcc ${INCL} -c $<

#C++编译
hellocpp:hellocpp.o
echo "开始编译"
g++ -o hellocpp hellocpp.o
rm -f hellocpp.o
mv hellocpp ${HOME}/bin
echo "编译结束"

#C编译
hello:hello.o
echo "开始编译"
gcc -o hello hello.o
rm -f hello.o
mv hello ${HOME}/bin
echo "编译结束"

常用的预定义变量。

  • $*   不包含扩展名的目标文件名称。
  • $+   所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
  • $<   第一个依赖文件的名称。
  • $?   所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
  • $@ 目标的完整名称。
  • $^  所有的依赖文件,以空格分开,不包含重复的依赖文件。
  • $% 如果目标是归档成员,则该变量表示目标的归档成员名称。

Makefile的最终展现:

img

实际编译结果

img

看着挺乱的吧,稍微改一改,双手奉上Makefile代码。

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
#最后形成的Makefile
INCL=-I${HOME}/incl
BIN=$(HOME)/bin
OBJ1=hellocpp.o
OBJ2=hello.o

.SUFFIXES: .cpp .c
.cpp.o:
g++ ${INCL} -c $<

.c.o:
gcc ${INCL} -c $<

all: hellocpp hello

#C++编译
hellocpp:${OBJ1}
@echo "============开始编译============"
g++ -o $@ $?
@rm -f ${OBJ1}
@mv $@ ${BIN}
@echo "============编译结束============"
@echo ""

#C编译
hello:${OBJ2}
@echo "============开始编译============"
gcc -o $@ $?
@rm -f ${OBJ2}
@mv $@ ${BIN}
@echo "============编译结束============"
@echo ""

命令前加@,表示当前命令不显示,最后编译结果是这样。

img


分享几个Makefile的示例DEMO,

编译SOCKET服务,使用mysql数据库。

img

Makefile代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#socket服务端编译(用到mysql数据库)
INCL=-I/usr/local/mysql/include -I$(HOME)/incl
LIB=-L/usr/local/mysql/lib -lmysqlclient -lmysqld -lmysqlservices -L$(HOME)/lib -lbanktest
BINDIR=$(HOME)/bin

.SUFFIXES: .cpp .c

.cpp.o:
g++ ${INCL} -c $<

.c.o:
gcc $(INCL) -c $<

all: clean server

server:server.o
@echo "============开始编译============"
gcc -o $@ $? $(LIB)
@mv $@ $(BINDIR)
@echo "============编译结束============"

clean:
@rm -f *.o

编译des md5 base64密码服务的Makefile

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#编译des md5 base64密码服务
INCL=-I/usr/local/mysql/include -I$(HOME)/incl
LIB=-L/usr/local/mysql/lib -lmysqlclient -lmysqld -lmysqlservices
BINDIR=$(HOME)/bin
LIBDIR=$(HOME)/lib

.SUFFIXES: .cpp .c

.cpp.o:
g++ ${INCL} -c $<
.c.o:
gcc $(INCL) -c $<

all: clean des md5 base64

des:des.o main_des.o
gcc -o $@ $? $(LIB)
mv $@ $(BINDIR)

md5:md5.o main_md5.o
gcc -o $@ $? $(LIB)
mv $@ $(BINDIR)

base64test:base64.o main_base64.o
gcc -o $@ $? $(LIB)
mv $@ $(BINDIR)

rsa:rsa.o main_rsa.o
gcc -o $@ $? $(LIB)
mv $@ $(BINDIR)

libjiami.a:des.o md5.o base64.o
ar -r $@ $?
mv $@ $(LIBDIR)

libdestest:main_des.o
gcc -o $@ $? $(LIB) -L$(HOME)/lib -ljiami
mv $@ $(BINDIR)

libtest.so:des.c md5.c base64.c
gcc -o $@ -fPIC -shared $?
mv $@ $(LIBDIR)

libmd5test:main_md5.o
gcc -o $@ $? $(LIB) -L$(HOME)/lib -ltest
mv $@ $(BINDIR)

libbanktest.a:banktest.o banksql.o
ar -r $@ $?
mv $@ $(LIBDIR)

banktest:banktest.o banksql.o
gcc -o $@ $? $(LIB) -L$(HOME)/lib -ltest
mv $@ $(BINDIR)

clean:
rm -f *.o

最后,加几点写Makefile的注意事项

  1. tab分隔,不能用空格。

  2. 每个makefile最好加一个all

  3. 注释用“#”符号

  4. 文件指示,引用其他的makefile文件

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022 ZHU
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信