From aadc6931a379a36ce86aa7e78f05c385bc87ff0d Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 7 Mar 2014 14:00:09 +0800 Subject: [PATCH] bug fixed --- source/archives.rst | 16 ++-- source/conf.py | 3 + source/functions.rst | 159 +++++++++++++++++++++++--------------- source/implicit_rules.rst | 105 ++++++++++++------------- source/introduction.rst | 4 +- source/invoke.rst | 9 ++- source/postscript.rst | 8 +- source/recipes.rst | 6 +- source/rules.rst | 38 ++++----- 9 files changed, 195 insertions(+), 153 deletions(-) diff --git a/source/archives.rst b/source/archives.rst index 4738217..272e65e 100644 --- a/source/archives.rst +++ b/source/archives.rst @@ -1,16 +1,17 @@ 使用make更新函数库文件 ====================== -函数库文件也就是对Object文件(程序编译的中间文件)的打包文件。在Unix下,一般是由命令"ar"来完成打包工作。 +函数库文件也就是对Object文件(程序编译的中间文件)的打包文件。在Unix下,一般是由命令“ar”来完成打包工作。 函数库文件的成员 + ---------------- 一个函数库文件由多个文件组成。你可以以如下格式指定函数库文件及其组成:: archive(member) -这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了"ar"命令来服务的。如: +这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了“ar”命令来服务的。如: .. code-block:: makefile @@ -38,18 +39,18 @@ 函数库成员的隐含规则 -------------------- -当make搜索一个目标的隐含规则时,一个特殊的特性是,如果这个目标是"a(m)"形式的,其会把目标变成"(m)"。于是,如果我们的成员是 "%.o"的模式定义,并且如果我们使用"make foo.a(bar.o)"的形式调用Makefile时,隐含规则会去找"bar.o"的规则,如果没有定义bar.o的规则,那么内建隐含规则生效,make会去找bar.c文件来生成bar.o,如果找得到的话,make执行的命令大致如下:: +当make搜索一个目标的隐含规则时,一个特殊的特性是,如果这个目标是“a(m)”形式的,其会把目标变成“(m)”。于是,如果我们的成员是“%.o”的模式定义,并且如果我们使用“make foo.a(bar.o)”的形式调用Makefile时,隐含规则会去找“bar.o”的规则,如果没有定义bar.o的规则,那么内建隐含规则生效,make会去找bar.c文件来生成bar.o,如果找得到的话,make执行的命令大致如下:: cc -c bar.c -o bar.o ar r foo.a bar.o rm -f bar.o -还有一个变量要注意的是"$%",这是专属函数库文件的自动化变量,有关其说明请参见"自动化变量"一节。 +还有一个变量要注意的是“$%”,这是专属函数库文件的自动化变量,有关其说明请参见“自动化变量”一节。 函数库文件的后缀规则 -------------------- -你可以使用"后缀规则"和"隐含规则"来生成函数库打包文件,如: +你可以使用“后缀规则”和“隐含规则”来生成函数库打包文件,如: .. code-block:: makefile @@ -67,9 +68,10 @@ $(AR) r $@ $*.o $(RM) $*.o + 注意事项 -------- -在进行函数库打包文件生成时,请小心使用make的并行机制("-j"参数)。如果多个ar命令在同一时间运行在同一个函数库打包文件上,就很有可以损坏这个函数库文件。所以,在make未来的版本中,应该提供一种机制来避免并行操作发生在函数打包文件上。 +在进行函数库打包文件生成时,请小心使用make的并行机制(“-j”参数)。如果多个ar命令在同一时间运行在同一个函数库打包文件上,就很有可以损坏这个函数库文件。所以,在make未来的版本中,应该提供一种机制来避免并行操作发生在函数打包文件上。 -但就目前而言,你还是应该不要尽量不要使用"-j"参数。 +但就目前而言,你还是应该不要尽量不要使用“-j”参数。 diff --git a/source/conf.py b/source/conf.py index bff09fa..5a9c837 100644 --- a/source/conf.py +++ b/source/conf.py @@ -197,7 +197,10 @@ latex_elements = { \setCJKmonofont{SimHei} \XeTeXlinebreaklocale "zh" \XeTeXlinebreakskip = 0pt plus 1pt +\parindent 2em \setcounter{tocdepth}{3} +\renewcommand\familydefault{\ttdefault} +\renewcommand\CJKfamilydefault{\CJKrmdefault} ''', 'releasename' : 'By SeisMan@GitHub', 'release' : '', diff --git a/source/functions.rst b/source/functions.rst index 10ac09b..02ef06e 100644 --- a/source/functions.rst +++ b/source/functions.rst @@ -35,6 +35,9 @@ 字符串处理函数 -------------- +subst +~~~~~ + .. code-block:: makefile $(subst ,,) @@ -43,39 +46,42 @@ - 功能:把字串中的字符串替换成。 - 返回:函数返回被替换过后的字符串。 -**示例** : +- 示例: -.. code-block:: makefile + .. code-block:: makefile - $(subst ee,EE,feet on the street) + $(subst ee,EE,feet on the street) 把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。 +patsubst +~~~~~~~~ + .. code-block:: makefile $(patsubst ,,) - 名称:模式字符串替换函数——patsubst。 -- 功能:查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式< pattern>,如果匹配的话,则以替换。这里,可以包括通配符 “%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个 “%”将是中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符) +- 功能:查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式< pattern>,如果匹配的话,则以替换。这里,可以包括通配符 “%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个 “%”将是中的那个“%”所代表的字串。(可以用“\\”来转义,以“\\%”来表示真实含义的“%”字符) - 返回:函数返回被替换过后的字符串。 +- 示例: -**示例** : + .. code-block:: makefile -.. code-block:: makefile - - $(patsubst %.c,%.o,x.c.c bar.c) + $(patsubst %.c,%.o,x.c.c bar.c) 把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o” -**备注** : +- 备注:这和我们前面“变量章节”说过的相关知识有点相似。如“$(var:=;)”相当于“$(patsubst ,,$(var))”,而“$(var: =)”则相当于“$(patsubst %,%,$(var))”。 -这和我们前面“变量章节”说过的相关知识有点相似。如“$(var:=;)”相当于“$(patsubst ,,$(var))”,而“$(var: =)”则相当于“$(patsubst %,%,$(var))”。 - -例如有:: + 例如有:: objects = foo.o bar.o baz.o, -那么,“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一样的。 + 那么,“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一样的。 + +strip +~~~~~ .. code-block:: makefile @@ -84,32 +90,36 @@ - 名称:去空格函数——strip。 - 功能:去掉;字串中开头和结尾的空字符。 - 返回:返回被去掉空格的字符串值。 +- 示例: -**示例** : + .. code-block:: makefile + + $(strip a b c ) + + 把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。 + +findstring +~~~~~~~~~~ .. code-block:: makefile - $(strip a b c ) - -把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。 - -.. code-block:: makefile - - $(findstring ,) + $(findstring ,) - 名称:查找字符串函数——findstring。 - 功能:在字串中查找字串。 - 返回:如果找到,那么返回,否则返回空字符串。 +- 示例: -**示例** : + .. code-block:: makefile -.. code-block:: makefile - - $(findstring a,a b c) - $(findstring a,b c) + $(findstring a,a b c) + $(findstring a,b c) 第一个函数返回“a”字符串,第二个返回“”字符串(空字符串) +filter +~~~~~~ + .. code-block:: makefile $(filter ,) @@ -117,16 +127,18 @@ - 名称:过滤函数——filter。 - 功能:以模式过滤字符串中的单词,保留符合模式的单词。可以有多个模式。 - 返回:返回符合模式;的字串。 +- 示例: -**示例** : + .. code-block:: makefile -.. code-block:: makefile + sources := foo.c bar.c baz.s ugh.h + foo: $(sources) + cc $(filter %.c %.s,$(sources)) -o foo - sources := foo.c bar.c baz.s ugh.h - foo: $(sources) - cc $(filter %.c %.s,$(sources)) -o foo + $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。 -$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。 +filter-out +~~~~~~~~~~ .. code-block:: makefile @@ -135,16 +147,18 @@ $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。 - 名称:反过滤函数——filter-out。 - 功能:以模式过滤字符串中的单词,去除符合模式的单词。可以有多个模式。 - 返回:返回不符合模式的字串。 +- 示例: -**示例** : + .. code-block:: makefile -.. code-block:: makefile - - objects=main1.o foo.o main2.o bar.o - mains=main1.o main2.o + objects=main1.o foo.o main2.o bar.o + mains=main1.o main2.o -$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - + $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 + +sort +~~~~ + .. code-block:: makefile $(sort ) @@ -152,9 +166,11 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:排序函数——sort。 - 功能:给字符串中的单词排序(升序)。 - 返回:返回排序后的字符串。 +- 示例:$(sort foo bar lose)返回“bar foo lose” 。 +- 备注:sort函数会去掉中相同的单词。 -**示例** :$(sort foo bar lose)返回“bar foo lose” 。 -**备注** :sort函数会去掉中相同的单词。 +word +~~~~ .. code-block:: makefile @@ -163,8 +179,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:取单词函数——word。 - 功能:取字符串中第个单词。(从一开始) - 返回:返回字符串中第个单词。如果中的单词数要大,那么返回空字符串。 +- 示例:$(word 2, foo bar baz)返回值是“bar”。 -**示例** :$(word 2, foo bar baz)返回值是“bar”。 +wordlist +~~~~~~~~ .. code-block:: makefile @@ -173,8 +191,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:取单词串函数——wordlist。 - 功能:从字符串中取从开始到的单词串。是一个数字。 - 返回:返回字符串中从的单词字串。如果中的单词数要大,那么返回空字符串。如果大于的单词数,那么返回从开始,到结束的单词串。 +- 示例:$(wordlist 2, 3, foo bar baz)返回值是“bar baz”。 -**示例** : $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。 +words +~~~~~ .. code-block:: makefile @@ -183,9 +203,11 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:单词个数统计函数——words。 - 功能:统计中字符串中的单词个数。 - 返回:返回中的单词数。 +- 示例:$(words, foo bar baz)返回值是“3”。 +- 备注:如果我们要取中最后的一个单词,我们可以这样:$(word $(words ),)。 -**示例** :$(words, foo bar baz)返回值是“3”。 -**备注** :如果我们要取中最后的一个单词,我们可以这样:$(word $(words ),)。 +firstword +~~~~~~~~~ .. code-block:: makefile @@ -194,9 +216,8 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:首单词函数——firstword。 - 功能:取字符串中的第一个单词。 - 返回:返回字符串的第一个单词。 - -**示例** :$(firstword foo bar)返回值是“foo”。 -**备注** :这个函数可以用word函数来实现:$(word 1,)。 +- 示例:$(firstword foo bar)返回值是“foo”。 +- 备注:这个函数可以用word函数来实现:$(word 1,)。 以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里,举一个现实中应用的例子。我们知道,make使用“VPATH”变量来指定“依赖文件”的搜索路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数CFLAGS,如: @@ -211,6 +232,9 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或是一系列的文件名来对待。 +dir +~~~ + .. code-block:: makefile $(dir ) @@ -218,8 +242,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:取目录函数——dir。 - 功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。 - 返回:返回文件名序列的目录部分。 +- 示例: $(dir src/foo.c hacks)返回值是“src/ ./”。 -**示例** : $(dir src/foo.c hacks)返回值是“src/ ./”。 +notdir +~~~~~~ .. code-block:: makefile @@ -228,9 +254,11 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:取文件函数——notdir。 - 功能:从文件名序列中取出非目录部分。非目录部分是指最後一个反斜杠(“/”)之后的部分。 - 返回:返回文件名序列的非目录部分。 - -**示例** : $(notdir src/foo.c hacks)返回值是“foo.c hacks”。 +- 示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。 +suffix +~~~~~~ + .. code-block:: makefile $(suffix ) @@ -238,8 +266,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:取後缀函数——suffix。 - 功能:从文件名序列中取出各个文件名的后缀。 - 返回:返回文件名序列的后缀序列,如果文件没有后缀,则返回空字串。 +- 示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。 -**示例** :$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。 +basename +~~~~~~~~ .. code-block:: makefile @@ -248,8 +278,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:取前缀函数——basename。 - 功能:从文件名序列中取出各个文件名的前缀部分。 - 返回:返回文件名序列的前缀序列,如果文件没有前缀,则返回空字串。 +- 示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。 -**示例** :$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。 +addsuffix +~~~~~~~~~ .. code-block:: makefile @@ -258,8 +290,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:加后缀函数——addsuffix。 - 功能:把后缀加到中的每个单词后面。 - 返回:返回加过后缀的文件名序列。 +- 示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。 -**示例** :$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。 +addprefix +~~~~~~~~~ .. code-block:: makefile @@ -268,8 +302,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:加前缀函数——addprefix。 - 功能:把前缀加到中的每个单词后面。 - 返回:返回加过前缀的文件名序列。 +- 示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。 -**示例** :$(addprefix src/,foo bar)返回值是“src/foo src/bar”。 +join +~~~~ .. code-block:: makefile @@ -278,19 +314,18 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 - 名称:连接函数——join。 - 功能:把中的单词对应地加到的单词后面。如果的单词个数要比的多,那么,中的多出来的单词将保持原样。如果的单词个数要比多,那么,多出来的单词将被复制到中。 - 返回:返回连接过后的字符串。 - -**示例** :$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。 +- 示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。 foreach 函数 ------------ -foreach函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile中的foreach函数几乎是仿照于Unix标准 Shell(/bin/sh)中的for语句,或是C-Shell(/bin/csh)中的foreach语句而构建的。它的语法是: +foreach函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile中的foreach函数几乎是仿照于Unix标准Shell(/bin/sh)中的for语句,或是C-Shell(/bin/csh)中的foreach语句而构建的。它的语法是: .. code-block:: makefile $(foreach ,,) -这个函数的意思是,把参数中的单词逐一取出放到参数所指定的变量中,然后再执行< text>所包含的表达式。每一次会返回一个字符串,循环过程中,的所返回的每个字符串会以空格分隔,最后当整个循环结束时,所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 +这个函数的意思是,把参数中的单词逐一取出放到参数所指定的变量中,然后再执行所包含的表达式。每一次会返回一个字符串,循环过程中,的所返回的每个字符串会以空格分隔,最后当整个循环结束时,所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 所以,最好是一个变量名,可以是一个表达式,而中一般会使用这个参数来依次枚举中的单词。举个例子: @@ -319,11 +354,11 @@ if函数很像GNU的make所支持的条件语句——ifeq(参见前面所述 $(if ,,) -可见,if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是三个。参数是 if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,会被计算,否则会被计算。 +可见,if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是三个。参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,会被计算,否则会被计算。 -而if函数的返回值是,如果为真(非空字符串),那个会是整个函数的返回值,如果为假(空字符串),那么会是整个函数的返回值,此时如果< else-part>没有被定义,那么,整个函数返回空字串。 +而if函数的返回值是,如果为真(非空字符串),那个会是整个函数的返回值,如果为假(空字符串),那么会是整个函数的返回值,此时如果没有被定义,那么,整个函数返回空字串。 -所以,只会有一个被计算。'' +所以,只会有一个被计算。 call函数 -------- diff --git a/source/implicit_rules.rst b/source/implicit_rules.rst index 741462d..e4c5d4a 100644 --- a/source/implicit_rules.rst +++ b/source/implicit_rules.rst @@ -54,19 +54,19 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 #. 编译C程序的隐含规则。 - “;.o”的目标的依赖目标会自动推导为“;.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)” + “.o”的目标的依赖目标会自动推导为“.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)” #. 编译C++程序的隐含规则。 - “;.o”的目标的依赖目标会自动推导为“;.cc”或是“;.C”,并且其生成命令是 “$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”) + “.o”的目标的依赖目标会自动推导为“.cc”或是“.C”,并且其生成命令是 “$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”) #. 编译Pascal程序的隐含规则。 - “;.o”的目标的依赖目标会自动推导为“;.p”,并且其生成命令是“$(PC) –c $(PFLAGS)”。 + “.o”的目标的依赖目标会自动推导为“.p”,并且其生成命令是“$(PC) –c $(PFLAGS)”。 #. 编译Fortran/Ratfor程序的隐含规则。 - “;.o”的目标的依赖目标会自动推导为“;.r”或“;.F”或“;.f”,并且其生成命令是: + “.o”的目标的依赖目标会自动推导为“.r”或“.F”或“.f”,并且其生成命令是: - “.f” “$(FC) –c $(FFLAGS)” - “.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)” @@ -74,25 +74,26 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 #. 预处理Fortran/Ratfor程序的隐含规则。 - “;.f”的目标的依赖目标会自动推导为“;.r”或“;.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是: + “.f”的目标的依赖目标会自动推导为“.r”或“.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是: + - “.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)” - “.r” “$(FC) –F $(FFLAGS) $(RFLAGS)” #. 编译Modula-2程序的隐含规则。 - “;.sym”的目标的依赖目标会自动推导为“;.def”,并且其生成命令是:“$(M2C) $ (M2FLAGS) $(DEFFLAGS)”。“;” 的目标的依赖目标会自动推导为“;.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。 + “.sym”的目标的依赖目标会自动推导为“.def”,并且其生成命令是:“$(M2C) $ (M2FLAGS) $(DEFFLAGS)”。“.o” 的目标的依赖目标会自动推导为“.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。 #. 汇编和汇编预处理的隐含规则。 - “;.o” 的目标的依赖目标会自动推导为“;.s”,默认使用编译品“as”,并且其生成命令是:“$ (AS) $(ASFLAGS)”。“;.s” 的目标的依赖目标会自动推导为“;.S”,默认使用C预编译器 “cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。 + “.o” 的目标的依赖目标会自动推导为“.s”,默认使用编译品“as”,并且其生成命令是:“$ (AS) $(ASFLAGS)”。“.s” 的目标的依赖目标会自动推导为“.S”,默认使用C预编译器 “cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。 #. 链接Object文件的隐含规则。 - “;”目标依赖于“;.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是: “$(CC) $(LDFLAGS) ;.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则:: + “”目标依赖于“.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是: “$(CC) $(LDFLAGS) .o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则:: x : y.o z.o -并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:: + 并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:: cc -c x.c -o x.o cc -c y.c -o y.o @@ -102,23 +103,23 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 rm -f y.o rm -f z.o -如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。 + 如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。 #. Yacc C程序时的隐含规则。 - “;.c”的依赖文件被自动推导为“n.y”(Yacc生成的文件),其生成命令是:“$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料) + “.c”的依赖文件被自动推导为“n.y”(Yacc生成的文件),其生成命令是:“$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料) #. Lex C程序时的隐含规则。 - “;.c”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料) + “.c”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料) #. Lex Ratfor程序时的隐含规则。 - “;.r”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。 + “.r”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。 #. 从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。 - “;.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“;.y”和“;.l”也是同样的规则。 + “.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“.y”和“.l”也是同样的规则。 隐含规则使用的变量 ------------------ @@ -194,24 +195,24 @@ Make会优化一些特殊的隐含规则,而不生成中间文件。如,从 定义模式规则 ------------ -你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有"%"字符。"%"的意思是表示一个或多个任意字符。在依赖目标中同样可以使用"%",只是依赖目标中的"%"的取值,取决于其目标。 +你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有“%”字符。“%“的意思是表示一个或多个任意字符。在依赖目标中同样可以使用“%”,只是依赖目标中的“%“的取值,取决于其目标。 -有一点需要注意的是,"%"的展开发生在变量和函数的展开之后,变量和函数的展开发生在make载入Makefile时,而模式规则中的"%"则发生在运行时。 +有一点需要注意的是,“%“的展开发生在变量和函数的展开之后,变量和函数的展开发生在make载入Makefile时,而模式规则中的“%“则发生在运行时。 模式规则介绍 ~~~~~~~~~~~~ -模式规则中,至少在规则的目标定义中要包含"%",否则,就是一般的规则。目标中的"%"定义表示对文件名的匹配,"%"表示长度任意的非空字符串。例如:"%.c"表示以".c"结尾的文件名(文件名的长度至少为3),而"s.%.c"则表示以"s."开头,".c"结尾的文件名(文件名的长度至少为5)。 +模式规则中,至少在规则的目标定义中要包含“%“,否则,就是一般的规则。目标中的“%“定义表示对文件名的匹配,“%“表示长度任意的非空字符串。例如:“%.c“表示以“.c“结尾的文件名(文件名的长度至少为3),而“s.%.c“则表示以“s.“开头,“.c“结尾的文件名(文件名的长度至少为5)。 -如果"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值,也就是说,目标中的模式的"%"决定了依赖目标中"%"的样子。例如有一个模式规则如下: +如果“%“定义在目标中,那么,目标中的“%“的值决定了依赖目标中的“%“的值,也就是说,目标中的模式的“%“决定了依赖目标中“%“的样子。例如有一个模式规则如下: .. code-block:: makefile %.o : %.c ; ; -其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是"a.o b.o",那么"%c"就是"a.c b.c"。 +其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是“a.o b.o“,那么“%c“就是“a.c b.c“。 -一旦依赖目标中的"%"模式被确定,那么,make会被要求去匹配当前目录下所有的文件名,一旦找到,make就会规则下的命令,所以,在模式规则中,目标可能会是多个的,如果有模式匹配出多个目标,make就会产生所有的模式目标,此时,make关心的是依赖的文件名和生成目标的命令这两件事。 +一旦依赖目标中的“%“模式被确定,那么,make会被要求去匹配当前目录下所有的文件名,一旦找到,make就会规则下的命令,所以,在模式规则中,目标可能会是多个的,如果有模式匹配出多个目标,make就会产生所有的模式目标,此时,make关心的是依赖的文件名和生成目标的命令这两件事。 模式规则示例 ~~~~~~~~~~~~ @@ -223,7 +224,7 @@ Make会优化一些特殊的隐含规则,而不生成中间文件。如,从 %.o : %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ -其中,"$@"表示所有的目标的挨个值,"$<"表示了所有依赖目标的挨个值。这些奇怪的变量我们叫"自动化变量",后面会详细讲述。 +其中,“$@“表示所有的目标的挨个值,“$<“表示了所有依赖目标的挨个值。这些奇怪的变量我们叫“自动化变量“,后面会详细讲述。 下面的这个例子中有两个目标是模式的: @@ -232,7 +233,7 @@ Make会优化一些特殊的隐含规则,而不生成中间文件。如,从 %.tab.c %.tab.h: %.y bison -d $< -这条规则告诉make把所有的[.y]文件都以"bison -d ;.y"执行,然后生成";. tab.c"和";.tab.h"文件。(其中,";"表示一个任意字符串)。如果我们的执行程序"foo"依赖于文件"parse.tab.o"和"scan.o",并且文件"scan.o"依赖于文件"parse.tab.h",如果"parse.y"文件被更新了,那么根据上述的规则,"bison -d parse.y"就会被执行一次,于是,"parse.tab.o"和"scan.o"的依赖文件就齐了。(假设,"parse.tab.o"由"parse.tab.c"生成,和"scan.o"由"scan.c"生成,而"foo"由 "parse.tab.o"和"scan.o"链接生成,而且foo和其[.o]文件的依赖关系也写好,那么,所有的目标都会得到满足) +这条规则告诉make把所有的[.y]文件都以“bison -d .y“执行,然后生成“. tab.c“和“.tab.h“文件。(其中,““表示一个任意字符串)。如果我们的执行程序“foo“依赖于文件“parse.tab.o“和“scan.o“,并且文件“scan.o“依赖于文件“parse.tab.h“,如果“parse.y“文件被更新了,那么根据上述的规则,“bison -d parse.y“就会被执行一次,于是,“parse.tab.o“和“scan.o“的依赖文件就齐了。(假设,“parse.tab.o“由“parse.tab.c“生成,和“scan.o“由“scan.c“生成,而“foo“由 “parse.tab.o“和“scan.o“链接生成,而且foo和其[.o]文件的依赖关系也写好,那么,所有的目标都会得到满足) 自动化变量 ~~~~~~~~~~ @@ -243,36 +244,36 @@ Make会优化一些特殊的隐含规则,而不生成中间文件。如,从 下面是所有的自动化变量及其说明: -- $@: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。 -- $%: 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是 "bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。 -- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。 +- $@: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,“$@“就是匹配于目标中模式定义的集合。 +- $%: 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)“,那么,“$%“就是 “bar.o“,“$@“就是“foo.a“。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。 +- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即“%“)定义的,那么“$<“将是符合模式的一系列的文件集。注意,其是一个一个取出来的。 - $?: 所有比目标新的依赖目标的集合。以空格分隔。 - $^:所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。 -- $+:这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。 -- $*: 这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以," $*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。 +- $+:这个变量很像“$^“,也是所有依赖目标的集合。只是它不去除重复的依赖目标。 +- $*: 这个变量表示目标模式中“%“及其之前的部分。如果目标是“dir/a.foo.b“,并且目标的模式是“a.%.b“,那么,“$*“的值就是“dir/a.foo“。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*“也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么“$*“就是除了后缀的那一部分。例如:如果目标是“foo.c“,因为“.c“是make所能识别的后缀名,所以,“ $*“的值就是“foo“。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用“$*“,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么“$*“就是空值。 -当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用,例如,假设有一个函数库文件叫"lib",其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是: +当你希望只对更新过的依赖文件进行操作时,“$?“在显式规则中很有用,例如,假设有一个函数库文件叫“lib“,其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是: .. code-block:: makefile lib : foo.o bar.o lose.o win.o ar r lib $? -在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。这是GNU make中老版本的特性,在新版本中,我们使用函数"dir"或"notdir"就可以做到了。"D"的含义就是Directory,就是目录,"F"的含义就是File,就是文件。 +在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上“D“或“F“字样。这是GNU make中老版本的特性,在新版本中,我们使用函数“dir“或“notdir“就可以做到了。“D“的含义就是Directory,就是目录,“F“的含义就是File,就是文件。 -下面是对于上面的七个变量分别加上"D"或是"F"的含义: +下面是对于上面的七个变量分别加上“D“或是“F“的含义: ``$(@D)`` - 表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。 + 表示“$@“的目录部分(不以斜杠作为结尾),如果“$@“值是“dir/foo.o“,那么“$(@D)“就是“dir“,而如果“$@“中没有包含斜杠的话,其值就是“.“(当前目录)。 ``$(@F)`` - 表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)"。 + 表示“$@“的文件部分,如果“$@“值是“dir/foo.o“,那么“$(@F)“就是“foo.o“,“$(@F)“相当于函数“$(notdir $@)“。 ``$(*D)``, ``$(*F)`` - 和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,\ ``$(*D)``\ 返回"dir",而\ ``$(*F)``\ 返回"foo" + 和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,\ ``$(*D)``\ 返回“dir“,而\ ``$(*F)``\ 返回“foo“ ``$(%D)``, ``$(%F)`` -分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不同的目录很有用。 +分别表示了函数包文件成员的目录部分和文件部分。这对于形同“archive(member)“形式的目标中的“member“中包含了不同的目录很有用。 ``$(/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。 如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。如: diff --git a/source/invoke.rst b/source/invoke.rst index c3a75c9..0365b97 100644 --- a/source/invoke.rst +++ b/source/invoke.rst @@ -103,11 +103,12 @@ make的参数 ``-B``, ``--always-make`` 认为所有的目标都需要更新(重编译)。 -``-C`` **, ``--directory``\ = ** +``-C`` **, ``--directory``\ =\ ** 指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make -C ~hchen/test -C prog”等价于“make -C ~hchen/test/prog”。 -``-debug`` [= **] +``-debug``\ [=\ **] 输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是的取值: + - a: 也就是all,输出所有的调试信息。(会非常的多) - b: 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。 - v: 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。 @@ -121,7 +122,7 @@ make的参数 ``-e``, ``--environment-overrides`` 指明环境变量的值覆盖makefile中定义的变量的值。 -``-f``\ = **, ``--file``\ = **, ``--makefile``\ = ** +``-f``\ =\ **, ``--file``\ =\ **, ``--makefile``\ =\ ** 指定需要执行的makefile。 ``-h``, ``--help`` @@ -130,7 +131,7 @@ make的参数 ``-i`` , ``--ignore-errors`` 在执行时忽略所有的错误。 -``-I`` **, ``--include-dir``\ = ** +``-I`` **, ``--include-dir``\ =\ ** 指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。 ``-j`` [**], ``--jobs``\ [=\ **] diff --git a/source/postscript.rst b/source/postscript.rst index e954873..7957eac 100644 --- a/source/postscript.rst +++ b/source/postscript.rst @@ -1,11 +1,11 @@ 后序 ==== -终于到写结束语的时候了,以上基本上就是GNU make的Makefile的所有细节了。其它的产商的make基本上也就是这样的,无论什么样的make,都是以文件的依赖性为基础的,其基本是都是遵循一个标准的。这篇文档中80%的技术细节都适用于任何的make,我猜测"函数"那一章的内容可能不是其它make所支持的,而隐含规则方面,我想不同的make会有不同的实现,我没有精力来查看GNU的make和VC的nmake、BCB的 make,或是别的UNIX下的make有些什么样的差别,一是时间精力不够,二是因为我基本上都是在Unix下使用make,以前在SCO Unix和 IBM的AIX,现在在Linux、Solaris、HP-UX、AIX和Alpha下使用,Linux和Solaris下更多一点。不过,我可以肯定的是,在Unix下的make,无论是哪种平台,几乎都使用了Richard Stallman开发的make和cc/gcc的编译器,而且,基本上都是 GNU的make(公司里所有的UNIX机器上都被装上了GNU的东西,所以,使用GNU的程序也就多了一些)。GNU的东西还是很不错的,特别是使用得深了以后,越来越觉得GNU的软件的强大,也越来越觉得GNU的在操作系统中(主要是Unix,甚至Windows)"杀伤力"。 +终于到写结束语的时候了,以上基本上就是GNU make的Makefile的所有细节了。其它的产商的make基本上也就是这样的,无论什么样的make,都是以文件的依赖性为基础的,其基本是都是遵循一个标准的。这篇文档中80%的技术细节都适用于任何的make,我猜测“函数”那一章的内容可能不是其它make所支持的,而隐含规则方面,我想不同的make会有不同的实现,我没有精力来查看GNU的make和VC的nmake、BCB的 make,或是别的UNIX下的make有些什么样的差别,一是时间精力不够,二是因为我基本上都是在Unix下使用make,以前在SCO Unix和 IBM的AIX,现在在Linux、Solaris、HP-UX、AIX和Alpha下使用,Linux和Solaris下更多一点。不过,我可以肯定的是,在Unix下的make,无论是哪种平台,几乎都使用了Richard Stallman开发的make和cc/gcc的编译器,而且,基本上都是 GNU的make(公司里所有的UNIX机器上都被装上了GNU的东西,所以,使用GNU的程序也就多了一些)。GNU的东西还是很不错的,特别是使用得深了以后,越来越觉得GNU的软件的强大,也越来越觉得GNU的在操作系统中(主要是Unix,甚至Windows)“杀伤力”。 -对于上述所有的make的细节,我们不但可以利用make这个工具来编译我们的程序,还可以利用make来完成其它的工作,因为规则中的命令可以是任何Shell之下的命令,所以,在Unix下,你不一定只是使用程序语言的编译器,你还可以在Makefile中书写其它的命令,如:tar、 awk、mail、sed、cvs、compress、ls、rm、yacc、rpm、ftp……等等,等等,来完成诸如"程序打包"、"程序备份"、" 制作程序安装包"、"提交代码"、"使用程序模板"、"合并文件"等等五花八门的功能,文件操作,文件管理,编程开发设计,或是其它一些异想天开的东西。比如,以前在书写银行交易程序时,由于银行的交易程序基本一样,就见到有人书写了一些交易的通用程序模板,在该模板中把一些网络通讯、数据库操作的、业务操作共性的东西写在一个文件中,在这些文件中用些诸如"@@@N、###N"奇怪字串标注一些位置,然后书写交易时,只需按照一种特定的规则书写特定的处理,最后在make时,使用awk和sed,把模板中的"@@@N、###N"等字串替代成特定的程序,形成C文件,然后再编译。这个动作很像数据库的" 扩展C"语言(即在C语言中用"EXEC SQL"的样子执行SQL语句,在用cc/gcc编译之前,需要使用"扩展C"的翻译程序,如cpre,把其翻译成标准C)。如果你在使用make时有一些更为绝妙的方法,请记得告诉我啊。 +对于上述所有的make的细节,我们不但可以利用make这个工具来编译我们的程序,还可以利用make来完成其它的工作,因为规则中的命令可以是任何Shell之下的命令,所以,在Unix下,你不一定只是使用程序语言的编译器,你还可以在Makefile中书写其它的命令,如:tar、 awk、mail、sed、cvs、compress、ls、rm、yacc、rpm、ftp等等,等等,来完成诸如“程序打包”、“程序备份”、“制作程序安装包”、“提交代码”、“使用程序模板”、“合并文件”等等五花八门的功能,文件操作,文件管理,编程开发设计,或是其它一些异想天开的东西。比如,以前在书写银行交易程序时,由于银行的交易程序基本一样,就见到有人书写了一些交易的通用程序模板,在该模板中把一些网络通讯、数据库操作的、业务操作共性的东西写在一个文件中,在这些文件中用些诸如“@@@N、###N”奇怪字串标注一些位置,然后书写交易时,只需按照一种特定的规则书写特定的处理,最后在make时,使用awk和sed,把模板中的“@@@N、###N”等字串替代成特定的程序,形成C文件,然后再编译。这个动作很像数据库的“扩展C”语言(即在C语言中用“EXEC SQL”的样子执行SQL语句,在用cc/gcc编译之前,需要使用“扩展C”的翻译程序,如cpre,把其翻译成标准C)。如果你在使用make时有一些更为绝妙的方法,请记得告诉我啊。 -回头看看整篇文档,不觉记起几年前刚刚开始在Unix下做开发的时候,有人问我会不会写Makefile时,我两眼发直,根本不知道在说什么。一开始看到别人在vi中写完程序后输入"!make"时,还以为是vi的功能,后来才知道有一个Makefile在作怪,于是上网查啊查,那时又不愿意看英文,发现就根本没有中文的文档介绍Makefile,只得看别人写的Makefile,自己瞎碰瞎搞才积累了一点知识,但在很多地方完全是知其然不知所以然。后来开始从事UNIX下产品软件的开发,看到一个400人年,近200万行代码的大工程,发现要编译这样一个庞然大物,如果没有Makefile,那会是多么恐怖的一样事啊。于是横下心来,狠命地读了一堆英文文档,才觉得对其掌握了。但发现目前网上对Makefile介绍的文章还是少得那么的可怜,所以想写这样一篇文章,共享给大家,希望能对各位有所帮助。 +回头看看整篇文档,不觉记起几年前刚刚开始在Unix下做开发的时候,有人问我会不会写Makefile时,我两眼发直,根本不知道在说什么。一开始看到别人在vi中写完程序后输入“!make”时,还以为是vi的功能,后来才知道有一个Makefile在作怪,于是上网查啊查,那时又不愿意看英文,发现就根本没有中文的文档介绍Makefile,只得看别人写的Makefile,自己瞎碰瞎搞才积累了一点知识,但在很多地方完全是知其然不知所以然。后来开始从事UNIX下产品软件的开发,看到一个400人,近200万行代码的大工程,发现要编译这样一个庞然大物,如果没有Makefile,那会是多么恐怖的一样事啊。于是横下心来,狠命地读了一堆英文文档,才觉得对其掌握了。但发现目前网上对Makefile介绍的文章还是少得那么的可怜,所以想写这样一篇文章,共享给大家,希望能对各位有所帮助。 现在我终于写完了,看了看文件的创建时间,这篇技术文档也写了两个多月了。发现,自己知道是一回事,要写下来,跟别人讲述又是另外一回事,而且,现在越来越没有时间专研技术细节,所以在写作时,发现在阐述一些细节问题时很难做到严谨和精练,而且对先讲什么后讲什么不是很清楚,所以,还是参考了一些国外站点上的资料和题纲,以及一些技术书籍的语言风格,才得以完成。整篇文档的提纲是基于GNU的Makefile技术手册的提纲来书写的,并结合了自己的工作经验,以及自己的学习历程。因为从来没有写过这么长,这么细的文档,所以一定会有很多地方存在表达问题,语言歧义或是错误。因些,我迫切地得等待各位给我指证和建议,以及任何的反馈。 @@ -15,7 +15,7 @@ 最最后,我还想介绍一下make程序的设计开发者。 -首当其冲的是: Richard Stallman +首当其冲的是:Richard Stallman 开源软件的领袖和先驱,从来没有领过一天工资,从来没有使用过Windows操作系统。对于他的事迹和他的软件以及他的思想,我无需说过多的话,相信大家对这个人并不比我陌生,这是他的主页:http://www.stallman.org/ 。这里只贴上一张他的近照: diff --git a/source/recipes.rst b/source/recipes.rst index d5d0d24..30c1b2b 100644 --- a/source/recipes.rst +++ b/source/recipes.rst @@ -8,7 +8,7 @@ 显示命令 -------- -通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如:: +通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来向屏幕显示一些信息。如:: @echo 正在编译XXX模块...... @@ -26,7 +26,7 @@ 当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。如: -示例一: +- 示例一: .. code-block:: makefile @@ -34,7 +34,7 @@ cd /home/hchen pwd -示例二: +- 示例二: .. code-block:: makefile diff --git a/source/rules.rst b/source/rules.rst index cc46c0b..399916d 100644 --- a/source/rules.rst +++ b/source/rules.rst @@ -55,7 +55,7 @@ prerequisites也就是目标所依赖的文件(或依赖目标)。如果其 波浪号(“~”)字符在文件名中也有比较特殊的用途。如果是“~/test”,这就表示当前用户的$HOME目录下的test目录。而 “~hchen/test”则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是 MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量“HOME”而定。 -通配符代替了你一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“\”,如“\*”来表示真实的“*”字符,而不是任意长度的字符串。 +通配符代替了你一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“\*”,那么可以用转义字符“\\”,如“\\\*”来表示真实的“\*”字符,而不是任意长度的字符串。 好吧,还是先来看几个例子吧: @@ -96,25 +96,25 @@ prerequisites也就是目标所依赖的文件(或依赖目标)。如果其 #. 列出一确定文件夹中的所有“.c”文件。 -.. code-block:: makefile + .. code-block:: makefile - objects := $(wildcard *.c) + objects := $(wildcard *.c) -#. 列出(1)中所有文件对应的".o"文件,在(3)中我们可以看到它是由make自动编译出的。 +#. 列出(1)中所有文件对应的“.o”文件,在(3)中我们可以看到它是由make自动编译出的。 -.. code-block:: makefile + .. code-block:: makefile - $(patsubst %.c,%.o,$(wildcard *.c)) + $(patsubst %.c,%.o,$(wildcard *.c)) -#. 由(1)(2)两步,可写出编译并链接所有“.c"和”.o"文件 +#. 由(1)(2)两步,可写出编译并链接所有“.c”和”.o“文件 -.. code-block:: makefile + .. code-block:: makefile - objects := $(patsubst %.c,%.o,$(wildcard *.c)) - foo : $(objects) - cc -o foo $(objects) + objects := $(patsubst %.c,%.o,$(wildcard *.c)) + foo : $(objects) + cc -o foo $(objects) -这种用法由关键字“wildcard”,“patsubst"指出,关于Makefile的关键字,我们将在后面讨论。 +这种用法由关键字“wildcard”,“patsubst”指出,关于Makefile的关键字,我们将在后面讨论。 文件搜寻 -------- @@ -140,7 +140,7 @@ Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果 \ ``vpath``\ 清除所有已被设置好了的文件搜索目录。 -vapth使用方法中的需要包含“%”字符。“%”的意思是匹配零或若干字符,(需引用“%”,使用“\%")例如,“%.h”表示所有以 “.h”结尾的文件。指定了要搜索的文件集,而则指定了< pattern>的文件集的搜索的目录。例如: +vapth使用方法中的需要包含“%”字符。“%”的意思是匹配零或若干字符,(需引用“%”,使用“\\%”)例如,“%.h”表示所有以 “.h”结尾的文件。指定了要搜索的文件集,而则指定了< pattern>的文件集的搜索的目录。例如: .. code-block:: makefile @@ -209,7 +209,7 @@ vapth使用方法中的需要包含“%”字符。“%”的意思是 prog3 : prog3.o sort.o utils.o cc -o prog3 prog3.o sort.o utils.o -我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于默认目标的特性是,总是被执行的,但由于“all”又是一个伪目标,伪目标只是一个标签不会生成文件,所以不会有“all”文件产生。于是,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。(注:这里的显式 “.PHONY : all" 不写的话一般情况也可以正确的执行,这样 make 可通过隐式规则推导出, “all" 是一个伪目标,执行 make 不会生成 ”all" 文件,而执行后面的多个目标。建议:显式写出是一个好习惯。) +我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于默认目标的特性是,总是被执行的,但由于“all”又是一个伪目标,伪目标只是一个标签不会生成文件,所以不会有“all”文件产生。于是,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。(注:这里的显式 “.PHONY : all” 不写的话一般情况也可以正确的执行,这样make可通过隐式规则推导出, “all” 是一个伪目标,执行make不会生成“all”文件,而执行后面的多个目标。建议:显式写出是一个好习惯。) 随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也可成为依赖。看下面的例子: @@ -266,9 +266,9 @@ target-parrtern是指明了targets的模式,也就是的目标集模式。 prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。 -这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的定义成“%. o”,意思是我们的;集合中都是以“.o”结尾的,而如果我们的定义成 “%.c”,意思是对所形成的目标集进行二次定义,其计算方法是,取模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。 +这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的定义成“%.o”,意思是我们的;集合中都是以“.o”结尾的,而如果我们的定义成 “%.c”,意思是对所形成的目标集进行二次定义,其计算方法是,取模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。 -所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“\”进行转义,来标明真实的“%”字符。 +所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“\\”进行转义,来标明真实的“%”字符。 看一个例子: @@ -290,7 +290,7 @@ prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式 bar.o : bar.c $(CC) -c $(CFLAGS) bar.c -o bar.o -试想,如果我们的“%.o”有几百个,那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子: +试想,如果我们的“%.o”有几百个,那么我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子: .. code-block:: makefile @@ -301,7 +301,7 @@ prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式 $(filter %.elc,$(files)): %.elc: %.el emacs -f batch-byte-compile $< -$(filter %.o,$(files))表示调用Makefile的filter函数,过滤“$files”集,只要其中模式为“%.o”的内容。其的它内容,我就不用多说了吧。这个例子展示了Makefile中更大的弹性。 +$(filter %.o,$(files))表示调用Makefile的filter函数,过滤“$files”集,只要其中模式为“%.o”的内容。其它的内容,我就不用多说了吧。这个例子展示了Makefile中更大的弹性。 自动生成依赖性 -------------- @@ -344,7 +344,7 @@ gcc -MM main.c的输出则是:: 那么,编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来,我们的Makefile也要根据这些源文件重新生成,让 Makefile自已依赖于源文件?这个功能并不现实,不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。 -于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或自成[.d]文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依赖关系了。 +于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或生成[.d]文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依赖关系了。 这里,我们给出了一个模式规则来产生[.d]文件: