makefile 学习笔记(一)

edit : main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o /
           insert.o search.o files.o utils.o

objects = main.o kbd.o command.o display.o
insert.osearch.o files.o utils.o
edit : ( o b j e c t s ) / (objects) / (objects)/表示毒地变量的引用
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么
whatever.c,就会是shater.o的依赖文件。并且cc -c whatever 也会被推导出来。
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o

edit : $(objects)
cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
rm edit $(objects)
这种方法,也就是make的“隐晦规则”。上面文件内容,“.PHONY”表示,clean是个未目标文件。

这种风格,让我们的makefile变得很简单,单我们的文件依赖关系就显得有点凌乱了。
这种风格不是太好,一是问价你的依赖关系看不清楚,二是如果文件一多,要加入几个新的.o文件,那就理不清楚了。
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o

edit : $(objects)
cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
rm edit $(objects)

Makefile里主要包含五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

1、显式规则。显式规则说明了,如何生成一个或多的的目标文件。
这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。

2、隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地
简略地书写Makefile,这是由make所支持的。

3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,
当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;
另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。

5、 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。
如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。

最后,还值得一提的是,在Makefile中的命令,必须要以[Tab]键开始。 没有看懂

在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和可以用一个或多个空格隔开。
include foo.make *.mk $(bar)
include foo.make a.mk b.mk c.mk e.mk f.mk

1.如果make执行时,有“-I”或“–include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。
2.如果目录/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。

-include
其表示,无论include过程中出现什么错误,都不要报错继续执行。

规则包含两个部分,一个是依赖关系,一个是生成目标的方法。

而只有一个命令“cc -c -g foo.c”(以Tab键开头)。这个规则告诉我们两件事:

一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。

make支持三各通配符:“*”,“?”和“[…]”。这是和Unix的B-Shell是相同的。

“~”
波浪号(“”)字符在文件名中也有比较特殊的用途。如果是“/test”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。

文件搜索

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,
你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。

Makefile文件中的特殊变量“VPATH”就是完成这个功能的,
如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:…/headers

另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,
这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。
它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。

vapth使用方法中的< pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。< pattern>指定了要搜索的文件集,而< directories>则指定了的文件集的搜索的目录。例如:

vpath %.h …/headers 该语句表示,要求make在“…/headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)

最早先的一个例子中,我们提到过一个“clean”的目标,这是一个“伪目标”,
“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

个目标的生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自动化变量“$@”(关于自动化变量,将在后面讲述),这个变量表示着目前规则中所有的目标的集合,这样说可能很抽象,还是看一个例子吧。

bigoutput littleoutput : text.g

       generate text.g -$(subst output,,$@) > $@

上述规则等价于:

bigoutput : text.g

       generate text.g -big > bigoutput

littleoutput : text.g

       generate text.g -little > littleoutput

其中,- ( s u b s t o u t p u t , , (subst output,, (substoutput,,@)中的“ ” 表 示 执 行 一 个 M a k e f i l e 的 函 数 , 函 数 名 为 s u b s t , 后 面 的 为 参 数 。 关 于 函 数 , 将 在 后 面 讲 述 。 这 里 的 这 个 函 数 是 截 取 字 符 串 的 意 思 , “ ”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。 这里的这个函数是截取字符串的意思,“ Makefilesubst@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。

<targets…>: : <prereq-patterns …>

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

如果我们的定义成“%.o”,意思是我们的集合中都是以“.o”结尾的
如果我们的定义成“%.c”,意思是对所形成的目标集进行二次定义
其计算方法是,取模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c

       $(CC) -c $(CFLAGS) $< -o $@

指明了我们的目标从 o b j e c t 中 获 取 , “ 而 命 令 中 的 “ object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”, 而命令中的“ object<”和“ @ ” 则 是 自 动 化 变 量 , “ @”则是自动化变量,“ @<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也褪恰癴oo.o bar.o”)。

files = foo.elc bar.o lose.o

( f i l t e r (filter %.o, (filter(files)): %.o: %.c

       $(CC) -c $(CFLAGS) $< -o $@

( f i l t e r (filter %.elc, (filter(files)): %.elc: %.el

       emacs -f batch-byte-compile $<

自动生成依赖性

main.o : main.c defs.h
大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。
cc -M main.c
其输出是:
main.o : main.c defs.h

如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。

这里,我们给出了一个模式规则来产生[.d]文件:
%.d: %.c

       @set -e; rm -f $@; \                  //这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm-f $@”的意思是删除所有的目标,也就是[.d]文件,

        $(CC) -M $(CPPFLAGS) $< > $@.    //第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“

; \

        sed 's,

.o[ :]*,\1.o $@ : ,g’ < $@.

$@; \

        rm -f $@.

我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过)

sources = foo.c bar.c
include ( s o u r c e s : . c = . d ) 上 述 语 句 中 的 “ (sources:.c=.d) 上述语句中的“ (sources:.c=.d)(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个“替换”的内容,
在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标

通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,

要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。
比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。
示例一:

   exec:

           cd /home/hchen

           pwd

示例二:

   exec:

           cd /home/hchen; pwd

当我们执行“make exec”时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,

而第二个例子中,cd就起作用了,pwd会打印出“/home/hchen”。

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。
如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。

为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。

clean:

       -rm -f *.o

subsystem:

       cd subdir && $(MAKE)

subsystem:

       $(MAKE) -C subdir

定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。
这两个例子的意思都是先进入“subdir”目录,然后执行make命令。

  示例一:

    export variable = value

其等价于:

    variable = value

    export variable



   其等价于:

    export variable := value

    其等价于:



   variable := value

   export variable

示例二:

   export variable += value



   其等价于:



   variable += value

   export variable

subsystem:

       cd subdir && $(MAKE) MAKEFLAGS=

如果你定义了环境变量MAKEFLAGS,那么你得确信其中的选项是大家都会用到的,如果其中有“-t”,“-n”,和“-q”参数,那么将会有让你意想不到的结果,或许会让你异常地恐慌。

一、变量的基础
有一些变量是很奇怪字串,如“ < ” 、 “ <”、“ <@”等,这些是自动化变量,我会在后面介绍。

objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)

$(objects) : defs.h

变量会在使用它的地方精确地展开,就像C/C++中的宏一样,例如:

foo = c
prog.o : prog.$(foo)
( f o o ) (foo) (foo)(foo) - ( f o o ) p r o g . (foo) prog. (foo)prog.(foo)

展开后得到:

prog.o : prog.c
cc -c prog.c

变量在声明时需要给予初值,而在使用时,需要给在变量名前 加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。

二、变量中的变量

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
echo $(foo)

CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar

当“CFLAGS”在命令中被展开时,会是“-Ifoo -Ibar -O”。但这种形式也有不好的地方
,那就是递归定义,如:

CFLAGS = $(CFLAGS) -O
或:

A = $(B)
B = $(A)

为了避免上面的这种方法,我们可以使用make中的另一种用变量来定义变量的方法。
这种方法使用的是“:=”操作符,如:

x := foo
y := $(x) bar
x := later

其等价于:

y := foo bar
x := later

y := $(x) bar
x := foo
那么,y的值是“bar”,而不是“foo bar”。

ifeq (0,${MAKELEVEL})
cur-dir := $(shell pwd)
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := M A K E h o s t − t y p e = {MAKE} host-type= MAKEhosttype={host-type} whoami=${whoami}
endif

nullstring :=
space := $(nullstring) # end of the line

nullstring 是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。

dir := /foo/bar # directory to put the frobs in
dir这个变量的值是“/foo/bar”,后面还跟了4个空格,如果我们这样使用这样变量来指定别的目录——“$(dir)/file”那么就完蛋了。

FOO ?= bar
其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做,其等价于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif

$(var:a=b)
${var:a=b}
把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。

foo := a.o b.o c.o
bar := ( f o o : 这 依 赖 于 被 替 换 字 串 中 的 有 相 同 的 模 式 , 模 式 中 必 须 包 含 一 个 “ (foo:%.o=%.c) 这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子同样让 (foo:(bar)变量的值为“a.c b.c c.c”。

x = y
y = z
a := ( ( ((x))

在这个例子中, ( x ) 的 值 是 “ y ” , 所 以 (x)的值是“y”,所以 (x)y( ( x ) ) 就 是 (x))就是 (x))(y),于是 ( a ) 的 值 就 是 “ z ” 。 ( 注 意 , 是 “ x = y ” , 而 不 是 “ x = (a)的值就是“z”。(注意,是“x=y”,而不是“x= (a)zx=yx=(y)”)

a := ( ( ((x))
:= $(y)
:= z

x = y
y = z
z = u
a := ( ( (($(x)))

这里的$(a)的值是“u”

x = $(y)
y = z
z = Hello
a := ( ( ((x))

a := ( ( ((x))
:= $(z)
:= Hello

x = variable1
variable2 := Hello
y = ( s u b s t 1 , 2 , (subst 1,2, (subst1,2,(x))
z = y
a := ( ( (($(z)))

最终, ( a ) 的 值 就 是 (a)的值就是 (a)(variable2)的值—— “Hello”。

a := ( ( (($(z)))
:= ( ( ((subst 1,2,$(x)))
:= ( ( ((subst 1,2,variable1))
subst函数把“variable1”中的所有“1”字串替换成“2”字串
:= ( ( ((subst 1,2,variable1))
:= $(variable2)
:= Hello

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值