Linux C语言 Makefile 的使用 函数

创建三个.c文件
终端输入:
创建目录:mkdir Makefile
进入目录:cd Makefile
使用gedit:gedit
在这里插入图片描述
第一个文件:main.c

#include <stdio.h> 
#include "input.h" 
#include "calcu.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 <stdio.h> 
#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 "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

上面的文件创建完毕后,尝试编译下。
终端输入:gcc main.c calcu.c input.c -o main

在这里插入图片描述
得到可执行文件main。
运行试试效果。
在这里插入图片描述

可以看出我们的代码按照我们所设想的工作了, 使用命令 “gcc main.c calcu.c input.c -o main”
看起来很简单是吧,只需要一行就可以完成编译,但是我们这个工程只有三个文件啊!如果几
千个文件呢?再就是如果有一个文件被修改了以,使用上面的命令编译的时候所有的文件都会
重新编译,如果工程有几万个文件(Linux 源码就有这么多文件!),想想这几万个文件编译一次
所需要的时间就可怕。最好的办法肯定是哪个文件被修改了,只编译这个被修改的文件即可,
其它没有修改的文件就不需要再次重新编译了,为此我们改变我们的编译方法。

创建Makefile文件:
终端输入:gedit

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 

在这里插入图片描述

如果提示:在这里插入图片描述
错误来源:
1、Makefile 中命令缩进没有使用 TAB 键!

Makefile 语法:

Makefile 里面是由一系列的规则组成的,这些规则格式如下:
目标…… : 依赖文件集合……

命令 1

命令 2

……
比如下面这条规则:
main : main.o input.o calcu.o
gcc -o main main.o input.o calcu.o

这条规则的目标是 main,main.o、input.o 和 calcu.o 是生成 main 的依赖文件,如果要更新
目标 main,就必须先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也
必须更新, “更新”就是执行一遍规则中的命令列表。
命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
命令列表中的每条命令必须以 TAB 键开始,不能使用空格
命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
make 命令会为 Makefile 中的每个以 TAB 开始的命令创建一个 Shell 进程去执行。

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 

上述代码中一共有 5 条规则,1~2 行为第一条规则,3~4 行为第二条规则,5~6 行为第三条
规则,7~8 行为第四条规则,10~12 为第五条规则,make 命令在执行这个 Makefile 的时候其执
行步骤如下:

首先更新第一条规则中的 main,第一条规则的目标成为默认目标,只要默认目标更新了那
么就认为 Makefile 的工作,完成了整个 Makefile 就是为了完成这个工作。在第一次编译的时候
由于 main 还不存在, 因此第一条规则会执行, 第一条规则依赖于文件 main.o、 input.o 和 calcu.o
这个三个.o 文件,这三个.o 文件目前还都没有,因此必须先更新这三个文件。make 会查找以这
三个.o 文件为目标的规则并执行。以 main.o 为例,发现更新 main.o 的是第二条规则,因此会执
行第二条规则,第二条规则里面的命令为“gcc –c main.c” ,这行命令很熟悉了吧,就是不链接
编译 main.c, 生成 main.o, 其它两个.o 文件同理。 最后一个规则目标是 clean, 它没有依赖文件,
因此会默认为依赖文件都是最新的,所以其对应的命令不会执行,当我们想要执行 clean 的话
可以直接使用命令“make clean” ,执行以后就会删除当前目录下所有的.o 文件以及 main,因此
clean 的功能就是完成工程的清理,
当执行“make clean”命令以后,前面编译出来的.o 和 main 可执行
文件都被删除掉了,也就是完成了工程清理工作。

我们在来总结一下 Make 的执行过程:

1、make 命令会在当前目录下查找以 Makefile(makefile 其实也可以)命名的文件。

2、 当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。

3、当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比
目标文件晚)的话就会执行后面的命令来更新目标。

这就是 make 的执行过程,make 工具就是在 Makefile 中一层一层的查找依赖关系,并执行
相应的命令。 编译出最终的可执行文件。 Makefile的好处就是 “自动化编译” , 一旦写好了Makefile
文件,以后只需要一个 make 命令即可完成整个工程的编译,极大的提高了开发效率。把 make
和 Makefile 和做菜类似,目标都是呈现出一场盛宴,
在这里插入图片描述

Makefile 变量

跟 C 语言一样 Makefile 也支持变量的,先看一下前面的例子:
main: main.o input.o calcu.o
gcc -o main main.o input.o calcu.o

上述 Makefile 语句中,main.o input.o 和 calcue.o 这三个依赖文件,我们输入了两遍,我们
这个 Makefile 比较小,如果 Makefile 复杂的时候这种重复输入的工作就会非常费时间,而且非
常容易输错,为了解决这个问题,Makefile 加入了变量支持。不像 C 语言中的变量有 int、char
等各种类型, Makefile 中的变量都是字符串! 类似 C 语言中的宏。 使用变量将上面的代码修改,
修改以后如下所示:

#Makefile 变量的使用 
objects = main.o input.o calcu.o 
main: $(objects) 
	gcc -o main $(objects) 

1、赋值符“=”
使用 “=” 在给变量的赋值的时候, 不一定要用已经定义好的值, 也可以使用后面定义的值,
比如如下代码:

name = xjq
curname = $(name) 
name = xue1995 

print: 
	@echo curname: $(curname) 

结果显示:xue1995

2、赋值符“:=”

name = xjq
curname := $(name) 
name = xue1995 

print: 
	@echo curname: $(curname) 

结果显示:xjq

3、赋值符“?=”

name = xjq
curname ?= $(name) 
name = xue1995 
curname ?= $(name) 
print: 
	@echo curname: $(curname) 

结果显示:xjq
上述代码的意思就是, 如果变量 curname 前面没有被赋值, 那么此变量就是 “xue1995” ,
如果前面已经赋过值了,那么就使用前面赋的值xjq。

4、变量追加“+=”

name = xjq
curname = $(name) 
name = xue1995 
curname += $(name) 
print: 
	@echo curname: $(curname) 

结果显示:xjqxue1995
有时候我们需要给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号“+=” 。

Makefile 自动化变量

在这里插入图片描述
下面这份是真正使用的Makefile。上面的那份文件,不是实际使用的。仅学习。

objects = main.o input.o calcu.o 
main: $(objects) 
	gcc -o main $(objects) 

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

.PHONY : clean

clean: 
	rm *.o 
	rm main

Makefile 伪目标

.PHONY : clean 

效果:声明 clean 为伪目标以后不管当前目录下是否存在名为“clean”的文件,输入“make clean”的话规则后面的 rm 命令都会执行。

Makefile 函数使用

1、函数 subst

函数 subst 用来完成字符串替换,调用形式如下:
$(subst ,, )

此函数的功能是将字符串 中的内容替换为,函数返回被替换以后的字符
串,比如如下示例:
$(subst xjq,XJQ,my name is zzk)

把字符串 “my name is xjq” 中的 “xjq” 替换为 “XJQ” , 替换完成以后的字符串为 “my name
is XJQ” 。

2、函数 patsubst

函数 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” 。

3、函数 dir

函数 dir 用来获取目录,使用方法如下:
$(dir <names…>)

此函数用来从文件名序列中提取出目录部分, 返回值是文件名序列的目录
部分,比如:
$(dir </src/a.c>)

提取文件“/src/a.c”的目录部分,也就是“/src” 。

4、函数 notdir

函数 notdir 看名字就是知道去除文件中的目录部分,也就是提取文件名,用法如下:
$(notdir <names…>)

此函数用与从文件名序列中提取出文件名非目录部分,比如:
$(notdir </src/a.c>)

提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c” 。

5、函数 foreach

foreach 函数用来完成循环,用法如下:
$(foreach , , )

此函数的意思就是把参数中的单词逐一取出来放到参数中, 然后再执行
包含的表达式。每次 都会返回一个字符串,循环的过程中, 中所包含的每个字符串
会以空格隔开, 最后当整个循环结束时, 所返回的每个字符串所组成的整个字符串将会是
函数 foreach 函数的返回值。

6、函数 wildcard

通配符 “%”只能用在规则中, 只有在规则中它才会展开,如果在变量定义和函数使用时,
通配符不会自动展开,这个时候就要用到函数 wildcard,使用方法如下:
$(wildcard PATTERN…)

比如:
$(wildcard *.c)

上面的代码是用来获取当前目录下所有的.c 文件,类似“%” 。

关于 Makefile 的相关内容就讲解到这里,本节只是对 Makefile 做了最基本的讲解,确保大
家能够完成后续的学习,Makefile 还有大量的知识没有提到,有兴趣的可以自行参考《跟我一
起写 Makefile》这份文档来深入学习 Makefile。

猜你喜欢

转载自blog.csdn.net/qq_29246181/article/details/106208837