Makefile基本原则和原理

makefile中的三要素:目标、依赖、命令
这里写图片描述

目标:要生成的目标文件(在Makefile中会把第一个目标作为终极目标,一切以生成终极目标为最终目的)
依赖:生成目标文件需要的一些文件
命令:借助依赖文件生成目标文件的手段

这里写图片描述

工作原理:要生成终极目标,先检查依赖条件是否都具备,如果有依赖条件不存才则再寻找新的规则来生成依赖条件

Makefile的编写

Makefile文件命名为makefile或Makefile
第一种形式

app:main.c insert_sort.c select_sort.c
    gcc *.c -o app -std=c99

直接使用命令生成目标文件,但是只要更新了其中的一个文件,所有文件都需要重新编译,大大浪费了时间

第二种形式

app:main.o insert_sort.o select_sort.o
    gcc main.o insert_sort.o select_sort.o -o app

main.o:main.c
    gcc main.c -c -std=c99

insert_sort.o:insert_sort.c
    gcc insert_sort.c -c -std=c99

select_sort.o:select_sort.c
    gcc select_sort.c -c -std=c99

先生成.o文件再生成目标文件,采用链式的方法可以大大节省时间,当其中一个文件发生改变时,只用重新编译更新文件

第三种形式
可以看出第二种形式虽然解决了第一种方式中存在的问题,但代码显得非常冗余,而且可维护性差,我们可以借助makefile中的变量来进一步进行修改。

makefile中的变量分为普通变量和自动变量。
普通变量:普通变量可以用户自己定义也可以使用Linux维护的一些变量。
变量定义及赋值:obj = a.o b.o c.o
变量取值:$(obj)
由Linux系统维护的一些变量通常为大写,常见的如下所示:
CC:默认值 cc
CPPFLAGS : 预处理器需要的选项 如:-I
CFLAGS:编译的时候使用的参数 –Wall –g -c
LDFLAGS :链接库使用的选项 –L -l

自动变量:
$@:规则中的目标
$<:规则中的第一个依赖条件
$^:规则中的所有依赖条件
注:上述三种自动变量只能在命令中使用

模式规则:
makefile中提供了模式规则匹配
%.o:%.c
其中%表示一个或多个

代码:
target = app
obj = main.o insert_sort.o select_sort.o
CC = gcc
CFLAGS = -std=c99

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

%.o:%.c
    $(CC) $< -c $(CFLAGS)

现在的版本看起来已经完全不一样了,几乎没有一点之前的影子,而且也显得非常简洁。但是我们仔细观察可以发现,虽然引用了变量,但是再给变量赋值时仍然需要列出所有的依赖文件,如果文件有成百上千个,就会带来很大的麻烦!!

第四种形式

在makefile中提供了函数功能来解决上述的问题,makefile中所有的函数必须都有返回值,下面介绍几种常用的函数

wildcard:查找指定目录下指定类型的文件,一个参数
用法:src = $(wildcard ./src/*.c)
注:在makefile的函数中,函数的调用方式为  函数名 参数1,参数2,参数3....

patsubst:匹配替换,从src中找到所有某种格式的文件换成另一种格式
obj = $(patsubst %.c,%.o,$(src))

代码:
target = app
CC = gcc
CFLAGS = -std=c99
src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))

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

%.o:%.c
    $(CC) $< -c $(CFLAGS)

这样整个代码就变得非常整洁、易维护了。但是如果我们想重新编译,删除.o文件,就必须手动删除,有没有办法可以使用makefile一键清空呢?

第五种形式

target = app
CC = gcc
CFLAGS = -std=c99
src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))

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

%.o:%.c
    $(CC) $< -c $(CFLAGS)

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

这里我们添加一个clean目标,我们发现这个目标没有依赖条件,我们称这样的目标为伪目标,并使用.PHONY来声明。

声明它为伪目标的原因是,如果在当前目录下碰巧有一个文件名也叫clean,就会发生冲突,执行make clean时就会提示你clean目标已经处于最新状态。

后面的-f参数表示强制执行该条命令,来清除报错。

这样一个非常实用的makefile版本就成型了!!
Logo

更多推荐