发新话题
打印

[转载]Make使用说明

[转载]Make使用说明

文章作者:linuxaid工程师sunmoon

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 ,是要在实践中掌握的
曾几何时,有人对我说:装B遭雷劈。我说:去你妈的。于是,这个人又对我说:如果再说脏话,上帝会惩罚你的。我说:我操上帝。结论:彪悍的人生不需要上帝。

TOP

发新话题