bug fixed

This commit is contained in:
Dongdong Tian 2014-03-07 14:00:09 +08:00
parent 13b125042e
commit aadc6931a3
9 changed files with 195 additions and 153 deletions

View File

@ -1,16 +1,17 @@
使用make更新函数库文件
======================
函数库文件也就是对Object文件程序编译的中间文件的打包文件。在Unix下一般是由命令"ar"来完成打包工作。
函数库文件也就是对Object文件程序编译的中间文件的打包文件。在Unix下一般是由命令“ar”来完成打包工作。
函数库文件的成员
----------------
一个函数库文件由多个文件组成。你可以以如下格式指定函数库文件及其组成::
archive(member)
这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了"ar"命令来服务的。如:
这个不是一个命令,而一个目标和依赖的定义。一般来说,这种用法基本上就是为了“ar”命令来服务的。如:
.. code-block:: makefile
@ -38,18 +39,18 @@
函数库成员的隐含规则
--------------------
当make搜索一个目标的隐含规则时一个特殊的特性是如果这个目标是"a(m)"形式的,其会把目标变成"(m)"。于是,如果我们的成员是 "%.o"的模式定义,并且如果我们使用"make foo.a(bar.o)"的形式调用Makefile时隐含规则会去找"bar.o"的规则如果没有定义bar.o的规则那么内建隐含规则生效make会去找bar.c文件来生成bar.o如果找得到的话make执行的命令大致如下::
当make搜索一个目标的隐含规则时一个特殊的特性是如果这个目标是“a(m)”形式的,其会把目标变成“(m)”。于是,如果我们的成员是“%.o”的模式定义并且如果我们使用“make foo.a(bar.o)”的形式调用Makefile时隐含规则会去找“bar.o”的规则如果没有定义bar.o的规则那么内建隐含规则生效make会去找bar.c文件来生成bar.o如果找得到的话make执行的命令大致如下::
cc -c bar.c -o bar.o
ar r foo.a bar.o
rm -f bar.o
还有一个变量要注意的是"$%",这是专属函数库文件的自动化变量,有关其说明请参见"自动化变量"一节。
还有一个变量要注意的是“$%”,这是专属函数库文件的自动化变量,有关其说明请参见“自动化变量”一节。
函数库文件的后缀规则
--------------------
你可以使用"后缀规则"和"隐含规则"来生成函数库打包文件,如:
你可以使用“后缀规则”和“隐含规则”来生成函数库打包文件,如:
.. code-block:: makefile
@ -67,9 +68,10 @@
$(AR) r $@ $*.o
$(RM) $*.o
注意事项
--------
在进行函数库打包文件生成时请小心使用make的并行机制"-j"参数。如果多个ar命令在同一时间运行在同一个函数库打包文件上就很有可以损坏这个函数库文件。所以在make未来的版本中应该提供一种机制来避免并行操作发生在函数打包文件上。
在进行函数库打包文件生成时请小心使用make的并行机制“-j”参数。如果多个ar命令在同一时间运行在同一个函数库打包文件上就很有可以损坏这个函数库文件。所以在make未来的版本中应该提供一种机制来避免并行操作发生在函数打包文件上。
但就目前而言,你还是应该不要尽量不要使用"-j"参数。
但就目前而言,你还是应该不要尽量不要使用“-j”参数。

View File

@ -197,7 +197,10 @@ latex_elements = {
\setCJKmonofont{SimHei}
\XeTeXlinebreaklocale "zh"
\XeTeXlinebreakskip = 0pt plus 1pt
\parindent 2em
\setcounter{tocdepth}{3}
\renewcommand\familydefault{\ttdefault}
\renewcommand\CJKfamilydefault{\CJKrmdefault}
''',
'releasename' : 'By SeisMan@GitHub',
'release' : '',

View File

@ -35,6 +35,9 @@
字符串处理函数
--------------
subst
~~~~~
.. code-block:: makefile
$(subst <from>,<to>,<text>)
@ -43,39 +46,42 @@
- 功能:把字串<text>中的<from>字符串替换成<to>。
- 返回:函数返回被替换过后的字符串。
**示例**
- 示例
.. code-block:: makefile
.. code-block:: makefile
$(subst ee,EE,feet on the street)
$(subst ee,EE,feet on the street)
把“feet on the street”中的“ee”替换成“EE”返回结果是“fEEt on the strEEt”。
patsubst
~~~~~~~~
.. code-block:: makefile
$(patsubst <pattern>,<replacement>,<text>)
- 名称模式字符串替换函数——patsubst。
- 功能:查找<text>中的单词单词以“空格”、“Tab”或“回车”“换行”分隔是否符合模式< pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符 “%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个 “%”将是<pattern>中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)
- 功能:查找<text>中的单词单词以“空格”、“Tab”或“回车”“换行”分隔是否符合模式< pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符 “%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个 “%”将是<pattern>中的那个“%”所代表的字串。(可以用“\\”来转义,以“\\%”来表示真实含义的“%”字符)
- 返回:函数返回被替换过后的字符串。
- 示例:
**示例**
.. code-block:: makefile
.. code-block:: makefile
$(patsubst %.c,%.o,x.c.c bar.c)
$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o]返回结果是“x.c.o bar.o”
**备注**
- 备注:这和我们前面“变量章节”说过的相关知识有点相似。如“$(var:<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
~~~~~
.. code-block:: makefile
@ -84,32 +90,36 @@
- 名称去空格函数——strip。
- 功能:去掉<string>;字串中开头和结尾的空字符。
- 返回:返回被去掉空格的字符串值。
- 示例:
**示例**
.. code-block:: makefile
$(strip a b c )
把字串“a b c ”去到开头和结尾的空格结果是“a b c”。
findstring
~~~~~~~~~~
.. code-block:: makefile
$(strip a b c )
把字串“a b c ”去到开头和结尾的空格结果是“a b c”。
.. code-block:: makefile
$(findstring <find>,<in>)
$(findstring <find>,<in>)
- 名称查找字符串函数——findstring。
- 功能:在字串<in>中查找<find>字串。
- 返回:如果找到,那么返回<find>,否则返回空字符串。
- 示例:
**示例**
.. code-block:: makefile
.. code-block:: makefile
$(findstring a,a b c)
$(findstring a,b c)
$(findstring a,a b c)
$(findstring a,b c)
第一个函数返回“a”字符串第二个返回“”字符串空字符串
filter
~~~~~~
.. code-block:: makefile
$(filter <pattern...>,<text>)
@ -117,16 +127,18 @@
- 名称过滤函数——filter。
- 功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
- 返回:返回符合模式<pattern>;的字串。
- 示例:
**示例**
.. code-block:: makefile
.. code-block:: makefile
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。
$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。
filter-out
~~~~~~~~~~
.. code-block:: makefile
@ -135,16 +147,18 @@ $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。
- 名称反过滤函数——filter-out。
- 功能:以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。
- 返回:返回不符合模式<pattern>的字串。
- 示例:
**示例**
.. code-block:: makefile
.. code-block:: makefile
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
sort
~~~~
.. code-block:: makefile
$(sort <list>)
@ -152,9 +166,11 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称排序函数——sort。
- 功能:给字符串<list>中的单词排序(升序)。
- 返回:返回排序后的字符串。
- 示例:$(sort foo bar lose)返回“bar foo lose” 。
- 备注sort函数会去掉<list>中相同的单词。
**示例** $(sort foo bar lose)返回“bar foo lose” 。
**备注** sort函数会去掉<list>中相同的单词。
word
~~~~
.. code-block:: makefile
@ -163,8 +179,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称取单词函数——word。
- 功能:取字符串<text>中第<n>个单词。(从一开始)
- 返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。
- 示例:$(word 2, foo bar baz)返回值是“bar”。
**示例** $(word 2, foo bar baz)返回值是“bar”。
wordlist
~~~~~~~~
.. code-block:: makefile
@ -173,8 +191,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称取单词串函数——wordlist。
- 功能:从字符串<text>中取从<ss>开始到<e>的单词串。<ss>和<e>是一个数字。
- 返回:返回字符串<text>中从<ss>到<e>的单词字串。如果<ss>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<ss>开始,到<text>结束的单词串。
- 示例:$(wordlist 2, 3, foo bar baz)返回值是“bar baz”。
**示例** $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。
words
~~~~~
.. code-block:: makefile
@ -183,9 +203,11 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称单词个数统计函数——words。
- 功能:统计<text>中字符串中的单词个数。
- 返回:返回<text>中的单词数。
- 示例:$(words, foo bar baz)返回值是“3”。
- 备注:如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text>),<text>)。
**示例** $(words, foo bar baz)返回值是“3”。
**备注** :如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text>),<text>)。
firstword
~~~~~~~~~
.. code-block:: makefile
@ -194,9 +216,8 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称首单词函数——firstword。
- 功能:取字符串<text>中的第一个单词。
- 返回:返回字符串<text>的第一个单词。
**示例** $(firstword foo bar)返回值是“foo”。
**备注** 这个函数可以用word函数来实现$(word 1,<text>)。
- 示例:$(firstword foo bar)返回值是“foo”。
- 备注这个函数可以用word函数来实现$(word 1,<text>)。
以上是所有的字符串操作函数如果搭配混合使用可以完成比较复杂的功能。这里举一个现实中应用的例子。我们知道make使用“VPATH”变量来指定“依赖文件”的搜索路径。于是我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数CFLAGS
@ -211,6 +232,9 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或是一系列的文件名来对待。
dir
~~~
.. code-block:: makefile
$(dir <names...>)
@ -218,8 +242,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称取目录函数——dir。
- 功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
- 返回:返回文件名序列<names>的目录部分。
- 示例: $(dir src/foo.c hacks)返回值是“src/ ./”。
**示例** $(dir src/foo.c hacks)返回值是“src/ ./”。
notdir
~~~~~~
.. code-block:: makefile
@ -228,9 +254,11 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称取文件函数——notdir。
- 功能:从文件名序列<names>中取出非目录部分。非目录部分是指最後一个反斜杠(“/”)之后的部分。
- 返回:返回文件名序列<names>的非目录部分。
**示例** : $(notdir src/foo.c hacks)返回值是“foo.c hacks”。
- 示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。
suffix
~~~~~~
.. code-block:: makefile
$(suffix <names...>)
@ -238,8 +266,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称取後缀函数——suffix。
- 功能:从文件名序列<names>中取出各个文件名的后缀。
- 返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。
- 示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。
**示例** $(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。
basename
~~~~~~~~
.. code-block:: makefile
@ -248,8 +278,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称取前缀函数——basename。
- 功能:从文件名序列<names>中取出各个文件名的前缀部分。
- 返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。
- 示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。
**示例** $(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。
addsuffix
~~~~~~~~~
.. code-block:: makefile
@ -258,8 +290,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称加后缀函数——addsuffix。
- 功能:把后缀<suffix>加到<names>中的每个单词后面。
- 返回:返回加过后缀的文件名序列。
- 示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
**示例** $(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
addprefix
~~~~~~~~~
.. code-block:: makefile
@ -268,8 +302,10 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称加前缀函数——addprefix。
- 功能:把前缀<prefix>加到<names>中的每个单词后面。
- 返回:返回加过前缀的文件名序列。
- 示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。
**示例** $(addprefix src/,foo bar)返回值是“src/foo src/bar”。
join
~~~~
.. code-block:: makefile
@ -278,19 +314,18 @@ $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。
- 名称连接函数——join。
- 功能:把<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 函数
------------
foreach函数和别的函数非常的不一样。因为这个函数是用来做循环用的Makefile中的foreach函数几乎是仿照于Unix标准 Shell/bin/sh中的for语句或是C-Shell/bin/csh中的foreach语句而构建的。它的语法是
foreach函数和别的函数非常的不一样。因为这个函数是用来做循环用的Makefile中的foreach函数几乎是仿照于Unix标准Shell/bin/sh中的for语句或是C-Shell/bin/csh中的foreach语句而构建的。它的语法是
.. code-block:: makefile
$(foreach <var>,<list>,<text>)
这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行< text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串以空格分隔将会是foreach函数的返回值。
这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串以空格分隔将会是foreach函数的返回值。
所以,<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用<var>这个参数来依次枚举<list>中的单词。举个例子:
@ -319,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函数
--------

View File

@ -54,19 +54,19 @@ make会在自己的“隐含规则”库中寻找可以用的规则如果找
#. 编译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)”
@ -74,25 +74,26 @@ make会在自己的“隐含规则”库中寻找可以用的规则如果找
#. 预处理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)”
#. 编译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
@ -102,23 +103,23 @@ make会在自己的“隐含规则”库中寻找可以用的规则如果找
rm -f y.o
rm -f z.o
如果没有一个源文件如上例中的x.c和你的目标名字如上例中的x相关联那么你最好写出自己的生成规则不然隐含规则会报错的。
如果没有一个源文件如上例中的x.c和你的目标名字如上例中的x相关联那么你最好写出自己的生成规则不然隐含规则会报错的。
#. 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”也是同样的规则。
隐含规则使用的变量
------------------
@ -194,24 +195,24 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
定义模式规则
------------
你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有"%"字符。"%"的意思是表示一个或多个任意字符。在依赖目标中同样可以使用"%",只是依赖目标中的"%"的取值,取决于其目标。
你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有“%”字符。“%“的意思是表示一个或多个任意字符。在依赖目标中同样可以使用“%”,只是依赖目标中的“%“的取值,取决于其目标。
有一点需要注意的是,"%"的展开发生在变量和函数的展开之后变量和函数的展开发生在make载入Makefile时而模式规则中的"%"则发生在运行时。
有一点需要注意的是,“%“的展开发生在变量和函数的展开之后变量和函数的展开发生在make载入Makefile时而模式规则中的“%“则发生在运行时。
模式规则介绍
~~~~~~~~~~~~
模式规则中,至少在规则的目标定义中要包含"%",否则,就是一般的规则。目标中的"%"定义表示对文件名的匹配,"%"表示长度任意的非空字符串。例如:"%.c"表示以".c"结尾的文件名文件名的长度至少为3而"s.%.c"则表示以"s."开头,".c"结尾的文件名文件名的长度至少为5
模式规则中,至少在规则的目标定义中要包含“%“,否则,就是一般的规则。目标中的“%“定义表示对文件名的匹配,“%“表示长度任意的非空字符串。例如:“%.c“表示以“.c“结尾的文件名文件名的长度至少为3而“s.%.c“则表示以“s.“开头,“.c“结尾的文件名文件名的长度至少为5
如果"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值,也就是说,目标中的模式的"%"决定了依赖目标中"%"的样子。例如有一个模式规则如下:
如果“%“定义在目标中,那么,目标中的“%“的值决定了依赖目标中的“%“的值,也就是说,目标中的模式的“%“决定了依赖目标中“%“的样子。例如有一个模式规则如下:
.. code-block:: makefile
%.o : %.c ; <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关心的是依赖的文件名和生成目标的命令这两件事。
一旦依赖目标中的“%“模式被确定那么make会被要求去匹配当前目录下所有的文件名一旦找到make就会规则下的命令所以在模式规则中目标可能会是多个的如果有模式匹配出多个目标make就会产生所有的模式目标此时make关心的是依赖的文件名和生成目标的命令这两件事。
模式规则示例
~~~~~~~~~~~~
@ -223,7 +224,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
其中,"$@"表示所有的目标的挨个值,"$<"表示了所有依赖目标的挨个值。这些奇怪的变量我们叫"自动化变量",后面会详细讲述。
其中,“$@“表示所有的目标的挨个值,“$<“表示了所有依赖目标的挨个值。这些奇怪的变量我们叫“自动化变量“,后面会详细讲述。
下面的这个例子中有两个目标是模式的:
@ -232,7 +233,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
%.tab.c %.tab.h: %.y
bison -d $<
这条规则告诉make把所有的[.y]文件都以"bison -d <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]文件的依赖关系也写好,那么,所有的目标都会得到满足)
自动化变量
~~~~~~~~~~
@ -243,36 +244,36 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
下面是所有的自动化变量及其说明:
- $@: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
- $%: 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是 "bar.o""$@"就是"foo.a"。如果目标不是函数库文件Unix下是[.a]Windows下是[.lib]),那么,其值为空。
- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
- $@: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,“$@“就是匹配于目标中模式定义的集合。
- $%: 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)“,那么,“$%“就是 “bar.o““$@“就是“foo.a“。如果目标不是函数库文件Unix下是[.a]Windows下是[.lib]),那么,其值为空。
- $<: 依赖目标中的第一个目标名字。如果依赖目标是以模式(即“%“)定义的,那么“$<“将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
- $?: 所有比目标新的依赖目标的集合。以空格分隔。
- $^:所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
- $+:这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
- $*: 这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出但是如果目标文件的后缀是make所识别的那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名所以" $*"的值就是"foo"。这个特性是GNU make的很有可能不兼容于其它版本的make所以你应该尽量避免使用"$*"除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的那么"$*"就是空值。
- $+:这个变量很像“$^“,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
- $*: 这个变量表示目标模式中“%“及其之前的部分。如果目标是“dir/a.foo.b“并且目标的模式是“a.%.b“那么“$*“的值就是“dir/a.foo“。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*“也就不能被推导出但是如果目标文件的后缀是make所识别的那么“$*“就是除了后缀的那一部分。例如如果目标是“foo.c“因为“.c“是make所能识别的后缀名所以“ $*“的值就是“foo“。这个特性是GNU make的很有可能不兼容于其它版本的make所以你应该尽量避免使用“$*“除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的那么“$*“就是空值。
当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用,例如,假设有一个函数库文件叫"lib"其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是
当你希望只对更新过的依赖文件进行操作时,“$?“在显式规则中很有用例如假设有一个函数库文件叫“lib“其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是
.. code-block:: makefile
lib : foo.o bar.o lose.o win.o
ar r lib $?
在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。这是GNU make中老版本的特性在新版本中我们使用函数"dir"或"notdir"就可以做到了。"D"的含义就是Directory就是目录"F"的含义就是File就是文件。
在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上“D“或“F“字样。这是GNU make中老版本的特性在新版本中我们使用函数“dir“或“notdir“就可以做到了。“D“的含义就是Directory就是目录“F“的含义就是File就是文件。
下面是对于上面的七个变量分别加上"D"或是"F"的含义:
下面是对于上面的七个变量分别加上“D“或是“F“的含义:
``$(@D)``
表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。
表示“$@“的目录部分(不以斜杠作为结尾),如果“$@“值是“dir/foo.o“那么“$(@D)“就是“dir“而如果“$@“中没有包含斜杠的话,其值就是“.“(当前目录)。
``$(@F)``
表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o""$(@F)"相当于函数"$(notdir $@)"
表示“$@“的文件部分,如果“$@“值是“dir/foo.o“那么“$(@F)“就是“foo.o““$(@F)“相当于函数“$(notdir $@)“
``$(*D)``, ``$(*F)``
和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,\ ``$(*D)``\ 返回"dir",而\ ``$(*F)``\ 返回"foo"
和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,\ ``$(*D)``\ 返回“dir“而\ ``$(*F)``\ 返回“foo“
``$(%D)``, ``$(%F)``
分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不同的目录很有用。
分别表示了函数包文件成员的目录部分和文件部分。这对于形同“archive(member)“形式的目标中的“member“中包含了不同的目录很有用。
``$(<D)``, ``$(<F)``
分别表示依赖文件的目录部分和文件部分。
@ -286,16 +287,16 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
``$(?D)``, ``$(?F)``
分别表示被更新的依赖文件的目录部分和文件部分。
最后想提醒一下的是,对于"$<",为了避免产生不必要的麻烦,我们最好给$后面的那个特定字符都加上圆括号,比如,"$(<)"就要比"$<"要好一些。
最后想提醒一下的是,对于“$<“,为了避免产生不必要的麻烦,我们最好给$后面的那个特定字符都加上圆括号,比如,“$(<)“就要比“$<“要好一些。
还得要注意的是,这些变量只使用在规则的命令中,而且一般都是"显式规则"和"静态模式规则"(参见前面"书写规则"一章)。其在隐含规则中并没有意义。
还得要注意的是,这些变量只使用在规则的命令中,而且一般都是“显式规则“和“静态模式规则“(参见前面“书写规则“一章)。其在隐含规则中并没有意义。
模式的匹配
~~~~~~~~~~
一般来说,一个目标的模式有一个有前缀或是后缀的"%",或是没有前后缀,直接就是一个"%"。因为"%"代表一个或多个字符,所以在定义好了的模式中,我们把"%"所匹配的内容叫做"茎",例如"%.c"所匹配的文件"test.c"中"test"就是"茎"。因为在目标和依赖目标中同时有"% "时,依赖目标的"茎"会传给目标,当做目标中的"茎"
一般来说,一个目标的模式有一个有前缀或是后缀的“%“,或是没有前后缀,直接就是一个“%“。因为“%“代表一个或多个字符,所以在定义好了的模式中,我们把“%“所匹配的内容叫做“茎“,例如“%.c“所匹配的文件“test.c“中“test“就是“茎“。因为在目标和依赖目标中同时有“% “时,依赖目标的“茎“会传给目标,当做目标中的“茎“
当一个模式匹配包含有斜杠(实际也不经常包含)的文件时,那么在进行模式匹配时,目录部分会首先被移开,然后进行匹配,成功后,再把目录加回去。在进行"茎"的传递时,我们需要知道这个步骤。例如有一个模式"e%t",文件"src/eat"匹配于该模式,于是"src/a"就是其"茎",如果这个模式定义在依赖目标中,而被依赖于这个模式的目标中又有个模式"c%r",那么,目标就是"src/car"。("茎"被传递)
当一个模式匹配包含有斜杠(实际也不经常包含)的文件时,那么在进行模式匹配时,目录部分会首先被移开,然后进行匹配,成功后,再把目录加回去。在进行“茎“的传递时我们需要知道这个步骤。例如有一个模式“e%t“文件“src/eat“匹配于该模式于是“src/a“就是其“茎“,如果这个模式定义在依赖目标中,而被依赖于这个模式的目标中又有个模式“c%r“那么目标就是“src/car“。“茎“被传递)
重载内建隐含规则
@ -316,14 +317,14 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
同样,你也可以重新定义一个全新的隐含规则,其在隐含规则中的位置取决于你在哪里写下这个规则。朝前的位置就靠前。
老式风格的"后缀规则"
老式风格的“后缀规则“
--------------------
后缀规则是一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容GNU make同样兼容于这些东西。后缀规则有两种方式"双后缀"和"单后缀"
后缀规则是一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容GNU make同样兼容于这些东西。后缀规则有两种方式“双后缀“和“单后缀“
双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如".c.o"相当于"%o : %c"。单后缀规则只定义一个后缀,也就是源文件的后缀。如".c"相当于"% : %.c"
双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如“.c.o“相当于“%o : %c“。单后缀规则只定义一个后缀也就是源文件的后缀。如“.c“相当于“% : %.c“
后缀规则中所定义的后缀应该是make所认识的如果一个后缀是make所认识的那么这个规则就是单后缀规则而如果两个连在一起的后缀都被 make所认识那就是双后缀规则。例如".c"和".o"都是make所知道。因而如果你定义了一个规则是".c.o"那么其就是双后缀规则,意义就是".c"是源文件的后缀,".o"是目标文件的后缀。如下示例:
后缀规则中所定义的后缀应该是make所认识的如果一个后缀是make所认识的那么这个规则就是单后缀规则而如果两个连在一起的后缀都被 make所认识那就是双后缀规则。例如“.c“和“.o“都是make所知道。因而如果你定义了一个规则是“.c.o“那么其就是双后缀规则意义就是“.c“是源文件的后缀“.o“是目标文件的后缀。如下示例:
.. code-block:: makefile
@ -337,7 +338,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
.c.o: foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
这个例子,就是说,文件".c.o"依赖于文件"foo.h",而不是我们想要的这样:
这个例子,就是说,文件“.c.o“依赖于文件“foo.h“,而不是我们想要的这样:
.. code-block:: makefile
@ -346,7 +347,7 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
后缀规则中,如果没有命令,那是毫无意义的。因为他也不会移去内建的隐含规则。
而要让make知道一些特定的后缀我们可以使用伪目标".SUFFIXES"来定义或是删除,如:
而要让make知道一些特定的后缀我们可以使用伪目标“.SUFFIXES“来定义或是删除,如:
.. code-block:: makefile
@ -361,22 +362,22 @@ Make会优化一些特殊的隐含规则而不生成中间文件。如
先清楚默认后缀,后定义自己的后缀列表。
make的参数"-r"或"-no-builtin-rules"也会使用得默认的后缀列表为空。而变量"SUFFIXE"被用来定义默认的后缀列表,你可以用".SUFFIXES"来改变后缀列表,但请不要改变变量"SUFFIXE"的值。
make的参数“-r“或“-no-builtin-rules“也会使用得默认的后缀列表为空。而变量“SUFFIXE“被用来定义默认的后缀列表你可以用“.SUFFIXES“来改变后缀列表但请不要改变变量“SUFFIXE“的值。
隐含规则搜索算法
----------------
比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意在下面我们没有提到后缀规则原因是所有的后缀规则在Makefile被载入内存时会被转换成模式规则。如果目标是"archive(member)"的函数库文件模式那么这个算法会被运行两次第一次是找目标T如果没有找到的话那么进入第二次第二次会把"member"当作T来搜索。
比如我们有一个目标叫 T。下面是搜索目标T的规则的算法。请注意在下面我们没有提到后缀规则原因是所有的后缀规则在Makefile被载入内存时会被转换成模式规则。如果目标是“archive(member)“的函数库文件模式那么这个算法会被运行两次第一次是找目标T如果没有找到的话那么进入第二次第二次会把“member”当作T来搜索。
#. 把T的目录部分分离出来。叫D而剩余部分叫N。如果T是"src/foo.o"那么D就是"src/"N就是"foo.o"
#. 把T的目录部分分离出来。叫D而剩余部分叫N。如果T是“src/foo.o“那么D就是“src/“N就是“foo.o“
#. 创建所有匹配于T或是N的模式规则列表。
#. 如果在模式规则列表中有匹配所有文件的模式,如"%",那么从列表中移除其它的模式。
#. 如果在模式规则列表中有匹配所有文件的模式,如“%“,那么从列表中移除其它的模式。
#. 移除列表中没有命令的规则。
#. 对于第一个在列表中的模式规则:
#. 推导其"茎"SS应该是T或是N匹配于模式中"%"非空的部分。
#. 计算依赖文件。把依赖文件中的"%"都替换成"茎"S。如果目标模式中没有包含斜框字符而把D加在第一个依赖文件的开头。
#. 测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则的依赖文件,那么这个文件就叫"理当存在"
#. 推导其“茎“SS应该是T或是N匹配于模式中“%“非空的部分。
#. 计算依赖文件。把依赖文件中的“%“都替换成“茎“S。如果目标模式中没有包含斜框字符而把D加在第一个依赖文件的开头。
#. 测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或者是一个显式规则的依赖文件,那么这个文件就叫“理当存在“
#. 如果所有的依赖文件存在或是理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。
#. 如果经过第5步没有模式规则被找到那么就做更进一步的搜索。对于存在于列表中的第一个模式规则
@ -387,6 +388,6 @@ make的参数"-r"或"-no-builtin-rules"也会使用得默认的后缀列表为
#. 对于不存在的依赖文件,递归调用这个算法查找他是否可以被隐含规则找到。
#. 如果所有的依赖文件存在或是理当存在,或是就根本没有依赖文件。那么这条规则被采用,退出该算法。
#. 如果没有隐含规则可以使用,查看".DEFAULT"规则,如果有,采用,把".DEFAULT"的命令给T使用。
#. 如果没有隐含规则可以使用,查看“.DEFAULT“规则如果有采用把“.DEFAULT“的命令给T使用。
一旦规则被找到,就会执行其相当的命令,而此时,我们的自动化变量的值才会生成。

View File

@ -245,7 +245,7 @@ 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
------------------
@ -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

@ -103,11 +103,12 @@ make的参数
``-B``, ``--always-make``
认为所有的目标都需要更新(重编译)。
``-C`` *<dir>*, ``--directory``\ = *<dir>*
``-C`` *<dir>*, ``--directory``\ =\ *<dir>*
指定读取makefile的目录。如果有多个“-C”参数make的解释是后面的路径以前面的作为相对路径并以最后的目录作为被指定目录。如“make -C ~hchen/test -C prog”等价于“make -C ~hchen/test/prog”。
``-debug`` [= *<options>*]
``-debug``\ [=\ *<options>*]
输出make的调试信息。它有几种不同的级别可供选择如果没有参数那就是输出最简单的调试信息。下面是<options>的取值:
- a: 也就是all输出所有的调试信息。会非常的多
- b: 也就是basic只输出简单的调试信息。即输出不需要重编译的目标。
- v: 也就是verbose在b选项的级别之上。输出的信息包括哪个makefile被解析不需要被重编译的依赖文件或是依赖目标等。
@ -121,7 +122,7 @@ make的参数
``-e``, ``--environment-overrides``
指明环境变量的值覆盖makefile中定义的变量的值。
``-f``\ = *<file>*, ``--file``\ = *<file>*, ``--makefile``\ = *<file>*
``-f``\ =\ *<file>*, ``--file``\ =\ *<file>*, ``--makefile``\ =\ *<file>*
指定需要执行的makefile。
``-h``, ``--help``
@ -130,7 +131,7 @@ make的参数
``-i`` , ``--ignore-errors``
在执行时忽略所有的错误。
``-I`` *<dir>*, ``--include-dir``\ = *<dir>*
``-I`` *<dir>*, ``--include-dir``\ =\ *<dir>*
指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。
``-j`` [*<jobsnum>*], ``--jobs``\ [=\ *<jobsnum>*]

View File

@ -1,11 +1,11 @@
后序
====
终于到写结束语的时候了以上基本上就是GNU make的Makefile的所有细节了。其它的产商的make基本上也就是这样的无论什么样的make都是以文件的依赖性为基础的其基本是都是遵循一个标准的。这篇文档中80%的技术细节都适用于任何的make我猜测"函数"那一章的内容可能不是其它make所支持的而隐含规则方面我想不同的make会有不同的实现我没有精力来查看GNU的make和VC的nmake、BCB的 make或是别的UNIX下的make有些什么样的差别一是时间精力不够二是因为我基本上都是在Unix下使用make以前在SCO Unix和 IBM的AIX现在在Linux、Solaris、HP-UX、AIX和Alpha下使用Linux和Solaris下更多一点。不过我可以肯定的是在Unix下的make无论是哪种平台几乎都使用了Richard Stallman开发的make和cc/gcc的编译器而且基本上都是 GNU的make公司里所有的UNIX机器上都被装上了GNU的东西所以使用GNU的程序也就多了一些。GNU的东西还是很不错的特别是使用得深了以后越来越觉得GNU的软件的强大也越来越觉得GNU的在操作系统中主要是Unix甚至Windows"杀伤力"
终于到写结束语的时候了以上基本上就是GNU make的Makefile的所有细节了。其它的产商的make基本上也就是这样的无论什么样的make都是以文件的依赖性为基础的其基本是都是遵循一个标准的。这篇文档中80%的技术细节都适用于任何的make我猜测“函数”那一章的内容可能不是其它make所支持的而隐含规则方面我想不同的make会有不同的实现我没有精力来查看GNU的make和VC的nmake、BCB的 make或是别的UNIX下的make有些什么样的差别一是时间精力不够二是因为我基本上都是在Unix下使用make以前在SCO Unix和 IBM的AIX现在在Linux、Solaris、HP-UX、AIX和Alpha下使用Linux和Solaris下更多一点。不过我可以肯定的是在Unix下的make无论是哪种平台几乎都使用了Richard Stallman开发的make和cc/gcc的编译器而且基本上都是 GNU的make公司里所有的UNIX机器上都被装上了GNU的东西所以使用GNU的程序也就多了一些。GNU的东西还是很不错的特别是使用得深了以后越来越觉得GNU的软件的强大也越来越觉得GNU的在操作系统中主要是Unix甚至Windows“杀伤力”
对于上述所有的make的细节我们不但可以利用make这个工具来编译我们的程序还可以利用make来完成其它的工作因为规则中的命令可以是任何Shell之下的命令所以在Unix下你不一定只是使用程序语言的编译器你还可以在Makefile中书写其它的命令tar、 awk、mail、sed、cvs、compress、ls、rm、yacc、rpm、ftp……等等,等等,来完成诸如"程序打包"、"程序备份"、" 制作程序安装包"、"提交代码"、"使用程序模板"、"合并文件"等等五花八门的功能,文件操作,文件管理,编程开发设计,或是其它一些异想天开的东西。比如,以前在书写银行交易程序时,由于银行的交易程序基本一样,就见到有人书写了一些交易的通用程序模板,在该模板中把一些网络通讯、数据库操作的、业务操作共性的东西写在一个文件中,在这些文件中用些诸如"@@@N、###N"奇怪字串标注一些位置然后书写交易时只需按照一种特定的规则书写特定的处理最后在make时使用awk和sed把模板中的"@@@N、###N"等字串替代成特定的程序形成C文件然后再编译。这个动作很像数据库的" 扩展C"语言即在C语言中用"EXEC SQL"的样子执行SQL语句在用cc/gcc编译之前需要使用"扩展C"的翻译程序如cpre把其翻译成标准C。如果你在使用make时有一些更为绝妙的方法请记得告诉我啊。
对于上述所有的make的细节我们不但可以利用make这个工具来编译我们的程序还可以利用make来完成其它的工作因为规则中的命令可以是任何Shell之下的命令所以在Unix下你不一定只是使用程序语言的编译器你还可以在Makefile中书写其它的命令tar、 awk、mail、sed、cvs、compress、ls、rm、yacc、rpm、ftp等等,等等,来完成诸如“程序打包”、“程序备份”、“制作程序安装包”、“提交代码”、“使用程序模板”、“合并文件”等等五花八门的功能,文件操作,文件管理,编程开发设计,或是其它一些异想天开的东西。比如,以前在书写银行交易程序时,由于银行的交易程序基本一样,就见到有人书写了一些交易的通用程序模板,在该模板中把一些网络通讯、数据库操作的、业务操作共性的东西写在一个文件中,在这些文件中用些诸如“@@@N、###N”奇怪字串标注一些位置然后书写交易时只需按照一种特定的规则书写特定的处理最后在make时使用awk和sed把模板中的“@@@N、###N”等字串替代成特定的程序形成C文件然后再编译。这个动作很像数据库的“扩展C”语言即在C语言中用“EXEC SQL”的样子执行SQL语句在用cc/gcc编译之前需要使用“扩展C”的翻译程序如cpre把其翻译成标准C。如果你在使用make时有一些更为绝妙的方法请记得告诉我啊。
回头看看整篇文档不觉记起几年前刚刚开始在Unix下做开发的时候有人问我会不会写Makefile时我两眼发直根本不知道在说什么。一开始看到别人在vi中写完程序后输入"!make"还以为是vi的功能后来才知道有一个Makefile在作怪于是上网查啊查那时又不愿意看英文发现就根本没有中文的文档介绍Makefile只得看别人写的Makefile自己瞎碰瞎搞才积累了一点知识但在很多地方完全是知其然不知所以然。后来开始从事UNIX下产品软件的开发看到一个400人近200万行代码的大工程发现要编译这样一个庞然大物如果没有Makefile那会是多么恐怖的一样事啊。于是横下心来狠命地读了一堆英文文档才觉得对其掌握了。但发现目前网上对Makefile介绍的文章还是少得那么的可怜所以想写这样一篇文章共享给大家希望能对各位有所帮助。
回头看看整篇文档不觉记起几年前刚刚开始在Unix下做开发的时候有人问我会不会写Makefile时我两眼发直根本不知道在说什么。一开始看到别人在vi中写完程序后输入“!make”还以为是vi的功能后来才知道有一个Makefile在作怪于是上网查啊查那时又不愿意看英文发现就根本没有中文的文档介绍Makefile只得看别人写的Makefile自己瞎碰瞎搞才积累了一点知识但在很多地方完全是知其然不知所以然。后来开始从事UNIX下产品软件的开发看到一个400人近200万行代码的大工程发现要编译这样一个庞然大物如果没有Makefile那会是多么恐怖的一样事啊。于是横下心来狠命地读了一堆英文文档才觉得对其掌握了。但发现目前网上对Makefile介绍的文章还是少得那么的可怜所以想写这样一篇文章共享给大家希望能对各位有所帮助。
现在我终于写完了看了看文件的创建时间这篇技术文档也写了两个多月了。发现自己知道是一回事要写下来跟别人讲述又是另外一回事而且现在越来越没有时间专研技术细节所以在写作时发现在阐述一些细节问题时很难做到严谨和精练而且对先讲什么后讲什么不是很清楚所以还是参考了一些国外站点上的资料和题纲以及一些技术书籍的语言风格才得以完成。整篇文档的提纲是基于GNU的Makefile技术手册的提纲来书写的并结合了自己的工作经验以及自己的学习历程。因为从来没有写过这么长这么细的文档所以一定会有很多地方存在表达问题语言歧义或是错误。因些我迫切地得等待各位给我指证和建议以及任何的反馈。
@ -15,7 +15,7 @@
最最后我还想介绍一下make程序的设计开发者。
首当其冲的是: Richard Stallman
首当其冲的是Richard Stallman
开源软件的领袖和先驱从来没有领过一天工资从来没有使用过Windows操作系统。对于他的事迹和他的软件以及他的思想我无需说过多的话相信大家对这个人并不比我陌生这是他的主页http://www.stallman.org/ 。这里只贴上一张他的近照:

View File

@ -8,7 +8,7 @@
显示命令
--------
通常make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前那么这个命令将不被make显示出来最具代表性的例子是我们用这个功能来屏幕显示一些信息。如::
通常make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前那么这个命令将不被make显示出来最具代表性的例子是我们用这个功能来屏幕显示一些信息。如::
@echo 正在编译XXX模块......
@ -26,7 +26,7 @@
当依赖目标新于目标时也就是当规则的目标需要被更新时make会一条一条的执行其后的命令。需要注意的是如果你要让上一条命令的结果应用在下一条命令时你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令你希望第二条命令得在cd之后的基础上运行那么你就不能把这两条命令写在两行上而应该把这两条命令写在一行上用分号分隔。如
示例一:
- 示例一:
.. code-block:: makefile
@ -34,7 +34,7 @@
cd /home/hchen
pwd
示例二:
- 示例二:
.. code-block:: makefile

View File

@ -55,7 +55,7 @@ prerequisites也就是目标所依赖的文件或依赖目标。如果其
波浪号(“~”)字符在文件名中也有比较特殊的用途。如果是“~/test”这就表示当前用户的$HOME目录下的test目录。而 “~hchen/test”则表示用户hchen的宿主目录下的test目录。这些都是Unix下的小知识了make也支持而在Windows或是 MS-DOS下用户没有宿主目录那么波浪号所指的目录则根据环境变量“HOME”而定。
通配符代替了你一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是如果我们的文件名中有通配符*”,那么可以用转义字符“\”,如“\*”来表示真实的“*”字符,而不是任意长度的字符串。
通配符代替了你一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是如果我们的文件名中有通配符\*”,那么可以用转义字符“\\”,如“\\\*”来表示真实的“\*”字符,而不是任意长度的字符串。
好吧,还是先来看几个例子吧:
@ -96,25 +96,25 @@ prerequisites也就是目标所依赖的文件或依赖目标。如果其
#. 列出一确定文件夹中的所有“.c”文件。
.. code-block:: makefile
.. code-block:: makefile
objects := $(wildcard *.c)
objects := $(wildcard *.c)
#. 列出1中所有文件对应的".o"文件3中我们可以看到它是由make自动编译出的。
#. 列出(1)中所有文件对应的“.o”文件3中我们可以看到它是由make自动编译出的。
.. code-block:: makefile
.. code-block:: makefile
$(patsubst %.c,%.o,$(wildcard *.c))
$(patsubst %.c,%.o,$(wildcard *.c))
#. 由12两步可写出编译并链接所有“.c"和”.o"文件
#. 由(1)(2)两步,可写出编译并链接所有“.c”和”.o“文件
.. code-block:: makefile
.. code-block:: makefile
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
这种用法由关键字“wildcard”“patsubst"指出关于Makefile的关键字我们将在后面讨论。
这种用法由关键字“wildcard”“patsubst指出关于Makefile的关键字我们将在后面讨论。
文件搜寻
--------
@ -140,7 +140,7 @@ Makefile文件中的特殊变量“VPATH”就是完成这个功能的如果
\ ``vpath``\
清除所有已被设置好了的文件搜索目录。
vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,(需引用“%”,使用“\%")例如,“%.h”表示所有以 “.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了< pattern>的文件集的搜索的目录。例如:
vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,(需引用“%”,使用“\\%”)例如,“%.h”表示所有以 “.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了< pattern>的文件集的搜索的目录。例如:
.. code-block:: makefile
@ -209,7 +209,7 @@ vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
我们知道Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标其依赖于其它三个目标。由于默认目标的特性是总是被执行的但由于“all”又是一个伪目标伪目标只是一个标签不会生成文件所以不会有“all”文件产生。于是其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。这里的显式 “.PHONY : all" 不写的话一般情况也可以正确的执行,这样 make 可通过隐式规则推导出, “all" 是一个伪目标,执行 make 不会生成 ”all" 文件,而执行后面的多个目标。建议:显式写出是一个好习惯。)
我们知道Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标其依赖于其它三个目标。由于默认目标的特性是总是被执行的但由于“all”又是一个伪目标伪目标只是一个标签不会生成文件所以不会有“all”文件产生。于是其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。这里的显式 “.PHONY : all” 不写的话一般情况也可以正确的执行这样make可通过隐式规则推导出 “all” 是一个伪目标执行make不会生成“all”文件,而执行后面的多个目标。建议:显式写出是一个好习惯。)
随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也可成为依赖。看下面的例子:
@ -266,9 +266,9 @@ target-parrtern是指明了targets的模式也就是的目标集模式。
prereq-parrterns是目标的依赖模式它对target-parrtern形成的模式再进行一次依赖目标的定义。
这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的<target-parrtern>定义成“%. o”意思是我们的<target>;集合中都是以“.o”结尾的而如果我们的<prereq-parrterns>定义成 “%.c”意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。
这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的<target-parrtern>定义成“%.o”意思是我们的<target>;集合中都是以“.o”结尾的而如果我们的<prereq-parrterns>定义成 “%.c”意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。
所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“\”进行转义,来标明真实的“%”字符。
所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“\\”进行转义,来标明真实的“%”字符。
看一个例子:
@ -290,7 +290,7 @@ prereq-parrterns是目标的依赖模式它对target-parrtern形成的模式
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
试想,如果我们的“%.o”有几百个我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子:
试想,如果我们的“%.o”有几百个我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子:
.. code-block:: makefile
@ -301,7 +301,7 @@ prereq-parrterns是目标的依赖模式它对target-parrtern形成的模式
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
$(filter %.o,$(files))表示调用Makefile的filter函数过滤“$files”集只要其中模式为“%.o”的内容。其它内容我就不用多说了吧。这个例子展示了Makefile中更大的弹性。
$(filter %.o,$(files))表示调用Makefile的filter函数过滤“$files”集只要其中模式为“%.o”的内容。其它内容我就不用多说了吧。这个例子展示了Makefile中更大的弹性。
自动生成依赖性
--------------
@ -344,7 +344,7 @@ gcc -MM main.c的输出则是::
那么编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来我们的Makefile也要根据这些源文件重新生成让 Makefile自已依赖于源文件这个功能并不现实不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中为每一个“name.c”的文件都生成一个“name.d”的Makefile文件[.d]文件中就存放对应[.c]文件的依赖关系。
于是,我们可以写出[.c]文件和[.d]文件的依赖关系并让make自动更新或成[.d]文件并把其包含在我们的主Makefile中这样我们就可以自动化地生成每个文件的依赖关系了。
于是,我们可以写出[.c]文件和[.d]文件的依赖关系并让make自动更新或成[.d]文件并把其包含在我们的主Makefile中这样我们就可以自动化地生成每个文件的依赖关系了。
这里,我们给出了一个模式规则来产生[.d]文件: