fix rst format

This commit is contained in:
Dongdong Tian 2015-11-09 11:41:27 +08:00
parent c21904a8ae
commit 956141d25f
6 changed files with 250 additions and 250 deletions

View File

@ -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`` 参数。

View File

@ -6,7 +6,7 @@
示例
----
下面的例子,判断$(CC)变量是否“gcc”如果是的话则使用GNU函数编译目标。
下面的例子,判断 ``$(CC)`` 变量是否 ``gcc`` 如果是的话则使用GNU函数编译目标。
.. code-block:: makefile
@ -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
@ -75,9 +75,9 @@
<text-if-false>
endif
其中<conditional-directive>;表示条件关键字如“ifeq”。这个关键字有四个。
其中 ``<conditional-directive>`` 表示条件关键字,如 ``ifeq`` 。这个关键字有四个。
第一个是我们前面所见过的“ifeq”
第一个是我们前面所见过的 ``ifeq``
.. code-block:: makefile
@ -87,7 +87,7 @@
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
比较参数“arg1”和“arg2”的值是否相同。当然参数中我们还可以使用make的函数。如
比较参数 ``arg1````arg2`` 的值是否相同。当然参数中我们还可以使用make的函数。如
.. code-block:: makefile
@ -95,9 +95,9 @@
<text-if-empty>
endif
这个示例中使用了“strip”函数如果这个函数的返回值是空Empty那么<text-if-empty>;就生效。
这个示例中使用了 ``strip`` 函数如果这个函数的返回值是空Empty那么 ``<text-if-empty>`` 就生效。
第二个条件关键字是“ifneq”。语法是:
第二个条件关键字是 ``ifneq`` 。语法是:
.. code-block:: makefile
@ -107,15 +107,15 @@
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"
其比较参数“arg1”和“arg2”的值是否相同如果不同则为真。和“ifeq”类似。
其比较参数 ``arg1````arg2`` 的值是否相同,如果不同,则为真。和 ``ifeq`` 类似。
第三个条件关键字是“ifdef”。语法是:
第三个条件关键字是 ``ifdef`` 。语法是:
.. code-block:: makefile
ifdef <variable-name>
如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假。当然,<variable- name>同样可以是一个函数的返回值。注意ifdef只是测试一个变量是否有值其并不会把变量扩展到当前位置。还是来看两个例子
如果变量 ``<variable-name>`` 的值非空,那到表达式为真。否则,表达式为假。当然, ``<variable-name>`` 同样可以是一个函数的返回值。注意, ``ifdef`` 只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子:
示例一:
@ -140,18 +140,18 @@
frobozz = no
endif
第一个例子中,“$(frobozz)”值是“yes”第二个则是“no”。
第一个例子中, ``$(frobozz)`` 值是 ``yes`` ,第二个则是 ``no``
第四个条件关键字是“ifndef”。其语法是:
第四个条件关键字是 ``ifndef`` 。其语法是:
.. code-block:: makefile
ifndef <variable-name>
这个我就不多说了,和“ifdef”是相反的意思。
这个我就不多说了,和 `ifdef`` 是相反的意思。
在<conditional-directive>这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令)。而注释符“#”同样也是安全的。“else”和“endif”也一样只要不是以[Tab]键开始就行了。
``<conditional-directive>`` 这一行上,多余的空格是被允许的,但是不能以 ``Tab`` 键做为开始(不然就被认为是命令)。而注释符 ``#`` 同样也是安全的。 ``else````endif`` 也一样,只要不是以 ``Tab`` 键开始就行了。
特别注意的是make是在读取Makefile时就计算条件表达式的值并根据条件表达式的值来选择语句所以你最好不要把自动化变量“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。
特别注意的是make是在读取Makefile时就计算条件表达式的值并根据条件表达式的值来选择语句所以你最好不要把自动化变量 ``$@`` 等)放入条件表达式中,因为自动化变量是在运行时才有的。
而且为了避免混乱make不允许把整个条件语句分成两部分放在不同的文件中。

View File

@ -6,7 +6,7 @@
函数的调用语法
--------------
函数调用,很像变量的使用,也是以“$”来标识的,其語法如下:
函数调用,很像变量的使用,也是以 ``$`` 来标识的,其語法如下:
.. code-block:: makefile
@ -18,7 +18,7 @@
${<function> <arguments>}
这里,<function>就是函数名make支持的函数不多。<arguments>为函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使用变量,为了风格的统一,函数和变量的括号最好一样,如使用“$(subst a,b,$(x))”这样的形式,而不是“$(subst a,b, ${x})”的形式。因为统一会更清楚,也会减少一些不必要的麻烦。
这里, ``<function>`` 就是函数名make支持的函数不多。 ``<arguments>`` 为函数的参数,参数间以逗号 ``,`` 分隔,而函数名和参数之间以“空格”分隔。函数调用以 ``$`` 开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使用变量,为了风格的统一,函数和变量的括号最好一样,如使用 ``$(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``
字符串处理函数
--------------
@ -42,8 +42,8 @@ subst
$(subst <from>,<to>,<text>)
- 名称:字符串替换函数——subst。
- 功能:把字串<text>中的<from>字符串替换成<to>。
- 名称:字符串替换函数
- 功能:把字串 ``<text>`` 中的 ``<from>`` 字符串替换成 ``<to>``
- 返回:函数返回被替换过后的字符串。
- 示例:
@ -52,7 +52,7 @@ subst
$(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
~~~~~~~~
@ -61,8 +61,8 @@ patsubst
$(patsubst <pattern>,<replacement>,<text>)
- 名称:模式字符串替换函数——patsubst
- 功能:查找<text>中的单词单词以“空格”、“Tab”或“回车”“换行”分隔是否符合模式< pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符 “%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个 “%”将是<pattern>中的那个“%”所代表的字串。(可以用“\\”来转义,以“\\%”来表示真实含义的“%”字符)
- 名称:模式字符串替换函数。
- 功能:查找 ``<text>`` 中的单词单词以“空格”、“Tab”或“回车”“换行”分隔是否符合模式 ``<pattern>`` ,如果匹配的话,则以 ``<replacement>`` 替换。这里, ``<pattern>`` 可以包括通配符 ``%`` ,表示任意长度的字串。如果 ``<replacement>`` 中也包含 ``%`` ,那么, ``<replacement>`` 中的这个 ``%`` 将是 ``<pattern>`` 中的那个 ``%`` 所代表的字串。(可以用 ``\`` 来转义,以 ``\%`` 来表示真实含义的 ``%`` 字符)
- 返回:函数返回被替换过后的字符串。
- 示例:
@ -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:<pattern>=<replacement>;)”相当于“$(patsubst <pattern>,<replacement>,$(var))”,而“$(var: <suffix>=<replacement>)”则相当于“$(patsubst %<suffix>,%<replacement>,$(var))”
- 备注:这和我们前面“变量章节”说过的相关知识有点相似。如 ``$(var:<pattern>=<replacement>;)`` 相当于 ``$(patsubst <pattern>,<replacement>,$(var))`` ,而 ``$(var: <suffix>=<replacement>)`` 则相当于 ``$(patsubst %<suffix>,%<replacement>,$(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 <string>)
- 名称:去空格函数——strip
- 功能:去掉<string>;字串中开头和结尾的空字符。
- 名称:去空格函数。
- 功能:去掉 ``<string>`` 字串中开头和结尾的空字符。
- 返回:返回被去掉空格的字符串值。
- 示例:
@ -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 <find>,<in>)
- 名称:查找字符串函数——findstring。
- 功能:在字串<in>中查找<find>字串。
- 返回:如果找到,那么返回<find>,否则返回空字符串。
- 名称:查找字符串函数
- 功能:在字串 ``<in>`` 中查找 ``<find>`` 字串。
- 返回:如果找到,那么返回 ``<find>`` ,否则返回空字符串。
- 示例:
.. 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 <pattern...>,<text>)
- 名称:过滤函数——filter。
- 功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
- 返回:返回符合模式<pattern>;的字串。
- 名称:过滤函数
- 功能:以 ``<pattern>`` 模式过滤 ``<text>`` 字符串中的单词,保留符合模式 ``<pattern>`` 的单词。可以有多个模式。
- 返回:返回符合模式 ``<pattern>`` 的字串。
- 示例:
.. 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,9 +144,9 @@ filter-out
$(filter-out <pattern...>,<text>)
- 名称:反过滤函数——filter-out。
- 功能:以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。
- 返回:返回不符合模式<pattern>的字串。
- 名称:反过滤函数
- 功能:以 ``<pattern>`` 模式过滤 ``<text>`` 字符串中的单词,去除符合模式 ``<pattern>`` 的单词。可以有多个模式。
- 返回:返回不符合模式 ``<pattern>`` 的字串。
- 示例:
.. code-block:: makefile
@ -154,7 +154,7 @@ filter-out
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 <list>)
- 名称:排序函数——sort。
- 功能:给字符串<list>中的单词排序(升序)。
- 名称:排序函数
- 功能:给字符串 ``<list>`` 中的单词排序(升序)。
- 返回:返回排序后的字符串。
- 示例:$(sort foo bar lose)返回“bar foo lose”
- 备注sort函数会去掉<list>中相同的单词。
- 示例: ``$(sort foo bar lose)`` 返回 ``bar foo lose``
- 备注: ``sort`` 函数会去掉 ``<list>`` 中相同的单词。
word
~~~~
@ -176,10 +176,10 @@ word
$(word <n>,<text>)
- 名称:取单词函数——word。
- 功能:取字符串<text>中第<n>个单词。(从一开始)
- 返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。
- 示例:$(word 2, foo bar baz)返回值是“bar”
- 名称:取单词函数
- 功能:取字符串 ``<text>`` 中第 ``<n>`` 个单词。(从一开始)
- 返回:返回字符串 ``<text>`` 中第 ``<n>`` 个单词。如果 ``<n>`` ``<text>`` 中的单词数要大,那么返回空字符串。
- 示例: ``$(word 2, foo bar baz)`` 返回值是 ``bar``
wordlist
~~~~~~~~
@ -188,10 +188,10 @@ wordlist
$(wordlist <ss>,<e>,<text>)
- 名称:取单词串函数——wordlist。
- 功能:从字符串<text>中取从<ss>开始到<e>的单词串。<ss>和<e>是一个数字。
- 返回:返回字符串<text>中从<ss>到<e>的单词字串。如果<ss>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<ss>开始,到<text>结束的单词串。
- 示例:$(wordlist 2, 3, foo bar baz)返回值是“bar baz”
- 名称:取单词串函数
- 功能:从字符串 ``<text>`` 中取从 ``<ss>`` 开始到 ``<e>`` 的单词串。 ``<ss>`` ``<e>`` 是一个数字。
- 返回:返回字符串 ``<text>`` 中从 ``<ss>`` ``<e>`` 的单词字串。如果 ``<ss>`` ``<text>`` 中的单词数要大,那么返回空字符串。如果 ``<e>`` 大于 ``<text>`` 的单词数,那么返回从 ``<ss>`` 开始,到 ``<text>`` 结束的单词串。
- 示例: ``$(wordlist 2, 3, foo bar baz)`` 返回值是 ``bar baz``
words
~~~~~
@ -200,11 +200,11 @@ words
$(words <text>)
- 名称:单词个数统计函数——words。
- 功能:统计<text>中字符串中的单词个数。
- 返回:返回<text>中的单词数。
- 示例:$(words, foo bar baz)返回值是“3”
- 备注:如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text>),<text>)。
- 名称:单词个数统计函数
- 功能:统计 ``<text>`` 中字符串中的单词个数。
- 返回:返回 ``<text>`` 中的单词数。
- 示例: ``$(words, foo bar baz)`` 返回值是 ``3``
- 备注:如果我们要取 ``<text>`` 中最后的一个单词,我们可以这样: ``$(word $(words <text>),<text>)``
firstword
~~~~~~~~~
@ -214,18 +214,18 @@ firstword
$(firstword <text>)
- 名称首单词函数——firstword。
- 功能:取字符串<text>中的第一个单词。
- 返回:返回字符串<text>的第一个单词。
- 示例:$(firstword foo bar)返回值是“foo”。
- 备注这个函数可以用word函数来实现$(word 1,<text>)。
- 功能:取字符串 ``<text>`` 中的第一个单词。
- 返回:返回字符串 ``<text>`` 的第一个单词。
- 示例: ``$(firstword foo bar)`` 返回值是 ``foo``
- 备注:这个函数可以用 ``word`` 函数来实现: ``$(word 1,<text>)``
以上是所有的字符串操作函数如果搭配混合使用可以完成比较复杂的功能。这里举一个现实中应用的例子。我们知道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搜索头文件路径的参数。
文件名操作函数
--------------
@ -240,9 +240,9 @@ dir
$(dir <names...>)
- 名称取目录函数——dir。
- 功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”
- 返回:返回文件名序列<names>的目录部分。
- 示例: $(dir src/foo.c hacks)返回值是“src/ ./”
- 功能:从文件名序列 ``<names>`` 中取出目录部分。目录部分是指最后一个反斜杠( ``/`` )之前的部分。如果没有反斜杠,那么返回 ``./``
- 返回:返回文件名序列 ``<names>`` 的目录部分。
- 示例: ``$(dir src/foo.c hacks)`` 返回值是 ``src/ ./``
notdir
~~~~~~
@ -252,9 +252,9 @@ notdir
$(notdir <names...>)
- 名称取文件函数——notdir。
- 功能:从文件名序列<names>中取出非目录部分。非目录部分是指最後一个反斜杠(“/”)之后的部分。
- 返回:返回文件名序列<names>的非目录部分。
- 示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”
- 功能:从文件名序列 ``<names>`` 中取出非目录部分。非目录部分是指最後一个反斜杠( ``/`` )之后的部分。
- 返回:返回文件名序列 ``<names>`` 的非目录部分。
- 示例: ``$(notdir src/foo.c hacks)`` 返回值是 ``foo.c hacks``
suffix
~~~~~~
@ -264,9 +264,9 @@ suffix
$(suffix <names...>)
- 名称取後缀函数——suffix。
- 功能:从文件名序列<names>中取出各个文件名的后缀。
- 返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。
- 示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。
- 功能:从文件名序列 ``<names>`` 中取出各个文件名的后缀。
- 返回:返回文件名序列 ``<names>`` 的后缀序列,如果文件没有后缀,则返回空字串。
- 示例: ``$(suffix src/foo.c src-1.0/bar.c hacks)`` 返回值是 ``.c .c``
basename
~~~~~~~~
@ -276,9 +276,9 @@ basename
$(basename <names...>)
- 名称取前缀函数——basename。
- 功能:从文件名序列<names>中取出各个文件名的前缀部分。
- 返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。
- 示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”
- 功能:从文件名序列 ``<names>`` 中取出各个文件名的前缀部分。
- 返回:返回文件名序列 ``<names>`` 的前缀序列,如果文件没有前缀,则返回空字串。
- 示例: ``$(basename src/foo.c src-1.0/bar.c hacks)`` 返回值是 ``src/foo src-1.0/bar hacks``
addsuffix
~~~~~~~~~
@ -288,9 +288,9 @@ addsuffix
$(addsuffix <suffix>,<names...>)
- 名称加后缀函数——addsuffix。
- 功能:把后缀<suffix>加到<names>中的每个单词后面。
- 功能:把后缀 ``<suffix>`` 加到 ``<names>`` 中的每个单词后面。
- 返回:返回加过后缀的文件名序列。
- 示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”
- 示例: ``$(addsuffix .c,foo bar)`` 返回值是 ``foo.c bar.c``
addprefix
~~~~~~~~~
@ -300,9 +300,9 @@ addprefix
$(addprefix <prefix>,<names...>)
- 名称加前缀函数——addprefix。
- 功能:把前缀<prefix>加到<names>中的每个单词后面。
- 功能:把前缀 ``<prefix>`` 加到 ``<names>`` 中的每个单词后面。
- 返回:返回加过前缀的文件名序列。
- 示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”
- 示例: ``$(addprefix src/,foo bar)`` 返回值是 ``src/foo src/bar``
join
~~~~
@ -312,9 +312,9 @@ join
$(join <list1>,<list2>)
- 名称连接函数——join。
- 功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。
- 功能:把 ``<list2>`` 中的单词对应地加到 ``<list1>`` 的单词后面。如果 ``<list1>`` 的单词个数要比 ``<list2>`` 的多,那么, ``<list1>`` 中的多出来的单词将保持原样。如果 ``<list2>`` 的单词个数要比 ``<list1>`` 多,那么, ``<list2>`` 多出来的单词将被复制到 ``<list2>`` 中。
- 返回:返回连接过后的字符串。
- 示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”
- 示例: ``$(join aaa bbb , 111 222 333)`` 返回值是 ``aaa111 bbb222 333``
foreach 函数
------------
@ -325,9 +325,9 @@ foreach函数和别的函数非常的不一样。因为这个函数是用来做
$(foreach <var>,<list>,<text>)
这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串以空格分隔将会是foreach函数的返回值。
这个函数的意思是,把参数 ``<list>```` 中的单词逐一取出放到参数 ``<var>`` 所指定的变量中,然后再执行 ``<text>`` 所包含的表达式。每一次 ``<text>`` 会返回一个字符串,循环过程中, ``<text>`` 的所返回的每个字符串会以空格分隔,最后当整个循环结束时, ``<text>`` 所返回的每个字符串所组成的整个字符串以空格分隔将会是foreach函数的返回值。
所以,<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用<var>这个参数来依次枚举<list>中的单词。举个例子:
所以, ``<var>`` 最好是一个变量名, ``<list>`` 可以是一个表达式,而 ``<text>`` 中一般会使用 ``<var>`` 这个参数来依次枚举 ``<list>`` 中的单词。举个例子:
.. code-block:: makefile
@ -335,9 +335,9 @@ foreach函数和别的函数非常的不一样。因为这个函数是用来做
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中的<var>参数是一个临时的局部变量foreach函数执行完后参数<var>的变量将不在作用其作用域只在foreach函数当中。
注意foreach中的 ``<var>`` 参数是一个临时的局部变量foreach函数执行完后参数 ``<var>`` 的变量将不在作用其作用域只在foreach函数当中。
if 函数
-------
@ -354,11 +354,11 @@ if函数很像GNU的make所支持的条件语句——ifeq参见前面所述
$(if <condition>,<then-part>,<else-part>)
可见if函数可以包含“else”部分或是不含。即if函数的参数可以是两个也可以是三个。<condition>参数是if的表达式如果其返回的为非空字符串那么这个表达式就相当于返回真于是<then-part>会被计算,否则<else-part>会被计算。
可见if函数可以包含“else”部分或是不含。即if函数的参数可以是两个也可以是三个。 ``<condition>`` 参数是if的表达式如果其返回的为非空字符串那么这个表达式就相当于返回真于是 ``<then-part>`` 会被计算,否则 ``<else-part>`` 会被计算。
而if函数的返回值是如果<condition>为真(非空字符串),那个<then-part>会是整个函数的返回值,如果<condition>为假(空字符串),那么<else-part>会是整个函数的返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。
而if函数的返回值是如果 ``<condition>`` 为真(非空字符串),那个 ``<then-part>`` 会是整个函数的返回值,如果 ``<condition>`` 为假(空字符串),那么 ``<else-part>`` 会是整个函数的返回值,此时如果 ``<else-part>`` 没有被定义,那么,整个函数返回空字串。
所以,<then-part>和<else-part>只会有一个被计算。
所以, ``<then-part>`` ``<else-part>`` 只会有一个被计算。
call函数
--------
@ -369,7 +369,7 @@ call函数是唯一一个可以用来创建新的参数化的函数。你可以
$(call <expression>;,<parm1>;,<parm2>;,<parm3>;...)
当make执行这个函数时<expression>;参数中的变量,如$(1)$(2)$(3)等,会被参数< parm1>;<parm2>;<parm3>;依次取代。而<expression>;的返回值就是 call函数的返回值。例如
当make执行这个函数时 ``<expression>`` ;参数中的变量,如 ``$(1)`` ``$(2)`` ``$(3)`` 等,会被参数 ``<parm1>;`` ``<parm2>;`` ``<parm3>;`` 依次取代。而 ``<expression>;`` 的返回值就是 call函数的返回值。例如
.. code-block:: makefile
@ -377,7 +377,7 @@ call函数是唯一一个可以用来创建新的参数化的函数。你可以
foo = $(call reverse,a,b)
那么,foo的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如:
那么, ``foo`` 的值就是 ``a b`` 。当然,参数的次序是可以自定义的,不一定是顺序的,如:
.. code-block:: makefile
@ -385,7 +385,7 @@ call函数是唯一一个可以用来创建新的参数化的函数。你可以
foo = $(call reverse,a,b)
此时的foo的值就是“b a”。''
此时的 ``foo`` 的值就是 ``b a``
origin函数
----------
@ -396,22 +396,22 @@ origin函数不像其它的函数他并不操作变量的值他只是告
$(origin <variable>;)
注意,<variable>;是变量的名字,不应该是引用。所以你最好不要在<variable>;中使用“$”字符。Origin函数会以其返回值来告诉你这个变量的“出生情况”下面是origin函数的返回值:
注意, ``<variable>;`` 是变量的名字,不应该是引用。所以你最好不要在 ``<variable>;`` 中使用 ``$`` 字符。Origin函数会以其返回值来告诉你这个变量的“出生情况”下面是origin函数的返回值:
“undefined”
如果<variable>;从来没有定义过origin函数返回这个值“undefined”。
“default”
如果<variable>;是一个默认的定义比如“CC”这个变量这种变量我们将在后面讲述。
“environment”
如果<variable>;是一个环境变量并且当Makefile被执行时“-e”参数没有被打开。
“file”
如果<variable>;这个变量被定义在Makefile中。
“command line”
如果<variable>;这个变量是被命令行定义的。
“override”
如果<variable>;是被override指示符重新定义的。
“automatic”
如果<variable>;是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。
``undefined``
如果 ``<variable>;`` 从来没有定义过origin函数返回这个值 ``undefined``
``default``
如果 ``<variable>;`` 是一个默认的定义比如“CC”这个变量这种变量我们将在后面讲述。
``environment``
如果 ``<variable>;`` 是一个环境变量并且当Makefile被执行时 ``-e`` 参数没有被打开。
``file``
如果 ``<variable>;`` 这个变量被定义在Makefile中。
``command line``
如果 ``<variable>;`` 这个变量是被命令行定义的。
``override``
如果 ``<variable>;`` 是被override指示符重新定义的。
``automatic``
如果 ``<variable>;`` 是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。
这些信息对于我们编写Makefile是非常有用的例如假设我们有一个Makefile其包了一个定义文件Make.def在 Make.def中定义了一个变量“bletch”而我们的环境中也有一个环境变量“bletch”此时我们想判断一下如果变量来源于环境那么我们就把之重定义了如果来源于Make.def或是命令行等非环境的那么我们就不重新定义它。于是在我们的Makefile中我们可以这样写
@ -423,7 +423,7 @@ origin函数不像其它的函数他并不操作变量的值他只是告
endif
endif
当然你也许会说使用override关键字不就可以重新定义环境中的变量了吗为什么需要使用这样的步骤是的我们用override是可以达到这样的效果可是override过于粗暴它同时会把从命令行定义的变量也覆盖了而我们只想重新定义环境传来的而不想重新定义命令行传来的。
当然,你也许会说,使用 ``override`` 关键字不就可以重新定义环境中的变量了吗?为什么需要使用这样的步骤?是的,我们用 ``override`` 是可以达到这样的效果,可是 ``override`` 过于粗暴,它同时会把从命令行定义的变量也覆盖了,而我们只想重新定义环境传来的,而不想重新定义命令行传来的。
shell函数
---------
@ -447,7 +447,7 @@ make提供了一些函数来控制make的运行。通常你需要检测一些
$(error <text ...>;)
产生一个致命的错误,<text ...>;是错误信息。注意error函数不会在一被使用就会产生错误信息所以如果你把其定义在某个变量中并在后续的脚本中使用这个变量那么也是可以的。例如
产生一个致命的错误, ``<text ...>`` 是错误信息。注意error函数不会在一被使用就会产生错误信息所以如果你把其定义在某个变量中并在后续的脚本中使用这个变量那么也是可以的。例如
示例一:

View File

@ -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程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.c”并且其生成命令是“$(CC) c $(CPPFLAGS) $(CFLAGS)”
``<n>.o`` 的目标的依赖目标会自动推导为 ``<n>.c`` ,并且其生成命令是 ``$(CC) c $(CPPFLAGS) $(CFLAGS)``
#. 编译C++程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”并且其生成命令是 “$(CXX) c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”
``<n>.o`` 的目标的依赖目标会自动推导为 ``<n>.cc`` 或是 ``<n>.C`` ,并且其生成命令是 ``$(CXX) c $(CPPFLAGS) $(CFLAGS)`` 。(建议使用 ``.cc`` 作为C++源文件的后缀,而不是 ``.C``
#. 编译Pascal程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.p”并且其生成命令是“$(PC) c $(PFLAGS)”
``<n>.o`` 的目标的依赖目标会自动推导为 ``<n>.p`` ,并且其生成命令是 ``$(PC) c $(PFLAGS)``
#. 编译Fortran/Ratfor程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”或“<n>.f”,并且其生成命令是:
``<n>.o`` 的目标的依赖目标会自动推导为 ``<n>.r````<n>.F````<n>.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程序的隐含规则。
“<n>.f”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是
``<n>.f`` 的目标的依赖目标会自动推导为 ``<n>.r````<n>.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程序的隐含规则。
“<n>.sym”的目标的依赖目标会自动推导为“<n>.def”并且其生成命令是“$(M2C) $ (M2FLAGS) $(DEFFLAGS)”。“<n>.o” 的目标的依赖目标会自动推导为“<n>.mod”并且其生成命令是“$(M2C) $(M2FLAGS) $(MODFLAGS)”
``<n>.sym`` 的目标的依赖目标会自动推导为 ``<n>.def`` ,并且其生成命令是: ``$(M2C) $(M2FLAGS) $(DEFFLAGS)````<n>.o`` 的目标的依赖目标会自动推导为 ``<n>.mod`` ,并且其生成命令是: ``$(M2C) $(M2FLAGS) $(MODFLAGS)``
#. 汇编和汇编预处理的隐含规则。
“<n>.o” 的目标的依赖目标会自动推导为“<n>.s”默认使用编译品“as”并且其生成命令是“$ (AS) $(ASFLAGS)”。“<n>.s” 的目标的依赖目标会自动推导为“<n>.S”默认使用C预编译器 “cpp”并且其生成命令是“$(AS) $(ASFLAGS)”
``<n>.o`` 的目标的依赖目标会自动推导为 ``<n>.s`` ,默认使用编译品 ``as`` ,并且其生成命令是: ``$ (AS) $(ASFLAGS)````<n>.s`` 的目标的依赖目标会自动推导为 ``<n>.S`` 默认使用C预编译器 ``cpp`` ,并且其生成命令是: ``$(AS) $(ASFLAGS)``
#. 链接Object文件的隐含规则。
“<n>”目标依赖于“<n>.o”通过运行C的编译器来运行链接程序生成一般是“ld”其生成命令是 “$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效同时也对多个Object文件由不同的源文件生成的也有效。例如如下规则::
``<n>`` 目标依赖于 ``<n>.o`` 通过运行C的编译器来运行链接程序生成一般是 ``ld`` ),其生成命令是: ``$(CC) $(LDFLAGS) <n>.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,28 +107,28 @@ make会在自己的“隐含规则”库中寻找可以用的规则如果找
#. Yacc C程序时的隐含规则。
“<n>.c”的依赖文件被自动推导为“n.y”Yacc生成的文件其生成命令是“$(YACC) $(YFALGS)”“Yacc”是一个语法分析器关于其细节请查看相关资料
``<n>.c`` 的依赖文件被自动推导为 ``n.y`` Yacc生成的文件其生成命令是 ``$(YACC) $(YFALGS)`` “Yacc”是一个语法分析器关于其细节请查看相关资料
#. Lex C程序时的隐含规则。
“<n>.c”的依赖文件被自动推导为“n.l”Lex生成的文件其生成命令是“$(LEX) $(LFALGS)”关于“Lex”的细节请查看相关资料
``<n>.c`` 的依赖文件被自动推导为 ``n.l`` Lex生成的文件其生成命令是 ``$(LEX) $(LFALGS)`` 关于“Lex”的细节请查看相关资料
#. Lex Ratfor程序时的隐含规则。
“<n>.r”的依赖文件被自动推导为“n.l”Lex生成的文件其生成命令是“$(LEX) $(LFALGS)”
``<n>.r`` 的依赖文件被自动推导为 ``n.l`` Lex生成的文件其生成命令是 ``$(LEX) $(LFALGS)``
#. 从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。
“<n>.ln” lint生成的文件的依赖文件被自动推导为“n.c”其生成命令是“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>.y”和“<n>.l”也是同样的规则。
``<n>.ln`` lint生成的文件的依赖文件被自动推导为 ``n.c`` ,其生成命令是: ``$(LINT) $(LINTFALGS) $(CPPFLAGS) -i`` 。对于 ``<n>.y````<n>.l`` 也是同样的规则。
隐含规则使用的变量
------------------
在隐含规则中的命令中基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值或是在make的命令行中传入这些值或是在你的环境变量中设置这些值无论怎么样只要设置了这些特定的变量那么其就会对隐含规则起作用。当然你也可以利用make的“-R”或 “--nobuiltin-variables”参数来取消你所定义的变量对隐含规则的作用。
在隐含规则中的命令中基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值或是在make的命令行中传入这些值或是在你的环境变量中设置这些值无论怎么样只要设置了这些特定的变量那么其就会对隐含规则起作用。当然你也可以利用make的 ``-R````--nobuiltin-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`` 。下面是所有隐含规则中会用到的变量:
关于命令的变量。
~~~~~~~~~~~~~~~~
@ -176,15 +176,15 @@ make会在自己的“隐含规则”库中寻找可以用的规则如果找
隐含规则链
----------
有些时候,一个目标可能被一系列的隐含规则所作用。例如,一个[.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 ; <command ......>;
其含义是,指出了怎么从所有的[.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 <n>.y”执行然后生成“<n>.tab.c”和“<n>.tab.h”文件。其中“<n>”表示一个任意字符串。如果我们的执行程序“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 <n>.y”执行然后生成“<n>.tab.c”和“<n>.tab.h”文件。其中“<n>”表示一个任意字符串。如果我们的执行程序“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`` 文件的依赖关系也写好,那么,所有的目标都会得到满足)
自动化变量
~~~~~~~~~~

View File

@ -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``\ 了。
#. 如果edit文件不存在或是edit所依赖的后面的 ``.o`` 文件的文件修改时间要比 ``edit`` 这个文件新,那么,他就会执行后面所定义的命令来生成 ``edit`` 这个文件。
#. 如果 ``edit`` 所依赖的 ``.o`` 文件也不存在那么make会在当前文件中找目标为 ``.o`` 文件的依赖性,如果找到则再根据那一个规则生成 ``.o`` 文件。(这有点像一个堆栈的过程)
#. 当然你的C文件和H文件是存在的啦于是make会生成 ``.o`` 文件,然后再用 ``.o`` 文件生成make的终极任务也就是执行文件 ``edit`` 了。
这就是整个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,16 +109,16 @@ 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
@ -146,16 +146,16 @@ 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
@ -185,7 +185,7 @@ GNU的make很强大它可以自动推导文件以及文件依赖关系后面
另类风格的makefiles
-------------------
既然我们的make可以自动推导命令那么我看到那堆\ ``.o``\\ ``.h``\ 的依赖就有点不爽,那么多的重复的\ ``.h``\ 能不能把其收拢起来好吧没有问题这个对于make来说很容易谁叫它提供了自动推导命令和文件的功能呢来看看最新风格的makefile吧。
既然我们的make可以自动推导命令那么我看到那堆 ``.o`` 和 ``.h`` 的依赖就有点不爽,那么多的重复的 ``.h`` 能不能把其收拢起来好吧没有问题这个对于make来说很容易谁叫它提供了自动推导命令和文件的功能呢来看看最新风格的makefile吧。
.. code-block:: makefile
@ -203,12 +203,12 @@ GNU的make很强大它可以自动推导文件以及文件依赖关系后面
clean :
rm edit $(objects)
这种风格让我们的makefile变得很简单但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的一是文件的依赖关系看不清楚二是如果文件一多要加入几个新的\ ``.o``\ 文件,那就理不清楚了。
这种风格让我们的makefile变得很简单但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的一是文件的依赖关系看不清楚二是如果文件一多要加入几个新的 ``.o`` 文件,那就理不清楚了。
清空目标文件的规则
------------------
每个Makefile中都应该写一个清空目标文件\ ``.o``\ 和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。这是一个“修养”(呵呵,还记得我的《编程修养》吗)。一般的风格都是:
每个Makefile中都应该写一个清空目标文件 ``.o`` 和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。这是一个“修养”(呵呵,还记得我的《编程修养》吗)。一般的风格都是:
.. code-block:: makefile
@ -236,16 +236,16 @@ Makefile里主要包含了五个东西显式规则、隐晦规则、变量定
#. 隐晦规则。由于我们的make有自动推导的功能所以隐晦的规则可以让我们比较简略地书写Makefile这是由make所支持的。
#. 变量的定义。在Makefile中我们要定义一系列的变量变量一般都是字符串这个有点像你C语言中的宏当Makefile被执行时其中的变量都会被扩展到相应的引用位置上。
#. 文件指示。其包括了三个部分一个是在一个Makefile中引用另一个Makefile就像C语言中的include一样另一个是指根据某些情况指定Makefile中的有效部分就像C语言中的预编译#if一样还有就是定义一个多行的命令。有关这一部分的内容我会在后续的部分中讲述。
#. 注释。Makefile中只有行注释和UNIX的Shell脚本一样其注释是用“#”字符这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:\ ``\#``\
#. 注释。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
------------------
@ -258,7 +258,7 @@ Makefile的文件名
filename可以是当前操作系统Shell的文件模式可以包含路径和通配符
在include前面可以有一些空字符但是绝不能是\ ``Tab``\ 键开始。\ ``include``\\ ``<filename>;``\ 可以用一个或多个空格隔开。举个例子你有这样几个Makefilea.mk、b.mk、c.mk还有一个文件叫foo.make以及一个变量$(bar)其包含了e.mk和f.mk那么下面的语句
在include前面可以有一些空字符但是绝不能是 ``Tab`` 键开始。 ``include`` 和 ``<filename>;`` 可以用一个或多个空格隔开。举个例子你有这样几个Makefilea.mk、b.mk、c.mk还有一个文件叫foo.make以及一个变量$(bar)其包含了e.mk和f.mk那么下面的语句
.. code-block:: makefile
@ -272,7 +272,7 @@ filename可以是当前操作系统Shell的文件模式可以包含路径和
make命令开始时会找寻include所指出的其它Makefile并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话make会在当前目录下首先寻找如果当前目录下没有找到那么make还会在下面的几个目录下找
#. 如果make执行时有“\ ``-I``\ ”或“\ ``--include-dir``\ ”参数那么make就会在这个参数所指定的目录下去寻找。
#. 如果make执行时有“ ``-I`` ”或“ ``--include-dir`` ”参数那么make就会在这个参数所指定的目录下去寻找。
#. 如果目录<prefix>/include一般是/usr/local/bin或/usr/include存在的话make也会去找。
如果有文件没有找到的话make会生成一条警告信息但不会马上出现致命错误。它会继续载入其它的文件一旦完成makefile的读取make会再重试这些没有找到或是不能读取的文件如果还是不行make才会出现一条致命信息。如果你想让make不理那些无法读取的文件而继续执行你可以在include前加一个减号“-”。如:

View File

@ -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有许多的内容闲言少叙。