有这样一个小工程,通过键盘输入两个整数,然后计算它们的和并打印输出。这个工程中main.c、input.c和calcu.c这三个C文件,以及input.h和calcu.h这两个头文件。其中main.c是主体,input.c负责接收键盘输入,calcu.c进行两个数相加运算。为了方便,将代码贴出如下:
"main.c"
#include
#include "input.h"
#include "calca.h"
int main(int argc, char *argv[]) {
int a, b, num;
input_int(&a, &b);
num = calcu(a, b);
printf("%d + %d = %d\r\n", a, b, num);
}
-------------------------------------------------------------------------------
"input.c"
#include
#include "input.h"
void input_int(int *a, int *b) {
printf("input two num:");
scanf("%d %d", a, b);
printf("\r\n");
}
-------------------------------------------------------------------------------
"calcu.c"
#include
#include "calcu.h"
int calcu(int a, int b) {
return (a + b);
}
-------------------------------------------------------------------------------
"input.h"
#ifndef _INPUT_H
#define _INPUT_H
void input_int(int *a, int *b);
#endif
-------------------------------------------------------------------------------
"calcu.h"
#ifndef _CALCU_H
#define _CALCU_H
int calcu(int a, int b);
#endif
现在我们对其进行编译并执行:
Makefile里面是由一系列规则组成的,这些规则的格式如下:
目标1 ... : 依赖 ...
命令1
命令2
...
目标2 ... : 依赖 ...
命令1
命令2
..
......
指令1:
命令1
命令2
...
指令2:
命令1
命令2
...
......
根据上述规则,本工程Makefile编写如下:
main:main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
main.o:main.c
gcc -c main.c
input.o:input.c
gcc -c input.c
calcu.o:calcu.c
gcc -c calcu.c
clean:
rm *.o
rm main
Makefile编写好后就可以使用make命令来编译工程了。
使用变量来对2.1中Makefile进行改写:
object = main.o input.o calcu.o
main:$(object)
gcc -o main $(object)
main.o:main.c
gcc -c main.c
input.o:input.c
gcc -c input.c
calcu.o:calcu.c
gcc -c calcu.c
clean:
rm *.o
rm main
在使用“=”时,我们要知道"=" 赋值的变量的真实值取决于他所引用的变量的最后一次有效值。
":=" 赋值的变量只会使用定义时的值。
上述代码的意思是,如果变量curname前面没有被赋值,那么此变量就是“leafye”,如果在前面已经赋值过了,就是用前面的值。
当需要对已经定义好的变量添加一些字符串时,就需要用到“+=”。
在2.1中我们对本工程的Makefile文件进行了编写。该Makefile的第4~9行是将对应的.c源文件编译为.o文件,如果工程中C文件很多的话,一条条规则编写显然是不可取的。为此,我们可以使用Makefile里的模式规则来将所有的.c文件编译为对应的.o文件。
模式规则中,使用“%”来进行文件名的匹配。例如“%.c”就表示所有的以.c结尾的文件。当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中“%”的值,使用方法如下:
%.o:%.c
命令
因此,2.1中的Makefile可以改为如下形式:
object = main.o input.o calcu.o
main:$(object)
gcc -o main $(object)
%.o:%.c
#命令
clean:
rm *.o
rm main
上面讲的规则模式中,目标和依赖都是一系列文件,每一次对模式规则进行解析的时候都会是不同的目标和依赖文件,而命令只有一行,如何通过一行命令来从不同的依赖文件中生成对应的目标?自动化变量就是来完成这个功能的。自动化变量会把模式中所定义的一系列文件自动地挨个取出,直至所有符合模式的文件都被取完。自动化变量只会吃现在规则的命令中。常用的自动化变量如下表所示。
我们用自动化变量来完成2.3中的Makefile,最终代码如下:
object = main.o input.o calcu.o
main:$(object)
gcc -o main $(object)
%.o:%.c
gcc -c $<
clean:
rm *.o
rm main
有时候我们需要编写一个规则来执行一些命令,但这个规则不是用来创建文件的,比如在前面的Makefile文件中有如下代码用来清理工程的功能。
clean:
rm *.o
rm main
当我们输入“make clean”以后,后面的“rm *.o” 和“rm main”总是会执行。但是,当目录下有个名为“clean”的文件时,执行“make clean”以后,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的命令不会被执行,设想的清理工程的功能就无法完成。为了避免这个问题,可以将clean声明为伪目标,声明方式如下:
.PHONY:clean
使用伪目标修改的Makefile文件如下:
object = main.o input.o calcu.o
main:$(object)
gcc -o main $(object)
%.o:%.c
gcc -c $<
.PHONY:clean
clean:
rm *.o
rm main
Makefile条件判断的语法有如下两种:
<条件关键字>
<条件为真时执行的语句>
endif
以及
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
其中条件关键字有4个:ifeq、ifneq、ifdef和ifndef,这4个关键字分为两对,ifeq与ifneq、ifdef和ifndef。 先来看ifeq和ifneq,ifeq用来判断是否相等,ifneq判断是否不相等,ifeq用法如下:
ifeq(<参数1>,<参数2>)
ifeq'<参数1>','<参数2>'
ifeq"<参数1>","<参数2>"
上述用法都是用来比较“参数1”和“参数2”是否相同,如果相同就为真。ifneq则相反。
ifdef和ifndef的用法如下:
ifdef<变量名>
如果“变量名”的值非空,那么表达式为真,否则表达式为假。ifndef则相反。
Makefile支持函数,我们可以直接使用。接下来介绍几个常用的函数,其他的函数大家可以参考《跟我一起写Makefile》这份文档。
函数patsubst用来完成模式字符串替换,使用方法如下:
$(patsubst,,)
此函数查找字符串
$(patsubst %.c,%.o,a.c b.c c.c)
将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”。
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数wildcard,使用方法如下:
$(wildcard PATTERN···)
比如:
$(wildcard *.c)
上面的代码是用来获取当前目录下所有的.c文件,类似“%”。