make的使用和makefile的编写

  • make的使用和makefile的编写
  1. 什么是make
  1. make机制概述
  1. make工具的最初设计目的是为了维护C程序文件,防止不必要的重新编译。make工具对于维护一些具有相互依赖关系的文件特别有用,其对,文件和命令的联系提供一套编码方法。在使用过程中只告诉make需要做什么,即提供一些规则,其他工作由make完成。
  2. make工具的工作是自动确定工程的那部分源程序文件需要重新编译,然后执行命令去编译他们。
  3. make机制的运行环境需要一个二进制命令行程序make和一个文本文件makefile

案例说明:

 

执行make命令进行滚编译:

  1. make与makefile的关系
  1. make是linux下的二进制程序,用来处理Makefile这种文本文件。在Linux的shell命令键入make的时候将自动寻找名称为” Makefile”作为编译文件,如果没有则是找”makefile”。找到编译文件后,根据makefile中的第一个目标自动寻找依赖关系,如果该依赖目标有依赖其他目标,make工具将一层层寻找直到找到最后一个目标文件为止。
  2. make工具的使用格式为:make [options参数] [target]…

表make工具参数选项

选项

含义

-f filename(常用)

显式的指定文件作为makefile

-C diname(常用)

指定make在运行后的工作目录为diname

-e(常用)

不允许在makefile中替换环境变量的值

-k(常用)

执行命令出错时,放弃当前目标继续维护其他目标

-n

按实际执行的顺序模拟执行命令(包括用@开头的命令),没有实际执行效果,仅仅用于显示执行过程

-p

显示makefile中所有变量的内部规则

-r

忽略内部规则

-s(常用)

执行但不显示命令,常用来检查Makefile的正确性

-S(常用)

如果执行命令出错就退出

-t

修改每个目标文件的创建日期

-I

忽略make执行中的错误

-V

显示make的版本号

make操作管理makefile的规则:

1)如果这个工程没有被编译过,那么所有C文件都需要被编译

2)如果这个工程有几个C文件被修改,则只需要编译修改过的C文件,并链接目标程序

3)如果这几个工程的头文件被改变了,则需要编译引用了这几个头文件的C文件,并链接目标程序

案例分析:

  1. makefile的书写规则

1)makefile有两种语法格式:

target :prerequisites

      command

      …

target :prerequisites;command

      command

      …

target是目标文件可以,多个文件可以以空格分开,可以使用通配符“*”、“?”“[]”。一般来说,make会以UNIX的标准shell,也就是/bin/sh来执行命令

2)在规则中使用通配符

makefile中支持三种通配符

makefile通配符描述表

符号

案例

描述

备注

*

clean:

     rm -f *.o

删除所有以.o为后缀名的文件

如果要使用*,可用转义字符“\”,即\*

?

print:*.c

lpr -p $?

touch print

$?是一个自动化变量,表示比目标新的依赖文件集合

[]

[a-d]

等同于[abcd]

通配符还可以用在变量中:

比如:objects = *.o  这里的*.o不会展开,objects的值就是*.o。

如果想让通配符在变量中展开,也就是让所有objects的值是所有.o文件的集合,那么可以这样:

objects := $(wildcard *.o)

这种用法是通过Makefile的关键字wildcard关键字指示的

3)伪目标

伪目标不是一个文件,而是一个标签。需要注意的是伪目标的取名不能和文件名重名。当然为了避免和文件名重名的情况,可以使用“.PHONY”标记伪目标,向make说明,不管是否有这个文件,这个目标都是伪目标

.PHONE:clean

伪目标没有依赖文件,但是可以为伪目标指定依赖文件。伪目标同样可以作为默认目标,只要将其放在第一个。案例如下:

4)多目标

makefile中的目标文件不止一个,其支持多目标。当makefile中多个目标同时依赖一个文件,并且生成的命令大体相同,就能把他们合并起来。

当然,多目标的生成规则的执行命令是同一个,这可能存在问题。可以使用自动化变量“$@”,该变量意味着目前规则中的目标集合。

案例如下:

5)makefile的命令

I)多条命令

Makefile中,make会按顺序一条条执行命令,每条命令必须以tab开头,除非命令是紧跟在依赖文件后的。在命令行之间的空格和空行会被忽略,但是空格或空行前以tab开头,make会认为其是一个空命令。

当依赖目标新于目标文件时,也就是当规则的最终目标需要被更新时,make会一条条执行后面的命令。如果想让上一条命令的结果应用在下一条命令上,则需要使用分号将这两个命令分离。

案例如下:

exec:     

       cd /home/zhangkai           #进入该目录

       pwd                                   #打印当前目录

执行make exec命令后,会进入/home/zhangkai目录,但是pwd打印结果仍然是makefile的目录。

但是如果改成:

exec:     

       cd /home/zhangkai ; pwd        

表示进入home/zhangkai,打印当前目录

II)返回码

当命令执行完毕后,make会自动检测他们的返回码,如果命令返回成功,那么make会继续执行下一条命令,直到规则中所有命令都成果返回后。如果某条命令出错,make就会终止当前规则,这将有可能终止所有规则的执行。

但有时候,命令的出错不代表规则就是错的。因此为了忽略命令的出错,可以在命令前加一个“-”,案例如下:

exec:     

  • cd /home/zhangkai            #进入该目录,并忽略错误

       pwd                                   #打印当前目录

  1. makefile的中使用变量

1)变量基础

I)变量的名字可以包含字符、数字和下划线,变量是大小写敏感的。

II)变量在声明时,需要赋值,而在使用时需要在变量名前加上“$”符号,最好用“()”或“{}”括起来,如果需要用真实的$字符,那么需要用$$表示。

案例如下:

objects = program.o foo.o utils.o

program : $(objects)

              cc -o program $(objects)

              $(objects):defs.h

等价于:

objects = program.o foo.o utils.o

program : program.o foo.o utils.o

              cc -o program program.o foo.o utils.o

              program.o foo.o utils.o:defs.h

III)赋值变量

make还支持变量的嵌套定义,案例如下:

foo = $(bar)              #可以使用后面定义好的变量

bar = $(ugh)

ugh = Huh?

all:

       echo $(foo)

执行结果为Huh?

make中还支持另一种定义变量的方法,使用“:=”操作符,如

x := foo

y :=$(x) bar

x := later

其等价于

y :=foo bar

x := later

使用“:=”操作符使得前面的变量不能使用后面的变量,只能使用前面定义好的变量。

y := $(x) bar

x := foo

此时y的值为bar 而不是 foo bar

IV)操作符“?=”

案例:FOO?=bar

其含义是如果变量FOO没有被定义过那么其值为bar,如果先前已经定义过那么该语句什么也不做。

其等价于:

ifeq($(origin FOO),undefined)

       FOO = bar

endif

这段代码中使用了ifeq条件判断语句和origin函数调用。origin函数返回函数变量FOO的出处,ifeq则判断两个参数是否相等。

V)赋值操作符“+=”

案例如下:

objects = main.o foo.o bar.o util.o

objects += anther.o

于是$(objects) 的值为main.o foo.o bar.o util.o anther.o ,其也可等效于下面这种方式:

objects = main.o foo.o bar.o util.o

objects := $(objects) anther.o

如果变量之前没有定义过,那么“+=”会自动变成“=”,如果前面有定义,那么“+=”会继承前次操作的赋值符。如果前一次是“:=”,那么“+=”会以“:=”作为其赋值符。案例如下:

variable := value

variable += more

等价于:

variable:=value

variable:=$(variable) more

  1. makefile中常见函数调用

1)函数基础

Makefile 可以调用函数,从而让Makefile的书写更加灵活。函数调用后,返回值可以当变量的。其语法如下:

$(<function> <arguments>) #函数名和参数使用空格隔开,参数见使用逗号隔开

或是

${<function> <arguments>}

< function >是函数名,<arguments>是函数参数,参数间可以逗号隔开,而函数名和参数之间以空格分隔。函数调用以“$”开头,以圆括号和花括号把函数名和参数括起来,如使用“$(subst,a,b,$(x))”这样的形式而不是“$(subst,a,b,${x})”

2)字符串处理函数

字符串处理函数表

函数

功能

返回值

$(subst,<from>,<to>,<text>)

字符串text中的from替换成to

字符串

$(patsubst <pattern>,<replacement>,<text>)

查找text中的单词是否符合pattern,如果匹配的话,用replacement替换

替换后的字符串

$(findstring <find>,<in>)

在字符串in中寻找find

如果找到返回该字符串,没找到返回空字符串

$(filter <pattern…>,<text>)

以pattern模式过滤text中的字符串,保留符合pattern模式的单词,多个模式间通过空格链接

返回符合pattern模式的单词

$(filter-out <pattern…>,<text>)

以pattern模式过滤字符串中的单词,去除符合模式pattern的字符,可有多个模式

返回不符合模式的字符串

$(sort<list>)

将字符串list中的单词按照字母升序排序,先比较单词的首字母,首字母相同比较下一字母,以此类推。当遇到相同单词时,sort将自动删除。

排序后的字符串

$(word <n>,<next>)

从字符串text中取出第n个单词,计数从1开始

返回第n个单词,如果n大于字符串的长度则返回空字符串

$(wordlist <s>,<e>,<text>)

从字符串中取出从s开始到e结束的单词串,s和e是一个数字

返回:返回从s到e的字符串。如果s比text的单词数大,那么返回空字符串,如果e大于text的单词数,那么返回从s开始到结束的单词串

返回从s到e的字符串。如果s比text的单词数大,那么返回空字符串,如果e大于text的单词数,那么返回从s开始到结束的单词串

$(words <text>)

统计text字符串中单词个数,计数从1开始

返回text中的单词数

$(firstword <text>)

取出text中的首个单词

返回text中的首个单词

I)字符串替换函数subst

格式:$(subst,<from>,<to>,<text>)

功能:把字符串text中的from替换成to

返回:返回替换后的字符串

II)模式字符串替换函数patsubst

格式:$(patsubst <pattern>,<replacement>,<text>)

功能:查找text中的单词是否符合pattern,如果匹配的话,用replacement替换

返回:替换后的字符串

案例:

$(patsubst %.c,%.o,x.c.c bar.c)

把字符串x.c.c bar.c符合模式%.c的单词替换成%.o,函数返回结果是:

x.c.o bar.o

III)查找字符串函数findstring

格式:$(findstring <find>,<in>)

功能:在字符串in中寻找find

返回:如果找到返回该字符串,没找到返回空字符串

IV)过滤函数filter

格式:$(filter <pattern…>,<text>)

功能:以pattern模式过滤text中的字符串,保留符合pattern模式的单词,多个模式间通过空格链接

返回:返回符合pattern模式的单词

案例:

sources :=foo.c bar.c baz.c ugh.h

foo:$(sources)

       cc $(filter %.c %.s,$(sources)) -o foo

$(filter %.c %.s,$(sources)的返回值是foo.c bar.c baz.c

V)反过滤函数filter-out

格式:$(filter-out <pattern…>,<text>)

功能:以pattern模式过滤字符串中的单词,去除符合模式pattern的字符,可有多个模式

返回:返回不符合模式的字符串

案例:

objects = main1.o foo.o mian2.o bar.o

mains = main1.o main2.o

$(filter-out $(mains),$(objects))

返回值为foo.o bar.o

VI)排序函数sort

格式:$(sort<list>)

功能:将字符串list中的单词按照字母升序排序,先比较单词的首字母,首字母相同比较下一字母,以此类推。当遇到相同单词时,sort将自动删除。

返回:排序后的字符串

案例:

$(sort lose foo bar lost)

返回值为bar foo lose lost

VII)取单词函数word

格式:$(word <n>,<next>)

功能:从字符串text中取出第n个单词,计数从1开始

返回:返回第n个单词,如果n大于字符串的长度则返回空字符串

案例:

$(word 2, programing linux C programing)

返回值:linux

VIII)去除单词串函数wordlist

格式:$(wordlist <s>,<e>,<text>)

功能:从字符串中取出从s开始到e结束的单词串,s和e是一个数字

返回:返回从s到e的字符串。如果s比text的单词数大,那么返回空字符串,如果e大于text的单词数,那么返回从s开始到结束的单词串

案例:

$(wordlist 2,4,I like linux C programing)

返回值:like linux C

IX)单词个数统计函数words

格式:$(words <text>)

功能:统计text字符串中单词个数,计数从1开始

返回:返回text中的单词数

案例:

$(words,I like linux C programing)

返回值为5

X)首单词函数firstword

格式:$(firstword <text>)

功能:取出text中的首个单词

返回:返回text中的首个单词

案例:

$(firstword I like linux c programing)

返回值:I

3)文件名操作函数

I)取目录函数dir

格式: $(dir <name…>)

功能: 从文件名序列(一个或多个)取出目录部分。目录部分是最后一个/之前的部分,如果没有/则返回“./”

返回:返回文件名序列的目录部分

案例:

$(dir usr/src/linux-2.4/Makefile hello.c)

返回值为: usr/src/linux-2.4/

II)取文件名函数notdir

格式:$(notdir<name…>)

功能:从文件名序列(一个或多个)中取出文件名部分。文件名部分为最后一个/之后的部分

返回:文件名序列中的文件名

案例:

$(dir usr/src/linux-2.4/Makefile hello.c)

返回值为: Makefile hello.c

III)取后缀名函数suffix

格式:$(suffix <name…>)

功能:从文件名序列name中取出各个文件名的后缀

返回:返回文件名序列name的后缀名序列,如果没有前缀则返回空字符串

案例:$(suffix usr/src/linux – 2.4/Makefile hello.c foo.s)

返回值:.c .s

IV)名称取前缀函数basename

格式:$(basename <names…>)

功能:从文件名序列中取出各个文件名的前缀部分

返回:返回文件名序列的前缀序列,如果文件没有前缀则返回空字符串

案例:$( basename usr/src/linux – 2.4/Makefile hello.c home/hacks)

返回值:usr/src/linux – 2.4/Makefile hello home/hacks

V)加后缀函数addsuffix

格式:$(addsuffix <suffix>,<names…>)

功能:将后缀suffix逐个加到names的单词后面

返回:返回已经加后缀的文件名序列

案例:$(addsuffix .c foo bar hello)

返回值:foo.c bar.c hello.c

VI)加前缀函数addprefix

格式:$(addprefix <prefix>,<names…>)

功能:把前缀prefix加到文件名序列names的前面

返回:返回加入前缀的文件名序列

案例:$( addprefix usr/src/linux – 2.4/kernel/,exit.c time.c)

返回值:usr/src/linux – 2.4/kernel/ exit.c usr/src/linux – 2.4/kernel/ time.c

4)循环函数

foreach循环函数

格式:$(foreach <var> ,<list>,<text>)

功能:将参数list中的单词逐一放到参数var中所指定的变量中,然后再执行text中的表达式。每一次text会返回一个字符串,循环过程中,text的所返回的每个字符串以空格分隔,最后当整个循环结束后,text返回的每个字符串以空格为分隔符组成新的字符串是foreach的返回值。所以var最好是个变量名,<list>可以是个表达式,而text一般会使用var这个参数来依次枚举list的单词。

案例:

names := a b c d

files :=$(foreach n,$(names),$(n).o)

返回值:names中的每个单词将会挨个取出,并赋值给n。$(n).o每次计算出一个值,最后将所有值以空格为分隔符组成新字符串,并赋值给files。

其值为a.o b.o c.o d.o

5)条件判断函数

条件判断函数表

关键字

典型表达形式

含义

ifeq

ifeq(<arg1>,<arg2>)

比较参数arg1和arg2是否相等,相等时表达式为真

ifneq

ifneq(<arg1>,<arg2>)

比较参数arg1和arg2是否相等,不相等时表达式为真

ifdef

ifdef(variable-name)

如果变量variable-name的值为非空,则表达式为真。variable-name可以是一个函数的返回值。ifdef只是测试一个变量是否有值,并不会把变量扩展到当前位置

ifndef

ifndef(variable-name)

如果变量variable-name的值为空,则表达式为真。

I)if函数

格式:

$(if <condition>,<then-part>)

或者:

$(if <condition>,<then-part>,<else-part>)

功能:当condition为真(非空字符串)时,执行then-part,否则执行else-part,最后进行返回

返回:返回then-part 或else-part

  1. makefile的隐式规则

隐式规则,即makefile中约定好的规则,不用写出来的规则。

案例:

foo:foo.o bar.o

       cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

可以看到这里并没有说明如何生成foo.o 和bar.o,此时make会自动推导这两个目标的规则和命令,也就是说完全没有必要写出下面这两条规则

foo.o:foo.c

       cc -c foo.c $(LDFAGS)

bar.o:bar.c

       cc -c bar.c $(LDFAGS)

1)隐式规则中的变量

隐式规则中的变量可分为两种,一种是和命令相关的,一种是和参数相关的。

案例如下:

$(CC) -c $(CFLAGS) $(CPPFALGS)

make的默认编译命令是cc,如果将$(CC)重定义为gcc,把变量$(CFLAGS)重定义为-g,那么隐式规则中的命令会以gcc -c -g $(CPPFALGS)的样子执行。

与命令相关的表

变量

含义

AR

数据库打包程序,默认命令是ar

AS

汇编语言编译程序,默认命令是as

CC

C语言编译程序,默认命令是cc

CXX

C++编译程序,默认命令是g++

CO

从RCS文件中扩展文件程序,默认命令是co

CPP

C程序的预处理器,默认命令是$(CC) -E

FC

Fortran和Ratfor的编译器和预处理程序。默认命令是f77

GET

从SCCS文件中扩展文件的程序,默认命令是get

LEX

Lex方法分析程序,针对C或Ratfor.默认命令是lex

PC

Pascal语言编译程序,默认命令是pc

YACC

yacc文法分析器,针对于C程序,默认命令为yacc

YACCR

yacc文法分析器,针对于Ratfor程序,默认命令为yacc -r

MAKEINFO

转换Texinfo源文件(.texi)到Info文件程序。默认命令为makeinfo

TEX

从Tex源文件创建Tex DVI文件程序。默认命令是tex

WEAVE

传唤web到Tex程序。默认命令是weave

TEXI2DVI

从Texinfo源文件创建Tex DVI文件程序,默认命令为texi2dvi

CWEAVE

转换Web到Tex语言的程序,默认命令是cweave

TANGLE

转换Web到Pascal语言程序,默认命令是tangle

CTANGLE

转换C Web到C。默认命令是ctangle

RM

删除文件,默认命令是rm -f

2)模式规则

I)模式规则基础

可以使用模式规则定义隐式规则,模式规则与一般规则类似,只是在模式规则中,目标(目标文件)的定义需要有%符。%定义对文件名的匹配,表示任意长度的字符串。在依赖目标中同样可以使用%,只是依赖目标的取值,取决于其目标文件。

在模式规则中,至少在规则中的目标文件中要包含%,并且目标文件中%的值将决定,依赖文件中%的值。

案例:

%.o:%.c;<command>

其含义是,指出了所有的.c文件都将生成对应的.o文件的规则。如果要生成的文件是a.o b.o,那么%.c就是a.c b.c

II)模式规则中的自动化变量

模式规则中的自动化变量,是为了通过同一个命令完成从不同依赖文件生成相应目标。这种自动化变量会把模式中所有定义的文件自动取出,直至所有符合模式的文件都取完了。且这种自动化变量只应出现在规则的命令中 。

Makefile中共有21个自动化变量,在表1中,后面的14个变量只是在前面7个变量后分别加上字母D和F。D:Directory  F:file

另外,对于变量$<,为了避免不必要的麻烦,可以写成$(<),以免造成歧义

表1 Makefile中的自动化变量

变量

含义

$@

表示规则中的所有目标文件集合。在模式中如果有多个目标,$@就是匹配于目标中模式定义的集合

$%

仅当目标文件是库函数文件时,表示规则中的目标成员名,如果目标不是库函数文件(unix是.a windows是.lib),其值为空

$<

依赖目标中的第一个目标名字,如果依赖目标是以模式(%)定义的,则$<是符合模式的一系列文件集

$?

所有比目标文件新的依赖文件集合,以空格分隔

$^

所有依赖目标的集合,以空格分隔。如果依赖目标中有多个重复的,则自动去除重复的依赖文件,只保留一份

$+

同$^但是不去除重复部分

$*

目标模式中%及其之前的部分

$(@D)

%@目录部分(不以斜杠作为结尾),如果$@中没有包含斜杠,其值为 .

$(@F)

$@的文件部分,相当于函数$(notdir $@)

$(*D)

同$(@D),取出文件目录部分

$(*F)

同$(@F),取出文件部分,但不带有后缀

$(%D)

函数包含文件成员的目录部分

$(%F)

函数包含文件成员的文件名部分

$(<D)

依赖目标中的第一个目标的目录部分

$(<F)

依赖目标的第一个目标的文件名部分

$(^D)

所有依赖目标的目录部分(无重复)

$(^F)

所有依赖目标的文件名部分(无重复)

$(+D)

所有依赖目标的目录部分(可以有重复)

$(+F)

所有依赖目标的文件名部分(可以有重复)

$(?D)

所有被更新文件的目录部分

$(?F)

所有被更新的文件名部分

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值