makefile文件

一.make/makefile基本规则

makefile文件中定义了一系列的规则来指定, 哪些文件需要先编译, 哪些文件需要后编译, 哪些文件需要重新编译, 甚至于进行更复杂的功能操作, 因为makefile就像一个Shell脚本一样, 其中也可以执行操作系统的命令. makefile带来的好处就是——“自动化编译”, 一旦写好, 只需要一个make命令, 整个工程完全自动编译, 极大的提高了软件开发的效率。
make/makefile:项目的自动化构建工具
Makefile:是一个文本文件,记录一个项目的构建规则流程
makefile的编写规则
目标对象:依赖对象
make:是一个解释程序对makefile中记录的构建规则流程逐步解释执行,完成项目的构建
make的解释执行规则
1.在命令行中敲击make指令,则表示运行make解释程序,程序会在当前目录下找到名称为makefile/Makefile的文件,解释执行其中的项目。
2.在规则中,找到要生成的第一个目标对象, (判断目标对象是否已经存在, 存在的话是否需要重新生成—根据原码文件的最后一次修改时间执行对象生成。
3. make每次在makefile中只会找到第一个目标对象进行生成,生成之后就会退出(也就是说不会去生成第二个对象)。
4. make在生成目标对象的时候,会先查找依赖对象的生成规则,先生成依赖对象,然后再去生成目标对象。

二、Makefile工作原理

在一目录下有几个如下所示的.c文件,然后我们用makefile进行编译

[root@localhost mfe]# ls
fun1.c  fun2.c   main.c  sum.c head.h

fun1.c

 1 #include <stdio.h>
 2 void fun1()
 3{
 4     printf("this is fun1\n");
   
 5}

fun2.c

1 #include <stdio.h>
2void fun2()
3{
4      printf("this is fun2\n");
5 }

main.c

  1  #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <sys/types.h>
  6 #include "head.h"
  7 
  8 int main(int argc, char *argv[])
  9 {
 10     printf("this is main!\n");
 11     int i = 0;
 12     for(i=0; i<argc; i++)
 13     {
 14         printf("[%d]:[%s]\n", i, argv[i]);
 15     }
 16 
 17     fun1();
 18     fun2();
 19 
 20     printf("sum(10)==[%d]\n", sum(10));
 21 
 22 
 23     return 0;
 24 }

sum.c

  1 #include <stdio.h>
  2 
  3 int sum(int len)
  4 {
  5     int i = 0;
  6     int sum = 0;
  7     for(i=0; i<len; i++)
  8     {
  9         sum += i;
 10     }
 11 
 12     return sum;
 13 }

head.h

  1 void fun1();
  2 void fun2();
  3 int sum(int len);

1.第一个版本

首先在Linux输入如下命令,打开一个makefile

[root@localhost mfe]# vim makefile

打开makefile后输入如下命令

main:main.c fun1.c fun2.c sum.c
       gcc main -o main.c fun1.c fun2.c sum.c head.h

然后在命令终端输入make、

[root@localhost mfe]# make
gcc -o main main.c fun1.c fun2.c sum.c

可以看到成功生成了main文件,然后在运行一下

[root@localhost mfe]# ./main
this is main!
[0]:[./main]
this is fun1
this is fun2
sum(10)==[45]

这是一种最为直接的方法,main是目标对象,依赖于main.c,fun1.c,fun2.c,sum.c,但这种方法效率极低,需要把所有的文件名称都输入而且要输入两遍,当文件比较多时,这种编译方法显然不太可取。

2.第二种方法

下面将采取另一种方法,并有如下定义
预定义变量的使用:$@ $^ $ < 用于指令中
$@表示目标对象
$^表示所有依赖对象
$<表示依赖对象的第一个
因此我们对makefile文件用如上命令改编一下

main:main.c fun1.c fun2.c sum.c
       gcc $^ -o $@

重新运行一下

[root@localhost mfe]# make
make: “main”是最新的。
[root@localhost mfe]# ls
fun1.c  fun2.c  head.h  main  main.c  makefile  sum.c head.h
[root@localhost mfe]# ./main
this is main!
[0]:[./main]
this is fun1
this is fun2
sum(10)==[45]

“main”是最新的说明我们成功生成了新的目标对象,这种方法最起码让我们少输入一遍,但还是得把文件名全部输入一遍,因此下面对目标对象:依赖对象进行一个改动

3.第三个版本

makefile中的函数有很多, 在这里介绍两个最常用的。
1.wildcard – 查找指定目录下的指定类型的文件
src= ( w i l d c a r d ∗ . c ) / / 找 到 当 前 目 录 下 所 有 后 缀 为 . c 的 文 件 , 赋 值 给 s r c 2. p a t s u b s t – 匹 配 替 换 o b j = (wildcard *.c)//找到当前目录下所有后缀为.c的文件,赋值给src 2.patsubst – 匹配替换 obj= (wildcard.c)//.c,src2.patsubstobj=(patsubst %.c,%.o, ( s r c ) ) / / 把 s r c 变 量 里 所 有 后 缀 为 . c 的 文 件 替 换 成 . o 在 m a k e f i l e 中 所 有 的 函 数 都 是 有 返 回 值 的 。 当 前 目 录 下 有 m a i n . c f u n 1. c f u n 2. c s u m . c s r c = (src)) //把src变量里所有后缀为.c的文件替换成.o 在makefile中所有的函数都是有返回值的。 当前目录下有main.c fun1.c fun2.c sum.c src= (src))//src.c.omakefilemain.cfun1.cfun2.csum.csrc=(wildcard *.c) 等价于src=main.c fun1.c fun2.c sum.c
obj=$(patsubst %.c,%.o, $(src))等价于obj=main.o fun1.o fun2.o sum.o

src=$(wildcard ./*./c)//获取当前目录下所有以.c结尾的文件名称
main:$(src)//目标对象:依赖对象
    gcc $^ -o $@

[root@localhost mfe]# make
make: “main”是最新的。
[root@localhost mfe]# ls
fun1.c  fun2.c  head.h  main  main.c  makefile  sum.c head.h
[root@localhost mfe]# ./main
this is main!
[0]:[./main]
this is fun1
this is fun2
sum(10)==[45]

4.第四个版本

第三个版本虽然不需要我们输入所有文件名称,但仍然不是效率最高的,假如其中某一个文件发生改变,则需要对所有.c文件进行编译,因为目标对象依赖的是全部.c文件,例如main.c发生如下改变

  //main.c文件
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <sys/types.h>
  6 #include "head.h"
  7 
  8 int main(int argc, char *argv[])
  9 {
 10     printf("this is main!\n");
 11     int i = 0;
 12     for(i=0; i<argc; i++)
 13     {
 14         printf("[%d]:[%s]\n", i, argv[i]);
 15     }
 16 
 17     fun1();
 18     fun2();
 19 
 20     printf("sum(10)==[%d]\n", sum(10));
 21     printf("hello bit\n");
 22     return 0;
 23 }

[root@localhost mfe]# make
gcc fun1.c fun2.c sum.c main.c -o main head.h
[root@localhost mfe]# ./main
this is main!
[0]:[./main]
this is fun1
this is fun2
sum(10)==[45]
hello bit

需要编译所有.c文件!

5.第五个版本

因此我们可以在项目构建中分两步走——编译与链接
这样的好处是:如果只是修改一个.c文件,以前直接对所有.c进行编译生成可执行程序的过程来说,需要重新编译.c生成可执行程序——效率低下
因此分为两步进行操作,先把每个.c生成自己的.o,然后再把所有的.o链接到一起,好处是一旦一个.c发生改变,只需对一个.c进行编译,生成.o之后重新链接一次就可以生成可执行程序,其他.c不需要重新进行编译。
程序如下

 src=$(wildcard ./*.c)
  2 obj=$(patsubst %.c,%.o,$(src))
  3 main:$(obj)
  4     gcc $^ -o $@
  5 %.o:%.c
  6     gcc -c $< -o $@

运行过程

[root@localhost mfe]# make
gcc -c fun1.c -o fun1.o
gcc -c fun2.c -o fun2.o
gcc -c sum.c -o sum.o
gcc -c main.c -o main.o
gcc fun1.o fun2.o sum.o main.o -o main
[root@localhost mfe]# ./main
this is main!
[0]:[./main]
this is fun1
this is fun2
sum(10)==[45]

6.第六个版本

makefile的清理操作
用途: 清除编译生成的中间.o文件和最终目标文件
make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令, 解决方案:
伪目标声明:
.PHONY:clean:声明一个目标对象与与外部文件无关,表示每次这个对象最终都要重新生成
对版本5补充一个清理操作就大功告成了。

  1 src=$(wildcard ./*.c)
  2 obj=$(patsubst %.c,%.o,$(src))
  3 main:$(obj)
  4     gcc $^ -o $@
  5 %.o:%.c
  6     gcc -c $< -o $@
  7 .PHONY:clean
  8 clean:
  9     rm -rf $(obj) main

[root@localhost mfe]# rm -r main
rm:是否删除普通文件 "main"?y
[root@localhost mfe]# make
gcc fun1.o fun2.o sum.o main.o -o main
[root@localhost mfe]# ./main
this is main!
[0]:[./main]
this is fun1
this is fun2
sum(10)==[45]
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值