From 956141d25f41c3cd4199dbb8926b7a864cda0288 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 9 Nov 2015 11:41:27 +0800 Subject: [PATCH] fix rst format --- source/archives.rst | 12 +- source/conditionals.rst | 64 +++++------ source/functions.rst | 236 +++++++++++++++++++------------------- source/implicit_rules.rst | 94 +++++++-------- source/introduction.rst | 88 +++++++------- source/overview.rst | 6 +- 6 files changed, 250 insertions(+), 250 deletions(-) diff --git a/source/archives.rst b/source/archives.rst index 272e65e..149a681 100644 --- a/source/archives.rst +++ b/source/archives.rst @@ -1,7 +1,7 @@ 使用make更新函数库文件 ====================== -函数库文件也就是对Object文件(程序编译的中间文件)的打包文件。在Unix下,一般是由命令“ar”来完成打包工作。 +函数库文件也就是对Object文件(程序编译的中间文件)的打包文件。在Unix下,一般是由命令 ``ar`` 来完成打包工作。 函数库文件的成员 @@ -11,7 +11,7 @@ archive(member) -这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了“ar”命令来服务的。如: +这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了 ``ar`` 命令来服务的。如: .. code-block:: makefile @@ -39,13 +39,13 @@ 函数库成员的隐含规则 -------------------- -当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 -还有一个变量要注意的是“$%”,这是专属函数库文件的自动化变量,有关其说明请参见“自动化变量”一节。 +还有一个变量要注意的是 ``$%`` ,这是专属函数库文件的自动化变量,有关其说明请参见“自动化变量”一节。 函数库文件的后缀规则 -------------------- @@ -72,6 +72,6 @@ 注意事项 -------- -在进行函数库打包文件生成时,请小心使用make的并行机制(“-j”参数)。如果多个ar命令在同一时间运行在同一个函数库打包文件上,就很有可以损坏这个函数库文件。所以,在make未来的版本中,应该提供一种机制来避免并行操作发生在函数打包文件上。 +在进行函数库打包文件生成时,请小心使用make的并行机制( ``-j`` 参数)。如果多个 ``ar`` 命令在同一时间运行在同一个函数库打包文件上,就很有可以损坏这个函数库文件。所以,在make未来的版本中,应该提供一种机制来避免并行操作发生在函数打包文件上。 -但就目前而言,你还是应该不要尽量不要使用“-j”参数。 +但就目前而言,你还是应该不要尽量不要使用 ``-j`` 参数。 diff --git a/source/conditionals.rst b/source/conditionals.rst index eadda83..eb148ac 100644 --- a/source/conditionals.rst +++ b/source/conditionals.rst @@ -6,13 +6,13 @@ 示例 ---- -下面的例子,判断$(CC)变量是否“gcc”,如果是的话,则使用GNU函数编译目标。 +下面的例子,判断 ``$(CC)`` 变量是否 ``gcc`` ,如果是的话,则使用GNU函数编译目标。 .. code-block:: makefile libs_for_gcc = -lgnu normal_libs = - + foo: $(objects) ifeq ($(CC),gcc) $(CC) -o foo $(objects) $(libs_for_gcc) @@ -20,18 +20,18 @@ $(CC) -o foo $(objects) $(normal_libs) endif -可见,在上面示例的这个规则中,目标“foo”可以根据变量“$(CC)”值来选取不同的函数库来编译程序。 +可见,在上面示例的这个规则中,目标 ``foo`` 可以根据变量 ``$(CC)`` 值来选取不同的函数库来编译程序。 -我们可以从上面的示例中看到三个关键字:ifeq、else和endif。ifeq的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else表示条件表达式为假的情况。endif表示一个条件语句的结束,任何一个条件表达式都应该以 endif结束。 +我们可以从上面的示例中看到三个关键字: ``ifeq`` 、 ``else`` 和 ``endif`` 。 ``ifeq`` 的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。 ``else`` 表示条件表达式为假的情况。 ``endif`` 表示一个条件语句的结束,任何一个条件表达式都应该以 ``endif`` 结束。 -当我们的变量$(CC)值是“gcc”时,目标foo的规则是: +当我们的变量 ``$(CC)`` 值是 ``gcc`` 时,目标 ``foo`` 的规则是: .. code-block:: makefile foo: $(objects) $(CC) -o foo $(objects) $(libs_for_gcc) -而当我们的变量$(CC)值不是“gcc”时(比如“cc”),目标foo的规则是: +而当我们的变量 ``$(CC)`` 值不是 ``gcc`` 时(比如 ``cc`` ),目标 ``foo`` 的规则是: .. code-block:: makefile @@ -44,13 +44,13 @@ libs_for_gcc = -lgnu normal_libs = - + ifeq ($(CC),gcc) libs=$(libs_for_gcc) else libs=$(normal_libs) endif - + foo: $(objects) $(CC) -o foo $(objects) $(libs) @@ -75,19 +75,19 @@ endif -其中;表示条件关键字,如“ifeq”。这个关键字有四个。 +其中 ```` 表示条件关键字,如 ``ifeq`` 。这个关键字有四个。 -第一个是我们前面所见过的“ifeq” +第一个是我们前面所见过的 ``ifeq`` .. code-block:: makefile - ifeq (, ) - ifeq '' '' - ifeq "" "" - ifeq "" '' - ifeq '' "" + ifeq (, ) + ifeq '' '' + ifeq "" "" + ifeq "" '' + ifeq '' "" -比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用make的函数。如: +比较参数 ``arg1`` 和 ``arg2`` 的值是否相同。当然,参数中我们还可以使用make的函数。如: .. code-block:: makefile @@ -95,27 +95,27 @@ endif -这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么;就生效。 +这个示例中使用了 ``strip`` 函数,如果这个函数的返回值是空(Empty),那么 ```` 就生效。 -第二个条件关键字是“ifneq”。语法是: +第二个条件关键字是 ``ifneq`` 。语法是: .. code-block:: makefile - ifneq (, ) - ifneq '' '' - ifneq "" "" - ifneq "" '' - ifneq '' "" + ifneq (, ) + ifneq '' '' + ifneq "" "" + ifneq "" '' + ifneq '' "" -其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。 +其比较参数 ``arg1`` 和 ``arg2`` 的值是否相同,如果不同,则为真。和 ``ifeq`` 类似。 -第三个条件关键字是“ifdef”。语法是: +第三个条件关键字是 ``ifdef`` 。语法是: .. code-block:: makefile - ifdef + ifdef -如果变量的值非空,那到表达式为真。否则,表达式为假。当然,同样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子: +如果变量 ```` 的值非空,那到表达式为真。否则,表达式为假。当然, ```` 同样可以是一个函数的返回值。注意, ``ifdef`` 只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子: 示例一: @@ -140,18 +140,18 @@ frobozz = no endif -第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。 +第一个例子中, ``$(frobozz)`` 值是 ``yes`` ,第二个则是 ``no``。 -第四个条件关键字是“ifndef”。其语法是: +第四个条件关键字是 ``ifndef`` 。其语法是: .. code-block:: makefile ifndef -这个我就不多说了,和“ifdef”是相反的意思。 +这个我就不多说了,和 `ifdef`` 是相反的意思。 -在这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令)。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。 +在 ```` 这一行上,多余的空格是被允许的,但是不能以 ``Tab`` 键做为开始(不然就被认为是命令)。而注释符 ``#`` 同样也是安全的。 ``else`` 和 ``endif`` 也一样,只要不是以 ``Tab`` 键开始就行了。 -特别注意的是,make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。 +特别注意的是,make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如 ``$@`` 等)放入条件表达式中,因为自动化变量是在运行时才有的。 而且为了避免混乱,make不允许把整个条件语句分成两部分放在不同的文件中。 diff --git a/source/functions.rst b/source/functions.rst index 02ef06e..0e50c70 100644 --- a/source/functions.rst +++ b/source/functions.rst @@ -6,7 +6,7 @@ 函数的调用语法 -------------- -函数调用,很像变量的使用,也是以“$”来标识的,其語法如下: +函数调用,很像变量的使用,也是以 ``$`` 来标识的,其語法如下: .. code-block:: makefile @@ -18,7 +18,7 @@ ${ } -这里,就是函数名,make支持的函数不多。为函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使用变量,为了风格的统一,函数和变量的括号最好一样,如使用“$(subst a,b,$(x))”这样的形式,而不是“$(subst a,b, ${x})”的形式。因为统一会更清楚,也会减少一些不必要的麻烦。 +这里, ```` 就是函数名,make支持的函数不多。 ```` 为函数的参数,参数间以逗号 ``,`` 分隔,而函数名和参数之间以“空格”分隔。函数调用以 ``$`` 开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使用变量,为了风格的统一,函数和变量的括号最好一样,如使用 ``$(subst a,b,$(x))`` 这样的形式,而不是 ``$(subst a,b, ${x})`` 的形式。因为统一会更清楚,也会减少一些不必要的麻烦。 还是来看一个示例: @@ -30,7 +30,7 @@ foo:= a b c bar:= $(subst $(space),$(comma),$(foo)) -在这个示例中,$(comma)的值是一个逗号。$(space)使用了$(empty)定义了一个空格,$(foo)的值是“a b c”,$ (bar)的定义用,调用了函数“subst”,这是一个替换函数,这个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数是替换操作作用的字串。这个函数也就是把$(foo)中的空格替换成逗号,所以$(bar)的值是“a,b,c”。 +在这个示例中, ``$(comma)`` 的值是一个逗号。 ``$(space)`` 使用了 ``$(empty)`` 定义了一个空格, ``$(foo)`` 的值是 ``a b c`` , ``$(bar)`` 的定义用,调用了函数 ``subst`` ,这是一个替换函数,这个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数是替换操作作用的字串。这个函数也就是把 ``$(foo)`` 中的空格替换成逗号,所以 ``$(bar)``的值是 ``a,b,c`` 。 字符串处理函数 -------------- @@ -40,29 +40,29 @@ subst .. code-block:: makefile - $(subst ,,) + $(subst ,,) -- 名称:字符串替换函数——subst。 -- 功能:把字串中的字符串替换成。 +- 名称:字符串替换函数 +- 功能:把字串 ```` 中的 ```` 字符串替换成 ```` 。 - 返回:函数返回被替换过后的字符串。 - 示例: - + .. code-block:: makefile $(subst ee,EE,feet on the street) -把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。 +把 ``feet on the street`` 中的 ``ee`` 替换成 ``EE`` ,返回结果是 ``fEEt on the strEEt`` 。 patsubst ~~~~~~~~ .. code-block:: makefile - $(patsubst ,,) + $(patsubst ,,) -- 名称:模式字符串替换函数——patsubst。 -- 功能:查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式< pattern>,如果匹配的话,则以替换。这里,可以包括通配符 “%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个 “%”将是中的那个“%”所代表的字串。(可以用“\\”来转义,以“\\%”来表示真实含义的“%”字符) +- 名称:模式字符串替换函数。 +- 功能:查找 ```` 中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 ```` ,如果匹配的话,则以 ```` 替换。这里, ```` 可以包括通配符 ``%`` ,表示任意长度的字串。如果 ```` 中也包含 ``%`` ,那么, ```` 中的这个 ``%`` 将是 ```` 中的那个 ``%`` 所代表的字串。(可以用 ``\`` 来转义,以 ``\%`` 来表示真实含义的 ``%`` 字符) - 返回:函数返回被替换过后的字符串。 - 示例: @@ -70,15 +70,15 @@ patsubst $(patsubst %.c,%.o,x.c.c bar.c) -把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o” +把字串 ``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 ~~~~~ @@ -87,8 +87,8 @@ strip $(strip ) -- 名称:去空格函数——strip。 -- 功能:去掉;字串中开头和结尾的空字符。 +- 名称:去空格函数。 +- 功能:去掉 ```` 字串中开头和结尾的空字符。 - 返回:返回被去掉空格的字符串值。 - 示例: @@ -96,7 +96,7 @@ strip $(strip a b c ) - 把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。 + 把字串 ``a b c `` 去到开头和结尾的空格,结果是 ``a b c`` 。 findstring ~~~~~~~~~~ @@ -105,9 +105,9 @@ findstring $(findstring ,) -- 名称:查找字符串函数——findstring。 -- 功能:在字串中查找字串。 -- 返回:如果找到,那么返回,否则返回空字符串。 +- 名称:查找字符串函数 +- 功能:在字串 ```` 中查找 ```` 字串。 +- 返回:如果找到,那么返回 ```` ,否则返回空字符串。 - 示例: .. code-block:: makefile @@ -115,7 +115,7 @@ findstring $(findstring a,a b c) $(findstring a,b c) -第一个函数返回“a”字符串,第二个返回“”字符串(空字符串) +第一个函数返回 ``a`` 字符串,第二个返回空字符串 filter ~~~~~~ @@ -124,9 +124,9 @@ filter $(filter ,) -- 名称:过滤函数——filter。 -- 功能:以模式过滤字符串中的单词,保留符合模式的单词。可以有多个模式。 -- 返回:返回符合模式;的字串。 +- 名称:过滤函数 +- 功能:以 ```` 模式过滤 ```` 字符串中的单词,保留符合模式 ```` 的单词。可以有多个模式。 +- 返回:返回符合模式 ```` 的字串。 - 示例: .. code-block:: makefile @@ -135,7 +135,7 @@ filter 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 ~~~~~~~~~~ @@ -144,17 +144,17 @@ filter-out $(filter-out ,) -- 名称:反过滤函数——filter-out。 -- 功能:以模式过滤字符串中的单词,去除符合模式的单词。可以有多个模式。 -- 返回:返回不符合模式的字串。 +- 名称:反过滤函数 +- 功能:以 ```` 模式过滤 ```` 字符串中的单词,去除符合模式 ```` 的单词。可以有多个模式。 +- 返回:返回不符合模式 ```` 的字串。 - 示例: .. code-block:: makefile 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 ~~~~ @@ -163,11 +163,11 @@ sort $(sort ) -- 名称:排序函数——sort。 -- 功能:给字符串中的单词排序(升序)。 +- 名称:排序函数 +- 功能:给字符串 ```` 中的单词排序(升序)。 - 返回:返回排序后的字符串。 -- 示例:$(sort foo bar lose)返回“bar foo lose” 。 -- 备注:sort函数会去掉中相同的单词。 +- 示例: ``$(sort foo bar lose)`` 返回 ``bar foo lose`` 。 +- 备注: ``sort`` 函数会去掉 ```` 中相同的单词。 word ~~~~ @@ -176,22 +176,22 @@ word $(word ,) -- 名称:取单词函数——word。 -- 功能:取字符串中第个单词。(从一开始) -- 返回:返回字符串中第个单词。如果中的单词数要大,那么返回空字符串。 -- 示例:$(word 2, foo bar baz)返回值是“bar”。 +- 名称:取单词函数 +- 功能:取字符串 ```` 中第 ```` 个单词。(从一开始) +- 返回:返回字符串 ```` 中第 ```` 个单词。如果 ```` 比 ```` 中的单词数要大,那么返回空字符串。 +- 示例: ``$(word 2, foo bar baz)`` 返回值是 ``bar`` 。 wordlist ~~~~~~~~ .. code-block:: makefile - $(wordlist ,,) + $(wordlist ,,) -- 名称:取单词串函数——wordlist。 -- 功能:从字符串中取从开始到的单词串。是一个数字。 -- 返回:返回字符串中从的单词字串。如果中的单词数要大,那么返回空字符串。如果大于的单词数,那么返回从开始,到结束的单词串。 -- 示例:$(wordlist 2, 3, foo bar baz)返回值是“bar baz”。 +- 名称:取单词串函数 +- 功能:从字符串 ```` 中取从 ```` 开始到 ```` 的单词串。 ```` 和 ```` 是一个数字。 +- 返回:返回字符串 ```` 中从 ```` 到 ```` 的单词字串。如果 ```` 比 ```` 中的单词数要大,那么返回空字符串。如果 ```` 大于 ```` 的单词数,那么返回从 ```` 开始,到 ```` 结束的单词串。 +- 示例: ``$(wordlist 2, 3, foo bar baz)`` 返回值是 ``bar baz`` 。 words ~~~~~ @@ -200,11 +200,11 @@ words $(words ) -- 名称:单词个数统计函数——words。 -- 功能:统计中字符串中的单词个数。 -- 返回:返回中的单词数。 -- 示例:$(words, foo bar baz)返回值是“3”。 -- 备注:如果我们要取中最后的一个单词,我们可以这样:$(word $(words ),)。 +- 名称:单词个数统计函数 +- 功能:统计 ```` 中字符串中的单词个数。 +- 返回:返回 ```` 中的单词数。 +- 示例: ``$(words, foo bar baz)`` 返回值是 ``3`` 。 +- 备注:如果我们要取 ```` 中最后的一个单词,我们可以这样: ``$(word $(words ),)`` 。 firstword ~~~~~~~~~ @@ -214,18 +214,18 @@ firstword $(firstword ) - 名称:首单词函数——firstword。 -- 功能:取字符串中的第一个单词。 -- 返回:返回字符串的第一个单词。 -- 示例:$(firstword foo bar)返回值是“foo”。 -- 备注:这个函数可以用word函数来实现:$(word 1,)。 +- 功能:取字符串 ```` 中的第一个单词。 +- 返回:返回字符串 ```` 的第一个单词。 +- 示例: ``$(firstword foo bar)`` 返回值是 ``foo``。 +- 备注:这个函数可以用 ``word`` 函数来实现: ``$(word 1,)`` 。 -以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里,举一个现实中应用的例子。我们知道,make使用“VPATH”变量来指定“依赖文件”的搜索路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数CFLAGS,如: +以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里,举一个现实中应用的例子。我们知道,make使用 ``VPATH`` 变量来指定“依赖文件”的搜索路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数 ``CFLAGS`` ,如: .. code-block:: makefile override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH))) -如果我们的“$(VPATH)”值是“src:../headers”,那么“$(patsubst %,-I%,$(subst :, ,$(VPATH)))”将返回“-Isrc -I../headers”,这正是cc或gcc搜索头文件路径的参数。 +如果我们的 ``$(VPATH)`` 值是 ``src:../headers`` ,那么 ``$(patsubst %,-I%,$(subst :, ,$(VPATH)))`` 将返回 ``-Isrc -I../headers`` ,这正是cc或gcc搜索头文件路径的参数。 文件名操作函数 -------------- @@ -237,36 +237,36 @@ dir .. code-block:: makefile - $(dir ) + $(dir ) - 名称:取目录函数——dir。 -- 功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。 -- 返回:返回文件名序列的目录部分。 -- 示例: $(dir src/foo.c hacks)返回值是“src/ ./”。 +- 功能:从文件名序列 ```` 中取出目录部分。目录部分是指最后一个反斜杠( ``/`` )之前的部分。如果没有反斜杠,那么返回 ``./`` 。 +- 返回:返回文件名序列 ```` 的目录部分。 +- 示例: ``$(dir src/foo.c hacks)`` 返回值是 ``src/ ./`` 。 notdir ~~~~~~ .. code-block:: makefile - $(notdir ) + $(notdir ) - 名称:取文件函数——notdir。 -- 功能:从文件名序列中取出非目录部分。非目录部分是指最後一个反斜杠(“/”)之后的部分。 -- 返回:返回文件名序列的非目录部分。 -- 示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。 - +- 功能:从文件名序列 ```` 中取出非目录部分。非目录部分是指最後一个反斜杠( ``/`` )之后的部分。 +- 返回:返回文件名序列 ```` 的非目录部分。 +- 示例: ``$(notdir src/foo.c hacks)`` 返回值是 ``foo.c hacks`` 。 + suffix ~~~~~~ .. code-block:: makefile - $(suffix ) + $(suffix ) - 名称:取後缀函数——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 ~~~~~~~~ @@ -276,33 +276,33 @@ basename $(basename ) - 名称:取前缀函数——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 - $(addsuffix ,) + $(addsuffix ,) - 名称:加后缀函数——addsuffix。 -- 功能:把后缀加到中的每个单词后面。 +- 功能:把后缀 ```` 加到 ```` 中的每个单词后面。 - 返回:返回加过后缀的文件名序列。 -- 示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。 +- 示例: ``$(addsuffix .c,foo bar)`` 返回值是 ``foo.c bar.c`` 。 addprefix ~~~~~~~~~ .. code-block:: makefile - $(addprefix ,) + $(addprefix ,) - 名称:加前缀函数——addprefix。 -- 功能:把前缀加到中的每个单词后面。 +- 功能:把前缀 ```` 加到 ```` 中的每个单词后面。 - 返回:返回加过前缀的文件名序列。 -- 示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。 +- 示例: ``$(addprefix src/,foo bar)`` 返回值是 ``src/foo src/bar`` 。 join ~~~~ @@ -312,41 +312,41 @@ join $(join ,) - 名称:连接函数——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语句而构建的。它的语法是: .. code-block:: makefile $(foreach ,,) -这个函数的意思是,把参数中的单词逐一取出放到参数所指定的变量中,然后再执行所包含的表达式。每一次会返回一个字符串,循环过程中,的所返回的每个字符串会以空格分隔,最后当整个循环结束时,所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 +这个函数的意思是,把参数 `````` 中的单词逐一取出放到参数 ```` 所指定的变量中,然后再执行 ```` 所包含的表达式。每一次 ```` 会返回一个字符串,循环过程中, ```` 的所返回的每个字符串会以空格分隔,最后当整个循环结束时, ```` 所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 -所以,最好是一个变量名,可以是一个表达式,而中一般会使用这个参数来依次枚举中的单词。举个例子: +所以, ```` 最好是一个变量名, ```` 可以是一个表达式,而 ```` 中一般会使用 ```` 这个参数来依次枚举 ```` 中的单词。举个例子: .. code-block:: makefile names := a b c d - + files := $(foreach n,$(names),$(n).o) -上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。 +上面的例子中, ``$(name)`` 中的单词会被挨个取出,并存到变量 ``n`` 中, ``$(n).o`` 每次根据 ``$(n)`` 计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以, ``$(files)`` 的值是 ``a.o b.o c.o d.o`` 。 -注意,foreach中的参数是一个临时的局部变量,foreach函数执行完后,参数的变量将不在作用,其作用域只在foreach函数当中。 +注意,foreach中的 ```` 参数是一个临时的局部变量,foreach函数执行完后,参数 ```` 的变量将不在作用,其作用域只在foreach函数当中。 if 函数 ------- - + if函数很像GNU的make所支持的条件语句——ifeq(参见前面所述的章节),if函数的语法是: .. code-block:: makefile - $(if ,) + $(if ,) 或是 @@ -354,38 +354,38 @@ if函数很像GNU的make所支持的条件语句——ifeq(参见前面所述 $(if ,,) -可见,if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是三个。参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,会被计算,否则会被计算。 +可见,if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是三个。 ```` 参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是, ```` 会被计算,否则 ```` 会被计算。 -而if函数的返回值是,如果为真(非空字符串),那个会是整个函数的返回值,如果为假(空字符串),那么会是整个函数的返回值,此时如果没有被定义,那么,整个函数返回空字串。 - -所以,只会有一个被计算。 +而if函数的返回值是,如果 ```` 为真(非空字符串),那个 ```` 会是整个函数的返回值,如果 ```` 为假(空字符串),那么 ```` 会是整个函数的返回值,此时如果 ```` 没有被定义,那么,整个函数返回空字串。 + +所以, ```` 和 ```` 只会有一个被计算。 call函数 -------- - + call函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式,这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。其语法是: .. code-block:: makefile $(call ;,;,;,;...) -当make执行这个函数时,;参数中的变量,如$(1),$(2),$(3)等,会被参数< parm1>;,;,;依次取代。而;的返回值就是 call函数的返回值。例如: +当make执行这个函数时, ```` ;参数中的变量,如 ``$(1)`` , ``$(2)`` , ``$(3)`` 等,会被参数 ``;`` , ``;`` , ``;`` 依次取代。而 ``;`` 的返回值就是 call函数的返回值。例如: .. code-block:: makefile reverse = $(1) $(2) - + foo = $(call reverse,a,b) -那么,foo的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如: +那么, ``foo`` 的值就是 ``a b`` 。当然,参数的次序是可以自定义的,不一定是顺序的,如: .. code-block:: makefile reverse = $(2) $(1) - + foo = $(call reverse,a,b) -此时的foo的值就是“b a”。'' +此时的 ``foo`` 的值就是 ``b a`` 。 origin函数 ---------- @@ -396,22 +396,22 @@ origin函数不像其它的函数,他并不操作变量的值,他只是告 $(origin ;) -注意,;是变量的名字,不应该是引用。所以你最好不要在;中使用“$”字符。Origin函数会以其返回值来告诉你这个变量的“出生情况”,下面,是origin函数的返回值: +注意, ``;`` 是变量的名字,不应该是引用。所以你最好不要在 ``;`` 中使用 ``$`` 字符。Origin函数会以其返回值来告诉你这个变量的“出生情况”,下面,是origin函数的返回值: -“undefined” - 如果;从来没有定义过,origin函数返回这个值“undefined”。 -“default” - 如果;是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。 -“environment” - 如果;是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。 -“file” - 如果;这个变量被定义在Makefile中。 -“command line” - 如果;这个变量是被命令行定义的。 -“override” - 如果;是被override指示符重新定义的。 -“automatic” - 如果;是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。 +``undefined`` + 如果 ``;`` 从来没有定义过,origin函数返回这个值 ``undefined`` +``default`` + 如果 ``;`` 是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。 +``environment`` + 如果 ``;`` 是一个环境变量,并且当Makefile被执行时, ``-e`` 参数没有被打开。 +``file`` + 如果 ``;`` 这个变量被定义在Makefile中。 +``command line`` + 如果 ``;`` 这个变量是被命令行定义的。 +``override`` + 如果 ``;`` 是被override指示符重新定义的。 +``automatic`` + 如果 ``;`` 是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。 这些信息对于我们编写Makefile是非常有用的,例如,假设我们有一个Makefile其包了一个定义文件Make.def,在 Make.def中定义了一个变量“bletch”,而我们的环境中也有一个环境变量“bletch”,此时,我们想判断一下,如果变量来源于环境,那么我们就把之重定义了,如果来源于Make.def或是命令行等非环境的,那么我们就不重新定义它。于是,在我们的Makefile中,我们可以这样写: @@ -423,12 +423,12 @@ origin函数不像其它的函数,他并不操作变量的值,他只是告 endif endif -当然,你也许会说,使用override关键字不就可以重新定义环境中的变量了吗?为什么需要使用这样的步骤?是的,我们用override是可以达到这样的效果,可是override过于粗暴,它同时会把从命令行定义的变量也覆盖了,而我们只想重新定义环境传来的,而不想重新定义命令行传来的。 +当然,你也许会说,使用 ``override`` 关键字不就可以重新定义环境中的变量了吗?为什么需要使用这样的步骤?是的,我们用 ``override`` 是可以达到这样的效果,可是 ``override`` 过于粗暴,它同时会把从命令行定义的变量也覆盖了,而我们只想重新定义环境传来的,而不想重新定义命令行传来的。 shell函数 --------- -shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量,如: +shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量,如: .. code-block:: makefile @@ -441,13 +441,13 @@ shell函数也不像其它的函数。顾名思义,它的参数应该就是操 -------------- make提供了一些函数来控制make的运行。通常,你需要检测一些运行Makefile时的运行时信息,并且根据这些信息来决定,你是让make继续执行,还是停止。 - + .. code-block:: makefile $(error ;) -产生一个致命的错误,;是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。例如: +产生一个致命的错误, ```` 是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。例如: 示例一: @@ -462,13 +462,13 @@ make提供了一些函数来控制make的运行。通常,你需要检测一些 .. code-block:: makefile ERR = $(error found an error!) - + .PHONY: err - + err: ; $(ERR) 示例一会在变量ERROR_001定义了后执行时产生error调用,而示例二则在目录err被执行时才发生error调用。 - + .. code-block:: makefile $(warning ;) diff --git a/source/implicit_rules.rst b/source/implicit_rules.rst index 5867534..49f5385 100644 --- a/source/implicit_rules.rst +++ b/source/implicit_rules.rst @@ -1,11 +1,11 @@ 隐含规则 ======== -在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是[.o]文件,Windows下是[.obj]文件)。本章讲述的就是一些在Makefile中的“隐含的”,早先约定了的,不需要我们再写出来的规则。 +在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是 ``.o`` 文件,Windows下是 ``.obj`` 文件)。本章讲述的就是一些在Makefile中的“隐含的”,早先约定了的,不需要我们再写出来的规则。 -“隐含规则”也就是一种惯例,make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。 +“隐含规则”也就是一种惯例,make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。例如,把 ``.c`` 文件编译成 ``.o`` 文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的 ``.o`` 文件。 -“隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。如系统变量“CFLAGS”可以控制编译时的编译器参数。 +“隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。如系统变量 ``CFLAGS`` 可以控制编译时的编译器参数。 我们还可以通过“模式规则”的方式写下自己的隐含规则。用“后缀规则”来定义隐含规则会有许多的限制。使用“模式规则”会更回得智能和清楚,但“后缀规则”可以用来保证我们Makefile的兼容性。 我们了解了“隐含规则”,可以让其为我们更好的服务,也会让我们知道一些“约定俗成”了的东西,而不至于使得我们在运行Makefile时出现一些我们觉得莫名其妙的东西。当然,任何事物都是矛盾的,水能载舟,亦可覆舟,所以,有时候“隐含规则”也会给我们造成不小的麻烦。只有了解了它,我们才能更好地使用它。 @@ -20,9 +20,9 @@ foo : foo.o bar.o cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS) -我们可以注意到,这个Makefile中并没有写下如何生成foo.o和bar.o这两目标的规则和命令。因为make的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。 +我们可以注意到,这个Makefile中并没有写下如何生成 ``foo.o`` 和 ``bar.o`` 这两目标的规则和命令。因为make的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。 -make会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把[.o]的目标的依赖文件置成[.c],并使用C的编译命令“cc –c $(CFLAGS) [.c]”来生成[.o]的目标。也就是说,我们完全没有必要写下下面的两条规则: +make会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把 ``.o`` 的目标的依赖文件置成 ``.c`` ,并使用C的编译命令 ``cc –c $(CFLAGS) foo.c`` 来生成 ``foo.o`` 的目标。也就是说,我们完全没有必要写下下面的两条规则: .. code-block:: makefile @@ -31,9 +31,9 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 bar.o : bar.c cc –c bar.c $(CFLAGS) -因为,这已经是“约定”好了的事了,make和我们约定好了用C编译器“cc”生成[.o]文件的规则,这就是隐含规则。 +因为,这已经是“约定”好了的事了,make和我们约定好了用C编译器 ``cc`` 生成 ``.o`` 文件的规则,这就是隐含规则。 -当然,如果我们为[.o]文件书写了自己的规则,那么make就不会自动推导并调用隐含规则,它会按照我们写好的规则忠实地执行。 +当然,如果我们为 ``.o`` 文件书写了自己的规则,那么make就不会自动推导并调用隐含规则,它会按照我们写好的规则忠实地执行。 还有,在make的“隐含规则库”中,每一条隐含规则都在库中有其顺序,越靠前的则是越被经常使用的,所以,这会导致我们有些时候即使我们显示地指定了目标依赖,make也不会管。如下面这条规则(没有命令): @@ -41,59 +41,59 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 foo.o : foo.p -依赖文件“foo.p”(Pascal程序的源文件)有可能变得没有意义。如果目录下存在了“foo.c”文件,那么我们的隐含规则一样会生效,并会通过“foo.c”调用C的编译器生成foo.o文件。因为,在隐含规则中,Pascal的规则出现在C的规则之后,所以,make找到可以生成 foo.o的C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令。 +依赖文件 ``foo.p`` (Pascal程序的源文件)有可能变得没有意义。如果目录下存在了 ``foo.c`` 文件,那么我们的隐含规则一样会生效,并会通过 ``foo.c`` 调用C的编译器生成 ``foo.o`` 文件。因为,在隐含规则中,Pascal的规则出现在C的规则之后,所以,make找到可以生成 ``foo.o`` 的C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出“依赖规则”,而不写命令。 隐含规则一览 ------------ -这里我们将讲述所有预先设置(也就是make内建)的隐含规则,如果我们不明确地写下规则,那么,make就会在这些规则中寻找所需要规则和命令。当然,我们也可以使用make的参数“-r”或“--no-builtin-rules”选项来取消所有的预设置的隐含规则。 +这里我们将讲述所有预先设置(也就是make内建)的隐含规则,如果我们不明确地写下规则,那么,make就会在这些规则中寻找所需要规则和命令。当然,我们也可以使用make的参数 ``-r`` 或 ``--no-builtin-rules`` 选项来取消所有的预设置的隐含规则。 -当然,即使是我们指定了“-r”参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了“后缀规则”来定义的,所以,只要隐含规则中有 “后缀列表”(也就一系统定义在目标.SUFFIXES的依赖目标),那么隐含规则就会生效。默认的后缀列表是:.out, .a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。具体的细节,我们会在后面讲述。 +当然,即使是我们指定了 ``-r`` 参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了“后缀规则”来定义的,所以,只要隐含规则中有 “后缀列表”(也就一系统定义在目标 ``.SUFFIXES`` 的依赖目标),那么隐含规则就会生效。默认的后缀列表是:.out, .a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。具体的细节,我们会在后面讲述。 还是先来看一看常用的隐含规则吧。 #. 编译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)” - - “.f” “$(FC) –c $(FFLAGS) $(RFLAGS)” + - ``.f`` ``$(FC) –c $(FFLAGS)`` + - ``.F`` ``$(FC) –c $(FFLAGS) $(CPPFLAGS)`` + - ``.f`` ``$(FC) –c $(FFLAGS) $(RFLAGS)`` #. 预处理Fortran/Ratfor程序的隐含规则。 - “.f”的目标的依赖目标会自动推导为“.r”或“.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是: + ``.f`` 的目标的依赖目标会自动推导为 ``.r`` 或 ``.F`` 。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是: - - “.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)” - - “.r” “$(FC) –F $(FFLAGS) $(RFLAGS)” + - ``.F`` ``$(FC) –F $(CPPFLAGS) $(FFLAGS)`` + - ``.r`` ``$(FC) –F $(FFLAGS) $(RFLAGS)`` #. 编译Modula-2程序的隐含规则。 - “.sym”的目标的依赖目标会自动推导为“.def”,并且其生成命令是:“$(M2C) $ (M2FLAGS) $(DEFFLAGS)”。“.o” 的目标的依赖目标会自动推导为“.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 @@ -107,40 +107,40 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 #. 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`` 也是同样的规则。 隐含规则使用的变量 ------------------ -在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。当然,你也可以利用make的“-R”或 “--no–builtin-variables”参数来取消你所定义的变量对隐含规则的作用。 +在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。当然,你也可以利用make的 ``-R`` 或 ``--no–builtin-variables`` 参数来取消你所定义的变量对隐含规则的作用。 -例如,第一条隐含规则——编译C程序的隐含规则的命令是“$(CC) –c $(CFLAGS) $(CPPFLAGS)”。Make默认的编译命令是“cc”,如果你把变量“$(CC)”重定义成“gcc”,把变量“$(CFLAGS)”重定义成“-g”,那么,隐含规则中的命令全部会以 “gcc –c -g $(CPPFLAGS)”的样子来执行了。 +例如,第一条隐含规则——编译C程序的隐含规则的命令是 ``$(CC) –c $(CFLAGS) $(CPPFLAGS)`` 。Make默认的编译命令是 ``cc`` ,如果你把变量 ``$(CC)`` 重定义成 ``gcc`` ,把变量 ``$(CFLAGS)`` 重定义成 ``-g`` ,那么,隐含规则中的命令全部会以 ``gcc –c -g $(CPPFLAGS)`` 的样子来执行了。 -我们可以把隐含规则中使用的变量分成两种:一种是命令相关的,如“CC”;一种是参数相的关,如“CFLAGS”。下面是所有隐含规则中会用到的变量: +我们可以把隐含规则中使用的变量分成两种:一种是命令相关的,如 ``CC`` ;一种是参数相的关,如 ``CFLAGS`` 。下面是所有隐含规则中会用到的变量: 关于命令的变量。 ~~~~~~~~~~~~~~~~ -- AR:函数库打包程序。默认命令是“ar”。 +- AR:函数库打包程序。默认命令是“ar”。 - AS:汇编语言编译程序。默认命令是“as”。 - CC:C语言编译程序。默认命令是“cc”。 - CXX:C++语言编译程序。默认命令是“g++”。 - CO:从 RCS文件中扩展文件程序。默认命令是“co”。 - CPP:C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。 - FC:Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。 -- GET:从SCCS文件中扩展文件的程序。默认命令是“get”。 +- GET:从SCCS文件中扩展文件的程序。默认命令是“get”。 - LEX:Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。 - PC:Pascal语言编译程序。默认命令是“pc”。 - YACC:Yacc文法分析器(针对于C程序)。默认命令是“yacc”。 @@ -160,10 +160,10 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空。 - ARFLAGS:函数库打包程序AR命令的参数。默认值是“rv”。 -- ASFLAGS:汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。 +- ASFLAGS:汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。 - CFLAGS:C语言编译器参数。 - CXXFLAGS:C++语言编译器参数。 -- COFLAGS:RCS命令参数。 +- COFLAGS:RCS命令参数。 - CPPFLAGS:C预处理器参数。( C 和 Fortran 编译器也会用到)。 - FFLAGS:Fortran语言编译器参数。 - GFLAGS:SCCS “get”程序参数。 @@ -171,20 +171,20 @@ make会在自己的“隐含规则”库中寻找可以用的规则,如果找 - LFLAGS:Lex文法分析器参数。 - PFLAGS:Pascal语言编译器参数。 - RFLAGS:Ratfor 程序的Fortran 编译器参数。 -- YFLAGS:Yacc文法分析器参数。 +- YFLAGS:Yacc文法分析器参数。 隐含规则链 ---------- -有些时候,一个目标可能被一系列的隐含规则所作用。例如,一个[.o]的文件生成,可能会是先被Yacc的[.y]文件先成[.c],然后再被C的编译器生成。我们把这一系列的隐含规则叫做“隐含规则链”。 +有些时候,一个目标可能被一系列的隐含规则所作用。例如,一个 ``.o`` 的文件生成,可能会是先被Yacc的[.y]文件先成 ``.c`` ,然后再被C的编译器生成。我们把这一系列的隐含规则叫做“隐含规则链”。 -在上面的例子中,如果文件[.c]存在,那么就直接调用C的编译器的隐含规则,如果没有[.c]文件,但有一个[.y]文件,那么Yacc的隐含规则会被调用,生成[.c]文件,然后,再调用C编译的隐含规则最终由[.c]生成[.o]文件,达到目标。 +在上面的例子中,如果文件 ``.c`` 存在,那么就直接调用C的编译器的隐含规则,如果没有 ``.c`` 文件,但有一个[.y]文件,那么Yacc的隐含规则会被调用,生成 ``.c`` 文件,然后,再调用C编译的隐含规则最终由 ``.c`` 生成 ``.o`` 文件,达到目标。 -我们把这种[.c]的文件(或是目标),叫做中间目标。不管怎么样,make会努力自动推导生成目标的一切方法,不管中间目标有多少,其都会执着地把所有的隐含规则和你书写的规则全部合起来分析,努力达到目标,所以,有些时候,可能会让你觉得奇怪,怎么我的目标会这样生成?怎么我的 makefile发疯了? +我们把这种 ``.c`` 的文件(或是目标),叫做中间目标。不管怎么样,make会努力自动推导生成目标的一切方法,不管中间目标有多少,其都会执着地把所有的隐含规则和你书写的规则全部合起来分析,努力达到目标,所以,有些时候,可能会让你觉得奇怪,怎么我的目标会这样生成?怎么我的 makefile发疯了? -在默认情况下,对于中间目标,它和一般的目标有两个地方所不同:第一个不同是除非中间的目标不存在,才会引发中间规则。第二个不同的是,只要目标成功产生,那么,产生最终目标过程中,所产生的中间目标文件会被以“rm -f”删除。 +在默认情况下,对于中间目标,它和一般的目标有两个地方所不同:第一个不同是除非中间的目标不存在,才会引发中间规则。第二个不同的是,只要目标成功产生,那么,产生最终目标过程中,所产生的中间目标文件会被以 ``rm -f`` 删除。 -通常,一个被makefile指定成目标或是依赖目标的文件不能被当作中介。然而,你可以明显地说明一个文件或是目标是中介目标,你可以使用伪目标“.INTERMEDIATE”来强制声明。(如:.INTERMEDIATE : mid ) +通常,一个被makefile指定成目标或是依赖目标的文件不能被当作中介。然而,你可以明显地说明一个文件或是目标是中介目标,你可以使用伪目标 ``.INTERMEDIATE`` 来强制声明。(如:.INTERMEDIATE : mid ) 你也可以阻止make自动删除中间目标,要做到这一点,你可以使用伪目标“.SECONDARY”来强制声明(如:.SECONDARY : sec)。你还可以把你的目标,以模式的方式来指定(如:%.o)成伪目标“.PRECIOUS”的依赖目标,以保存被隐含规则所生成的中间文件。 @@ -210,14 +210,14 @@ Make会优化一些特殊的隐含规则,而不生成中间文件。如,从 %.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关心的是依赖的文件名和生成目标的命令这两件事。 模式规则示例 ~~~~~~~~~~~~ -下面这个例子表示了,把所有的[.c]文件都编译成[.o]文件. +下面这个例子表示了,把所有的 ``.c`` 文件都编译成 ``.o`` 文件. .. code-block:: makefile @@ -233,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`` 文件的依赖关系也写好,那么,所有的目标都会得到满足) 自动化变量 ~~~~~~~~~~ @@ -246,7 +246,7 @@ Make会优化一些特殊的隐含规则,而不生成中间文件。如,从 - $@: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,“$@”就是匹配于目标中模式定义的集合。 - $%: 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。 -- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即“%”)定义的,那么“$<”将是符合模式的一系列的文件集。注意,其是一个一个取出来的。 +- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即“%”)定义的,那么“$<”将是符合模式的一系列的文件集。注意,其是一个一个取出来的。 - $?: 所有比目标新的依赖目标的集合。以空格分隔。 - $^:所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。 - $+:这个变量很像“$^”,也是所有依赖目标的集合。只是它不去除重复的依赖目标。 @@ -311,7 +311,7 @@ Make会优化一些特殊的隐含规则,而不生成中间文件。如,从 你可以取消内建的隐含规则,只要不在后面写命令就行。如: .. code-block:: makefile - + %.o : %.s 同样,你也可以重新定义一个全新的隐含规则,其在隐含规则中的位置取决于你在哪里写下这个规则。朝前的位置就靠前。 @@ -381,7 +381,7 @@ make的参数“-r”或“-no-builtin-rules”也会使用得默认的后缀列 #. 如果所有的依赖文件存在或是理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。 #. 如果经过第5步,没有模式规则被找到,那么就做更进一步的搜索。对于存在于列表中的第一个模式规则: - + #. 如果规则是终止规则,那就忽略它,继续下一条模式规则。 #. 计算依赖文件。(同第5步) #. 测试所有的依赖文件是否存在或是理当存在。 diff --git a/source/introduction.rst b/source/introduction.rst index 4470681..fc324a0 100644 --- a/source/introduction.rst +++ b/source/introduction.rst @@ -49,7 +49,7 @@ command 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 @@ -70,32 +70,32 @@ command rm edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o -反斜杠(\\)是换行符的意思。这样比较便于makefile的易读。我们可以把这个内容保存在名字为“makefile”或“Makefile”的文件中,然后在该目录下直接输入命令\ ``make``\ 就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下\ ``make clean``\ 就可以了。 +反斜杠( ``\`` )是换行符的意思。这样比较便于makefile的易读。我们可以把这个内容保存在名字为“makefile”或“Makefile”的文件中,然后在该目录下直接输入命令 ``make`` 就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下 ``make clean`` 就可以了。 -在这个makefile中,目标文件(target)包含:执行文件edit和中间目标文件(\ ``*.o``\ ),依赖文件(prerequisites)就是冒号后面的那些\ ``.c``\ 文件和\ ``.h``\ 文件。每一个\ ``.o``\ 文件都有一组依赖文件,而这些\ ``.o``\ 文件又是执行文件\ ``edit``\ 的依赖文件。依赖关系的实质就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。 +在这个makefile中,目标文件(target)包含:执行文件edit和中间目标文件( ``*.o`` ),依赖文件(prerequisites)就是冒号后面的那些 ``.c`` 文件和 ``.h`` 文件。每一个 ``.o`` 文件都有一组依赖文件,而这些 ``.o`` 文件又是执行文件 ``edit`` 的依赖文件。依赖关系的实质就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。 -在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个\ ``Tab``\ 键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。 +在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个 ``Tab`` 键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。 -这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像c语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。 +这里要说明一点的是, ``clean`` 不是一个文件,它只不过是一个动作名字,有点像c语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。 make是如何工作的 ---------------- -在默认的方式下,也就是我们只输入\ ``make``\ 命令。那么, +在默认的方式下,也就是我们只输入 ``make`` 命令。那么, -#. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。 -#. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。 -#. 如果edit文件不存在,或是edit所依赖的后面的\ ``.o``\ 文件的文件修改时间要比\ ``edit``\ 这个文件新,那么,他就会执行后面所定义的命令来生成\ ``edit``\ 这个文件。 -#. 如果\ ``edit``\ 所依赖的\ ``.o``\ 文件也不存在,那么make会在当前文件中找目标为\ ``.o``\ 文件的依赖性,如果找到则再根据那一个规则生成\ ``.o``\ 文件。(这有点像一个堆栈的过程) -#. 当然,你的C文件和H文件是存在的啦,于是make会生成\ ``.o``\ 文件,然后再用\ ``.o``\ 文件生成make的终极任务,也就是执行文件\ ``edit``\ 了。 +#. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。 +#. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。 +#. 如果edit文件不存在,或是edit所依赖的后面的 ``.o`` 文件的文件修改时间要比 ``edit`` 这个文件新,那么,他就会执行后面所定义的命令来生成 ``edit`` 这个文件。 +#. 如果 ``edit`` 所依赖的 ``.o`` 文件也不存在,那么make会在当前文件中找目标为 ``.o`` 文件的依赖性,如果找到则再根据那一个规则生成 ``.o`` 文件。(这有点像一个堆栈的过程) +#. 当然,你的C文件和H文件是存在的啦,于是make会生成 ``.o`` 文件,然后再用 ``.o`` 文件生成make的终极任务,也就是执行文件 ``edit`` 了。 -这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。 +这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。 -通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——\ ``make clean``\ ,以此来清除所有的目标文件,以便重编译。 +通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令—— ``make clean`` ,以此来清除所有的目标文件,以便重编译。 -于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如\ ``file.c``\ ,那么根据我们的依赖性,我们的目标\ ``file.o``\ 会被重编译(也就是在这个依性关系后面所定义的命令),于是\ ``file.o``\ 的文件也是最新的啦,于是\ ``file.o``\ 的文件修改时间要比\ ``edit``\ 要新,所以\ ``edit``\ 也会被重新链接了(详见\ ``edit``\ 目标文件后定义的命令)。 +于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如 ``file.c`` ,那么根据我们的依赖性,我们的目标 ``file.o`` 会被重编译(也就是在这个依性关系后面所定义的命令),于是 ``file.o`` 的文件也是最新的啦,于是 ``file.o`` 的文件修改时间要比 ``edit`` 要新,所以 ``edit`` 也会被重新链接了(详见 ``edit`` 目标文件后定义的命令)。 -而如果我们改变了\ ``command.h``\ ,那么,\ ``kdb.o``\ 、\ ``command.o``\ 和\ ``files.o``\ 都会被重编译,并且,\ ``edit``\ 会被重链接。 +而如果我们改变了 ``command.h`` ,那么, ``kdb.o`` 、 ``command.o`` 和 ``files.o`` 都会被重编译,并且, ``edit`` 会被重链接。 makefile中使用变量 ------------------ @@ -109,22 +109,22 @@ makefile中使用变量 cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o -我们可以看到\ ``.o``\ 文件的字符串被重复了两次,如果我们的工程需要加入一个新的\ ``.o``\ 文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。 +我们可以看到 ``.o`` 文件的字符串被重复了两次,如果我们的工程需要加入一个新的 ``.o`` 文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。 -比如,我们声明一个变量,叫\ ``objects``\ ,\ ``OBJECTS``\ ,\ ``objs``\ ,\ ``OBJS``\,\ ``obj``\ 或是\ ``OBJ``\,反正不管什么啦,只要能够表示obj文件就行了。我们在makefile一开始就这样定义: +比如,我们声明一个变量,叫 ``objects`` , ``OBJECTS`` , ``objs`` , ``OBJS``\, ``obj`` 或是 ``OBJ``\,反正不管什么啦,只要能够表示obj文件就行了。我们在makefile一开始就这样定义: .. code-block:: makefile objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o -于是,我们就可以很方便地在我们的makefile中以\ ``$(objects)``\ 的方式来使用这个变量了,于是我们的改良版makefile就变成下面这个样子: +于是,我们就可以很方便地在我们的makefile中以 ``$(objects)`` 的方式来使用这个变量了,于是我们的改良版makefile就变成下面这个样子: .. code-block:: makefile 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 : main.c defs.h @@ -146,25 +146,25 @@ makefile中使用变量 clean : rm edit $(objects) -于是如果有新的\ ``.o``\ 文件加入,我们只需简单地修改一下\ ``objects``\ 变量就可以了。 +于是如果有新的 ``.o`` 文件加入,我们只需简单地修改一下 ``objects`` 变量就可以了。 关于变量更多的话题,我会在后续给你一一道来。 让make自动推导 -------------- -GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个\ ``.o``\ 文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。 +GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个 ``.o`` 文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。 -只要make看到一个\ ``.o``\ 文件,它就会自动的把\ ``.c``\ 文件加在依赖关系中,如果make找到一个\ ``whatever.o``\ ,那么\ ``whatever.c``\ ,就会是\ ``whatever.o``\ 的依赖文件。并且\ ``cc -c whatever.c``\ 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的新makefile又出炉了。 +只要make看到一个 ``.o`` 文件,它就会自动的把 ``.c`` 文件加在依赖关系中,如果make找到一个 ``whatever.o`` ,那么 ``whatever.c`` ,就会是 ``whatever.o`` 的依赖文件。并且 ``cc -c whatever.c`` 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的新makefile又出炉了。 .. code-block:: makefile 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 @@ -173,7 +173,7 @@ GNU的make很强大,它可以自动推导文件以及文件依赖关系后面 search.o : defs.h buffer.h files.o : defs.h buffer.h command.h utils.o : defs.h - + .PHONY : clean clean : rm edit $(objects) @@ -185,16 +185,16 @@ GNU的make很强大,它可以自动推导文件以及文件依赖关系后面 另类风格的makefiles ------------------- -既然我们的make可以自动推导命令,那么我看到那堆\ ``.o``\ 和\ ``.h``\ 的依赖就有点不爽,那么多的重复的\ ``.h``\ ,能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了自动推导命令和文件的功能呢?来看看最新风格的makefile吧。 +既然我们的make可以自动推导命令,那么我看到那堆 ``.o`` 和 ``.h`` 的依赖就有点不爽,那么多的重复的 ``.h`` ,能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了自动推导命令和文件的功能呢?来看看最新风格的makefile吧。 .. code-block:: makefile 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 @@ -203,12 +203,12 @@ GNU的make很强大,它可以自动推导文件以及文件依赖关系后面 clean : rm edit $(objects) -这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的,一是文件的依赖关系看不清楚,二是如果文件一多,要加入几个新的\ ``.o``\ 文件,那就理不清楚了。 +这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的,一是文件的依赖关系看不清楚,二是如果文件一多,要加入几个新的 ``.o`` 文件,那就理不清楚了。 清空目标文件的规则 ------------------ -每个Makefile中都应该写一个清空目标文件(\ ``.o``\ 和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。这是一个“修养”(呵呵,还记得我的《编程修养》吗)。一般的风格都是: +每个Makefile中都应该写一个清空目标文件( ``.o`` 和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。这是一个“修养”(呵呵,还记得我的《编程修养》吗)。一般的风格都是: .. code-block:: makefile @@ -230,27 +230,27 @@ GNU的make很强大,它可以自动推导文件以及文件依赖关系后面 Makefile里有什么? ------------------ -Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。 +Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。 -#. 显式规则。显式规则说明了,如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 -#. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写Makefile,这是由make所支持的。 -#. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。 -#. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。 -#. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:\ ``\#``\ 。 +#. 显式规则。显式规则说明了,如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 +#. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写Makefile,这是由make所支持的。 +#. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。 +#. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。 +#. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如: ``\#`` 。 -最后,还值得一提的是,在Makefile中的命令,必须要以\ ``Tab``\ 键开始。 +最后,还值得一提的是,在Makefile中的命令,必须要以 ``Tab`` 键开始。 Makefile的文件名 ---------------- 默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。 -当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的\ ``-f``\ 和\ ``--file``\ 参数,如:\ ``make -f Make.Linux``\ 或\ ``make --file Make.AIX``\ 。 +当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的 ``-f`` 和 ``--file`` 参数,如: ``make -f Make.Linux`` 或 ``make --file Make.AIX`` 。 引用其它的Makefile ------------------ -在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是: +在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是: .. code-block:: makefile @@ -258,24 +258,24 @@ Makefile的文件名 filename可以是当前操作系统Shell的文件模式(可以包含路径和通配符)。 -在include前面可以有一些空字符,但是绝不能是\ ``Tab``\ 键开始。\ ``include``\ 和\ ``;``\ 可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句: +在include前面可以有一些空字符,但是绝不能是 ``Tab`` 键开始。 ``include`` 和 ``;`` 可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句: .. code-block:: makefile include foo.make *.mk $(bar) -等价于: +等价于: .. code-block:: makefile include foo.make a.mk b.mk c.mk e.mk f.mk -make命令开始时,会找寻include所指出的其它Makefile,并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找: +make命令开始时,会找寻include所指出的其它Makefile,并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找: -#. 如果make执行时,有“\ ``-I``\ ”或“\ ``--include-dir``\ ”参数,那么make就会在这个参数所指定的目录下去寻找。 +#. 如果make执行时,有“ ``-I`` ”或“ ``--include-dir`` ”参数,那么make就会在这个参数所指定的目录下去寻找。 #. 如果目录/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。 -如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。如: +如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。如: .. code-block:: makefile diff --git a/source/overview.rst b/source/overview.rst index 04288a5..29e686a 100644 --- a/source/overview.rst +++ b/source/overview.rst @@ -14,12 +14,12 @@ makefile带来的好处就是——“自动化编译”,一旦写好,只需 关于程序的编译和链接 -------------------- -在此,我想多说关于程序编译的一些规范和方法,一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是\ ``.obj``\ 文件,UNIX下是\ ``.o``\ 文件,即Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。 +在此,我想多说关于程序编译的一些规范和方法,一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 ``.obj`` 文件,UNIX下是 ``.o`` 文件,即Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。 编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。 -链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是\ ``.lib``\ 文件,在UNIX下,是Archive File,也就是\ ``.a``\ 文件。 +链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 ``.lib`` 文件,在UNIX下,是Archive File,也就是 ``.a`` 文件。 -总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:\ ``Link 2001错误``\ ,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File。 +总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是: ``Link 2001错误`` ,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File。 好,言归正传,gnu的make有许多的内容,闲言少叙。