﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-奇奇的空间-文章分类-UNIX</title><link>http://www.cppblog.com/jjbird/category/948.html</link><description>奇奇的空间</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 08:00:53 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 08:00:53 GMT</pubDate><ttl>60</ttl><item><title>Linux下软件安装详解</title><link>http://www.cppblog.com/jjbird/articles/5770.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Mon, 17 Apr 2006 09:04:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/5770.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/5770.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/5770.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/5770.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/5770.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="10" align="left" border="0">
				<tbody>
						<tr>
								<td>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<font size="2">在Windows下安装软件时，只需用鼠标双击软件的安装程序，或者用Zip等解压缩软件解压缩即可安装。在Linux下安装软件对初学者来说，难度高于Windows下软件安装。下面我就详细讲解Linux下如何安装软件。 <br /><br />先来看看Linux软件扩展名。软件后缀为.rpm最初是Red Hat Linux提供的一种包封装格式，现在许多Linux发行版本都使用；后缀为.deb是Debain Linux提供的一种包封装格式；后缀为.tar.gz、tar.Z、tar.bz2或.tgz是使用Unix系统打包工具tar打包的；后缀为.bin 的一般是一些商业软件。通过扩展名可以了解软件格式，进而了解软件安装。 <br /><br /><b>RPM格式软件包的安装</b><br /><br />1.简介<br />几乎所有的Linux发行版本都使用某种形式的软件包管理安装、更新和卸载软件。与直接从源代码安装相比，软件包管理易于安装和卸载；易于更新已安装的软件包；易于保护配置文件；易于跟踪已安装文件。 <br /><br />RPM全称是Red Hat Package Manager（Red Hat包管理器）。RPM本质上就是一个包，包含可以立即在特定机器体系结构上安装和运行的Linux软件。RPM示意图见图1。 <br /><br /></font>
		<center>
				<font size="2">
				</font> </center>
		<br />
		<br />大多数Linux RPM软件包的命名有一定的规律，它遵循名称-版本-修正版-类型－MYsoftware-1.2 -1.i386.rpm 。 <br /><br />2.安装RPM包软件 <br />＃　rpm -ivh MYsoftware-1.2 -1.i386.rpm <br /><br />RPM命令主要参数： <br /><br /><ccid></ccid><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid>-i  安装软件。<br />-t  测试安装，不是真的安装。 <br />-p  显示安装进度。<br />-f  忽略任何错误。<br />-U  升级安装。<br />-v  检测套件是否正确安装。</ccid></pre></td></tr></tbody></table><br /><br />这些参数可以同时采用。更多的内容可以参考RPM的命令帮助。 <br /><br />3.卸载软件 <br />＃　rpm -e 软件名 <br /><br />需要说明的是，上面代码中使用的是软件名，而不是软件包名。例如，要卸载software-1.2.-1.i386.rpm这个包时，应执行：<br />＃rpm -e software <br /><br />4.强行卸载RPM包<br />有时除去一个RPM是不行的，尤其是系统上有别的程序依赖于它的时候。如果执行命令会显示如下错误信息： <br /><br /><ccid></ccid><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid>＃# rpm -e xsnow<br />error: removing these packages would break dependencies:<br />        /usr/X11R6/bin/xsnow is needed by x-amusements-1.0-1</ccid></pre></td></tr></tbody></table><br /><br />在这种情况下，可以用--force选项重新安装xsnow： <br /><br /><ccid></ccid><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid>＃# rpm -ivh --force xsnow-1.41-1.i386.rpm<br />xsnow</ccid></pre></td></tr></tbody></table><br /><br />这里推荐使用工具软件Kleandisk，用它可以安全彻底清理掉不再使用的RPM包。详细情况请查看2003年《开放系统世界》第12期。 <br /><br />5.安装.src.rpm类型的文件<br />目前RPM有两种模式，一种是已经过编码的（i386.rpm），一种是未经编码的（src.rpm）。<br />rpm --rebuild Filename.src.rpm <br /><br />这时系统会建立一个文件Filenamr.rpm，在/usr/src/redflag/RPMS/子目录下，一般是i386，具体情况和Linux发行版本有关。然后执行下面代码即可：<br />rpm -ivh /usr/src/regflag/RPMS/i386/Filename.rpm <br /><br /><b>使用deb打包的软件安装 </b><br /><br />deb 是Debian Linux提供的一个包管理器，它与RPM十分类似。但由于RPM出现得早，并且应用广泛，所以在各种版本的Linux中都常见到，而Debian的包管理器dpkg只出现在Debina Linux中。它的优点是不用被严格的依赖性检查所困扰，缺点是只在Debian Linux发行版中才能见到这个包管理工具。 <br /><br />1. 安装<br />＃　dpkg -i MYsoftware-1.2.-1.deb <br /><br />2. 卸载<br />＃　dpkg -e MYsoftware <br /><br /><b>使用源代码进行软件安装</b><br /><br />和RPM安装方式相比，使用源代码进行软件安装会复杂一些，但是用源代码安装软件是Linux下进行软件安装的重要手段，也是运行Linux的最主要的优势之一。使用源代码安装软件，能按照用户的需要选择定制的安装方式进行安装，而不是仅仅依靠那些在安装包中的预配置的参数选择安装。另外，仍然有一些软件程序只能从源代码处进行安装。 <br /><br />现在有很多地方都提供源代码包，到底在什么地方获得取决于软件的特殊需要。对于那些使用比较普遍的软件，如Sendmail，可以从商业网站处下载源代码软件包（如http://www.sendmail.org ）。一般的软件包，可从开发者的Web站点下载。下面介绍一下安装步骤： <br /><br />1.解压数据包<br />源代码软件通常以.tar.gz做为扩展名,也有tar.Z、tar.bz2或.tgz为扩展名的。不同扩展名解压缩命令也不相同，见表1。 <br /><br /><center><img src="http://www.pc-file.com/UploadFiles/200522185247580.jpg" /></center><br /><br />2.编译软件<br />成功解压缩源代码文件后，进入解包的目录。在安装前阅读Readme文件和Install文件。尽管许多源代码文件包都使用基本相同的命令，但是有时在阅读这些文件时能发现一些重要的区别。例如，有些软件包含一个可以安装的安装脚本程序（.sh）。在安装前阅读这些说明文件，有助于安装成功和节约时间。 <br /><br />在安装软件以前要成为root用户。实现这一点通常有两种方式：在另一台终端以root用户登录，或者输入“su”，此时系统会提示输入root用户的密码。输入密码以后，就将一直拥有root用户的权限。如果已经是root用户，那就可以进行下一步。 <br /><br />通常的安装方法是从安装包的目录执行以下命令： <br /><br /><ccid></ccid><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid>gunzip soft1.tar.gz<br />cd soft1<br />＃. /configure ＃配置＃<br />make ＃调用make＃<br />make install  ＃安装源代码＃</ccid></pre></td></tr></tbody></table><br /><br />删除安装时产生的临时文件：<br />＃make clean <br /><br />卸载软件：<br />＃make uninstall <br /><br />有些软件包的源代码编译安装后可以用make uninstall命令卸载。如果不提供此功能，则软件的卸载必须手动删除。由于软件可能将文件分散地安装在系统的多个目录中，往往很难把它删除干净，应该在编译前进行配置。 <br /><br /><b>.bin文件安装</b><br /><br />扩展名为.bin文件是二进制的，它也是源程序经编译后得到的机器语言。有一些软件可以发布为以.bin为后缀的安装包，例如，流媒体播放器 RealONE。如果安装过RealONE的Windows版的话，那么安装RealONE for Linux版本(文件名：r1p1_linux22_libc6_i386_a1.bin)就非常简单了：<br />＃chmod +x r1p1_linux22_libc6_i386_a1.bin<br />./ r1p1_linux22_libc6_i386_a1.bin <br /><br />接下来选择安装方式，有普通安装和高级安装两种。如果不想改动安装目录，就可选择普通安装，整个安装过程几乎和在Windwos下一样。 <br /><br />.bin文件的卸载，以RealONE for Linux为例，如果采用普通安装方式的话，在用户主目录下会有Real和Realplayer9两个文件夹，把它们删除即可。 <br /><br /><b>Linux绿色软件</b><br /><br />Linux 也有一些绿色软件，不过不是很多。Linux系统提供一种机制：自动响应软件运行进程的要求,为它设定好可以马上运行的环境。这种机制可以是一种接口，或者是中间件。程序员编写的程序可以直接拷贝分发，不用安装，只要点击程序的图标，访问操作系统提供的接口，设定好就可以工作。若要删除软件，直接删除就可以,不用链接文件。这是最简单的软件安装、卸载方式。 <br /><br />上面介绍了Linux软件安装的方法，对于Linux初学者来说，RPM安装是一个不错的选择。如果想真正掌握Linux系统，源代码安装仍然是Linux下软件安装的重要手段<img src ="http://www.cppblog.com/jjbird/aggbug/5770.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-04-17 17:04 <a href="http://www.cppblog.com/jjbird/articles/5770.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Makefile文件的格式与用法</title><link>http://www.cppblog.com/jjbird/articles/3380.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Tue, 21 Feb 2006 08:53:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/3380.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/3380.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/3380.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/3380.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/3380.html</trackback:ping><description><![CDATA[&nbsp;GNU Make 工具 <BR>&nbsp;&nbsp;&nbsp;&nbsp;~~~~~~~~~~~~~~~~ <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.1 基本 makefile 结构 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;GNU Make 的主要工作是读进一个文本文件， makefile 。这个文 件里主要是有关哪些文件（‘target’目的文件）是从哪些别的 文件（‘dependencies’依靠文件）中产生的，用什么命令来进行 这个产生过程。有了这些信息， make 会检查磁碟上的文件，如果 目的文件的时间戳（该文件生成或被改动时的时间）比至少它的一 个依靠文件旧的话， make 就执行相应的命令，以便更新目的文件。 （目的文件不一定是最后的可执行档，它可以是任何一个文件。） <BR>&nbsp;&nbsp;&nbsp;&nbsp;makefile 一般被叫做“makefile”或“Makefile”。当然你可以 在 make 的命令行指定别的文件名。如果你不特别指定，它会寻 找“makefile”或“Makefile”，因此使用这两个名字是最简单 的。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;一个 makefile 主要含有一系列的规则，如下： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;: ... <BR>&nbsp;&nbsp;&nbsp;&nbsp;(tab)<COMMAND> <BR>&nbsp;&nbsp;&nbsp;&nbsp;(tab)<COMMAND> <BR>&nbsp;&nbsp;&nbsp;&nbsp;. <BR>&nbsp;&nbsp;&nbsp;&nbsp;. <BR>&nbsp;&nbsp;&nbsp;&nbsp;. <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;例如，考虑以下的 makefile ： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 开始 === <BR>&nbsp;&nbsp;&nbsp;&nbsp;myprog : foo.o bar.o <BR>&nbsp;&nbsp;&nbsp;&nbsp; gcc foo.o bar.o -o myprog <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;foo.o : foo.c foo.h bar.h <BR>&nbsp;&nbsp;&nbsp;&nbsp; gcc -c foo.c -o foo.o <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;bar.o : bar.c bar.h <BR>&nbsp;&nbsp;&nbsp;&nbsp; gcc -c bar.c -o bar.o <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 结束 === <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;这是一个非常基本的 makefile —— make 从最上面开始，把上 面第一个目的，‘myprog’，做为它的主要目标（一个它需要保 证其总是最新的最终目标）。给出的规则说明只要文件‘myprog’ 比文件‘foo.o’或‘bar.o’中的任何一个旧，下一行的命令将 会被执行。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;但是，在检查文件 foo.o 和 bar.o 的时间戳之前，它会往下查 找那些把 foo.o 或 bar.o 做为目标文件的规则。它找到的关于 foo.o 的规则，该文件的依靠文件是 foo.c, foo.h 和 bar.h 。 它从下面再找不到生成这些依靠文件的规则，它就开始检查磁碟 上这些依靠文件的时间戳。如果这些文件中任何一个的时间戳比 foo.o 的新，命令 'gcc -o foo.o foo.c' 将会执行，从而更新 文件 foo.o 。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;接下来对文件 bar.o 做类似的检查，依靠文件在这里是文件 bar.c 和 bar.h 。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;现在， make 回到‘myprog’的规则。如果刚才两个规则中的任 何一个被执行，myprog 就需要重建（因为其中一个 .o 档就会比 ‘myprog’新），因此连接命令将被执行。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;希望到此，你可以看出使用 make 工具来建立程序的好处——前 一章中所有繁琐的检查步骤都由 make 替你做了：检查时间戳。 你的源码文件里一个简单改变都会造成那个文件被重新编译（因 为 .o 文件依靠 .c 文件），进而可执行文件被重新连接（因为 .o 文件被改变了）。其实真正的得益是在当你改变一个 header 档的时候——你不再需要记住那个源码文件依靠它，因为所有的 资料都在 makefile 里。 make 会很轻松的替你重新编译所有那 些因依靠这个 header 文件而改变了的源码文件，如有需要，再 进行重新连接。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;当然，你要确定你在 makefile 中所写的规则是正确无误的，只 列出那些在源码文件中被 #include 的 header 档…… <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.2 编写 make 规则 (Rules) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;最明显的（也是最简单的）编写规则的方法是一个一个的查 看源码文件，把它们的目标文件做为目的，而Ｃ源码文件和被它 #include 的 header 档做为依靠文件。但是你也要把其它被这些 header 档 #include 的 header 档也列为依靠文件，还有那些被 包括的文件所包括的文件……然后你会发现要对越来越多的文件 进行管理，然后你的头发开始脱落，你的脾气开始变坏，你的脸 色变成菜色，你走在路上开始跟电线杆子碰撞，终于你捣毁你的 电脑显示器，停止编程。到低有没有些容易点儿的方法呢？ <BR>&nbsp;&nbsp;&nbsp;&nbsp;当然有！向编译器要！在编译每一个源码文件的时候，它实在应 该知道应该包括什么样的 header 档。使用 gcc 的时候，用 -M 开关，它会为每一个你给它的Ｃ文件输出一个规则，把目标文件 做为目的，而这个Ｃ文件和所有应该被 #include 的 header 文 件将做为依靠文件。注意这个规则会加入所有 header 文件，包 括被角括号(`&lt;', `&gt;')和双引号(`"')所包围的文件。其实我们可以 相当肯定系统 header 档（比如 stdio.h, stdlib.h 等等）不会 被我们更改，如果你用 -MM 来代替 -M 传递给 gcc，那些用角括 号包围的 header 档将不会被包括。（这会节省一些编译时间） <BR>&nbsp;&nbsp;&nbsp;&nbsp;由 gcc 输出的规则不会含有命令部分；你可以自己写入你的命令 或者什么也不写，而让 make 使用它的隐含的规则（参考下面的 2.4 节）。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.3 Makefile 变量 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;上面提到 makefiles 里主要包含一些规则。它们包含的其它的东 西是变量定义。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;makefile 里的变量就像一个环境变量(environment variable)。 事实上，环境变量在 make 过程中被解释成 make 的变量。这些 变量是大小写敏感的，一般使用大写字母。它们可以从几乎任何 地方被引用，也可以被用来做很多事情，比如： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;i) 贮存一个文件名列表。在上面的例子里，生成可执行文件的 规则包含一些目标文件名做为依靠。在这个规则的命令行 里同样的那些文件被输送给 gcc 做为命令参数。如果在这 里使用一个变数来贮存所有的目标文件名，加入新的目标 文件会变的简单而且较不易出错。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;ii) 贮存可执行文件名。如果你的项目被用在一个非 gcc 的系 统里，或者如果你想使用一个不同的编译器，你必须将所 有使用编译器的地方改成用新的编译器名。但是如果使用一 个变量来代替编译器名，那么你只需要改变一个地方，其 它所有地方的命令名就都改变了。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;iii) 贮存编译器旗标。假设你想给你所有的编译命令传递一组 相同的选项（例如 -Wall -O -g）；如果你把这组选项存 入一个变量，那么你可以把这个变量放在所有呼叫编译器 的地方。而当你要改变选项的时候，你只需在一个地方改 变这个变量的内容。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;要设定一个变量，你只要在一行的开始写下这个变量的名字，后 面跟一个 = 号，后面跟你要设定的这个变量的值。以后你要引用 这个变量，写一个 $ 符号，后面是围在括号里的变量名。比如在 下面，我们把前面的 makefile 利用变量重写一遍： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 开始 === <BR>&nbsp;&nbsp;&nbsp;&nbsp;OBJS = foo.o bar.o <BR>&nbsp;&nbsp;&nbsp;&nbsp;CC = gcc <BR>&nbsp;&nbsp;&nbsp;&nbsp;CFLAGS = -Wall -O -g <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myprog : $(OBJS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; $(CC) $(OBJS) -o myprog <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;foo.o : foo.c foo.h bar.h <BR>&nbsp;&nbsp;&nbsp;&nbsp; $(CC) $(CFLAGS) -c foo.c -o foo.o <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;bar.o : bar.c bar.h <BR>&nbsp;&nbsp;&nbsp;&nbsp; $(CC) $(CFLAGS) -c bar.c -o bar.o <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 结束 === <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;还有一些设定好的内部变量，它们根据每一个规则内容定义。三个 比较有用的变量是 $@, $&lt;和 $^ （这些变量不需要括号括住）。 $@ 扩展成当前规则的目的文件名， $&lt; 扩展成依靠列表中的第 一个依靠文件，而 $^ 扩展成整个依靠的列表（除掉了里面所有重 复的文件名）。利用这些变量，我们可以把上面的 makefile 写成： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 开始 === <BR>&nbsp;&nbsp;&nbsp;&nbsp;OBJS = foo.o bar.o <BR>&nbsp;&nbsp;&nbsp;&nbsp;CC = gcc <BR>&nbsp;&nbsp;&nbsp;&nbsp;CFLAGS = -Wall -O -g <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myprog : $(OBJS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; $(CC) $^ -o $@ <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;foo.o : foo.c foo.h bar.h <BR>&nbsp;&nbsp;&nbsp;&nbsp; $(CC) $(CFLAGS) -c $&lt;-o $@ <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;bar.o : bar.c bar.h <BR>&nbsp;&nbsp;&nbsp;&nbsp; $(CC) $(CFLAGS) -c $&lt;-o $@ <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 结束 === <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;你可以用变量做许多其它的事情，特别是当你把它们和函数混合 使用的时候。如果需要更进一步的了解，请参考 GNU Make 手册。 ('man make', 'man makefile') <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.4 隐含规则 (Implicit Rules) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;请注意，在上面的例子里，几个产生 .o 文件的命令都是一样的。 都是从 .c 文件和相关文件里产生 .o 文件，这是一个标准的步 骤。其实 make 已经知道怎么做——它有一些叫做隐含规则的内 置的规则，这些规则告诉它当你没有给出某些命令的时候，应该 怎么办。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;如果你把生成 foo.o 和 bar.o 的命令从它们的规则中删除， make 将会查找它的隐含规则，然后会找到一个适当的命令。它的命令会 使用一些变量，因此你可以按照你的想法来设定它：它使用变量 CC 做为编译器（象我们在前面的例子），并且传递变量 CFLAGS （给 C 编译器，C++ 编译器用 CXXFLAGS ），CPPFLAGS （ C 预 处理器旗标）， TARGET_ARCH （现在不用考虑这个），然后它加 入旗标 '-c' ，后面跟变量 $&lt;（第一个依靠名），然后是旗 标 '-o' 跟变量 $@ （目的文件名）。一个Ｃ编译的具体命令将 会是： <BR>&nbsp;&nbsp;&nbsp;&nbsp;$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $&lt;-o $@ <BR>&nbsp;&nbsp;&nbsp;&nbsp;当然你可以按照你自己的需要来定义这些变量。这就是为什么用 gcc 的 -M 或 -MM 开关输出的码可以直接用在一个 makefile 里。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.5 假象目的 (Phony Targets) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;假设你的一个项目最后需要产生两个可执行文件。你的主要目标 是产生两个可执行文件，但这两个文件是相互独立的——如果一 个文件需要重建，并不影响另一个。你可以使用“假象目的”来 达到这种效果。一个假象目的跟一个正常的目的几乎是一样的， 只是这个目的文件是不存在的。因此， make 总是会假设它需要 被生成，当把它的依赖文件更新后，就会执行它的规则里的命令 行。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;如果在我们的 makefile 开始处输入： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;all : exec1 exec2 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;其中 exec1 和 exec2 是我们做为目的的两个可执行文件。 make 把这个 'all' 做为它的主要目的，每次执行时都会尝试把 'all' 更新。但既然这行规则里没有哪个命令来作用在一个叫 'all' 的 实际文件（事实上 all 并不会在磁碟上实际产生），所以这个规 则并不真的改变 'all' 的状态。可既然这个文件并不存在，所以 make 会尝试更新 all 规则，因此就检查它的依靠 exec1, exec2 是否需要更新，如果需要，就把它们更新，从而达到我们的目的。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;假象目的也可以用来描述一组非预设的动作。例如，你想把所有由 make 产生的文件删除，你可以在 makefile 里设立这样一个规则： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;veryclean : <BR>&nbsp;&nbsp;&nbsp;&nbsp; rm *.o <BR>&nbsp;&nbsp;&nbsp;&nbsp; rm myprog <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;前提是没有其它的规则依靠这个 'veryclean' 目的，它将永远 不会被执行。但是，如果你明确的使用命令 'make veryclean' ， make 会把这个目的做为它的主要目标，执行那些 rm 命令。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;如果你的磁碟上存在一个叫 veryclean 文件，会发生什么事？这 时因为在这个规则里没有任何依靠文件，所以这个目的文件一定是 最新的了（所有的依靠文件都已经是最新的了），所以既使用户明 确命令 make 重新产生它，也不会有任何事情发生。解决方法是标 明所有的假象目的（用 .PHONY），这就告诉 make 不用检查它们 是否存在于磁碟上，也不用查找任何隐含规则，直接假设指定的目 的需要被更新。在 makefile 里加入下面这行包含上面规则的规则： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;.PHONY : veryclean <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;就可以了。注意，这是一个特殊的 make 规则，make 知道 .PHONY 是一个特殊目的，当然你可以在它的依靠里加入你想用的任何假象 目的，而 make 知道它们都是假象目的。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.6 函数 (Functions) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;makefile 里的函数跟它的变量很相似——使用的时候，你用一个 $ 符号跟开括号，函数名，空格后跟一列由逗号分隔的参数，最后 用关括号结束。例如，在 GNU Make 里有一个叫 'wildcard' 的函 数，它有一个参数，功能是展开成一列所有符合由其参数描述的文 件名，文件间以空格间隔。你可以像下面所示使用这个命令： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;SOURCES = $(wildcard *.c) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;这行会产生一个所有以 '.c' 结尾的文件的列表，然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;另一个有用的函数是 patsubst （ patten substitude, 匹配替 换的缩写）函数。它需要３个参数——第一个是一个需要匹配的式样，第二个表示用什么来替换它，第三个是一个需要被处理的 由空格分隔的字列。例如，处理那个经过上面定义后的变量， <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;OBJS = $(patsubst %.c,%.o,$(SOURCES)) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;这行将处理所有在 SOURCES 字列中的字（一列文件名），如果它的 结尾是 '.c' ，就用 '.o' 把 '.c' 取代。注意这里的 % 符号将匹 配一个或多个字符，而它每次所匹配的字串叫做一个‘柄’(stem) 。 在第二个参数里， % 被解读成用第一参数所匹配的那个柄。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.7 一个比较有效的 makefile <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;利用我们现在所学的，我们可以建立一个相当有效的 makefile 。 这个 makefile 可以完成大部分我们需要的依靠检查，不用做太大 的改变就可直接用在大多数的项目里。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;首先我们需要一个基本的 makefile 来建我们的程序。我们可以让 它搜索当前目录，找到源码文件，并且假设它们都是属于我们的项 目的，放进一个叫 SOURCES 的变量。这里如果也包含所有的 *.cc 文件，也许会更保险，因为源码文件可能是 C++ 码的。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;SOURCES = $(wildcard *.c *.cc) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;利用 patsubst ，我们可以由源码文件名产生目标文件名，我们需 要编译出这些目标文件。如果我们的源码文件既有 .c 文件，也有 .cc 文件，我们需要使用相嵌的 patsubst 函数呼叫： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;OBJS = $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCES))) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;最里面一层 patsubst 的呼叫会对 .cc 文件进行后缀替代，产生的结 果被外层的 patsubst 呼叫处理，进行对 .c 文件后缀的替代。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;现在我们可以设立一个规则来建可执行文件： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;myprog : $(OBJS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; gcc -o myprog $(OBJS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;进一步的规则不一定需要， gcc 已经知道怎么去生成目标文件 (object files) 。下面我们可以设定产生依靠信息的规则： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;depends : $(SOURCES) <BR>&nbsp;&nbsp;&nbsp;&nbsp; gcc -M $(SOURCES) &gt; depends <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;在这里如果一个叫 'depends' 的文件不存在，或任何一个源码文件 比一个已存在的 depends 文件新，那么一个 depends 文件会被生 成。depends 文件将会含有由 gcc 产生的关于源码文件的规则（注 意 -M 开关）。现在我们要让 make 把这些规则当做 makefile 档 的一部分。这里使用的技巧很像 C 语言中的 #include 系统——我 们要求 make 把这个文件 include 到 makefile 里，如下： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;include depends <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;GNU Make 看到这个，检查 'depends' 目的是否更新了，如果没有， 它用我们给它的命令重新产生 depends 档。然后它会把这组（新） 规则包含进来，继续处理最终目标 'myprog' 。当看到有关 myprog 的规则，它会检查所有的目标文件是否更新——利用 depends 文件 里的规则，当然这些规则现在已经是更新过的了。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;这个系统其实效率很低，因为每当一个源码文件被改动，所有的源码 文件都要被预处理以产生一个新的 'depends' 文件。而且它也不是 100% 的安全，这是因为当一个 header 档被改动，依靠信息并不会 被更新。但就基本工作来说，它也算相当有用的了。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;2.8 一个更好的 makefile <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;这是一个我为我大多数项目设计的 makefile 。它应该可以不需要修 改的用在大部分项目里。我主要把它用在 djgpp 上，那是一个 DOS 版的 gcc 编译器。因此你可以看到执行的命令名、 'alleg' 程序包、 和 RM -F 变量都反映了这一点。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 开始 === <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;###################################### <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# Generic makefile <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# by George Foot <BR>&nbsp;&nbsp;&nbsp;&nbsp;# email: george.foot@merton.ox.ac.uk <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# Copyright (c) 1997 George Foot <BR>&nbsp;&nbsp;&nbsp;&nbsp;# All rights reserved. <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 保留所有版权 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# No warranty, no liability; <BR>&nbsp;&nbsp;&nbsp;&nbsp;# you use this at your own risk. <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 没保险，不负责 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 你要用这个，你自己担风险 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# You are free to modify and <BR>&nbsp;&nbsp;&nbsp;&nbsp;# distribute this without giving <BR>&nbsp;&nbsp;&nbsp;&nbsp;# credit to the original author. <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 你可以随便更改和散发这个文件 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 而不需要给原作者什么荣誉。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# （你好意思？） <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;###################################### <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;### Customising <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 用户设定 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# Adjust the following if necessary; EXECUTABLE is the target <BR>&nbsp;&nbsp;&nbsp;&nbsp;# executable's filename, and LIBS is a list of libraries to link in <BR>&nbsp;&nbsp;&nbsp;&nbsp;# (e.g. alleg, stdcx, iostr, etc). You can override these on make's <BR>&nbsp;&nbsp;&nbsp;&nbsp;# command line of course, if you prefer to do it that way. <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 如果需要，调整下面的东西。 EXECUTABLE 是目标的可执行文件名， LIBS <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 是一个需要连接的程序包列表（例如 alleg, stdcx, iostr 等等）。当然你 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 可以在 make 的命令行覆盖它们，你愿意就没问题。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;EXECUTABLE := mushroom.exe <BR>&nbsp;&nbsp;&nbsp;&nbsp;LIBS := alleg <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;# Now alter any implicit rules' variables if you like, e.g.: <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 现在来改变任何你想改动的隐含规则中的变量，例如 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;CFLAGS := -g -Wall -O3 -m486 <BR>&nbsp;&nbsp;&nbsp;&nbsp;CXXFLAGS := $(CFLAGS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;# The next bit checks to see whether rm is in your djgpp bin <BR>&nbsp;&nbsp;&nbsp;&nbsp;# directory; if not it uses del instead, but this can cause (harmless) <BR>&nbsp;&nbsp;&nbsp;&nbsp;# `File not found' error messages. If you are not using DOS at all, <BR>&nbsp;&nbsp;&nbsp;&nbsp;# set the variable to something which will unquestioningly remove <BR>&nbsp;&nbsp;&nbsp;&nbsp;# files. <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 下面先检查你的 djgpp 命令目录下有没有 rm 命令，如果没有，我们使用 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# del 命令来代替，但有可能给我们 'File not found' 这个错误信息，这没 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 什么大碍。如果你不是用 DOS ，把它设定成一个删文件而不废话的命令。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;# （其实这一步在 UNIX 类的系统上是多余的，只是方便 DOS 用户。 UNIX <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 用户可以删除这５行命令。） <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ifneq ($(wildcard $(DJDIR)/bin/rm.exe),) <BR>&nbsp;&nbsp;&nbsp;&nbsp;RM-F := rm -f <BR>&nbsp;&nbsp;&nbsp;&nbsp;else <BR>&nbsp;&nbsp;&nbsp;&nbsp;RM-F := del <BR>&nbsp;&nbsp;&nbsp;&nbsp;endif <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;# You shouldn't need to change anything below this point. <BR>&nbsp;&nbsp;&nbsp;&nbsp;# <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 从这里开始，你应该不需要改动任何东西。（我是不太相信，太ＮＢ了！） <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;SOURCE := $(wildcard *.c) $(wildcard *.cc) <BR>&nbsp;&nbsp;&nbsp;&nbsp;OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCE))) <BR>&nbsp;&nbsp;&nbsp;&nbsp;DEPS := $(patsubst %.o,%.d,$(OBJS)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS)) \ <BR>&nbsp;&nbsp;&nbsp;&nbsp;$(patsubst %.d,%.cc,$(MISSING_DEPS))) <BR>&nbsp;&nbsp;&nbsp;&nbsp;CPPFLAGS += -MD <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;.PHONY : everything deps objs clean veryclean rebuild <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;everything : $(EXECUTABLE) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;deps : $(DEPS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;objs : $(OBJS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;clean : <BR>&nbsp;&nbsp;&nbsp;&nbsp; @$(RM-F) *.o <BR>&nbsp;&nbsp;&nbsp;&nbsp; @$(RM-F) *.d <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;veryclean: clean <BR>&nbsp;&nbsp;&nbsp;&nbsp; @$(RM-F) $(EXECUTABLE) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;rebuild: veryclean everything <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ifneq ($(MISSING_DEPS),) <BR>&nbsp;&nbsp;&nbsp;&nbsp;$(MISSING_DEPS) : <BR>&nbsp;&nbsp;&nbsp;&nbsp; @$(RM-F) $(patsubst %.d,%.o,$@) <BR>&nbsp;&nbsp;&nbsp;&nbsp;endif <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;-include $(DEPS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;$(EXECUTABLE) : $(OBJS) <BR>&nbsp;&nbsp;&nbsp;&nbsp; gcc -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS)) <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;=== makefile 结束 === <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;有几个地方值得解释一下的。首先，我在定义大部分变量的时候使 用的是 := 而不是 = 符号。它的作用是立即把定义中参考到的函 数和变量都展开了。如果使用 = 的话，函数和变量参考会留在那 儿，就是说改变一个变量的值会导致其它变量的值也被改变。例 如： <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;A = foo <BR>&nbsp;&nbsp;&nbsp;&nbsp;B = $(A) <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 现在 B 是 $(A) ，而 $(A) 是 'foo' 。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;A = bar <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 现在 B 仍然是 $(A) ，但它的值已随着变成 'bar' 了。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;B := $(A) <BR>&nbsp;&nbsp;&nbsp;&nbsp;# 现在 B 的值是 'bar' 。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;A = foo <BR>&nbsp;&nbsp;&nbsp;&nbsp;# B 的值仍然是 'bar' 。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;make 会忽略在 # 符号后面直到那一行结束的所有文字。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;ifneg...else...endif 系统是 makefile 里让某一部分码有条件的 失效／有效的工具。 ifeq 使用两个参数，如果它们相同，它把直 到 else （或者 endif ，如果没有 else 的话）的一段码加进 makefile 里；如果不同，把 else 到 endif 间的一段码加入 makefile （如果有 else ）。 ifneq 的用法刚好相反。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;'filter-out' 函数使用两个用空格分开的列表，它把第二列表中所 有的存在于第一列表中的项目删除。我用它来处理 DEPS 列表，把所 有已经存在的项目都删除，而只保留缺少的那些。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;我前面说过， CPPFLAGS 存有用于隐含规则中传给预处理器的一些 旗标。而 -MD 开关类似 -M 开关，但是从源码文件 .c 或 .cc 中 形成的文件名是使用后缀 .d 的（这就解释了我形成 DEPS 变量的 步骤）。DEPS 里提到的文件后来用 '-include' 加进了 makefile 里，它隐藏了所有因文件不存在而产生的错误信息。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;如果任何依靠文件不存在， makefile 会把相应的 .o 文件从磁碟 上删除，从而使得 make 重建它。因为 CPPFLAGS 指定了 -MD ， 它的 .d 文件也被重新产生。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;最后， 'addprefix' 函数把第二个参数列表的每一项前缀上第一 个参数值。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;这个 makefile 的那些目的是（这些目的可以传给 make 的命令行 来直接选用）： <BR>&nbsp;&nbsp;&nbsp;&nbsp;everything:（预设） 更新主要的可执行程序，并且为每一个 源码文件生成或更新一个 '.d' 文件和一个 '.o' 文件。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;deps: 只是为每一个源码程序产生或更新一个 '.d' 文件。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;objs: 为每一个源码程序生成或更新 '.d' 文件和目标文件。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;clean: 删除所有中介／依靠文件（ *.d 和 *.o ）。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;veryclean: 做 `clean' 和删除可执行文件。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;rebuild: 先做 `veryclean' 然后 `everything' ；既完全重建。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;除了预设的 everything 以外，这里头只有 clean ， veryclean ， 和 rebuild 对用户是有意义的。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;我还没有发现当给出一个源码文件的目录，这个 makefile 会失败的 情况，除非依靠文件被弄乱。如果这种弄乱的情况发生了，只要输入 `make clean' ，所有的目标文件和依靠文件会被删除，问题就应该 被解决了。当然，最好不要把它们弄乱。如果你发现在某种情况下这 个 makefile 文件不能完成它的工作，请告诉我，我会把它整好的。 <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;3 总结 <BR>&nbsp;&nbsp;&nbsp;&nbsp;~~~~~~~~~~~~~~~ <BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;我希望这篇文章足够详细的解释了多文件项目是怎么运作的，也说明了 怎样安全而合理的使用它。到此，你应该可以轻松的利用 GNU Make 工 具来管理小型的项目，如果你完全理解了后面几个部分的话，这些对于 你来说应该没什么困难。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;GNU Make 是一件强大的工具，虽然它主要是用来建立程序，它还有很多 别的用处。如果想要知道更多有关这个工具的知识，它的句法，函数， 和许多别的特点，你应该参看它的参考文件 (info pages, 别的 GNU 工具也一样，看它们的 info pages. )。 <img src ="http://www.cppblog.com/jjbird/aggbug/3380.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-02-21 16:53 <a href="http://www.cppblog.com/jjbird/articles/3380.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux环境下用GDB调试 GCC 程序</title><link>http://www.cppblog.com/jjbird/articles/3373.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Tue, 21 Feb 2006 06:19:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/3373.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/3373.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/3373.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/3373.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/3373.html</trackback:ping><description><![CDATA[　Linux 包含了一个叫 gdb 的 GNU 调试程序. gdb 是一个用来调试 C 和 C++ 程序的强力调试器. 它使你能在程序运行时观察程序的内部结构和内存的使用情况. 以下是 gdb 所提供的一些功能: <BR>　　<BR>　　它使你能监视你程序中变量的值. <BR>　　<BR>　　它使你能设置断点以使程序在指定的代码行上停止执行. <BR>　　<BR>　　它使你能一行行的执行你的代码. <BR>　　<BR>　　在命令行上键入 gdb 并按回车键就可以运行 gdb 了, 如果一切正常的话, gdb 将被启动并且你将在屏幕上看到类似的内容: <BR>　　<BR>　　GDB is free software and you are welcome to distribute copies of it<BR>　　under certain conditions; type "show copying" to see the conditions.<BR>　　There is absolutely no warranty for GDB; type "show warranty" for details.<BR>　　GDB 4.14 (i486-slakware-linux), Copyright 1995 Free Software Foundation, Inc.<BR>　　(gdb)<BR>　　 <BR>　　当你启动 gdb 后, 你能在命令行上指定很多的选项. 你也可以以下面的方式来运行 gdb : <BR>　　gdb <BR>　　<BR>　　当你用这种方式运行 gdb , 你能直接指定想要调试的程序. 这将告诉gdb 装入名为 fname 的可执行文件. 你也可以用 gdb 去检查一个因程序异常终止而产生的 core 文件, 或者与一个正在运行的程序相连. 你可以参考 gdb 指南页或在命令行上键入 gdb -h 得到一个有关这些选项的说明的简单列表. <BR>　　<BR>　　为调试编译代码(Compiling Code for Debugging) <BR>　　<BR>　　为了使 gdb 正常工作, 你必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号. gdb 利用这些信息使源代码和机器码相关联. 在编译时用 -g 选项打开调试选项. <BR>　　<BR>　　gdb 基本命令 <BR>　　<BR>　　gdb 支持很多的命令使你能实现不同的功能. 这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令, 表1列出了你在用 gdb 调试时会用到的一些命令. <BR>　　<BR>　　表1. 基本 gdb 命令. <BR>　　<BR>　　命 令 描 述　<BR>　　file 装入想要调试的可执行文件　<BR>　　kill 终止正在调试的程序 <BR>　　list 列出产生执行文件的源代码的一部分 <BR>　　next 执行一行源代码但不进入函数内部 <BR>　　step 执行一行源代码而且进入函数内部 <BR>　　run　执行当前被调试的程序 <BR>　　quit 终止 gdb　<BR>　　watch 使你能监视一个变量的值而不管它何时被改变 <BR>　　break　在代码里设置断点, 这将使程序执行到这里时被挂起 <BR>　　make 使你能不退出 gdb 就可以重新产生可执行文件 <BR>　　shell 使你能不离开 gdb 就执行 UNIX shell 命令 <BR>　　<BR>　　gdb 支持很多与 UNIX shell 程序一样的命令编辑特征. 你能象在 bash 或 tcsh里那样按 Tab 键让 gdb 帮你补齐一个惟一的命令, 如果不惟一的话 gdb 会列出所有匹配的命令. 你也能用光标键上下翻动历史命令.<BR><BR>　gdb 应用举例 <BR>　　<BR>　　本节用一个实例教你一步步的用 gdb 调试程序. 被调试的程序相当的简单, 但它展示了 gdb 的典型应用. <BR>　　<BR>　　下面列出了将被调试的程序. 这个程序被称为 greeting , 它显示一个简单的问候, 再用反序将它列出. <BR>　　<BR>　　#include　&lt;stdio.h&gt;<BR>　　main ()<BR>　　{<BR>　　　char my_string[] = "hello there";<BR>　　　my_print (my_string);<BR>　　　my_print2 (my_string);<BR>　　}<BR>　　void my_print (char *string)<BR>　　{<BR>　　　printf ("The string is %s\n", string);<BR>　　}<BR>　　void my_print2 (char *string)<BR>　　{<BR>　　　char *string2;<BR>　　　int size, i;<BR>　　　size = strlen (string);<BR>　　　string2 = (char *) malloc (size + 1);<BR>　　　for (i = 0; i &lt; size; i++)<BR>　　　　string2[size - i] = string[i];<BR>　　　string2[size+1] = `\0';<BR>　　　printf ("The string printed backward is %s\n", string2);<BR>　　}<BR>　　 <BR>　　<BR>　　<BR>　　用下面的命令编译它: <BR>　　gcc -o test test.c <BR>　　<BR>　　这个程序执行时显示如下结果: <BR>　　The string is hello there<BR>　　The string printed backward is <BR>　　<BR>　　输出的第一行是正确的, 但第二行打印出的东西并不是我们所期望的. 我们所设想的输出应该是: <BR>　　The string printed backward is ereht olleh <BR>　　<BR>　　由于某些原因, my_print2 函数没有正常工作. 让我们用 gdb 看看问题究竟出在哪儿, 先键入如下命令:<BR>　　gdb greeting <BR>　　<BR>　　注意: 记得在编译 greeting 程序时把调试选项打开. <BR>　　<BR>　　如果你在输入命令时忘了把要调试的程序作为参数传给 gdb , 你可以在 gdb 提示符下用 file 命令来载入它: <BR>　　(gdb) file greeting <BR>　　<BR>　　这个命令将载入 greeting 可执行文件就象你在 gdb 命令行里装入它一样. <BR>　　<BR>　　这时你能用 gdb 的 run 命令来运行 greeting 了. 当它在 gdb 里被运行后结果大约会象这样: <BR>　　<BR>　　(gdb) run<BR>　　Starting program: /root/greeting<BR>　　The string is hello there<BR>　　The string printed backward is<BR>　　Program exited with code 041<BR>　　 <BR>　　<BR>　　<BR>　　这个输出和在 gdb 外面运行的结果一样. 问题是, 为什么反序打印没有工作? 为了找出症结所在, 我们可以在 my_print2 函数的 for 语句后设一个断点, 具体的做法是在 gdb 提示符下键入 list 命令三次, 列出源代码: <BR>　　(gdb) list<BR>　　(gdb) list<BR>　　(gdb) list <BR>　　<BR>　　技巧: 在 gdb 提示符下按回车健将重复上一个命令. <BR>　　<BR>　　第一次键入 list 命令的输出如下: <BR>　　<BR>　　1　　　 #include　&lt;stdio.h&gt;<BR>　　2<BR>　　3　　　 main ()<BR>　　4　　　 {<BR>　　5　　　　 char my_string[] = "hello there";<BR>　　6<BR>　　7　　　　 my_print (my_string);<BR>　　8　　　　 my_print2 (my_string);<BR>　　9　　　 }<BR>　　10<BR>　　 <BR>　　<BR>　　<BR>　　如果按下回车, gdb 将再执行一次 list 命令, 给出下列输出: <BR>　　<BR>　　11　　　my_print (char *string)<BR>　　12　　　{<BR>　　13　　　　printf ("The string is %s\n", string);<BR>　　14　　　}<BR>　　15<BR>　　16　　　my_print2 (char *string)<BR>　　17　　　{<BR>　　18　　　　char *string2;<BR>　　19　　　　int size, i;<BR>　　20<BR>　　 <BR>　　<BR>　　<BR>　　再按一次回车将列出 greeting 程序的剩余部分: <BR>　　<BR>　　21　　　　size = strlen (string);<BR>　　22　　　　string2 = (char *) malloc (size + 1);<BR>　　23　　　　for (i = 0; i &lt; size; i++)<BR>　　24　　　　　string2[size - i] = string[i];<BR>　　25　　　　string2[size+1] = `\0';<BR>　　26　　　　printf ("The string printed backward is %s\n", string2);<BR>　　27　　　}<BR>　　 <BR>　　<BR>　　<BR>　　根据列出的源程序, 你能看到要设断点的地方在第24行, 在 gdb 命令行提示符下键入如下命令设置断点: <BR>　　(gdb) break 24 <BR>　　<BR>　　gdb 将作出如下的响应:<BR>　　Breakpoint 1 at 0x139: file greeting.c, line 24<BR>　　(gdb) <BR>　　<BR>　　现在再键入 run 命令, 将产生如下的输出: <BR>　　<BR>　　Starting program: /root/greeting<BR>　　The string is hello there<BR>　　Breakpoint 1, my_print2 (string = 0xbfffdc4 "hello there") at greeting.c :24<BR>　　24　string2[size-i]=string[i]<BR>　　 <BR>　　<BR>　　<BR>　　你能通过设置一个观察 string2[size - i] 变量的值的观察点来看出错误是怎样产生的, 做法是键入: <BR>　　(gdb) watch string2[size - i] <BR>　　<BR>　　gdb 将作出如下回应:<BR>　　Watchpoint 2: string2[size - i] <BR>　　<BR>　　现在可以用 next 命令来一步步的执行 for 循环了: <BR>　　(gdb) next <BR>　　<BR>　　经过第一次循环后, gdb 告诉我们 string2[size - i] 的值是 `h`. gdb 用如下的显示来告诉你这个信息: <BR>　　<BR>　　Watchpoint 2, string2[size - i]<BR>　　Old value = 0 `\000'<BR>　　New value = 104 `h'<BR>　　my_print2(string = 0xbfffdc4 "hello there") at greeting.c:23<BR>　　23 for (i=0; i&lt;size; i++)<BR>　　 <BR>　　<BR>　　<BR>　　这个值正是期望的. 后来的数次循环的结果都是正确的. 当 i=10 时, 表达式 string2[size - i] 的值等于 `e`, size - i 的值等于 1, 最后一个字符已经拷到新串里了. <BR>　　<BR>　　如果你再把循环执行下去, 你会看到已经没有值分配给 string2[0] 了, 而它是新串的第一个字符, 因为 malloc 函数在分配内存时把它们初始化为空(null)字符. 所以 string2 的第一个字符是空字符. 这解释了为什么在打印 string2 时没有任何输出了. <BR>　　<BR>　　现在找出了问题出在哪里, 修正这个错误是很容易的. 你得把代码里写入 string2 的第一个字符的的偏移量改为 size - 1 而不是 size. 这是因为 string2 的大小为 12, 但起始偏移量是 0, 串内的字符从偏移量 0 到 偏移量 10, 偏移量 11 为空字符保留. <BR>　　<BR>　　为了使代码正常工作有很多种修改办法. 一种是另设一个比串的实际大小小 1 的变量. 这是这种解决办法的代码: <BR>　　<BR>　　#include　&lt;stdio.h&gt;<BR>　　main ()<BR>　　{<BR>　　　char my_string[] = "hello there";<BR>　　　my_print (my_string);<BR>　　　my_print2 (my_string);<BR>　　}<BR>　　my_print (char *string)<BR>　　{<BR>　　　printf ("The string is %s\n", string);<BR>　　}<BR>　　my_print2 (char *string)<BR>　　{<BR>　　　char *string2;<BR>　　　int size, size2, i;<BR>　　　size = strlen (string);<BR>　　　size2 = size -1;<BR>　　　string2 = (char *) malloc (size + 1);<BR>　　　for (i = 0; i &lt; size; i++)<BR>　　　　string2[size2 - i] = string[i];<BR>　　　string2[size] = `\0';<BR>　　　printf ("The string printed backward is %s\n", string2);<BR>　　}<img src ="http://www.cppblog.com/jjbird/aggbug/3373.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-02-21 14:19 <a href="http://www.cppblog.com/jjbird/articles/3373.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下命令行下编译c程序</title><link>http://www.cppblog.com/jjbird/articles/3372.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Tue, 21 Feb 2006 06:13:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/3372.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/3372.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/3372.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/3372.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/3372.html</trackback:ping><description><![CDATA[　　C 是所有版本的UNIX上的系统语言.<BR>　　<BR>　　C 在过去的二十年中有了很大的发展. 在80年代末期美国国家标准协会(American NationalStandards Institute)发布了一个被称为 ANSI C 的 C 语言标准.这更加保证了将来在不同平台上的 C 的一致性. 在80年代还出现了一种 C 的面向对象的扩展称为 C++.<BR>　　<BR>　　Linux 上可用的 C 编译器是 GNU C 编译器, 它建立在自由软件基金会的编程许可证的基础上, 因此可以自由发布. 你能在 Linux 的发行光盘上找到它.<BR>　　<BR>　　随 Slackware Linux 发行的 GNU C 编译器(GCC)是一个全功能的 ANSI C 兼容编译器. 如果你熟悉其他操作系统或硬件平台上的一种 C 编译器, 你将能很快地掌握 GCC. 这和java在命令行下<BR>　　<BR>　　编译也很类似我们将在下面介绍如何使用 GCC 和一些 GCC 编译器最常用的选项.<BR>　　<BR>　　格式 gcc [option] [sourcefilename]<BR>　　<BR>　　gcc的选项十分繁多,大约有上百中,不过平时常用的不是非常多,我们将要介绍一些常用的选项:<BR>　　<BR>　　最简单的是:gcc hello.c<BR>　　<BR>　　默认的情况下将生成a.out的可执行性文件,你只需要在终端上输入./a.out就可以看到执行的结果.<BR>　　<BR>　　如果你想指定生成目标文件的名字那么你可以加上 -o选项,命令如下:<BR>　　<BR>　　gcc -o hello hello.c<BR>　　<BR>　　gcc也允许你只编译源程序,这样可以只检查编译时刻的错误,有利也调试程序,用-c选项可以达到这个目的<BR>　　<BR>　　命令如下:<BR>　　<BR>　　gcc -c hello hello.c<BR>　　<BR>　　结果生成了一个中间代码hello.o文件<BR>　　<BR>　　 编译选项告诉 GCC 在为 C 代码产生了汇编语言文件后停止编译. GCC 产生的汇编语言文件的缺省扩展名是 .s . -E 选项指示编译器仅对输入文件进行预处理. 当这个选项被使用时, 预处理器的输出被送到标准输出而不是储存在文件里.<BR>　　<BR>　　之后你可以对上述中间代码进行链接运行,使用命令:<BR>　　<BR>　　gcc -o hello.out hello.c<BR>　　<BR>　　优 化 选 项<BR>　　<BR>　　当你用 GCC 编译 C 代码时, 它会试着用最少的时间完成编译并且使编译后的代码易于调试. 易于调试意味着编译后的代码与源代码有同样的执行次序, 编译后的代码没有经过优化.有很多选项可用于告诉 GCC 在耗费更多编译时间和牺牲易调试性的基础上产生更小更快的可执行文件. 这些选项中最典型的是-O 和 -O2 选项.<BR>　　<BR>　　-O 选项告诉 GCC 对源代码进行基本优化. 这些优化在大多数情况下都会使程序执行的更快. -O2 选项告诉 GCC 产生尽可能小和尽可能快的代码. -O2 选项将使编译的速度比使用 -O时慢. 但通常产生的代码执行速度会更快.<BR>　　<BR>　　GCC 支持数种调试和剖析选项. 在这些选项里你会最常用到的是 -g 和 -pg 选项.<BR>　　<BR>　　 选项告诉 GCC 产生能被 GNU 调试器使用的调试信息以便调试你的程序. GCC 提供了一个很多其他 C 编译器里没有的特性, 在 GCC 里你能使 -g 和 -O (产生优化代码)联用. 这一点非常有用因为你能在与最终产品尽可能相近的情况下调试你的代码. 在你同时使用这两个选项时你必须清楚你所写的某些代码已经在优化时被 GCC 作了改动. 关于调试 C 程序的更多信息请看下一节"用 gdb 调试 C 程序" .<BR>　　<BR>　　如下命令 gcc -c -g hello.c<BR>　　<BR>　　pg 选项告诉 GCC 在你的程序里加入额外的代码, 执行时, 产生 gprof 用的剖析信息以显示你的程序的耗时情况.<BR>　　<BR>　　关于ggc的详细信息可用man gcc来查看<img src ="http://www.cppblog.com/jjbird/aggbug/3372.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-02-21 14:13 <a href="http://www.cppblog.com/jjbird/articles/3372.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>