Makefile项目管理

Makefile初学需要掌握一个规则,两个函数,三个自动变量。

首先,了解一下基本原则。

1.基本原则

(1)若想生成目标,检查规则中的依赖条件是否存在。如不存在,则寻找是否有规则用来生成该依赖文件。

(2)检查规则中的目标是否需要更新,必须先检查它的所有依赖。依赖中有任一个被更新,则目标必须更新。

可能到这有点懵,继续看下面。

2.一个规则

目标:依赖条件

        命令

注意命令前有一个tab缩进,不可省略。

举个例子,现文件夹mf下文件如下:

Makefile项目管理_第1张图片

test.c文件内容如下: 

#include

int main()
{
    int a=8;
    int b=4;

    printf("a+b=%d\n",add(a,b));
    printf("a-b=%d\n",minus(a,b));
}

add.c文件内容如下: 

//add.c
int add(int value1,int value2)
{
    return value1+value2;
}

minus.c文件内容如下:

//minus.c
int minus(int value1,int value2)
{
    return value1-value2;
}

现在创建一个文件,为了使用下面的命令,文件名必须为makefile或者Makefile。

写入如下内容:

test:add.o minus.o test.o
    gcc test.o add.o minus.o -o test

add.o:add.c
    gcc -c add.c -o add.o

minus.o:minus.c
    gcc -c minus.c -o minus.o

test.o:test.c
    gcc -c test.c -o test.o

内容为四个规则,其实一点都不复杂,解读如下: 

第一个规则

test:add.o minus.o test.o
    gcc test.o add.o minus.o -o test

 它的意思是test依赖于add.o、minus.o、test.o三个目标文件,使用命令gcc test.o add.o minus.o -o test生成。

但目前并不存在add.o、minus.o、test.o,所以要寻找另外的规则用来生成add.o、minus.o、test.o这三个依赖文件。

因此找到第二、三、四条规则,只拿其中第二条做例子:

add.o:add.c
    gcc -c add.c -o add.o

add.o依赖add.c使用gcc -c add.c -o add.o命令生成,add.o又成为第一条规则的其中一条依赖。

再根据第三、四条规则,生成minus.o,test.o,这三条规则其实只是执行了预处理、编译、汇编,暂时还没有链接。之后第一条规则链接,最终生成test可执行程序。

上面不懂程序编译四步骤看下面这个:

动态库和静态库-CSDN博客

因此,可以理解基本原则的第一句话。

输入make命令,运行test结果:

Makefile项目管理_第2张图片

现在解释第二句话。

假如说我们写的add函数过于简单,我们改动add.c。

add.c内容如下:

int add(int value1,int value2)
{
    return value1+value2+100;
}

 然后直接执行make:

Makefile项目管理_第3张图片

我们发现,它只执行了 add.c的预处理、编译、汇编生成add.o文件,然后又和其他两个目标文件链接然后生成可执行程序。

另外两个目标文件并没有经历预处理、编译、汇编这三个过程,大量节省了系统资源和时间。

这是怎么做到的呢?

test:add.o minus.o test.o
    gcc test.o add.o minus.o -o test

add.o:add.c
    gcc -c add.c -o add.o

因为makefile发现add.c的文件最后改动时间晚于add.o文件,add.o依赖于add.c文件生成,那怎么能晚于add.c文件的最后改动时间呢,于是makefile重新执行这条规则。

然后就又发现test的其中add.o依赖文件的生成晚于test文件的生成,test文件依赖于add.o文件生成,那怎么能晚于add.o文件的生成时间呢,于是makefile重新执行这条规则。

现在可以理解基本原则第二句话。

其中要注意的是makefile默认将第一条规则认为是终极任务。上面任意一条和第一条规则位置相互调换,可能达不到所要实现的目标。

可以在makefile中写入如下的内容:

ALL:test

这将指定生成test为终极任务,而不用再管下面的顺序了。

3.两个函数

1.wildcard

src=$(wildcard *.XXX)

找到当前目录下所有后缀为.XXX的文件,组成列表,赋值给src。

2.patsubst

obj=$(patsubst %.XXX1,%.XXX2,$(src))

将src变量里所有后缀为.XXX1的文件替换成后缀为.XXX2文件。

具体这俩函数有啥用,观察修改后的makefile:

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

ALL:test

test:$(obj)
    gcc $(obj) -o test

add.o:add.c
    gcc -c add.c -o add.o

minus.o:minus.c
    gcc -c minus.c -o minus.o

test.o:test.c
    gcc -c test.c -o test.o

可以看出src=$(wildcard *.c)这一句相当于src=test.c add.c minus .c

而obj=$(patsubst %.c,%.o,$(src))相当于obj=test.o add.o minus.o。

然后make,运行test:

Makefile项目管理_第4张图片

可以看出这俩函数就是个替换的作用,省的就是写各种.c、.o文件了。

另外可以在makefile文件加上:

clean:
        -rm -rf $(obj) test 

注意-rm前有一个tab。在命令行输入make clean就只会执行clean的规则,但输入make时忽略clean规则。可以看到我们删除.o文件和test文件。

Makefile项目管理_第5张图片

但千万注意别把clean写错,把源文件删了。因此,防止删除源文件,可以在其后面加上-n会显示clean具体干了啥,但不会执行clean。如:

Makefile项目管理_第6张图片

可以看出实际并未执行clean规则。

clean下面的命令前有一个“-”,这个表示删除不存在的文件时,不会报错。

例如:我们提前删除test.o,然后执行make clean。如下:

Makefile项目管理_第7张图片

 其实make clean是要删除test.o的,但当前目录已经没有了,得力于“-”,因此不会报错。

另外,千万要注意的是假如当前目录中有ALL或者clean的文件,则make或者make clean可能不能执行,它将ALL和clean当成一个文件,这时需要makefile加上:

.PHONY:clean ALL

4.三个自动变量

$@:在规则的命令中,表示规则中的目标。

$<:在规则的命令中,表示规则中第一个条件。如果将该变量应用在模式规则中,它可将依赖条件列表中的依赖依次取出,套用模式规则。

$^:在规则的命令中,表示规则中的所有依赖条件,组成一个列表,以空格隔开,如果这个列表中有重复的项则消除重复项。

利用这三自动变量,将makefile内容改为:

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

ALL:test

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

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

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

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

然后输入make命令:

Makefile项目管理_第8张图片

即便是这样,makefile仍有不足之处,因为如果程序扩展,比如说添加div.c源文件,加了一个除法的源文件,我们仍然需要手动的加一条规则:

div.o:div.c
    gcc -c div.c -o div.o

 针对该问题,模式规则应运而生。我们观察下面几个目标文件生成:

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

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

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

格式基本上是一致的。这时只需要这样:

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

用这一条规则可以替换上面三条规则,而且如果程序扩展,就在当前目录添加源文件,比如说上面的div.c,然后make即可。此时也能看出三个自动变量的作用了。

另外,如果想要指定另一条依赖的生成使用什么规则,可以使用静态模式规则:

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

这样的意思就是对obj中.o文件都是从上面这条模式规则来的,而不是从另外的模式规则生成的。

最后,如果想要加一些可选项,例如我想要在编译时加上-g和-Wall选项。可以这样:

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

myArgs=-Wall -g

ALL:test

test:$(obj)
    gcc $^ -o $@ $(myArgs)

add.o:add.c
    gcc -c $< -o $@ $(myArgs)

minus.o:minus.c
    gcc -c $< -o $@ $(myArgs)

test.o:test.c
    gcc -c $< -o $@ $(myArgs)

你可能感兴趣的:(linux)