您的位置:首页 > 其它

0721Makefile的制作

2015-07-22 18:41 387 查看
用途

工作原理

版本一

版本二

引入变量

版本三
引入函数

版本四

版本五

用途

项目代码编译管理

节省编译项目的时间

工作原理

分析个目标文件和依赖之间的关系

根据一览关系自底向上执行命令

根据修改时间比较目标新旧,确定更新

如果目标不依赖任何条件,则执行对应命令,以示更新

目录结构

.

├── a.c

├── b.c

├── c.c

├── d.c

├── calc.h

└── main.c

制作Makefile文件

版本一

`vi Makefile`创建一个Makefile文件,一般第一个M要大写,


Makefile语法规则:三要素:目标、依赖、命令

Makefile内容


app:main.c a.c b.c c.c d.c
gcc main.c a.c b.c c.c d.c -o app


在上列命令中
app
是目标,
main.c a.c b.c c.c d.c
是依赖或者条件,
gcc main.c a.c b.c c.c d.c -o app
是命令。

保存之后,直接运行make命令,执行过程如下:

$ make

gcc main.c a.c b.c c.c d.c -o app


执行结果,生成app的可执行文件,和a.out一样,
./app
可以成功运行。

make
这条命令是默认执行当前的Makefile文件

三要素解析:
app
是目标,如果要生成目标,就要依赖
main.c a.c b.c c.c d.c
,如何依赖目标生成文件,就要执行命令
gcc main.c a.c b.c c.c d.c -o app


格式:命令顶格写,要加冒号
:
,依赖跟在后面,命名前面要有一个tab制表符的间隔。

版本二

Makefile内容


app:main.o a.o b.o c.o d.o
gcc gcc main.o a.o b.o c.o d.o -o app

main.o:main.c
gcc -c main.c
a.o:a.c
gcc -c a.c
b.o:b.c
gcc -c b.c
c.o:c.c
gcc -c c.c
d.o:d.c
gcc -c d.c




当Makefile开始执行的时候,会形成一个类似上图的树状依赖关系图,然后从下往上一条一条执行,生成树根的目标app

保存之后,直接运行make命令,执行过程如下:

$ make

gcc -c main.c

gcc -c a.c

gcc -c b.c

gcc -c c.c

gcc -c d.c

gcc main.o a.o b.o c.o d.o -o app


执行结果正常运行

如果这时候,打开a.c,修改一下,加入一个行

再次执行make命令,执行过程如下:

$ make

gcc -c a.c

gcc main.o a.o b.o c.o d.o -o app


结果是只把a.c重新编译,然后在和其他的.o文件重新链接,生成一个新的app

如果是版本一的话,所有的.c文件都要重新编译,然后链接生成文件,浪费时间,这就是版本二的优势,提高编译效率

那make是如何得知a.c被更改了呢?是根据a.c和app的时间决定的,如果a.c的修改时间新于目标app的时间,那就会重新编译、链接。

如果把Makefile的内容改为

app:main.o a.o b.o c.o d.o
gcc gcc main.o a.o b.o c.o d.o -o app


再次执行make命令,执行过程如下:

$ make

cc    -c -o main.o main.c

cc    -c -o a.o a.c

cc    -c -o b.o b.c

cc    -c -o c.o c.c

cc    -c -o d.o d.c

gcc main.o a.o b.o c.o d.o -o app


依旧执行成功,但是通过
cc
编译的,通过
whih cc
ls
查看符号链接,发现
cc
还是链接的
gcc


为什么并没有设置.o的目标,但是还是过
cc
编译了,这是因为makefile的内置规则,通过
make -p
命令查看,最好重定向到文件里,方便观察。

如果把Makefile的内容改为

app:main.o a.o b.o c.o d.o
gcc main.o a.o b.o c.o d.o -o app

%.o:%.c
gcc -c $< -o $@


执行过程如下:

$ make

gcc -c main.c

gcc -c a.c

gcc -c b.c

gcc -c c.c

gcc -c d.c

gcc main.o a.o b.o c.o d.o -o app


%.o:%.c:语义是把.c文件转换成.o,如何转,执行命令。
%   :相当通配符
$<  :表示所有依赖当中的第一个依赖 `%.c`.
$@  :表示目标`%.o`
$^  :表示依赖库中的所有依赖, `main.o a.o b.o c.o d.o`


利用上述规则把

app:main.o a.o b.o c.o d.o

gcc main.o a.o b.o c.o d.o -o app


这条语句修改成

app:main.o a.o b.o c.o d.o

gcc $^ -o $@


引入变量

常见变量

CFLAGC=
LDFLAGC=
CPPFLAGS=


CFLAGC=
:一般指编译时传递参数 -c -g,只编译不连接

LDFLAGC=
:用来指定库是在什么位置的

CPPFLAGS=
:一般指编译时传递参数 -I./

例子:定义一个
test
变量;把变量赋值
"hello"
,调用变量要用
$()
的方式。

test = "hello"

abc:

echo $(test)


执行过程:

$ make abc

echo "hello"

hello


解析:
make
,默认执行第一个目标,
make abc
,make后面加上目标, 只执行该目标的命令;如果目标不依赖任何条件,则执行对应命令,以示更新,所以执行echo命令回显”hello”。

通过引入变量,在改变一下Makefile的内容

CFLAGC= -c -g -Wall
LDFLAGC=
CPPFLAGS= -I./

obj = main.o a.o b.o c.o d.o
app:$(obj)
gcc  $^ -o $@

%.o:%.c
gcc $(CFLAGC) $(CPPFLAGS) $< -o $@


执行过程:

$ make

gcc -c -g -Wall -I./ main.c -o main.o

gcc -c -g -Wall -I./ a.c -o a.o

gcc -c -g -Wall -I./ b.c -o b.o

gcc -c -g -Wall -I./ c.c -o c.o

gcc -c -g -Wall -I./ d.c -o d.o

gcc main.o a.o b.o c.o d.o -o app


版本三

引入函数

引用函数也需要$


wildcard :指本目录中的所有文件,`wildcard *.c`就是本目录所有的.c文件
patsubst :查找替换函数 例:`patsubst %.c,%.o, $(wildcard *c)`,把所有在(wildcard *c)出现的.c替换成.o,这是`%.c,%.o`替换.
.PHONY :生成伪目标,会在版本四中用到


通过引入函数,在改变一下Makefile的内容

CFLAGC= -c -g -Wall
LDFLAGC=
CPPFLAGS= -I./

src = $(wildcard *c)
obj = $(patsubst %.c,%.o, $(src))

app:$(obj)
gcc $^ -o $@

%.o:%.c
gcc $(CFLAGC) $(CPPFLAGS) $< -o $@


执行过程:

$ make

gcc -c -g -Wall -I./ a.c -o a.o

gcc -c -g -Wall -I./ b.c -o b.o

gcc -c -g -Wall -I./ c.c -o c.o

gcc -c -g -Wall -I./ d.c -o d.o

gcc -c -g -Wall -I./ main.c -o main.o

gcc a.o b.o c.o d.o main.o -o app


执行结果没有问题

版本四

基本的Makefile的功能已经差不多了,但是每次运行make的时候都会生成.o文件和app可执行文件,在下次重新修改代码之后,还有手动删除.o文件和app可执行文件,之后再进行make操作,很繁琐,所以要进行下一步优化。

初步完善后

CFLAGC= -c -g -Wall
LDFLAGC=
CPPFLAGS= -I./

src = $(wildcard *c)
obj = $(patsubst %.c,%.o, $(src))

target = app

$(target):$(obj)
gcc $^ -o $@

%.o:%.c
gcc $(CFLAGC) $(CPPFLAGS) $< -o $@

clean:
rm -f $(obj)
rm -f $(target)


当需要删除.o文件和app可执行文件的时候,可以执行
make clean
命令,
$(obj)
代表所有的.o文件,
$(target)
代表app可执行文件。加
rm -f
的目的是强制删除,这是为了避免在没有要删除文件的时候执行
make clean
也就是
rm
的时候会出现报错的情况。

但是有一种情况,当目录下有个文件名叫做clean,这个时候执行
make clean
就不会执行删除命令了,会有下面这样的提示,

$ make clean

make: “clean”是最新的。


为了避免这中情况,需要加入一个clean的目标
.PHONY:clean
,这样make在执行
.PHONY
这个目标的时候就会找依赖
clean
,然后就会运行
clean
里面的命令。

版本五

为了让这个Makefile在其他平台能够正常运行,尤其是在ARM处理器等其他处理器,要重新编译,就需要用到其他编译器,所以把gcc提取出来做成一个变量,这样下次换平台的时候直接改变量就行了,不用再把所有的gcc挨着改了
CC : gcc
,还有在命令前面加
-
号,作用就是如果本条命令执行失败,不会停止执行下一条命令。

现阶段的使用的Makefile

CFLAGC= -c -g -Wall
LDFLAGC=
CPPFLAGS= -I./
CC = gcc

src = $(wildcard *c)
obj = $(patsubst %.c,%.o, $(src))

target = app

$(target):$(obj)
$(CC) $^ -o $@

%.o:%.c
$(CC) $(CFLAGC) $(CPPFLAGS) $< -o $@

.PHONY:clean
clean:
-rm -f $(obj)
-rm -f $(target)


注意:用这个Makefile的时候,文件夹里不要有其他项目的.c文件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: