Makefile是一个c语言的编译工具。如果学过Java,可能会认识Maven工具,makefile也是类似的工作。
Makefile能帮助c语言建立自动化的编译。一旦写好,执行一个make命令就可以编译整个工程。当然编写Makefile文件的时候有很多知识点在里面。这篇文章主要讲解如何编写基础性以及常用的Makefile文件。
没有Makefile工具时如何进行编译
首先我们看一个例子:
当前我们有3个文件,分别是test.h test.c main.c
在main.c中,头文件定义如下1
在test.h中,头文件定义如下1
2
void test_print();
在test.c中1
2
3
4
5
void test_print()
{
printf("this is test.c \n");
}
当我们编译运行程序时,我们使用的命令是1
gcc test.h main.c -o main
运行结果就是
this is test.c
对于小程序我们可以使用这样的命令,但是当我们这样运行一个大项目时,我们需要多少文件名来定义,此时Makefile就是一个很好的工具
makefile简单示例
就如上面的三个文件
我们vim 一个名为Makefile的文件1
2
3
4main.o:test.o main.c
gcc test.o main.c
test.o:test.c
gcc -c test.c
编辑好Makefile文件后,我们返回,执行make后,将会输出1
2gcc -c test.c
gcc test.o main.c
这样是不是比原始的编译方便很多呢。其实也不是罪方便的,既然发明了make工具,那么肯定会让make工具更加方便开发人员使用的。
Makefile具体使用
伪目标
伪目标:不管是不是最新的,都需要重新生成;使用.PHONY来声明一个目标是伪目标;执行伪目标的效果等于执行了某一个动作,并不产生目标文件。例如添加一个伪目标:1
2
3
4
5
6main.o:test.o main.c
gcc test.o main.c
test.o:test.c
gcc -c test.c
clean : 【这是一个伪目标】
rm -f $(OBJECTS) main
使用make来执行伪目标1
2$ make clean
rm -f test.o main.c main.o
Makefile自动变量
选项名 | 作用 |
---|---|
$@ | 编写规则中啊哟生成的目标对象 |
$^ | 编写规则中所有依赖文件列表 |
$< | 编写规定中第一个依赖对象 |
因此,上面的Makefile文件我们可以改下如下:1
2
3
4main.o:test.o main.c
gcc -g $^ -o $@
test.o:test.c
gcc -g -c $< -o $@
执行make,可以看到效果还是一样的:1
2gcc -c test.c
gcc test.o main.c
编译生成多个可执行文件
1 | bin=main main2 //自定义变量bin |
为了生成目标文件all,需要生成bin,也就是main main2.这样就生成了两个可执行文件,利用自定义变量可以简化这段Makefile,但是这样写看起来内容其实还是很多的,因此下面我将介绍make的内嵌函数
make常用内嵌函数
首先看到make中函数的调用形式1
$(function arguments) //functions是函数名称,arguments是参数,使用$来调用
函数名与参数之间是空格
以下三个重要的内嵌函数
$(wildcard path)
当前目录下匹配模式的文件1
src=$(wildcard *.c) // 在当前目录下搜索所以.c文件,文件名称保存到src中
$(patsubst pattern,replacement,text)模式替换函数,就是把text中文件列表从模式pattern替换为replacement模式
1
2$(patsubst %.c,%.o,$src) // 把src中的.c文件列表中的文件从.c替换为.o
等价于:$(src:.c=.o) //这种方式更为常用shell函数
shell函数可以执行shell下的命令,同样是使用$来引用的,例如1
$(shell ls -d */) //将当前目录下的所有文件列出来
下面我们通过一个例子来使用上面三个函数。假设当前目录下有main.c文件,同时还有若干个目录,每个目录中都有各自的.c文件,利用所有的.c文件编译生成最后的main文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14CC = gcc
CFLAGS = -g
BIN = main
SUBDIR = $(shell ls -d */) // SUBDIR变量保存了子目录的列表
ROOTSRC = $(wildcard *.c) //ROOTSRC保存了当前目录下的.c文件列表
ROOTOBJ = $(ROOTSRC:%.c = %.o) //ROOTBOJ 保存了当前目录下.c文件同名的.o列表
SUBSRC = $(shell find $(SUBDIR) -name '*.c') //SUBSRC 保存了所有子目录下的的.c文件
SUBOBJ = $(SUBSRC:%.c = %.o) //SUBOBJ保存了所有子目录下的.c文件同名的.o文件列表
$(BIN):$(ROOTOBJ) $(SUBOBJ) //main的生成依赖与当前目录及所有子目录下的.o文件
$(CC) $(CFLAGS) -o $(BIN) $(ROOTOBJ) $(SUBOBJ)
.o .c:
$(CC) $(CFLAGS) -c %< -o $@
clean:
rm -f $(BIN) $(ROOTOBJ) $(SUBOBJ)