免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: 雨丝风片
打印 上一主题 下一主题

[FreeBSD] 【FreeBSD system programming 】中文翻译计划及所有异义提交处 [复制链接]

论坛徽章:
0
31 [报告]
发表于 2006-02-13 15:28 |只看该作者
原帖由 孙轩 于 2006-2-13 15:17 发表
小弟不才,试翻弟3章


非常感谢!
一楼帖子的记录已更新。

论坛徽章:
0
32 [报告]
发表于 2006-02-13 15:33 |只看该作者
大家在翻译过程中如果遇到了什么问题就可以到这里来讨论,发挥群体优势!

论坛徽章:
0
33 [报告]
发表于 2006-02-13 15:40 |只看该作者

第一章 FreeBSD's make

今天刚开始着手,弄了几段,先来占个坑,偶认领的第一章的后续内容将在这个坑里面陆续补充完整。请大家多提意见,比如文理不通、用词不当、风格太差以及错别字等等,

第一章 FreeBSD的make


雨丝风片@chinaunix.net


1.1 FreeBSD的make

作为常用的和基本的Unix软件开发工具,make是一个可以跟踪全部的文件依赖关系的非常好的簿记工具程序。要管理依赖关系这样的项目细节常常需要花费很多的时间,甚至会拖延开发进度。当多个开发人员合作一个项目的时候,依赖关系的跟踪就可能变得相当困难了。事实上,正确地使用make可以帮助我们加快应用程序的开发,从而提高生产效率。

虽然make最初的设计是用来对应用程序版本构建的维护过程进行管理的,我们实际上还可以通过创建一系列的基于目标依赖关系的Unix shell命令来让make完成多种多样的额外工作。这些依赖关系可以用很多种方式定义——包括需要进行编译的源文件、所需的库文件、shell命令以及其它的目标。

make有多种风格的版本,其中包括GNU make和System V make。并不是在每个make版本中都有我们接下来讨论的那些特性,具体使用哪个版本完全取决于你的个人喜好。我们将主要关注跟随FreeBSD一起发布的make(也叫做bmake或pmake),尤其是如何通过它来编译和更新FreeBSD系统,也就是所谓的make world。虽然我们关注的是FreeBSD make,但我们在这里讨论的所有东西对于各种BSD版本来说都是适用的。

我们首先会讲述一个Makefile的基本文件布局和语法。如果这对于你来说太简单了,那你可以直接跳到本章结束处的示例部分去阅读。(注意,我们给出的代码示例只用于演示我们关于make目标和依赖关系的讨论,它们并不一定是可以运行的代码。)

当然,和其它工具程序一样,最开始应该先去看看man page,以对make提供的命令行选项的概要和细节有一个正式的了解。同时,和其它工具程序一样,学习make的最好方法就是使用它。创建一些小型的源文件(可以使用任何语言),然后尝试一些下面给出的例子。我们希望读完本章之后你除了理解make的语法规则之外,还知道它是如何工作的。

1.2 Makefile布局

总的说来,你使用make的方式就是让它去读一个Makefile,你需要在Makefile里指定一个目标及其依赖关系。在运行的时候,make会按顺序搜索名字为Makefile或makefile的文件。这个Makefile通常是放在一个工程的根目录下的,如果想指定其它的Makefile,可以在命令行上用-f (filename)的选项给出。

  1. make -f OtherMakefile
复制代码


1.3 语法

一个Makefile的结构由四个基本行组成,它们都可以通过在行尾添加‘\’字符来扩展到下一行(和shell编程相似)。注释是以‘#’号开始的,至行尾结束。

  1. ########################################
  2. # Simple Makefile with comment example #
  3. ########################################

  4. # when run, it will just echo hello
  5. all:
  6.    echo "hello"
复制代码


要使用make来编译一个工程,首先需要确定在你的当前工作目录中已有一个正确的Makefile,然后再通过下列命令之一来使用make:

  1. bash$ make   

  2. bash$ make all

  3. bash$ make <target name>
复制代码


1.4 目标

用来指定目标的方式有很多种,不过最常用的就是用目标文件或一个工程的名字。工程名字不应当包含有空格或标点符号,不过这只是个惯例而已;少量的空格和标点符号也是允许的。这个名字必须写在一个新行的开头,必须以单冒号(:)、双冒号(::)或感叹号(!)三者之一结束。

  1. myprog:
  2.      <some commands to the compile myprog target>

  3. another::
  4.      <some commands to the compile another target>

  5. sample!
  6.      <some commands to the compile sample target>
复制代码


在这些目标名字之后是所需的依赖条件,包括名字、变量以及其它的目标等等。如果你的依赖条件太多的话,可以用一个‘\’和一个newline来将它们分开。所有的依赖条件都必须Makefile内定义或者存在于某个外部文件中,否则make将无法知道如何去完成依赖操作。

一些示例如下:

  1. all: driver.cpp network_class.cpp file_io_class.cpp network_libs.cpp file_io_libs.cpp

  2. all: myprog.o

  3. myprog.o:
复制代码


上例中,all和myprog.o是要make的目标。注意myprog.o既是一个目标又是一个依赖条件。make首先会到myprog.o那儿,执行它的命令,然后返回到all那儿,再执行它的命令。这种操作序列是make的功能基础。

按照惯例,all:目标是你的目标中的最高者,这意味着make将从这儿开始去寻找要完成all:目标都需要哪些东西。不过all:目标并不是必需的,如果没有的话,make就会简单地选择所有列出的目标中的第一个,只对其实施操作,除非你在命令行上指定了某个目标。对于那些有一个核心的应用程序需要维护和构建的工程来说,我们建议你使用all:目标;这是一个通用的惯例,有助于避免错误和不必要的任务。

上例所示的依赖序列只是很简单的一个。下面是一个更为复杂和灵活的依赖序列,我们没有给出用于具体目标的命令:

  1. all: myprog.o lib

  2. lib: lex

  3. lex:

  4. myprog.o: app.h
复制代码


注意,在这个例子中,all:目标有两个依赖条件:myprog.o和lib。这两个依赖条件本身又都是目标,make将首先去编译myprog.o。在make编译myprog.o的时候,会发现有一个和app.h的依赖关系。app.h并没有在这个makefile里定义,但app.h却是一个在当前目录中的头文件。

用于myprog.o的命令完成之后,make即返回到all:处,继续处理下一个依赖条件,在此例中是lib。依赖条件lib本身也有一个依赖条件lex,所以make在完成lib之前会先去完成lex:。

注意:正如你所看到的,这些依赖关系可能会非常长,或者嵌套得很深。如果你有一个很大的Makefile,那一定要好好地组织一下,把目标的顺序弄好。

1.5 求值规则

依赖关系是按照依赖于目标名字结束符号的严格规则来求值的。一旦make认为满足规则,它将通过执行相应的命令来创建特定的目标(比如编译该目标)。例如,使用单冒号:可以让你对需要进行编译的目标进行更为精细的控制。也就是说,你可以指定某个特定的目标文件每次都需要重新编译或者仅当它的源文件过时之后才编译。这些规则都是基于目标名字的结束符号的,如下:

如果目标名字以单冒号(:)结束,它将根据以下两个规则来创建:
  • 如果目标尚未存在,就像我们在上面举的例子里的all:一样,make就会创建它。
  • 如果任意一个源文件具有比当前目标更新的时间戳。在上例中如果app.h或myprog.c具有更新的时间戳,myprog.o就会被make。这种情况只需简单地用一下touch命令即可出现

  1.     touch myprog.c
复制代码


如果目标名字以双冒号(::)结束,它将根据以下三个规则来创建:
  • 如果任意一个源文件具有比当前目标更新地时间戳。
  • 该目标不存在。
  • 该目标没有与之关联的源文件。


如果目标名字以感叹号(!)结束,只要make把它所需的全部依赖条件都创建完毕就会来创建它。

你只能在目标或源文件的最后一个组成部分中使用通配表达式?、*和[],而且只能用于描述已经存在的文件。比如:

  1. myprog.[oc]
复制代码


而使用花括号{}的表达式则不一定非得描述已经存在的文件。比如:

  1. {mypgog,test}.o

  2. # the expression above would match myprog.o test.o only
复制代码


最后需要注意一点:可变表达式是按照目录顺序来处理的,而非字母顺序,就跟在shell表达式中一样。例如,如果你的目标有某些基于字母顺序的依赖条件,下面这个表达式可能就不对了:

  1. {dprog,aprog,bprog,cprog}.cpp
复制代码


1.6 变量

make能够使用变量这一点是非常重要的。例如,你有一个名字为a.c的源文件,由于某种原因你想把它的名字改成b.c。通常情况下,你得把你的makefile里的每个a.c的实例都改成b.c。但是,如果你写成以下方式:

  1. MYSRC = a.c
复制代码


你只需要把这一行更新成新的名字即可,如下:

  1. MYSRC = b.c
复制代码


你因此而节省了时间,这也正是make的首要角色:项目管理。

变量可用$(<变量名字>或就用一个$来引用,但后者未被广泛使用,因此建议别那样写。

  1. $(GCC) = /usr/local/bin/gcc
复制代码


make有四种不同类型的变量,下面将按照被搜索的顺序列出它们。(make的搜索将一直进行到发现某个数值的第一个实例为止。)
  • 局部变量:这些是赋给特定目标的数值。
  • 命令行:命令行变量是在命令行上传给make的。
  • 全局变量:全局变量是在该Makefile或任何所包含的Makefile内赋值的。你在一个Makefile内最常看到的就是这些变量。
  • 环境变量:环境变量是在Makefile之外,也就是运行make的shell里设置的。


这些变量可以在Makefile里用以下五种操作符进行定义:

  • 等号“=”是最常用的操作符,这和shell的情况是类似的。数值被直接赋给了变量。例如:
    1. VAR = < value >
    复制代码

  • 加号等号“+=”的意思是附加赋值,通过把所赋数值附加到当前数值的后面来完成对变量的赋值。例如:
    1. VAR += < value to append >
    复制代码

  • 问号等号“?=”的意思是条件赋值,仅当该变量从未赋值时才进行赋值。条件赋值在向一个字符串数值添加前缀的时候非常有用。例如:
    1. VAR ?= < value if unset >
    复制代码

  • 冒号等号“:=”的意思是扩展赋值,在赋值前会对所赋数值进行扩展;通常这种扩展是在所赋变量被引用的时候才进行的。例如:
    1. VAR := < value to expand >
    复制代码

  • 感叹号等号“!=”的意思是shell命令赋值。在命令被扩展并发给shell执行完毕之后,将命令结果赋给变量。结果中的newline字符将被空格取代。例如:
    1. VAR != < shell command to execute >
    复制代码



注意:有些变量是在外部的系统级的Makefile内定义的(在/etc/make.conf或/etc/defaults/make.conf中),如果你在设置环境变量方面遇到了问题,就去检查一下系统级文件里的设置。
      
一个完整的例子如下:

  1.   #####################
  2.   # Example make file #
  3.   #####################
  4.   CURDIR != pwd
  5.   CFLAGS ?= -g
  6.   CFLAGS += -Wall -O2
  7.   
  8.   all:
  9.       echo $CFLAGS
  10.   #######################
  11.   
  12.   bash$ CFLAGS="-g -Wall" make
复制代码


在上例中,CURDIR被设置成了shell命令pwd的结果。(注意,这种赋值并不需要backtick命令(即' ')。)CFLAGS如果从未设置,会首先被设置成-g,然后不管它的当前值是什么,都会被附加上-Wall -O2。

1.7 命令

如果没有命令,那make什么都不是,只有把命令告诉make它才能完成它的工作。make只能运行这些命令,然后基于shell的退出状态判断这些命令是否成功。因此,如果命令失败,shell返回一个错误,make将会因错误而退出并于该处终止。make可以就被看作是另外一个命令——除了运行其它命令之外,它和那些命令并没有什么实际的交互。

命令必须与一个目标相关联,任何一个目标都可以有多个命令。例如:

  1. # example for an all-install target

  2. all-install:
  3.    $(CC) $(CFLAGS)  $(MYSRC)
  4.    cp  $(MYPROG) $(INSTALL_DIR)
  5.    echo "Finished the build and install"
复制代码

每个命令都必须在一个目标之后以新行开始,在实际命令起始位置之前必须要有一个tab键,如上所示。

对于大多数情况而言,Makefile里的命令只要是个有效的shell命令就行,命令还经常会包括变量。例如:

  1. CPP = -g++
  2. CFLAGS = -Wall -O2

  3. myprog.o:
  4.     $(CPP) $(CFLAGS) -c myprog.c
复制代码

下面这个例子告诉make使用给定值编译myprog.c。这些命令可能会比一行要长,它们也可以被写来完成其它的任务。这是非常重要的,因为编译器可以接受相当多的命令行选项、环境设置以及定义等等,比如:

  1. CLFAGS = $(LINK_FLAGS) $(LINK_LIBS) $(OTHER_LIBS) \
  2. $(OPTIMIZER_FLAGS) $(DEFINES) $(NO_KERNEL) $(OBJS) \
  3. $(CPU_TYPE_FLAGS) $(USE_MY_MALLOC) $(UDEFINES) \
  4. $(SRC_FILE)  $(OTHER_SRC_FILES)
复制代码

下面这个例子告诉make删除所有的object文件、core文件以及应用程序本身,然后把log文件移走,这些操作都可以很方便的完成。

  1. CPP = -g++
  2. CFLAGS = -Wall -O2
  3. APP = myapp
  4. DATE != date +%m%d%y_%H_%M
  5. LOG = debug


  6. myprog.o:
  7.       $(CPP) $(CFLAGS) -c myprog.c
  8.       rm -f  *.o  *.core $(APP)
  9.       mv  $(LOG).log  $(LOG)_$(DATE).log

  10. clean:
  11.     rm -f *.o *.core $(APP)
  12.     mv $(LOG).log $(LOG)_$(DATE).log
复制代码

但是,如果并不存在log文件,make就会因错误而退出。为了避免这种情况,可以在命令之前加上“-”号。通过在命令前面加上减号,你就告诉了make忽略执行命令时遇到的错误。(不过make仍然会打印出错误信息。)因此,在某个命令出现错误之后make仍将继续。例如:

  1. clean:
  2.     -rm -f *.o *.core $(APP)
  3.     -mv $(LOG).log $(LOG)_$(DATE).log
复制代码


这将使得make忽略掉rm和mv命令可能遇到的错误。

你还可以让make禁止掉“echo”之类的命令的输出。echo首先告诉make打印出包括echo语句在内的整个命令,然后再执行命令并将字符串打印到屏幕上:
  1.     echo $(SOME_DEFINED_STRING)
复制代码


要避免这种情况,可以在echo命令之前加上“@”符号,这将告诉make只打印字符串,比如:
  1.     @echo $(SOME_DEFINED_STRING)
复制代码


“-”号和“@”号都既可用于变量,又可用于形为字符串的命令,但需要确定你对变量命令的引用是正确的。下例演示了如何对命令使用@操作符:
  1. ECHO = echo
  2. MESSAGE = "Print this message"

  3. msg::
  4.   @$(ECHO) $(MESSAGE)  
复制代码


1.8 条件语句(#if,#ifndef等等)

如果你对C和C++比较熟悉,那你肯定知道条件预处理命令。功能繁多的make也有一个类似的特性。条件语句使你可以选择Makefile里的哪个部分需要被处理。这些条件语句最多可以嵌套30层,并且可以放在Makefile里的任何地方。每条语句都必须以一个圆点(.)开始,而且条件语句块必须以.endif结束。

条件语句允许使用逻辑操作符,比如逻辑AND“&&”、逻辑OR“||”,整条语句还可以用“!”操作符取反。“!”操作符具有最高优先级,其后依次是逻辑AND和逻辑OR。括号可以被用来指定优先级顺序。关系运算符也是可以使用的,比如“>”、“>=”、“<”、“<=”、“==”和“!=”。这些操作符可被用于十进制和十六进制的数值。对于字符串则可以使用“==”和“!=”操作符。如果没有给出操作符,那么将会把数值和0进行比较。

在下面的例子中,在对VER变量进行赋值之后对条件进行测试。注意,如果VER变量未被赋值,则最后的.else条目将被求值为真,TAG将被赋成2.4_stable的值。

  1. .if $(VER) >= 2.4
  2.   TAG = 2.4_current
  3. .elif $(VER) == 2.3
  4.   TAG = 2.3_release
  5. .else
  6.   TAG = 2.4_stable
  7. .endif
复制代码


条件语句可用于测试变量,也可以用于下面这种函数风格的表达式。

这些用法中有些是有简写形式的。出于兼容性方面的考虑,我们列出了简写形式。非简写形式意思更为确定,也更容易被理解,但需要你敲入更多的字符。

在使用简写形式的时候是不必使用括号的。此外,简写形式还可以和if/else语句以及其它的简写形式混合在一起:

make( < arg > ) short hand [ .ifmake, .ifnmake, .elifmake, .elifnmake ]

在上例中,make将以一个目标名字作为它的参数。如果这个目标已在命令行上给出,或者它就是缺省进行make的目标,那么该值就为真。下例中将根据make()表达式的规则给CFLAGS赋值:

  1. .if make(debug)
  2.   CFLAGS += -g
  3. .elif make(production)
  4.   CFLAGS += -O2
  5. .endif
复制代码


下面是用简写形式表示的相同代码:

  1. .ifmake debug
  2.   CFLAGS += -g
  3. .elifmake production
  4.   CFLAGS += -O2
  5. .endif
复制代码


target( < arg > )

这种形式将以一个目标名字作为参数。仅当该target已被定义时该值才为真。这个表达式没有简写形式。例如:

  1. .if target(debug)
  2.   FILES += $(DEBUG_FILES)
  3. .endif
复制代码


在上例中,如果debug目标返回真的话就会对FILES变量进行附加操作。

empty ( < arg > )

这种形式以一个变量为参数,并允许使用修饰符。当变量被扩展之后是一个空字符串时该值为真。这个表达式没有简写形式。此外需要注意的是,你在使用这个表达式的时候并不需要对数值进行引用,记住是VAR而不是$(VAR)。例如:

  1. .if empty (CFLAGS)
  2.    CFLAGS = -Wall -g
  3. .endif
复制代码


defined( < arg > ) short hand [ .ifdef , .ifndef , .elifdef, elifndef ]

下面这个例子采用一个变量作为参数。仅当变量已被定义时该值为真。

  1. .if defined(OS_VER)
  2.   .if $(OS_VER) == 4.4
  3.      DIRS += /usr/local/4.4-STABLE_src
  4.   .endif
  5. .else
  6.   DIRS += /usr/src
  7. .endif
复制代码


下面是简写形式:

  1. .ifdef OS_VER
  2. . if $(OS_VER) == 4.4
  3.   DIRS += /usr/local/4.4-STABLE_src
  4. . endif
  5. .else
  6.   OS_VER = 3.2
  7.   DIRS += /usr/src
  8. .endif
复制代码


正如你所看到的,make允许嵌套的条件和define表达式。和C不同的是,你不能对if语句和变量赋值语句进行缩进。如果想让你的条件语句块更清晰一点的话,你可以在圆点之后、if之前加一些空格。示例如下:

  1. .if $(DEBUG) == 1
  2.    $(CFLAGS) = -g
  3. .     ifndef $(DEBUG_FLAGS)
  4.          $(FLAGS) = $(DEBUG_FLAGS)
  5. .     endif
  6. .endif
复制代码


exists( < arg > )

下面的例子演示了如何使用exists,以及如何给一个目标添加条件语句。如果存在tmp目录,make将运行-rm tmp/*.o命令。正如你在这个例子中看到的,.if语句仅在clean目标中被求值;所运行的命令必须遵从常规的命令语法。

  1. clean:
  2.     -rm -f *.o *.core
  3. .if exists(tmp)
  4.     -rm tmp/*.o
  5. .endif
复制代码


1.9 系统Makefiles,模板以及.include指令

C的一个重要特性就是它的简单明了的预处理指令,也就我们常说的#include。这个特性也在make中实现了。所不同的是,如果你想包含另外一个Makefile,那你得在文件末尾包含它,而不是像C那样在文件头包含,这是因为变量是按顺序进行赋值的。Makefile中又有Makefile可能会造成混淆,不过包含一个Makefile的基本语法却是很简单的。注意,在include单词前面必须要加一个圆点(.)。基本语法如下:

  1. .include file name
复制代码


如果要指定一个位于系统make目录中的Makefile,可以使用尖括号:

  1. .include <file name>
复制代码


如果所指定的文件位于当前目录或者是由-I命令行选项指定的目录中,则使用双引号,这和C的#include是类似的:

  1. .include "file name"
复制代码


在下面的例子中,如果已在project.mk中定义了CFLAGS变量,它将被这里的新声明所取代。当包含多个Makefile时这就可能造成一些麻烦。

  1. .include "../project.mk"

  2. CFLAGS = -g
复制代码


FreeBSD系统有很多的系统Makefile可供包含,与之对应的是完成各种任务的例程。在大多数的FreeBSD系统中,这些Makefile位于/usr/share/mk/目录里。除此之外,还有一个/etc/defaults/make.conf,它是可以被/etc/make.conf取代的。(细节请参见man make.conf。)

下面是一个常用的Makefile的简单列表。如果你正准备进行大量的内核编程或应用程序移植,那就好好利用Makefile吧。这些Makefile的形式为bsd.<type>.mk,其中<type>表示这些Makefile的用途。
  • bsd.dep.mk:这是一个用来处理Makefile依赖关系的非常有用的包含文件。
  • bsd.port.mk:这是一个在构建应用程序的ports的时候使用的包含文件。

使用.include指令的好处就是你可以把你的工程的Makefile分成很多的片断。比如,你的工程可以有一个主Makefile,由所有其它的子Makefile进行包含。这个主Makefile可以包含编译器变量及其所需的选项。这样一来,就不必在每个Makefile里都指定编译器和所需选项了,对编译器名字的引用也因此而得到了简化。这些公共使用的片断之后还可以用于其它的Makefile,对编译程序等例程的修改也将随之在所有的Makefile中生效。

1.10 高级选项

高级的make选项主要为了增加灵活性。我们建议你仔细阅读一下make的man page,以便对make有一个深入的了解。下面这些选项是我们最常使用的。

局部变量

make可以使用专门定义的局部变量,其范围仅限于当前目标。下面列出了七个这样的局部变量,同时还给出了它们的System V兼容的老的表示形式。(不建议使用System V的老的表示形式,之所以列出它们,只是出于向后兼容的考虑。)

这个变量就是目标的名字:

  1. .TARGET
  2. old style notation: '@'
复制代码


这个变量包含了当前目标的全部源文件的列表:

  1. .ALLSRC
  2. old style notation: '>'
复制代码


这个变量是当前目标所隐含的源文件。它的数值是这个目标的源文件的名字和路径。(示例见下面的.SUFFIX部分。)

  1. .IMPSRC
  2. old style notation: '<'
复制代码


这个变量保存的是已经被确定为过期的源文件的列表:

  1. .OODATE
  2. old style notation: '?'
复制代码


这个变量的值是不包括后缀和路径的文件名:

  1. .PREFIX
  2. old style notation: '*'
复制代码


这个变量的值是档案文件的名字:

  1. .ARCHIVE
  2. old style notation: '!'
复制代码


这个变量的值是档案成员的名字:

  1. .MEMBER
  2. old style notation: '%'
复制代码


当在依赖关系行中使用这些局部变量的时候,只有.TARGET、.PREFIX、.ARCHIVE和.MEMBER具有该目标的值。

另外还有一个非常不错的指令:

  1. .undef <variable>
复制代码


这会让你非常方便地取消对一个变量的定义。注意,只有全局变量才能取消定义。例如:

  1. .ifdef DEBUG
  2. .undef RELEASE
  3. .endif
复制代码


1.11 转换规则(后缀规则)

转换规则规定了如何去创建一个目标。你可以利用这些规则,省下为每个目标文件编写规则的时间。语法很简单:.SUFFIXES: (suffix list)

注意,可能有多个不同的后缀共享相同的转换后缀(.cpp、.cc、.c都可以转换成a.o)。如果没有列出后缀,make将把之前的所有后缀都删除。这可能会在你有多个.SUFFIXES规则的时候造成很大的混乱。我们建议你只使用一个规则,并把它放在Makefile的底部。把.SUFFIXES语句块中列出的东西和target进行比较,你就容易理解它的结构了。

  1. .SUFFIXES: .cpp .c .o .sh

  2. .c.o:
  3.      $(CC) $(CFLAGS) -c ${.IMPSRC}

  4. .cpp.o:
  5.      $(CPP) $(CXXFLAGS) -c ${.IMPSRC}

  6. .sh:
  7.      cp ${.IMPSRC} ${.TARGET}
  8.      chmod +x ${.TARGET}
复制代码


上面给出的这些规则将会编译C和C++源文件。不过对于.sh:规则而言,它也可以告诉make去创建相应的shell脚本。注意,如果想在这个例子中列出某个shell脚本作为目标,那就不能添加.sh扩展名,不过那个shell脚本本身却必须是带有.sh后缀的。

如果我们把不带.sh后缀的install_script列出来作为一个依赖条件,那就必须存在一个带有.sh后缀的shell脚本,名字为install_script.sh。也就是说,如果某个文件可以被列出来作为一个目标,那么在这个文件的存在期间,仅当make认为那个目标和该文件比起来过了期之后才会去创建它,make并不会去创建这个文件。示例见下;更多的信息请参见apps.h的例子:

  1. all: install_script $(OBJS)
  2.      $(CPP) $(CFLAGS) -o $(APP) $(OBJS) $(LINK)
复制代码


1.12 有用的命令行选项

下面给出了一些非常容易学习和使用的命令行选项。这并不是一个完整的列表,要想知道其它的选项还得去看man page。

-D <variable name to define>

这个选项将在命令行上定义一个变量,如果你的Makefile里面有.ifdef语句,那么使用这个选项就非常方便了。例如:

  1. .ifdef DEBUG
  2.   CFLAGS += -g -D__DEBUG__
  3. .endif
复制代码


于是,当你运行命令make -D DEBUG的时候,make就会正确的设置CFLAGS,在编译你的应用程序的时候也把调试语句编进去。

-E < variable name to override >

这个选项将用环境变量的值取代Makefile中给变量赋的值。在使用这个选项之前,需要先设置你的shell中的环境变量。例如:

  1. bash $ CFLAGS="-O2 -Wall" make -E CFLAGS
复制代码


-e

和大写形式类似,-e将用环境变量取代Makefile中的所有变量。如果某个变量没有定义相应的环境变量,则按正常方式赋值。

-f <makefile to use>

这个选项使你可以在命令行上指定Makefile,这一点在你需要多个Makefile时很有用。例如:

  1. bash$  make -f Makefile.BSD
复制代码


-j < number of max_jobs >

这个选项使你可以指定make能够派生出多少个job来。一般来说make只会派生出一个,但对于一个非常大的工程而言,如果想让你的硬件物尽其用的话,那就指定4个吧,如下:

  1. make -j 4 world
复制代码


如果超过了4个,有时反而会延长执行时间,不过有些人倒是可能觉得CPU被六个或更多的job折腾的样子很有趣。

-n

这个选项对于Makefile的调试很有用,它可以让你看到make究竟会执行哪些命令,但又不会实际去执行它们。对于有很多命令的大型工程,最好是把输出重定向到一个外部文件中,否则就会有浩如烟海的命令显示出来。示例如下:

  1. bash $ make -n >> make_debug.log 2>&1
复制代码


-V < variable name >

这个选项将基于全局的上下文打印变量的值。与前一个选项类似,make也不会去构建任何目标。你可以在命令行上指定多个-V选项,如下:

  1. make -V CFLAGS -V LINK -V OBJS
复制代码


1.13 一个最后的例子

下面列出的Makefile是一个可以重复使用的Makefile的例子。在你包含了它之后,它就知道从列出的.SUFFIXES规则中得知如何去编译C++源文件。它还知道如何去安装应用程序和清除开发目录。这显然并不是一个很好理解的Makefile,但它却是一个很好的创建通用模板风格的Makefile的范例,这种Makefile包含了那些用于开发的公共例程。这不只是节省了在创建每个Makefile时重复输入这些公共规则的时间,它还能让开发人员重复使用已知的好例程。

  1. ########################################################
  2. #
  3. # FILE: Makefile
  4. #
  5. # AUTHOR: Nathan Boeger
  6. #
  7. # NOTES:
  8. #  This is a generic Makefile for *BSD make, you will
  9. #  need to customize the listed variables below inside
  10. #  the Makefile for your application.
  11. #
  12. # INSTALL_DIR = name of the directory that you want to install
  13. #   this applicaion (Ex: /usr/local/bin/ )
  14. #
  15. # APP          = name of the application
  16. #
  17. # C_SRC      = C source files (Ex: pstat.c )
  18. #
  19. # CPP_SRC  = CPP source files (Ex: node.cpp)
  20. #
  21. #
  22. # $Id: ch01.html,v 1.5 2004/08/10 14:41:39 nathan Exp $
  23. #########################################################

  24. # Make the OBJ's from our defined C & C++ files
  25. .ifdef CPP_SRC
  26. OBJS            =       ${CPP_SRC:.cpp=.o}
  27. .endif

  28. .ifdef C_SRC
  29. OBJS            +=      ${C_SRC:.c=.o}
  30. .endif

  31. # define the  Compiler. The compiler flags will be appended to
  32. # if defined, else they are just assigned the values below
  33. CPP             =        g++
  34. CFLAGS          +=       -Wall -Wmissing-prototypes -O
  35. LINK            +=       -lc

  36. # Add a debug flag.
  37. .ifdef DEBUG
  38.   CFLAGS += -g
  39. .endif

  40. # Targets
  41. all: ${OBJS}
  42.     $(CPP) $(CFLAGS) -o $(APP) ${OBJS} $(LINK)

  43. depend:
  44.     $(CPP) -E -MM ${C_SRC} ${CPP_SRC}  > .depend

  45. #######################################################
  46. #
  47. #        INSTALL SECTION
  48. #
  49. # install will copy the defined application (APP) into the
  50. # directory INSTALL_DIR and chmod it 0755
  51. # for more information on install read MAN(1) install
  52. ########################################################
  53. install: all
  54.     install -b -s $(APP) $(INSTALL_DIR)
  55.   
  56. clean
  57.      rm -f $(APP) *.o *.core

  58. # SUFFIX RULES
  59. .SUFFIXES: .cpp .c .o

  60. .c.o:
  61.        $(CPP) $(CFLAGS) -c ${.IMPSRC}
  62. .cpp.o:
  63.        $(CPP) $(CFLAGS) -c ${.IMPSRC}
复制代码

下面给出的Makefile是需要你在工程目录内部创建的:

  1. #######################################################
  2. #       PROJECT Makefile
  3. #
  4. # This is what the programs makefile would look like
  5. # These are the only variables you will need to define
  6. ######################################################

  7. APP            = myapp
  8. C_SRC          = debug_logger.c
  9. CPP_SRC        = myapp.cpp  base_classes.cpp
  10. INSTALL_DIR    = /usr/local/bin/

  11. # And include the template Makefile, make sure its
  12. # path is correct.  

  13. .include "../../bsd_make.mk"
复制代码

[ 本帖最后由 雨丝风片 于 2006-3-4 08:24 编辑 ]

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
34 [报告]
发表于 2006-02-13 17:25 |只看该作者
我从chapter 8 开始翻译,如果翻完该章还没人认领前面的,我依次反向翻译各个章节

论坛徽章:
0
35 [报告]
发表于 2006-02-13 17:34 |只看该作者
原帖由 FinalBSD 于 2006-2-13 17:25 发表
我从chapter 8 开始翻译,如果翻完该章还没人认领前面的,我依次反向翻译各个章节


老大出手,要啥啥有!
前面已经给你记上一笔了!

现在这根绳子一头一尾还有中间都给点着了,看多快能烧完!

论坛徽章:
1
寅虎
日期:2013-09-29 23:15:15
36 [报告]
发表于 2006-02-13 17:49 |只看该作者
看到大家火红火红的,真是高兴!
可惜自己能力有限,帮不上忙,惭愧.

论坛徽章:
0
37 [报告]
发表于 2006-02-13 18:10 |只看该作者
不错,学习

论坛徽章:
0
38 [报告]
发表于 2006-02-13 18:31 |只看该作者
原帖由 congli 于 2006-2-13 17:49 发表
看到大家火红火红的,真是高兴!
可惜自己能力有限,帮不上忙,惭愧.


老大何必如此谦虚呢,该出手时就出手,学以致用,用以致学哈!
我都是硬着头皮上的,反正有大伙儿在后面撑着呢!

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
39 [报告]
发表于 2006-02-13 18:35 |只看该作者
                   第八章 FreeBSD 5.x
                                                      译者:FinalBSD@hotmail.com

8.1  FreeBSD 5.x
  2003年1月发布的FreeBSD-5.x 分支,是FreeBSD项目的一个重要的里程碑。在近3年的开发中,FreeBSD不管是在内核还是基本系统上都有了许多的变化。大部分的这些变化会影响到系统管理员,FreeBSD 编程者则不会,因此也不会影响到此书中讨论的任何一部分。一些例外将在下面进行阐述。

8.2  启动布局(Boot Layout)
  第一个变化是启动文件的组织方式。FreeBSD 5.x系统已将所有的模块和内核文件都移到/boot目录下。和老版本的FreeBSD一样,一些和系统启动相关的配置文件也位于此目录下。这个看起来很小的变化实际上提供了更多的便利,因为现在,在不同的设备间分离/和/boot(包含所有的内核和内核模块)十分容易。

8.3  Devfs
  FreeBSD 5.x中,我们最喜欢的特性是devfs.之前的版本,/dev被塞满了超过1000个文件。设备节点和大多数支持的设备都有一项作为文件保存在该目录下。如你所想,这个目录变得非常的大并且包含了许多不必要的文件。比如,一个有IDE设备的系统会在此目录下包含SCSI设备文件,哪怕是该系统没有任何SCSI设备。devfs中这一切弊病都之前将不复存在.
现在/dev下仅仅包含那些真实存在的设备项。实际上,FreeBSD 5.x并不和之前版本一样将/dev当作到文件系统的一个挂载点,并称之为devfs。

  devfs文件系统和proc文件系统相近。二者都是挂载点,该挂载点包含在硬盘上不存在的文件。这些文件由内核创建并且仅以文件方式出现。事实上,它们是在系统启动后被建立的。Devfs提供了更多的便利性,因为你能支持多个devfs挂载点。比如,如果你想chroot或者jail一个进程,你不需要手动创建/dev目录,取而代之的是,你可以简单的为你的进程创建一个新的挂载点并挂载devfs.

  devfs的另一个优点是可以告诉你系统真实存在哪些设备。你要做的仅仅是cd到/dev目录然后列出那些文件。这就很方便用户得到系统上所有的设备列表,更重要的是,检测到哪些设备。

8.4  a.out
  FreeBSD 5.x系列已经在基本系统中去除了对a.out二进制格式的支持。但是你仍然可以加入a.out二进制支持。这是因为a.out是种相当老的格式,并且现在都会优先选择新的ELF格式。ELF格式更灵活而且目前被广泛使用。

8.5  gcc-3.2工具链
  FreeBSD 5.x 现在使用gcc-3.2 工具链作为基本系统。这是个很重要的改变:gcc-3.x更接近ISO,并且它的C++ ABI更稳定。然而,这或许会给一些人带来麻烦。他们编写的一些程序在使用gcc-3.x进行编译前也许需要进行更新。如果你使用flex或者yacc,请确定你使用的是最新的版本,或者为你当前的版本打好补丁,因为已经确认知道他们会导致问题出现。

8.6  SMPng
  FreeBSD 5.x 已经改进为支持SMP的系统了,这一改进都来自于我们常说的SMPng(下一代SMP).尽管之前版本也支持SMP,但是性能有待提高。

8.7 内核调度实体(KSE)
  另一个新的特性是内核调度实体(KSE).KSE是个内核支持的线程系统,和Scheduler Activations在概念概念上很接近。特别的,在内核端,KSE在于对FreeBSD的调度的修改。并且在用户端使用的是POSIX线程实现方式,这种方式会利用内核提供的额外工具。然而,你不需要配置任何特殊的内核参数,就可以编译得到一个具有KSE相关修改的内核。

  为了在应用程序中使用KSE,你可以使用libpthreads来链入之。libpthreads默认并没编译进系统,所以你首先需要在系统上安装好libpthreads。然后,在它的makefile中,将-pthread选项改为-lpthread并重新链入(relink).

8.8 小结
  FreeBSD在几年的发展中已经变得成熟,并且现在是一个可用的非常稳定的操作系统了。有了SMP的增强支持和内核线程,FreeBSD会一如既往的提供强稳定性和高性能。已经加入了对一些新平台的支持,比如Sparc64和ia64.这些新的平台会帮助BSD发行版多年来继续提供高质量的开源选择。

论坛徽章:
0
40 [报告]
发表于 2006-02-13 18:41 |只看该作者
原帖由 FinalBSD 于 2006-2-13 18:35 发表
                   第八章 FreeBSD 5.x
                                                      译者:FinalBSD@hotmail.com

8.1  FreeBSD 5.x
  2003年1月发布的FreeB ...


老大,你的效率也太高了!

那偶直接把第七章也划到你的名下了?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP