| 关键词: 文件 目标 Make 命令 makefile 属性 mylib 成员 一个 规则 |
Make是一种代码维护工具.在大中型项目中,他将根据程序各个模块的更新情况,自动的维护和生成目标.能很好的做到”不多”,”不重””不漏”. 不多:他只更新那些需要更新的文件,而不懂那些并不过时的东西 不重:是指当make对某个文件进行更新时,即使有很多文件过时,他也将只更新一次,当然,这一次就足够了 不漏:是指他不会漏下任何应该更新的文件. 1,make的基本使用 make在使用时有一系列的规则,他将根就这些规则来解释他的配置脚本,以达到它的设计目的.这个配置脚本的缺剩名是makefile ,Makefile.也可以用其它文件名.我们这里所讲的make的应用就是怎样写出高效,简洁,正确的makefile文件. Make的使用形式为 Make [option] [macrodef][target] Option指出make的工作行为,make的具体选项有: -c dir make在开始运行后的工作目录为指定目录 -e 不允许在makefile中队环境的宏赋值 -f filename 使用指定的文件作makefiel -k 执行命令出错时,放弃改目标去维护其它目标 -n 按实际运行时的执行顺序现实命令,包括用@开头的命令,但不振的执行 -p -现实makefile中所有的宏定义,河内部规则 -r 忽略内部规则 -s 执行但不显示命令,常用来检查makefile的正确性 -S 执行make时 出错即退出 -t 修改每个目标文件的创建日期,但又不正的创建 -V 打印make的版本号 -x 将所有的宏定义输出的shell环境中 -i 忽略运行make 中的错误,而又不退出make 在使用make 维护目标的时候,为了调试错误,常使用 make 2 > errofile.这样错误信息就都写入了errofile 2.1makefile 的基本书写 make 使用的依据是依赖关系.如又工程example ,他依赖于main.o e1.o e2.o,而main.o 依赖于main.c l.h .e1.o依赖于e1.c l.h ,e2.o 依赖于e2.c. 这样就形成一棵树.及双亲节点,依赖于孩子节点.当make 进行维护时.他呼对这棵树.进行一次后序遍历.如果发现孩子节点形成的时间晚于双亲节点形成的时间.便开始维护.原理十分简单. 现在,我们开始,我们的主要内容: 如何书写makefile.makefile 是供make 使用的一个配置脚本.他是按一定的规则写成的.它的组成是以一个要维护的目标为一个单元,每个单元的书写形式如下: 要维护的目标列表 要维护的目标属性 分割符(一般为 依赖的文件列表 命令行的属性 ;第一条命令 tab键 命令1….. 需要说明的是,第一条命令可以加; 跟在依赖文件列表后,也可以和其它的命令另起一行写.一定要记得加tab键.否则make 不认识.当一条命令太长一行放不下是使用/l来换行. 注释行用#开头. 这样一来.我们前面举的例子就可以写成这样的makfile example : main.o e1.o e2.o gcc -o example main.o e1.o e2.o main.o :main.c l.h gcc -c main.c e1.o :e1.c l.h gcc -c e1.c l.h e2.o:e2.c gcc -c e2.c 可以用cat -e -v-t makefile 其中-v-t可以让tab键显示^I ,是makefile 的末尾显示$,这样可以检验,makefile的正确性. 可见,书写一个简单的makefile文件并不困难.然而在大中型项目中.要通过makfile 做大量的维护与调试.这样会是程序员增加很大的工作量.因此,make 引入了一些属性变量,和宏变量,来控制make .甚至可以像c预言的条件编译那样.通过一些shell语句来控制.接下来.我们将一一讲解. 2.2 makfile中的属性控制 2.2.1分割符 分割符一般用: 但也用::,:!:^,:-一下举例说明一下这几个分割符的作用一般目标文件只能出现一次.只有使用::时才可以出现多次如: a.o::a.c #命令1 a.o::b.c #命令2 意思是:如果a.c更新.使用命令1.b.c更新使用命令2 :^ 将指定的依赖文件和目标文件一有的依赖文件结合起来 :- 清除目标文件原有的依赖文件,将指定文件为目标文件的唯一依赖文件 :! 对每个更新过的依赖文件都执行一遍命令菜单 如: a.o :b.c a.o:^ c.c 此时a.o 的依赖文件是b.c 和c.c a.o:- a.c 此时 a.o 的依赖文件只剩下a.c了 2.2.2 命令行属性 命令好属性较简单只有三种 + 当运行条件成熟(依赖文件过时) 始终执行,即使 make 有 -n -q -t 选项 - 当此行出错时,忽略继续运行make @ 不显示本行命令(make 执行使命令行在屏幕上打印) 2.2.3 目标文件属性 目标属性比较多.这里举几个常用的例子 .IGNORE 类似于命令行中的减号,在本目标维护出错时.并不马上退出make 而是继续运行 .SILENT 类似于命令行中的@ ,维护本目标时不在屏幕上显示 .PRECIOUS 保留中间文件 .LIBARY 说明维护的目标是一个库 .SYMBOL 说明维护的目标是指定入口的库成员 .LIBRARYM 说明维护的是库的一个成员 在使用目标文件的属性时有两种方法 .IGNORE main.o e1.o 和 main.o .IGNORE : …. …… e1.o .INGORE :….. ……. 效果是相同的,在实际使用时,可以根据个人的具体情况使用. 2.2.3 伪目标 在make 中有两类目标,一类是实目标.一类是伪目标.伪目标不要求make生成世纪的目标文件,只是完成一些辅助性的工作,如打印,删除等.伪目标有两种.一种是任意给出名字如: lp: pr *.c/lp 将所有的源文件分页打印.由于系统中没有lp 文件,他总是认为是过时的,因此这个命令总被执行. 另一种方式是使用linux提供的伪目标 .ERRO :依赖文件 命令行 作用时,一旦遇到错误时,执行命令行 .INCLUDE :file1 file2 作用是: 将文件包含进来,注意:这里的包含不是指针的转移而是拷贝 .INCLUDE /usr .INCLUDE file1 make 首先在当前目录下寻找file1,如没有则在/usr下找.另一种使用方法是: /INCLDE <dir/filename> .IMPORT 将一些宏载入 .如: .IMPORT shell 这样makefile 便可以使用shell 里的宏了. .EXPORT 和.IMPORT恰恰相反.是将makefile里定义的宏,输出到环境.不赘述. 当需要维护几个目标时可使用这样的伪目标: all: main.o main1.o …. 重要说明是: 命令行属性>>目标文件属性>>make 命令行属性 3.宏的使用 我们在各种应用中已熟悉宏的概念.不赘述.在make 中使用宏也要先定以后使用.宏名可以是任意数字.字母 下划线的组合. 不过不能用数字开头. 宏的定义方式有3种 = 直接将后面的字符串赋给宏 := 后面跟字符串常量,将他的值赋给宏 += 宏原来的值加上一个空格,将它的内容赋给宏 宏的应用各是有两种: $(宏名) 或 ${宏名} 宏名可以嵌套使用.如 h2=mylib.a index=2 则在makefile 中调用$(h$(index)) ,就等于调用 mylib.a.使用shell 环境的宏.不需要定义.只要用.IMPORT 在如就行. 3.2 内定义宏 控制宏为了控制make 的属性,定义了一系列的内定义宏.分为两类.控制宏用于控制make 的属性.属性宏代表一些特殊的值. 控制宏: .DIREPATR 表示路径中用于分割目录与文件的分割符一般为 / .MAKDIR 调用make的绝对路径名 .NULL 空字符串 .PWD 运行make 时活动目录的绝对目录名 .SHELL 运行是的shell 名 另一类宏.我们成为属性宏.和目标文件的属性书写和意义都相同.用于设置这些属性.如 .IGNORE=yes 将整个makefile里的目标文件都设为.IGNORE.其它的属性宏.和目标文件属性里面的相同. 3.3 动态宏 引入动态宏的目的是,简化makefile的书写.他利用了很多一用的代码.但给makefile 带来了不可读性 $@ 当前目标文件的名字 $% 同$@,不过若维护的目标是库时.$@指库名 $% 指库成员 $> 适用于lib(member)的情况$>指库名 如$@ $* 目标文件,去掉后缀 $& 当前目标文件.在所有依赖文件 $^ 当前目标文件 在本单元中的依赖文件 $? 当情目标文件 在所有单元中比目标新的依赖文件 $< 当前目标文件 在本单元中比目标文件新的文件 这些宏只能用在命令行中.make 提供可与此对应的动态宏用在依赖文件中.他们分别是在上面的宏上在加一个$ $$@ 在依赖文件列表中,代表目标文件的名字 等等.这样,我们原先的makefile便可以写成: example:main.o e1.o e2.o gcc -c $? main.c e1.c:$$*.c l.h gcc -o $*.c e2.c :$$*.c gcc -o $*.c 3.4 宏的修改 一个宏定义以后.可以用make的某些功能.对其进行修改 1,如:file=/usr/l/main.c 引入修改描述符: d :仅展开目录 b :仅展开文件名,不包括扩展名 f :展开全名 这样$(file:d)等于main . 其它的类推 2,替换宏中的字符串 规则 宏名: s /远字符串/新字符串 file =file1.o file2.o file3.o file:s/file/fun 则file=fun1.o fun2.o fun3.o 3.还有 宏名:^前缀 宏名:+后缀 file= a b c file=^"1"则 file=1a 1b 1c 4.内部规则 4.1make 的内部规则是系统或者后呼吁定义的一些规则.他在定义时.不必指出全名.只是定义了一些最常用的依赖关系和依赖命令.和c 编程有关的就以下.这几种情况 与c编程又过的文件扩张名是 .o 目标文件 .c c语言源文件 .C c++语言源文件 .h 头文件 与c 相关的规则主要有 .c: $(CC)$CFLAGS)-O $@ $< .c .o: $(CC)$CFLAGS)-c $@ $< 这里面使用到了一些宏.以上两句话的意思是对.c 文件缺剩是的编译.和编译成.o 文件.有必要说一些宏的定义 宏名 初始值 说明 AR Ar 库管理命令 ARFLAGS -ruv 库管理命令选项 CC cc C编译命令 CGLAGS -o C编译命令选项 C++C CC C++编译命令 C++FLAGS -O C++编译命令 MAKE make MAKE命令 MAKEFLAGS NULL MAKE命令选项 LIBSUFFIX .a 库的扩展名 A .a 库的扩展名 有了内部规则.我们的makefile 会更加简练如原先的makefile 我们可以写成 example:main.o e1.o e2.o gcc -o example main.o e1.o e2.o main.o e1.o :l.h 4.2修改和定义内部规则 修改内部规则.可以有两种办法:一是修改内部规则用到的宏.而是直接修改内部规则的命令行 如我们前面提到的规则: .c .o: $(CC)$(CFLAGS)-c $@ $< 我们只要定义 CC=6788 其命令行就变成了 6788 -O -c $@ $< 原因是makefile 内部的宏定义大于make 的默认定义 至于写该命令行,直接在makefile 中写就行了.他会踢换掉默认的定义 如 .c .o: $(CC) -c $@ $< 就会以下面这个定义为主 对于一些没有的后缀名可以自己定义,各时如: new roman 后缀名1 后缀名2 … .SUFFIXES .n 就定义了一个新的后缀n .从目标文件生成文字表文件的命令是: nm 目标文件 > 名字表文件 现在我们定义一个内部规则,即.o 文件依赖于通明的.o 文件 .SUFFIXES .n .o.n nm $< > $@ 这样的话.make 会根据.o文件的更新,自动的维护.n 文件 5.库的使用 5.1库的建立和维护 在开发大中型软件时.常把一些编译好的模块同意放到一个库中.在编译时可以节省时间和空间. 库中的文件一般成为库的成员.成员的表示形式一般为 库名(库成员名) 如: mylib(myrea.o) 用make 维护库时,我们将库名作为目标文件格式如下: 库名: 库名(库成员名1) 库名(库成员名2) 库名(库成员名3) make 遇到这样的命令行 ,自动吧库名当成目标文件,并赋予.LIBRARY属性 例如: mylib.a :mylib.a(myread.o) … 也可以这样定义 mylib.a .LIBRAAY :myread.o …还有一种定义方式是: 库名: ((rentry)) make 遇到这种情况,把库名置成.LIBRARY 把entery 置成 .SYMBOL.然后根据entry 找到相应的文件以后的处理和直接些文件相同 make 从.o 文件维护库的命令是 ae -ruv 库名 目标文件名 如维护一个名为mylib的库.makefile 需要这样写 mylib:mylib(f1.o) gcc -c fi.c ar -ruv muylib f1.o rm -f f1.o mylib:mylib(f2.o) gcc -c f2.c ar -ruv muylib f2.o rm -f f2.o …… 利用动态宏我们可以将他该成这样: mylib:mylib(f1.o f2.o…..) gcc -c ($?:b).c ar -ruv $@ $? rm -f $@ 我们也可以利用内部规则,来简化所有makefie 中 库的维护 .o.a: $(AR) $(ARFLAGS) $@ $? rm -f $? 然后库的makfile 写成 mylib: mylib(f1.o) mylib(f2.o)……… 还有一条内部过则是: .a make $@ 即make 将自动维护所有的库 5.2 利用库进行连接 使用库进行连接时.只需要将库名.o文件名说明就行.形式如下: 库名(库成员名) 或 库名(((成员entery)) 例如想在有一可执行程序eampl2.exe 依赖于mylib 中的f1.o ,在makefile 中可以这么写: eampl2.exe:mylib(f1.o) #连接命令行(一般是ld) make 读到这样的规则就知道 mylib 是一个库.有.LIBRAAY的属性 f1.0 是库的成员函数 有.LIBRAYM的属性 eampl2.exe 依赖于f1.o 在这里也有意跳内部规则 .o.e $(LD) $(LDFLAGES) -o $@ $< $(LDLIB) 其中宏的定义是 LD 连接命令ld LDFLAGEA ld 的选项] LDLIB 库名列表 因此只要把你自己定义的库,加到库名列表中,在使用内部规则就行了.如上例可以写成: LDLIB+=mylib.a # 将自定义的库加到 库名列表中去 eaple2.exe:mylib(f1.o) 7.条件编译 make 中还引入了一些shell 语法.以帮助进行类似于c 语言的于编译的功能.下面是在linux核心的makfile 中抄来的一段.我们可以看一下他是如何进行条件维护的 ifeq (.config,$(wildcard .config)) include .config ifeq (.depend,$(wildcard .depend)) include .depend do-it-all: Version vmlinux else CONFIGURATION = depend do-it-all: depend endif else CONFIGURATION = config do-it-all: config endif 类似于c 语言的 if else 语句.不同的是他是比较两个值是否相等,来判断是否满足条件.因为在makefile中.很多值是由字符串组成的,所以这个于就十分有用. 还有这样的语句: ifdef CONFIG_PCI DRIVERS := $(DRIVERS) drivers/pci/pci.a endif 这个语句够简单,我多说什么了. 本文只是一个简单的说明.make 的内容很多.有兴趣的可以参考它的man 手册.不过编制好的makefile ,是要在实践中掌握的. |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|