fix rst format
This commit is contained in:
parent
c21904a8ae
commit
956141d25f
@ -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`` 参数。
|
||||
|
@ -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不允许把整个条件语句分成两部分放在不同的文件中。
|
||||
|
@ -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函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。例如:
|
||||
|
||||
示例一:
|
||||
|
||||
|
@ -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”或 “--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`` 。下面是所有隐含规则中会用到的变量:
|
||||
|
||||
关于命令的变量。
|
||||
~~~~~~~~~~~~~~~~
|
||||
@ -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`` 文件的依赖关系也写好,那么,所有的目标都会得到满足)
|
||||
|
||||
自动化变量
|
||||
~~~~~~~~~~
|
||||
|
@ -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>;``\ 可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句:
|
||||
在include前面可以有一些空字符,但是绝不能是 ``Tab`` 键开始。 ``include`` 和 ``<filename>;`` 可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.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前加一个减号“-”。如:
|
||||
|
@ -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有许多的内容,闲言少叙。
|
||||
|
Loading…
x
Reference in New Issue
Block a user