makefile管理工具

一 Makefile简介

1.1什么是Makefile?

Makefile是一个工程管理的工具。它的本质是一个文 件,这个文件中存放的是对代码的编译的规则。 Makefile就会根据文件的"时间戳"来决定工程内的文件 本次是否需要参与编译。

1.2什么是make?

make是一个可执行程序,在/usr/bin目录下存放着如 果在/usr/bin目录下找不到这个程序,需要使用sudo apt-get install make来安装这个程序。当在终端上执行 make的时候,它就会解析当前目录下的Makefile文件。 并根据Makefile里的编译规则来编译当前的工程。

1.3为什么使用?

在实际的开发过程中,仅仅使用gcc命令对程序进行编译 时非常低效的,原因主要有以下两点: (1)程序往往是由多个源文件组成的,源文件的个数 越多,那么gcc的命令越长。此外,各种编译规则也会加 大gcc命令行的复杂度,所以在开发调试的过程中,通过 输入gcc命令行来编译程序时很麻烦的。 (2)在程序的整个开发过程中,调试的工作量占到了 整体工作量的70%以上。在调试程序的过程中,每次调 试一般只会修改部分源文件。而在使用gcc命令行编译程 序时,gcc会把那些没有被修改的源文件一起编译,这样 就会影响编译的总体效率。 为了提高编译程序的效率,很多基于windows平台上的 开发工具都提供了工程管理器。用户只需要单击一个 “make”按钮就可以启动工程管理器对整个程序进行自动 编译,在整个编译的过程中是不需要人工干预的。这种 工程管理器被形象的称为全自动工程管理器。 GCC提供了半自动化的工程管理器Make.所谓半自动化 是指在使用工程管理器前需要人工编写程序的编译规 则,所有的编译规则都保存在Makefile文件中,全自动 化的工程管理器在编译程序前会自动生成Makefile文 件。

1.4.优越性

(1)使用方便。通过命令”make“就可以启动Make工程 管理器对程序进行编译,所以不再需要每次都输入gcc命 令行。Make启动后会根据Makefile文件中的编译规则命 令自动对源文件进行编译链接,最终生成可执行文件。

(2)调试效率高。为了提高编译程序的效率,Make会 检查每个源文件的修改时间(时间戳)。只有在上次编 译之后被修改的源文件才会在接下来的编译过程中被编 译和链接,这样就能避免多余的编译工作量。为了保证 源文件具有正确的时间戳,必须保证操作系统时间的正 确性(注意 VMWare虚拟机的CMOS时间是否正确)。

二.makefile

2.1 makfile编译规则 Make工程管理器是完全根据Makefile文件中的编译规则 命令进行工作的,Makefile文件由以下三项基本内容组 成:

(1) 需要生成的目标文件(Target file)

(2) 生成目标文件所需要的依赖文件(dependency)

(3) 生成目标文件的编译规则命令行(command) 这三项内容按照如下格式进行组织: 其中,makefile规则在书写command命令前必须加一 个键。

target file:dependency

command

file command Makefile工程管理器在编译程序时会检查每个依赖文件 的时间戳,一旦发现某个依赖文件的时间戳比目标文件 要新,就会执行目标文件的规则命令来重新生成目标文 件。这个过程称为目标文件的依赖规则检查。依赖规则 检查时Make工程管理器的最核心的工作任务之一。下面 以编译程序test(由 a.c b.c 和b.h组成)为例来描述Make 的工作过程。

//a.c #include "b.h" int main() { hello(); return 0; } //b.h void hello(); //b.c #include void hello() { printf("hello"); }

Make工程管理器编译Test程序的过程如下:

(1) Make工程管理器首先会在当前目录下读取Makefile 文件

(2) 查找Makefile文件中的第一个目标文件(在本例中为 test),该文件也是Make工程管理器本次编译任务的最 终目标。

(3) 把目标文件test的依赖文件当作目标文件进行依赖规 则检查。这是一个递归的检查过程,在本例中就是依次 把a.o和b.o作为目标文件来检查各自的依赖规则。Make 会根据以下三种情况进行处理。 ① 如果当前目录下没有或缺少依赖文件,则执行其规 则命令生成依赖文件(假如缺少a.o,则执行命令”cc -c a.c“生成a.o)。 ② 如果存在依赖文件,则把其作为目标文件来检查依 赖规则(假如 a.c比a.o新,则执行命令”cc -c a.c“更新 a.o) ③ 如果目标文件比所有依赖文件都新,则不做处理。 //makefile test: a.o b.o cc a.o b.o -o test // -o 指定输出文件名 a.o:a.c b.h cc -c a.c //-c 只编译不链接,生成目标文件 b.o:b.c cc -c b.c

(4) 递归执行第三步之后,就会得到目标文件test所有最 新的依赖文件了,接着Make会根据以下三种情况进行处 理: ① 如果目标文件test不存在(比如第一次编译),则 执行规则命令生成test ② 如果目标文件test存在,但存在比test要新的依赖 文件,则执行规则命令更新test。 ③ 目标文件test存在,且比所有依赖文件新,则不做 处理。

2.2 Makefile特性介绍 源文件数量越是多的程序,其编译规则就会越复杂, 导致Makefile文件也就越复杂。为了简化Makefile的编 写,丰富编译程序的方法和手段,Makefile提供了很多 类似高级编程语言的语法机制。

2.2.1 变量 在Makefile文件中,存在大量的文件名,而且这些文 件名都是重复出现的。所以在源文件比较多的情况下, 很容易发生遗漏或写错文件名。而且一旦源文件的名称 发生了变化,还容易造成与其它文件名不一致的错误。 于是,Makefile提供了变量名来代替文件名。变量的使 用方式: 例如: 该Makefile中用变量obj来代替”a.o b.o“,当源文件名发生 改动或增删源文件时,只要对变量obj的值斤西瓜相应的 修改就可以了,这样就可以避免文件名不一致或遗漏的 错误。Makefile中的变量的命名可以使用字符,数字和 下划线,但要注意变量名对大小写是敏感的。

变量定义的方式: (一)通过 ”=“来实现。例如: a1 = $(s2); a2 = $(s3); a3 = a.o 这种方式下变量a1的值是a.o,也就是说前面的变量可 以通过后面的变量来定义。但使用这种方式定义变量 时,要防止出现死循环情况。 $ (变量名) 或 ${变量名} obj = a.o b.o test : $(obj) cc -o test $(obj) a.o:a.c b.h cc -c a.c b.o:b.c cc -c b.c (二)通过 ”:=“来实现。例如: 这种方式下变量a1的值是a.o,变量a2的值是a.o b.o。 例如: 这张方式下变量a1的值是b.o,而不是”a.o b.o“,也就是 说前面的变量不能通过后面的变量来定义。 (三)通过 ”+=“来实现。例如: (四)通过 "?="来实现。例如: a1:= a.o a2:= $(a1) b.o a1:= $(a2) b.o a2:= a.o a1 = a.o; a1 += b.o; 这种方式下变量a1的值是”a.o b.o“。也就是说”+=“可以 实现给变量追加值。等同于如下示例: a1 = a.o; a1:=$(a1) b.o 可以看出,Makefile的”+=“和c语言中的"+="是十分相似 的。 a1 := a.o a1 ?= b.o 这种方式下变量a1的值是a.o,而不是b.o,也就是说, 如果变量a1已经在前面定义过了,那么后面的定义就无 效了。 2.2.2 自动推导 为了进一步简化Makefile书写,Make工程管理器提供了 自动推导功能。自动推导功能默认每个目标文件都有一 个与之对应的依赖文件。例如,a.o文件有依赖文件a.c 与之对应,这样在Makefile中就不需要指定与目标文件 对应的依赖文件名了。此外,自动推导功能还能推导出 与目标文件对应的基本编译规则命令。例如,a.o文件的 规则命令为”gcc -c -o a.c“ 例如: 结果为: 可以看到,Makefile分别推导出了目标文件a.o和b.o的 规则命令”cc -c -o a.o a.c“与”cc -c -o b.o b.c“。 obj = a.o b.o test:$(obj) cc -o test $(obj) linux@linux:~/81-makefile/test$ make cc -c -o a.o a.c cc -c -o b.o b.c cc -o test a.o b.o 伪目标:伪目标不是真正的目标文件,只是一个符号。 为了不和真是的目标文件混淆,最好使用”.PHONY“对伪 目标进行标识。

例如: (1) all。运行命令”make all“后,Make会把all当成最终 的目标。由于伪目标和真实目标一样都有依赖文件,所 以Make会更新all的依赖文件test,a.o和b.o。如下所示: obj = a.o b.o .PHONY: all all: test $(obj) test:$(obj) cc -o test $(obj) .PHONY:clean clean: rm -rf *.o test test_dir = ~/81-makefile/t_d .PHONY: install install: mkdir $(test_dir) cp test $(test_dir) .PHONY: uninstall uninstall: rm -rf $(test_dir) linux@linux:~/81-makefile/test$ make all cc -c -o a.o a.c cc -c -o b.o b.c cc -o test a.o b.o (2) clean。运行命令”make clean“后,Make会执行命 令:rm -rf *.o test,,这样所有的.o文件和test就全被 删除了。如下所示: (3)install。运行命令”make install“后,Make会顺序执行 命令”mkdir $(test_dir)“和”cp test $(test_dir)“,把test 文件复制到test_dir变量指定的目录中去(这里只是模拟 安装过程,并不是真正的安装方法),如下所示: (4)uninstall 。运行命令”make clean“后,Make会执行 命令”rm -rf $(test_dir)“。这样就可以把变量test_dir指 定的目录以及目录中的文件全部删除。如下所示。 在Makefile中,伪目标是非常有用的。例如,在递归编 译,并行编译等场合中,使用伪目标可以方便地控制编 译过程。 linux@linux:~/81-makefile/test$ make clean rm -rf *.o test linux@linux:~/81-makefile/test$ make install mkdir ~/81-makefile/t_d cp test ~/81-makefile/t_d linux@linux:~/81-makefile/test$ make uninstall rm -rf ~/81-makefile/t_d 2.3 文件查找 为了便于管理和组织,程序的源文件都根据功能的不 同放置在不同的子目录中。但源文件被分散存储后, Makefile又如何才能找到这些源文件呢?Makefile提供 了以下两种方法。 (1)VPATH = 目录 :目录 ... 例如: Make会在当前的路径找不到文件时按照顺序依次查找/a 和/b目录 (2)vpath。和VPATH不同的是,vpath并不是变量, 而是关键字,其作用和VPATH类似,但使用方式更加灵 活。vpath的使用方法为 vpath 模式 目录:目录... 例如: Make会在当前路径找不到文件时,按照顺序依次查找/a 和/b目录中所有的C文件。vpath也可以对不同路径采用 不同的搜索模式。例如: export VPATH= /a : /b vpath %.c /a : /b vpath %.c /a : /b make会在当前路径找不到文件时按照顺序依次查找/a 和/b目录中所有的C文件。vpath也可以对不同的路径采 用不同的搜索模式。例如: Make会在当前路径找不到源文件时先查找/a目录下的C 文件,然后查找/b目录下的头文件。例如,首先 在/home目录下新建一个目录b,然后把b.c文件放入目录 b中。 练习: 将多文件编译的Makefile加上变量 TARGET?=demo OBJS:=main.o add.o sub.o CC:=gcc CFLAGS:= -c -o CFLAGSs:=-o vpath %.c /a vpath %.h /b VPATH = /home/b obj = a.o b.o .PHONY : all all:test $(obj) test:$(obj) cc -o test $(obj) 2.4 Makefile中的通配符 2.5Makefile命令中的特殊字符 $(TARGET):$(OBJS) $(CC) $(OBJS) $(CFLAGSs) $(TARGET) main.o:main.c $(CC) main.c $(CFLAGS) main.o add.o:add.c $(CC) add.c $(CFLAGS) add.o sub.o:sub.c $(CC) sub.c $(CFLAGS) sub.o clean: rm $(OBJS) $(TARGET) Makefile中的通配符:*和% 在Makefile中使用命令,这个*就是命令的通配符 %它是Makefile文件所特有的通配符 #aa=`ls *.c` #命令置换 aa=$(shell ls *.c) #在Makefile中起一个shell,将命令在shell中执行, #将执行的结果赋值给aa变量 2.6条件判断 (1)和C语言的条件编译类似,make也可以在运行时 进行条件判断,然后进入分支继续编译,条件判断的书 写格式为: $@:目标 $ ifeq(参数1,参数2)。作用:比较参数1和参数2 的值是否相同,相同为真,相异为假 2> ifneq(参数1,参数2)。作用:比较参数1和参数 2的值是否相同,相异为真,相同为假 3> ifdef(参数)。作用:参数非空为真,空为假。 4> ifndef(参数)。作用:参数空为真,非空为假。 变量a1的值为a.o,变量a2的值为y.o 条件表达式 如果真执行的文本段 else 如果假执行的文本段 endif 例如: a1 = a.o a2 = b.o ifeq ($(a1),$(a2)) a1 = x.o else a2 = y.o

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值