2004年10月16日

今天办了件不怎么爽的事情:上午实达的人早早的来到实验室,准备为说服李老师作最后的努力。不过李老师把他们提出的问题都分析清楚后还是比较坚决的说“只要我们的方案没有问题我们还是愿意尝试”。实达的人看到僵持不下,无法继续下一步的工作,因此作出了让步。不过能看出来他们很不情愿,不经意中他们也说出了他们之所以坚持用port绑定ip,用3层不用2层的方式的根本原因(我猜的)——ip绑在vlan上,是一种比较落后的技术方案(他们的观点),这样的方案不利于他们今后对万兆网络的宣传。听到这里,我和师姐都认为,其实,这个理由比什么技术上的所谓优劣论都更有说服力,既然这次是大家的合作,而不只是简单的雇佣和买卖关系,我们完全也会站在他们的角度来考虑问题。可惜,他们一直没有说出这个理由,也许只是为了显得他们不自私。

 

这是他们的失败,也是他们没有很好的,开诚布公的和我们进行交流的一个结果。我想通过和我们比较熟悉的实达售前来说明一下我们其实很有诚意想和他们一起把万兆示范网建好。结果不会说话,说的是“你们应该如何如何”。。这样不合适的表达方式,最终让我的好心办了坏事。也是实达的售前更加坚定我是在挑实达毛病的想法~无奈。hoho

 

下次我会争取把话想好了再说。希望这次的改造顺利,无论如何,也是有利于我们学校和自身的。

 

BTW,实达人体现出来的是一种较平民化的散漫,没有华为人那种如狼似虎的决心和干劲。不过不能就此说实达差,只是反映出的精神面貌不一样,我喜欢华为的风格更多些。

 

后话~万一哪天实达人看到我的blog,呵呵,会不会有K我的冲动呢?但愿不要了。

发信人: joviqq (smtp projecting&freeradius), 信区: CPlusPlus
标  题: GNU make 指南
发信站: BBS 水木清华站 (Wed Mar 17 16:12:49 2004), 转信

翻译: 哈少

译者按: 本文是一篇介绍 GNU Make 的文章,读完后读者应该基本掌握了 make 的用法。
而 make 是所有想在 Unix (当然也包括 Linux )系统上编程的用户必须掌握的工具。如
果你写的程序中没有用到 make ,则说明你写的程序只是个人的练习程序,不具有任何实用
的价值。也许这么说有点 儿偏激,但 make 实在是应该用在任何稍具规模的程序中的。希
望本文可以为中国的 Unix 编程初学者提供一点儿有用的资料。中国的 Linux 用户除了学
会安装红帽子以外, 实在应该尝试写一些有用的程序。个人想法,大家参考。

 

——————————————————————————–
C-Scene 题目 #2
多文件项目和 GNU Make 工具
作者: 乔治富特 (Goerge Foot)
电子邮件: george.foot@merton.ox.ac.uk
Occupation: Student at Merton College, Oxford University, England
职业:学生,默尔顿学院,牛津城大学,英格兰
IRC匿名: gfoot

——————————————————————————–
拒绝承诺:作者对于任何因此而对任何事物造成的所有损害(你所拥有或不 拥有的实际的
,抽象的,或者虚拟的)。所有的损坏都是你自己的责任,而 与我无关。

所有权: “多文件项目”部分属于作者的财产,版权归乔治富特1997年 五月至七月。
其它部分属 CScene 财产,版权 CScene 1997年,保留所有 版权。本 CScene 文章的
分发,部分或全部,应依照所有其它 CScene 的文章 的条件来处理。

0) 介绍
~~~~~~~~~~~~~~~
本文将首先介绍为什么要将你的C源代码分离成几个合理的独立档案,什么时 候需要分,
怎么才能分的好。然后将会告诉你 GNU Make 怎样使你的编译和连 接步骤自动化。对于其
它 Make 工具的用户来说,虽然在用其它类似工具时要 做适当的调整,本文的内容仍然是
非常有用的。如果对你自己的编程工具有怀 疑,可以实际的试一试,但请先阅读用户手册

1) 多文件项目
~~~~~~~~~~~~~~~~~~~~~~

1.1为什么使用它们?

首先,多文件项目的好处在那里呢?
它们看起来把事情弄的复杂无比。又要 header 文件,又要 extern 声明,而且如果需要查
找一个文件,你要在更多的文件里搜索。

但其实我们有很有力的理由支持我们把一个项目分解成小块。当你改动一行代码,编译器需
要全部重新编译来生成一个新的可执行文件。但如果你的项目是分开在几个小文件里,当你
改动其中一个文件的时候,别的源文件的目标文件(object files)已经存在,所以没有什么
原因去重新编译它们。你所需要做的只是重现编译被改动过的那个文件,然后重新连接所有
的目标文件罢了。在大型的项目中,这意味着从很长的(几分钟到几小时)重新编译缩短为
十几,二十几秒的简单调整。

只要通过基本的规划,将一个项目分解成多个小文件可使你更加容易的找到一段代码。很简
单,你根据代码的作用把你的代码分解到不同的文件里。当你要看一段代码时,你可以准确
的知道在那个文件中去寻找它。

从很多目标文件生成一个程序包 (Library)比从一个单一的大目标文件生成要好的多。当然
实际上这是否真是一个优势则是由你所用的系统来决定的。但是当使用 gcc/ld (一个 GNU
C 编译/连接器) 把一个程序包连接到一个程序时,在连接的过程中,它会尝试不去连接没
有使用到的部分。但它每次只能从程序包中把一个完整的目标文件排除在外。因此如果你参
考一个程序包中某一个目标档中任何一个符号的话,那么这个目标文件整个都会被连接进来
。要是一个程序包被非常充分的分解了的话,那么经连接后,得到的可执行文件会比从一个
大目标文件组成的程序包连接得到的文件小得多。

又因为你的程序是很模块化的,文件之间的共享部分被减到最少,那就有很多好处——可以
很容易的追踪到臭虫,这些模块经常是可以用在其它的项目里的,同时别人也可以更容易的
理解你的一段代码是干 什么的。当然此外还有许多别的好处……

1.2 何时分解你的项目

很明显,把任何东西都分解是不合理的。象“世界,你们好”这样的简单程序根本就不能分
,因为实在也没什么可分的。把用于测试用的小程序分解也是没什么意思的。但一般来说,
当分解项目有助于布局、发展和易读性的时候,我都会采取它。在大多数的情况下,这都是
适用的。(所谓“世界,你们好”,既 ‘hello world’ ,只是一个介绍一种编程语言时惯
用的范例程序,它会在屏幕上显示一行 ‘hello world’ 。是最简单的程序。)

如果你需要开发一个相当大的项目,在开始前,应该考虑一下你将如何实现它,并且生成几
个文件(用适当的名字)来放你的代码。当然,在你的项目开发的过程中,你可以建立新的
文件,但如果你这么做的话,说明你可能改变了当初的想法,你应该想想是否需要对整体结
构也进行相应的调整。

对于中型的项目,你当然也可以采用上述技巧,但你也可以就那么开始输入你的代码,当你
的码多到难以管理的时候再把它们分解成不同的档案。但以我的经验来说,开始时在脑子里
形成一个大概的方案,并且尽量遵从它,或在开发过程中,随着程序的需要而修改,会使开
发变得更加容易。

1.3 怎样分解项目

先说明,这完全是我个人的意见,你可以(也许你真的会?)用别的方式来做。这会触动到
有关编码风格的问题,而大家从来就没有停止过在这个问题上的争论。在这里我只是给出我
自己喜欢的做法(同时也给出这么做的原因):

i) 不要用一个 header 文件指向多个源码文件(例外:程序包 的 header 文件)。用一个
 header定义一个源码文件的方式 会更有效,也更容易查寻。否则改变一个源文件的结构(
并且 它的 header 文件)就必须重新编译好几个文件。

ii) 如果可以的话,完全可以用超过一个的 header 文件来指向同 一个源码文件。有时将
不可公开调用的函数原型,类型定义 等等,从它们的C源码文件中分离出来是非常有用的
。使用一 个 header 文件装公开符号,用另一个装私人符号意味着如果 你改变了这个源码
文件的内部结构,你可以只是重新编译它而 不需要重新编译那些使用它的公开 header 文
件的其它的源文 件。

iii) 不要在多个 header 文件中重复定义信息。 如果需要, 在其中一个 header 文件里
#include 另一个,但 是不要重复输入相同的 header 信息两次。原因是如果你以后改 变
了这个信息,你只需要把它改变一次,不用搜索并改变另外一 个重复的信息。

iv) 在每一个源码文件里, #include 那些声明了源码文件中的符 号的所有 header 文件
。这样一来,你在源码文件和 header 文件对某些函数做出的矛盾声明可以比较容易的被编
译器发现。

1.4 对于常见错误的注释

a) 定义符 (Identifier) 在源码文件中的矛盾:在C里,变量和函数的缺 省状态是公用的
。因此,任何C源码档案都可以引用存在于其它源 码档中的通用 (global) 函数和通用变
量,既使这个档案没有那个变 量或函数的声明或原型。因此你必须保证在不同的两个档案
里不能 用同一个符号名称,否则会有连接错误或者在编译时会有警告。

一种避免这种错误的方法是在公用的符号前加上跟其所在源文件有 关的前缀。比如:所有
在 gfx.c 里的函数都加上前缀“gfx_”。如果 你很小心的分解你的程序,使用有意义的函
数名称,并且不是过分 使用通用变量,当然这根本就不是问题。

要防止一个符号在它被定义的源文件以外被看到,可在它的定义前 加上关键字“static”
。这对只在一个档案内部使用,其它档案都 都不会用到的简单函数是很有用的。

b) 多次定义的符号: header 档会被逐字的替换到你源文件里 #include 的位置的。因此
,如果 header 档被 #include 到一个以上的源文件 里,这个 header 档中所有的定义就
会出现在每一个有关的源码文件 里。这会使它们里的符号被定义一次以上,从而出现连接
错误(见 上)。

解决方法: 不要在 header 档里定义变量。你只需要在 header 档里声明它们然后在适当
的C源码文件(应该 #include 那个 header 档的那个)里定义它们(一次)。对于初学者
来说,定义和声明是 很容易混淆的。声明的作用是告诉编译器其所声明的符号应该存在,
 并且要有所指定的类型。但是,它并不会使编译器分配贮存空间。 而定义的做用是要求编
译器分配贮存空间。当做一个声明而不是做 定义的时候,在声明前放一个关键字“extern
”。

例如,我们有一个叫“counter”的变量,如果想让它成为公用的, 我们在一个源码程序(
只在一个里面)的开始定义它:“int counter;”,再在相关的 header 档里声明它:“ex
tern int counter;”。

函数原型里隐含着 extern 的意思,所以不需顾虑这个问题。

c) 重复定义,重复声明,矛盾类型:
请考虑如果在一个C源码文件中 #include 两个档 a.h 和 b.h, 而 a.h 又 #include 了
b.h 档(原因是 b.h 档定义了一些 a.h 需要的类型),会发生什么事呢?这时该C源码文
件 #include 了 b.h 两次。因此每一个在 b.h 中的 #define 都发生了两次,每一 个声明
发生了两次,等等。理论上,因为它们是完全一样的拷贝, 所以应该不会有什么问题,但
在实际应用上,这是不符合C的语法 的,可能在编译时出现错误,或至少是警告。

解决的方法是要确定每一个 header 档在任一个源码文件中只被包 含了一次。我们一般是
用预处理器来达到这个目的的。当我们进入 每一个 header 档时,我们为这个 header 档
#define 一个巨集 指令。只有在这个巨集指令没有被定义的前提下,我们才真正使用 该 h
eader 档的主体。在实际应用上,我们只要简单的把下面一段 码放在每一个 header 档的
开始部分:

#ifndef FILENAME_H
#define FILENAME_H

然后把下面一行码放在最后:

#endif

用 header 档的档名(大写的)代替上面的 FILENAME_H,用底线 代替档名中的点。有些人
喜欢在 #endif 加上注释来提醒他们这个 #endif 指的是什么。例如:

#endif /* #ifndef FILENAME_H */

我个人没有这个习惯,因为这其实是很明显的。当然这只是各人的 风格不同,无伤大雅。

你只需要在那些有编译错误的 header 档中加入这个技巧,但在所 有的 header 档中都加
入也没什么损失,到底这是个好习惯。

1.5 重新编译一个多文件项目

清楚的区别编译和连接是很重要的。编译器使用源码文件来产生某种 形式的目标文件(obje
ct files)。在这个过程中,外部的符号参考并 没有被解释或替换。然后我们使用连接器来
连接这些目标文件和一些 标准的程序包再加你指定的程序包,最后连接生成一个可执行程
序。 在这个阶段,一个目标文件中对别的文件中的符号的参考被解释,并 报告不能被解释
的参考,一般是以错误信息的形式报告出来。

基本的步骤就应该是,把你的源码文件一个一个的编译成目标文件的格 式,最后把所有的
目标文件加上需要的程序包连接成一个可执行文件。 具体怎么做是由你的编译器决定的。
这里我只给出 gcc (GNU C 编译 器)的有关命令,这些有可能对你的非 gcc 编译器也适
用。

gcc 是一个多目标的工具。它在需要的时候呼叫其它的元件(预处理 程序,编译器,组合
程序,连接器)。具体的哪些元件被呼叫取决于 输入文件的类型和你传递给它的开关。

一般来说,如果你只给它C源码文件,它将预处理,编译,组合所有 的文件,然后把所得
的目标文件连接成一个可执行文件(一般生成的 文件被命名为 a.out )。你当然可以这么
做,但这会破坏很多我们 把一个项目分解成多个文件所得到的好处。

如果你给它一个 -c 开关,gcc 只把给它的文件编译成目标文件, 用源码文件的文件名命
名但把其后缀由“.c”或“.cc”变成“.o”。 如果你给它的是一列目标文件, gcc 会把
它们连接成可执行文件, 缺省文件名是 a.out 。你可以改变缺省名,用开关 -o 后跟你指
定 的文件名。

因此,当你改变了一个源码文件后,你需要重新编译它: ‘gcc -c filename.c’ 然后重新
连接你的项目: ‘gcc -o exec_filename *.o’。 如果你改变了一个 header 档,你需要重
新编译所有 #include 过 这个档的源码文件,你可以用 ‘gcc -c file1.c file2.c file3.
c’ 然后象上边一样连接。

当然这么做是很繁琐的,幸亏我们有些工具使这个步骤变得简单。 本文的第二部分就是介
绍其中的一件工具:GNU Make 工具。

(好家伙,现在才开始见真章。您学到点儿东西没?)

2) GNU Make 工具
~~~~~~~~~~~~~~~~

2.1 基本 makefile 结构

GNU Make 的主要工作是读进一个文本文件, makefile 。这个文 件里主要是有关哪些文件
(‘target’目的文件)是从哪些别的 文件(‘dependencies’依靠文件)中产生的,用
什么命令来进行 这个产生过程。有了这些信息, make 会检查磁碟上的文件,如果 目的文
件的时间戳(该文件生成或被改动时的时间)比至少它的一 个依靠文件旧的话, make 就
执行相应的命令,以便更新目的文件。 (目的文件不一定是最后的可执行档,它可以是任
何一个文件。)

makefile 一般被叫做“makefile”或“Makefile”。当然你可以 在 make 的命令行指定别
的文件名。如果你不特别指定,它会寻 找“makefile”或“Makefile”,因此使用这两个
名字是最简单 的。

一个 makefile 主要含有一系列的规则,如下:

: …
(tab)<command>
(tab)<command>
.
.
.

例如,考虑以下的 makefile :

=== makefile 开始 ===
myprog : foo.o bar.o
  gcc foo.o bar.o -o myprog

foo.o : foo.c foo.h bar.h
  gcc -c foo.c -o foo.o

bar.o : bar.c bar.h
  gcc -c bar.c -o bar.o
=== makefile 结束 ===

这是一个非常基本的 makefile —— make 从最上面开始,把上 面第一个目的,‘myprog
’,做为它的主要目标(一个它需要保 证其总是最新的最终目标)。给出的规则说明只要
文件‘myprog’ 比文件‘foo.o’或‘bar.o’中的任何一个旧,下一行的命令将 会被执行

但是,在检查文件 foo.o 和 bar.o 的时间戳之前,它会往下查 找那些把 foo.o 或 bar.o
 做为目标文件的规则。它找到的关于 foo.o 的规则,该文件的依靠文件是 foo.c, foo.h
 和 bar.h 。 它从下面再找不到生成这些依靠文件的规则,它就开始检查磁碟 上这些依靠
文件的时间戳。如果这些文件中任何一个的时间戳比 foo.o 的新,命令 ‘gcc -o foo.o fo
o.c’ 将会执行,从而更新 文件 foo.o 。

接下来对文件 bar.o 做类似的检查,依靠文件在这里是文件 bar.c 和 bar.h 。

现在, make 回到‘myprog’的规则。如果刚才两个规则中的任 何一个被执行,myprog 就
需要重建(因为其中一个 .o 档就会比 ‘myprog’新),因此连接命令将被执行。

希望到此,你可以看出使用 make 工具来建立程序的好处——前 一章中所有繁琐的检查步
骤都由 make 替你做了:检查时间戳。 你的源码文件里一个简单改变都会造成那个文件被
重新编译(因 为 .o 文件依靠 .c 文件),进而可执行文件被重新连接(因为 .o 文件被
改变了)。其实真正的得益是在当你改变一个 header 档的时候——你不再需要记住那个源
码文件依靠它,因为所有的 资料都在 makefile 里。 make 会很轻松的替你重新编译所有
那 些因依靠这个 header 文件而改变了的源码文件,如有需要,再 进行重新连接。

当然,你要确定你在 makefile 中所写的规则是正确无误的,只 列出那些在源码文件中被
#include 的 header 档……

2.2 编写 make 规则 (Rules)

最明显的(也是最简单的)编写规则的方法是一个一个的查 看源码文件,把它们的目标文
件做为目的,而C源码文件和被它 #include 的 header 档做为依靠文件。但是你也要把其
它被这些 header 档 #include 的 header 档也列为依靠文件,还有那些被 包括的文件所
包括的文件……然后你会发现要对越来越多的文件 进行管理,然后你的头发开始脱落,你
的脾气开始变坏,你的脸 色变成菜色,你走在路上开始跟电线杆子碰撞,终于你捣毁你的
 电脑显示器,停止编程。到低有没有些容易点儿的方法呢?

当然有!向编译器要!在编译每一个源码文件的时候,它实在应 该知道应该包括什么样的
header 档。使用 gcc 的时候,用 -M 开关,它会为每一个你给它的C文件输出一个规则,
把目标文件 做为目的,而这个C文件和所有应该被 #include 的 header 文 件将做为依靠
文件。注意这个规则会加入所有 header 文件,包 括被角括号(`<’, `>’)和双引号(`”‘)所
包围的文件。其实我们可以 相当肯定系统 header 档(比如 stdio.h, stdlib.h 等等)不
会 被我们更改,如果你用 -MM 来代替 -M 传递给 gcc,那些用角括 号包围的 header 档
将不会被包括。(这会节省一些编译时间)

由 gcc 输出的规则不会含有命令部分;你可以自己写入你的命令 或者什么也不写,而让 m
ake 使用它的隐含的规则(参考下面的 2.4 节)。

2.3 Makefile 变量

上面提到 makefiles 里主要包含一些规则。它们包含的其它的东 西是变量定义。

makefile 里的变量就像一个环境变量(environment variable)。 事实上,环境变量在 mak
e 过程中被解释成 make 的变量。这些 变量是大小写敏感的,一般使用大写字母。它们可
以从几乎任何 地方被引用,也可以被用来做很多事情,比如:

i) 贮存一个文件名列表。在上面的例子里,生成可执行文件的 规则包含一些目标文件名做
为依靠。在这个规则的命令行 里同样的那些文件被输送给 gcc 做为命令参数。如果在这
里使用一个变数来贮存所有的目标文件名,加入新的目标 文件会变的简单而且较不易出错

ii) 贮存可执行文件名。如果你的项目被用在一个非 gcc 的系 统里,或者如果你想使用一
个不同的编译器,你必须将所 有使用编译器的地方改成用新的编译器名。但是如果使用一
 个变量来代替编译器名,那么你只需要改变一个地方,其 它所有地方的命令名就都改变了

iii) 贮存编译器旗标。假设你想给你所有的编译命令传递一组 相同的选项(例如 -Wall -
O -g);如果你把这组选项存 入一个变量,那么你可以把这个变量放在所有呼叫编译器 的
地方。而当你要改变选项的时候,你只需在一个地方改 变这个变量的内容。

要设定一个变量,你只要在一行的开始写下这个变量的名字,后 面跟一个 = 号,后面跟你
要设定的这个变量的值。以后你要引用 这个变量,写一个 $ 符号,后面是围在括号里的变
量名。比如在 下面,我们把前面的 makefile 利用变量重写一遍:

=== makefile 开始 ===

OBJS = foo.o bar.o
CC = gcc
CFLAGS = -Wall -O -g

myprog : $(OBJS)
  $(CC) $(OBJS) -o myprog

foo.o : foo.c foo.h bar.h

  $(CC) $(CFLAGS) -c foo.c -o foo.o

bar.o : bar.c bar.h
  $(CC) $(CFLAGS) -c bar.c -o bar.o
=== makefile 结束 ===

还有一些设定好的内部变量,它们根据每一个规则内容定义。三个 比较有用的变量是 $@,
$< 和 $^ (这些变量不需要括号括住)。 $@ 扩展成当前规则的目的文件名, $< 扩展成
依靠列表中的第 一个依靠文件,而 $^ 扩展成整个依靠的列表(除掉了里面所有重 复的文
件名)。利用这些变量,我们可以把上面的 makefile 写成:

=== makefile 开始 ===
OBJS = foo.o bar.o
CC = gcc
CFLAGS = -Wall -O -g

myprog : $(OBJS)
  $(CC) $^ -o $@

foo.o : foo.c foo.h bar.h
  $(CC) $(CFLAGS) -c $< -o $@

bar.o : bar.c bar.h
  $(CC) $(CFLAGS) -c $< -o $@
=== makefile 结束 ===

你可以用变量做许多其它的事情,特别是当你把它们和函数混合 使用的时候。如果需要更
进一步的了解,请参考 GNU Make 手册。 (‘man make’, ‘man makefile’)

2.4 隐含规则 (Implicit Rules)

请注意,在上面的例子里,几个产生 .o 文件的命令都是一样的。 都是从 .c 文件和相关
文件里产生 .o 文件,这是一个标准的步 骤。其实 make 已经知道怎么做——它有一些叫
做隐含规则的内 置的规则,这些规则告诉它当你没有给出某些命令的时候,应该 怎么办。

如果你把生成 foo.o 和 bar.o 的命令从它们的规则中删除, make 将会查找它的隐含规则
,然后会找到一个适当的命令。它的命令会 使用一些变量,因此你可以按照你的想法来设
定它:它使用变量 CC 做为编译器(象我们在前面的例子),并且传递变量 CFLAGS (给 C
 编译器,C++ 编译器用 CXXFLAGS ),CPPFLAGS ( C 预 处理器旗标), TARGET_ARCH
(现在不用考虑这个),然后它加 入旗标 ‘-c’ ,后面跟变量 $< (第一个依靠名),然
后是旗 标 ‘-o’ 跟变量 $@ (目的文件名)。一个C编译的具体命令将 会是:

$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@

当然你可以按照你自己的需要来定义这些变量。这就是为什么用 gcc 的 -M 或 -MM 开关输
出的码可以直接用在一个 makefile 里。

2.5 假象目的 (Phony Targets)

假设你的一个项目最后需要产生两个可执行文件。你的主要目标 是产生两个可执行文件,
但这两个文件是相互独立的——如果一 个文件需要重建,并不影响另一个。你可以使用“
假象目的”来 达到这种效果。一个假象目的跟一个正常的目的几乎是一样的, 只是这个目
的文件是不存在的。因此, make 总是会假设它需要 被生成,当把它的依赖文件更新后,
就会执行它的规则里的命令 行。

如果在我们的 makefile 开始处输入:

all : exec1 exec2

其中 exec1 和 exec2 是我们做为目的的两个可执行文件。 make 把这个 ‘all’ 做为它的
主要目的,每次执行时都会尝试把 ‘all’ 更新。但既然这行规则里没有哪个命令来作用在
一个叫 ‘all’ 的 实际文件(事实上 all 并不会在磁碟上实际产生),所以这个规 则并不
真的改变 ‘all’ 的状态。可既然这个文件并不存在,所以 make 会尝试更新 all 规则,因
此就检查它的依靠 exec1, exec2 是否需要更新,如果需要,就把它们更新,从而达到我们
的目的。

假象目的也可以用来描述一组非预设的动作。例如,你想把所有由 make 产生的文件删除,
你可以在 makefile 里设立这样一个规则:

veryclean :
  rm *.o
  rm myprog

前提是没有其它的规则依靠这个 ‘veryclean’ 目的,它将永远 不会被执行。但是,如果你
明确的使用命令 ‘make veryclean’ , make 会把这个目的做为它的主要目标,执行那些 r
m 命令。

如果你的磁碟上存在一个叫 veryclean 文件,会发生什么事?这 时因为在这个规则里没有
任何依靠文件,所以这个目的文件一定是 最新的了(所有的依靠文件都已经是最新的了)
,所以既使用户明 确命令 make 重新产生它,也不会有任何事情发生。解决方法是标 明所
有的假象目的(用 .PHONY),这就告诉 make 不用检查它们 是否存在于磁碟上,也不用查
找任何隐含规则,直接假设指定的目 的需要被更新。在 makefile 里加入下面这行包含上
面规则的规则:

.PHONY : veryclean

就可以了。注意,这是一个特殊的 make 规则,make 知道 .PHONY 是一个特殊目的,当然
你可以在它的依靠里加入你想用的任何假象 目的,而 make 知道它们都是假象目的。

2.6 函数 (Functions)

makefile 里的函数跟它的变量很相似——使用的时候,你用一个 $ 符号跟开括号,函数名
,空格后跟一列由逗号分隔的参数,最后 用关括号结束。例如,在 GNU Make 里有一个叫
‘wildcard’ 的函 数,它有一个参数,功能是展开成一列所有符合由其参数描述的文 件名
,文件间以空格间隔。你可以像下面所示使用这个命令:

SOURCES = $(wildcard *.c)

这行会产生一个所有以 ‘.c’ 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需
要一定要把结果存入一个变量。

另一个有用的函数是 patsubst ( patten substitude, 匹配替 换的缩写)函数。它需要
3个参数——第一个是一个需要匹配的 式样,第二个表示用什么来替换它,第三个是一个
需要被处理的 由空格分隔的字列。例如,处理那个经过上面定义后的变量,

OBJS = $(patsubst %.c,%.o,$(SOURCES))

这行将处理所有在 SOURCES 字列中的字(一列文件名),如果它的 结尾是 ‘.c’ ,就用 ‘
.o’ 把 ‘.c’ 取代。注意这里的 % 符号将匹 配一个或多个字符,而它每次所匹配的字串叫
做一个‘柄’(stem) 。 在第二个参数里, % 被解读成用第一参数所匹配的那个柄。

2.7 一个比较有效的 makefile

利用我们现在所学的,我们可以建立一个相当有效的 makefile 。 这个 makefile 可以完
成大部分我们需要的依靠检查,不用做太大 的改变就可直接用在大多数的项目里。

首先我们需要一个基本的 makefile 来建我们的程序。我们可以让 它搜索当前目录,找到
源码文件,并且假设它们都是属于我们的项 目的,放进一个叫 SOURCES 的变量。这里如果
也包含所有的 *.cc 文件,也许会更保险,因为源码文件可能是 C++ 码的。

SOURCES = $(wildcard *.c *.cc)

利用 patsubst ,我们可以由源码文件名产生目标文件名,我们需 要编译出这些目标文件
。如果我们的源码文件既有 .c 文件,也有 .cc 文件,我们需要使用相嵌的 patsubst 函
数呼叫:

OBJS = $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCES)))

最里面一层 patsubst 的呼叫会对 .cc 文件进行后缀替代,产生的结 果被外层的 patsubs
t 呼叫处理,进行对 .c 文件后缀的替代。

现在我们可以设立一个规则来建可执行文件:

myprog : $(OBJS)
  gcc -o myprog $(OBJS)

进一步的规则不一定需要, gcc 已经知道怎么去生成目标文件 (object files) 。下面我
们可以设定产生依靠信息的规则:

depends : $(SOURCES)
  gcc -M $(SOURCES) > depends

在这里如果一个叫 ‘depends’ 的文件不存在,或任何一个源码文件 比一个已存在的 depen
ds 文件新,那么一个 depends 文件会被生 成。depends 文件将会含有由 gcc 产生的关于
源码文件的规则(注 意 -M 开关)。现在我们要让 make 把这些规则当做 makefile 档 的
一部分。这里使用的技巧很像 C 语言中的 #include 系统——我 们要求 make 把这个文件
 include 到 makefile 里,如下:

include depends

GNU Make 看到这个,检查 ‘depends’ 目的是否更新了,如果没有, 它用我们给它的命令
重新产生 depends 档。然后它会把这组(新) 规则包含进来,继续处理最终目标 ‘myprog
‘ 。当看到有关 myprog 的规则,它会检查所有的目标文件是否更新——利用 depends 文
件 里的规则,当然这些规则现在已经是更新过的了。

这个系统其实效率很低,因为每当一个源码文件被改动,所有的源码 文件都要被预处理以
产生一个新的 ‘depends’ 文件。而且它也不是 100% 的安全,这是因为当一个 header 档
被改动,依靠信息并不会 被更新。但就基本工作来说,它也算相当有用的了。

2.8 一个更好的 makefile

这是一个我为我大多数项目设计的 makefile 。它应该可以不需要修 改的用在大部分项目
里。我主要把它用在 djgpp 上,那是一个 DOS 版的 gcc 编译器。因此你可以看到执行的
命令名、 ‘alleg’ 程序包、 和 RM -F 变量都反映了这一点。

=== makefile 开始 ===

######################################
#
# Generic makefile
#
# by George Foot
# email: george.foot@merton.ox.ac.uk
#
# Copyright (c) 1997 George Foot
# All rights reserved.
# 保留所有版权
#
# No warranty, no liability;
# you use this at your own risk.
# 没保险,不负责
# 你要用这个,你自己担风险

#
# You are free to modify and
# distribute this without giving
# credit to the original author.
# 你可以随便更改和散发这个文件
# 而不需要给原作者什么荣誉。
# (你好意思?)
#
######################################

### Customising
# 用户设定
#
# Adjust the following if necessary; EXECUTABLE is the target
# executable’s filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make’s
# command line of course, if you prefer to do it that way.
#
# 如果需要,调整下面的东西。 EXECUTABLE 是目标的可执行文件名, LIBS
# 是一个需要连接的程序包列表(例如 alleg, stdcx, iostr 等等)。当然你
# 可以在 make 的命令行覆盖它们,你愿意就没问题。
#

EXECUTABLE := mushroom.exe
LIBS := alleg

# Now alter any implicit rules’ variables if you like, e.g.:
#
# 现在来改变任何你想改动的隐含规则中的变量,例如

CFLAGS := -g -Wall -O3 -m486
CXXFLAGS := $(CFLAGS)

# The next bit checks to see whether rm is in your djgpp bin
# directory; if not it uses del instead, but this can cause (harmless)
# `File not found’ error messages. If you are not using DOS at all,
# set the variable to something which will unquestioningly remove
# files.
#
# 下面先检查你的 djgpp 命令目录下有没有 rm 命令,如果没有,我们使用
# del 命令来代替,但有可能给我们 ‘File not found’ 这个错误信息,这没
# 什么大碍。如果你不是用 DOS ,把它设定成一个删文件而不废话的命令。
# (其实这一步在 UNIX 类的系统上是多余的,只是方便 DOS 用户。 UNIX
# 用户可以删除这5行命令。)

ifneq ($(wildcard $(DJDIR)/bin/rm.exe),)
RM-F := rm -f
else
RM-F := del
endif

# You shouldn’t need to change anything below this point.
#
# 从这里开始,你应该不需要改动任何东西。(我是不太相信,太NB了!)

SOURCE := $(wildcard *.c) $(wildcard *.cc)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCE)))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS)) \
$(patsubst %.d,%.cc,$(MISSING_DEPS)))
CPPFLAGS += -MD

.PHONY : everything deps objs clean veryclean rebuild

everything : $(EXECUTABLE)

deps : $(DEPS)

objs : $(OBJS)

clean :
  @$(RM-F) *.o
  @$(RM-F) *.d

veryclean: clean
  @$(RM-F) $(EXECUTABLE)

rebuild: veryclean everything

ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
  @$(RM-F) $(patsubst %.d,%.o,$@)
endif

-include $(DEPS)

$(EXECUTABLE) : $(OBJS)
  gcc -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))

=== makefile 结束 ===

有几个地方值得解释一下的。首先,我在定义大部分变量的时候使 用的是 := 而不是 = 符
号。它的作用是立即把定义中参考到的函 数和变量都展开了。如果使用 = 的话,函数和变
量参考会留在那 儿,就是说改变一个变量的值会导致其它变量的值也被改变。例 如:

A = foo
B = $(A)
# 现在 B 是 $(A) ,而 $(A) 是 ‘foo’ 。
A = bar
# 现在 B 仍然是 $(A) ,但它的值已随着变成 ‘bar’ 了。
B := $(A)
# 现在 B 的值是 ‘bar’ 。
A = foo
# B 的值仍然是 ‘bar’ 。

make 会忽略在 # 符号后面直到那一行结束的所有文字。

ifneg…else…endif 系统是 makefile 里让某一部分码有条件的 失效/有效的工具。 i
feq 使用两个参数,如果它们相同,它把直 到 else (或者 endif ,如果没有 else 的话
)的一段码加进 makefile 里;如果不同,把 else 到 endif 间的一段码加入 makefile
(如果有 else )。 ifneq 的用法刚好相反。

‘filter-out’ 函数使用两个用空格分开的列表,它把第二列表中所 有的存在于第一列表中
的项目删除。我用它来处理 DEPS 列表,把所 有已经存在的项目都删除,而只保留缺少的
那些。

我前面说过, CPPFLAGS 存有用于隐含规则中传给预处理器的一些 旗标。而 -MD 开关类似
 -M 开关,但是从源码文件 .c 或 .cc 中 形成的文件名是使用后缀 .d 的(这就解释了我
形成 DEPS 变量的 步骤)。DEPS 里提到的文件后来用 ‘-include’ 加进了 makefile 里,
它隐藏了所有因文件不存在而产生的错误信息。

如果任何依靠文件不存在, makefile 会把相应的 .o 文件从磁碟 上删除,从而使得 make
 重建它。因为 CPPFLAGS 指定了 -MD , 它的 .d 文件也被重新产生。

最后, ‘addprefix’ 函数把第二个参数列表的每一项前缀上第一 个参数值。

这个 makefile 的那些目的是(这些目的可以传给 make 的命令行 来直接选用):

everything:(预设) 更新主要的可执行程序,并且为每一个 源码文件生成或更新一个 ‘.
d’ 文件和一个 ‘.o’ 文件。

deps: 只是为每一个源码程序产生或更新一个 ‘.d’ 文件。

objs: 为每一个源码程序生成或更新 ‘.d’ 文件和目标文件。

clean: 删除所有中介/依靠文件( *.d 和 *.o )。

veryclean: 做 `clean’ 和删除可执行文件。

rebuild: 先做 `veryclean’ 然后 `everything’ ;既完全重建。

除了预设的 everything 以外,这里头只有 clean , veryclean , 和 rebuild 对用户是
有意义的。

我还没有发现当给出一个源码文件的目录,这个 makefile 会失败的 情况,除非依靠文件
被弄乱。如果这种弄乱的情况发生了,只要输入 `make clean’ ,所有的目标文件和依靠文
件会被删除,问题就应该 被解决了。当然,最好不要把它们弄乱。如果你发现在某种情况
下这 个 makefile 文件不能完成它的工作,请告诉我,我会把它整好的。

3 总结
~~~~~~~~~~~~~~~

我希望这篇文章足够详细的解释了多文件项目是怎么运作的,也说明了 怎样安全而合理的
使用它。到此,你应该可以轻松的利用 GNU Make 工 具来管理小型的项目,如果你完全理
解了后面几个部分的话,这些对于 你来说应该没什么困难。

GNU Make 是一件强大的工具,虽然它主要是用来建立程序,它还有很多 别的用处。如果想
要知道更多有关这个工具的知识,它的句法,函数, 和许多别的特点,你应该参看它的参
考文件 (info pages, 别的 GNU 工具也一样,看它们的 info pages. )。

——————————————————————————–
C Scene 官方网站: http://cscene.differnet.org
C Scene 官方电邮: cscene@mindless.com

——————————————————————————–
This page is Copyright ? 1997 By C Scene. All Rights Reserved

——————————————————————————–

2004年10月15日

1。CA代码的整理进入调试阶段,问题不少,不过可能跟调试的环境有关。想到以后的开发,应该针对AS3和AS2.1编写不同的Makefile并且调试代码。

2。实达的技术员。。。让我失望的一个所谓精英团队。没有华为人的那种干劲儿,东扯西拉的,还想糊弄人,我们应该是合作的方式,可能他们和上级/总部的沟通不够。

3。今天中午游泳状态差,游了不到700米,忽冷忽热的水让我受不了,还有自己对水还是没有完全消除恐惧感。慢慢来。

4。手机,小灵通同时欠费。Ft。还有饭卡。。。

5。我该睡觉了。

在网上找到了 陈皓 写的好文,这里加上一个联接。收藏起来。:)
http://blog.csdn.net/haoel/

2004年09月23日

CVS使用手册

作者: 车东 Email: chedongATbigfoot.com/chedongATchedong.com

写于:2002/07/10 最后更新: 09/02/2004 16:00:58 Feed Back >> 
Creative Commons License

版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
http://www.chedong.com/tech/cvs_card.html

关键词:CVS CVSWeb CVSTrac WinCVS CVSROOT

内容摘要:

CVS是一个C/S系统,多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到保证文件同步的目的。工作模式如下:

 CVS服务器(文件版本库) / | \ (版 本 同 步) / | \开发者1 开发者2 开发者3

作为一般开发人员挑选2,6看就可以了,CVS的管理员则更需要懂的更多一些,最后还简单介绍了一些Windows下的cvs客户端使用,CVS远程用户认证的选择及与BUG跟踪系统等开发环境的集成问题。

  1. CVS环境初始化:CVS环境的搭建 管理员
  2. CVS的日常使用:日常开发中最常用的CVS命令, 开发人员 管理员
  3. CVS的分支开发:项目按照不同进度和目标并发进行 管理员
  4. CVS的用户认证:通过SSH的远程用户认证,安全,简单 管理员
  5. CVSWEB:CVS的WEB访问界面大大提高代码版本比较的效率 管理员
  6. CVS TAG:将$Id$ 加入代码注释中,方便开发过程的跟踪开发人员
  7. CVS vs VSS: CVS和Virsual SourceSafe的比较 开发人员 管理员
  8. WinCVS: 通过SSH认证的WinCVS认证设置
  9. 基于CVSTrac的小组开发环境搭建:通过CVSTrac实现web界面的CVS用户管理,集成的BUG跟踪和WIKI交流
  10. CVS中的用户权限管理:基于系统用户的CVS权限管理和基于CVSROOT/passwd的虚拟用户管理

一个系统20%的功能往往能够满足80%的需求,CVS也不例外,以下是CVS最常用的功能,可能还不到它全部命令选项的20%,作为一般开发人员平时会用cvs update和cvs commit就够了,更多的需求在实际应用过程中自然会出现,不时回头看看相关文档经常有意外的收获。

CVS环境初始化

环境设置:指定CVS库的路径CVSROOT

tcsh
setenv CVSROOT /path/to/cvsroot
bash
CVSROOT=/path/to/cvsroot ; export CVSROOT

后面还提到远程CVS服务器的设置:
CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH

初始化:CVS版本库的初始化。
cvs init

一个项目的首次导入
cvs import -m “write some comments here” project_name vendor_tag release_tag
执行后:会将所有源文件及目录导入到/path/to/cvsroot/project_name目录下
vender_tag: 开发商标记
release_tag: 版本发布标记

项目导出:将代码从CVS库里导出
cvs checkout project_name
cvs 将创建project_name目录,并将最新版本的源代码导出到相应目录中。这个checkout和Virvual SourceSafe中的check out不是一个概念,相对于Virvual SourceSafe的check out是cvs update, check in是cvs commit。

CVS的日常使用

注意:第一次导出以后,就不是通过cvs checkout来同步文件了,而是要进入刚才cvs checkout project_name导出的project_name目录下进行具体文件的版本同步(添加,修改,删除)操作。

将文件同步到最新的版本
cvs update
不制定文件名,cvs将同步所有子目录下的文件,也可以制定某个文件名/目录进行同步
cvs update file_name
最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次,并养成“先同步 后修改”的习惯,和Virvual SourceSafe不同,CVS里没有文件锁定的概念,所有的冲突是在commit之前解决,如果你修改过程中,有其他人修改并commit到了CVS 库中,CVS会通知你文件冲突,并自动将冲突部分用
>>>>>>
content on cvs server
<<<<<<
content in your file
>>>>>>
标记出来,由你确认冲突内容的取舍。
版本冲突一般是在多个人修改一个文件造成的,但这种项目管理上的问题不应该指望由CVS来解决。

确认修改写入到CVS库里
cvs commit -m “write some comments here” file_name

注意:CVS的很多动作都是通过cvs commit进行最后确认并修改的,最好每次只修改一个文件。在确认的前,还需要用户填写修改注释,以帮助其他开发人员了解修改的原因。如果不用写-m “comments”而直接确认`cvs commit file_name` 的话,cvs会自动调用系统缺省的文字编辑器(一般是vi)要求你写入注释。
注释的质量很重要:所以不仅必须要写,而且必须写一些比较有意义的内容:以方便其他开发人员能够很好的理解
不好的注释,很难让其他的开发人员快速的理解:比如: -m “bug fixed” 甚至 -m “”
好的注释,甚至可以用中文: -m “在用户注册过程中加入了Email地址校验”

修改某个版本注释:每次只确认一个文件到CVS库里是一个很好的习惯,但难免有时候忘了指定文件名,把多个文件以同样注释commit到CVS库里了,以下命令可以允许你修改某个文件某个版本的注释:
cvs admin -m 1.3:”write some comments here” file_name

添加文件
创建好新文件后,比如:touch new_file
cvs add new_file
注意:对于图片,Word文档等非纯文本的项目,需要使用cvs add -kb选项按2进制文件方式导入(k表示扩展选项,b表示binary),否则有可能出现文件被破坏的情况
比如:
cvs add -kb new_file.gif
cvs add -kb readme.doc

如果关键词替换属性在首次导入时设置错了怎么办?
cvs admin -kkv new_file.css

然后确认修改并注释
cvs ci -m “write some comments here”

删除文件
将某个源文件物理删除后,比如:rm file_name
cvs rm file_name
然后确认修改并注释
cvs ci -m “write some comments here”
以上面前2步合并的方法为:
cvs rm -f file_name
cvs ci -m “why delete file”
注意:很多cvs命令都有缩写形式:commit=>ci; update=>up; checkout=>co/get; remove=>rm;

添加目录
cvs add dir_name

查看修改历史
cvs log file_name
cvs history file_name

查看当前文件不同版本的区别
cvs diff -r1.3 -r1.5 file_name
查看当前文件(可能已经修改了)和库中相应文件的区别
cvs diff file_name
cvs的web界面提供了更方便的定位文件修改和比较版本区别的方法,具体安装设置请看后面的cvsweb使用

正确的通过CVS恢复旧版本的方法
如果用cvs update -r1.2 file.name
这个命令是给file.name加一个STICK TAG: “1.2″ ,虽然你的本意只是想将它恢复到1.2版本
正确的恢复版本的方法是:cvs update -p -r1.2 file_name >file_name
如果不小心已经加成STICK TAG的话:用cvs update -A 解决

移动文件/文件重命名
cvs里没有cvs move或cvs rename,因为这两个操作是可以由先cvs remove old_file_name,然后cvs add new_file_name实现的。

删除/移动目录
最方便的方法是让管理员直接移动,删除CVSROOT里相应目录(因为CVS一个项目下的子目录都是独立的,移动到$CVSROOT目录下都可以作为新的独立项目:好比一颗树,其实砍下任意一枝都能独立存活),对目录进行了修改后,要求其开发人员重新导出项目cvs checkout project_name 或者用cvs update -dP同步。

项目发布导出不带CVS目录的源文件
做开发的时候你可能注意到了,每个开发目录下,CVS都创建了一个CVS/目录。里面有文件用于记录当前目录和CVS库之间的对应信息。但项目发布的时候你一般不希望把文件目录还带着含有CVS信息的CVS目录吧,这个一次性的导出过程使用cvs export命令,不过export只能针对一个TAG或者日期导出,比如:
cvs export -r release1 project_name
cvs export -D 20021023 project_name
cvs export -D now project_name

CVS Branch:项目多分支同步开发

确认版本里程碑:多个文件各自版本号不一样,项目到一定阶段,可以给所有文件统一指定一个阶段里程碑版本号,方便以后按照这个阶段里程碑版本号导出项目,同时也是项目的多个分支开发的基础。

cvs tag release_1_0

开始一个新的里程碑
cvs commit -r 2 标记所有文件开始进入2.x的开发

注意:CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。

版本分支的建立
在开发项目的2.x版本的时候发现1.x有问题,但2.x又不敢用,则从先前标记的里程碑:release_1_0导出一个分支 release_1_0_patch
cvs rtag -b -r release_1_0 release_1_0_patch proj_dir

一些人先在另外一个目录下导出release_1_0_patch这个分支:解决1.0中的紧急问题,
cvs checkout -r release_1_0_patch
而其他人员仍旧在项目的主干分支2.x上开发

在release_1_0_patch上修正错误后,标记一个1.0的错误修正版本号
cvs tag release_1_0_patch_1

如果2.0认为这些错误修改在2.0里也需要,也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中:
cvs update -j release_1_0_patch_1

CVS的远程认证通过SSH远程访问CVS

使用cvs本身基于pserver的远程认证很麻烦,需要定义服务器和用户组,用户名,设置密码等,

常见的登陆格式如下:
cvs -d :pserver:cvs_user_name@cvs.server.address:/path/to/cvsroot login
例子:
cvs -d :pserver:cvs@samba.org:/cvsroot login

不是很安全,因此一般是作为匿名只读CVS访问的方式。从安全考虑,通过系统本地帐号认证并通过SSH传输是比较好的办法,通过在客户机的 /etc/profile里设置一下内容:
CVSROOT=:ext:$USER@cvs.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH
所有客户机所有本地用户都可以映射到CVS服务器相应同名帐号了。

比如:

CVS服务器是192.168.0.3,上面CVSROOT路径是/home/cvsroot,另外一台开发客户机是192.168.0.4,如果 tom在2台机器上都有同名的帐号,那么从192.168.0.4上设置了:
export CVSROOT=:ext:tom@192.168.0.3:/home/cvsroot
export CVS_RSH=ssh
tom就可以直接在192.168.0.4上对192.168.0.3的cvsroot进行访问了(如果有权限的话)
cvs checkout project_name
cd project_name
cvs update

cvs commit

如果CVS所在服务器的SSH端口不在缺省的22,或者和客户端与CVS服务器端SSH缺省端口不一致,有时候设置了:
:ext:$USER@test.server.address#port:/path/to/cvsroot

仍然不行,比如有以下错误信息:
ssh: test.server.address#port: Name or service not known
cvs [checkout aborted]: end of file from server (consult above messages if any)

解决的方法是做一个脚本指定端口转向(不能使用alias,会出找不到文件错误):
创建一个/usr/bin/ssh_cvs文件,假设远程服务器的SSH端口是非缺省端口:34567
#!/bin/sh
/usr/bin/ssh -p 34567 “$@”
然后:chmod +x /usr/bin/ssh_cvs
并CVS_RSH=ssh_cvs; export CVS_RSH

注意:port是指相应服务器SSH的端口,不是指cvs专用的pserver的端口

CVSWEB:提高文件浏览效率

CVSWEB就是CVS的WEB界面,可以大大提高程序员定位修改的效率:

使用的样例可以看:http://www.freebsd.org/cgi/cvsweb.cgi

CVSWEB的下载:CVSWEB从最初的版本已经演化出很多功能界面更丰富的版本,这个是我个人感觉安装设置比较方便的:
原先在:http://www.spaghetti-code.de/software/linux/cvsweb/,但目前已经删除,目前仍可以在本站下载CVSWEB,其实最近2年FreeBSD的CVSWeb项目已经有了更好的发展吧,而当初没有用FreeBSD那个版本主要就是因为没有彩色的文件Diff功能。
下载解包:
tar zxf cvsweb.tgz
把配置文件cvsweb.conf放到安全的地方(比如和apache的配置放在同一个目录下),
修改:cvsweb.cgi让CGI找到配置文件:
$config = $ENV{‘CVSWEB_CONFIG’} || ‘/path/to/apache/conf/cvsweb.conf’;

转到/path/to/apache/conf下并修改cvsweb.conf:

  1. 修改CVSROOT路径设置:
    %CVSROOT = (
    ‘Development’ => ‘/path/to/cvsroot’, #<==修改指向本地的CVSROOT
    );
  2. 缺省不显示已经删除的文档:
    “hideattic” => “1″,#<==缺省不显示已经删除的文档
  3. 在配置文件cvsweb.conf中还可以定制页头的描述信息,你可以修改$long_intro成你需要的文字

CVSWEB可不能随便开放给所有用户,因此需要使用WEB用户认证:
先生成 passwd:
/path/to/apache/bin/htpasswd -c cvsweb.passwd user

修改httpd.conf: 增加
<Directory “/path/to/apache/cgi-bin/cvsweb/”>
AuthName “CVS Authorization”
AuthType Basic
AuthUserFile /path/to/cvsweb.passwd
require valid-user
</Directory>

CVS TAGS: $Id: cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $

将$Id: cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $ 加在程序文件开头的注释里是一个很好的习惯,cvs能够自动解释更新其中的内容成:file_name version time user_name 的格式,比如:cvs_card.txt,v 1.1 2002/04/05 04:24:12 chedong Exp,可以这些信息了解文件的最后修改人和修改时间

几个常用的缺省文件:default.php<?php/* * Copyright (c) 2002 Company Name. * $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $ */

?>====================================Default.java: 注意文件头一般注释用 /* 开始 JAVADOC注释用 /** 开始的区别/* * Copyright (c) 2002 MyCompany Name. * $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $ */

package com.mycompany;

import java.;

/** * comments here */public class Default { /** * Comments here * @param * @return */ public toString() {

 }}====================================default.pl:#!/usr/bin/perl -w# Copyright (c) 2002 Company Name.# $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $

# file comments here

use strict;

CVS vs VSS

CVS没有文件锁定模式,VSS在check out同时,同时记录了文件被导出者锁定。

CVS的update和commit, VSS是get_lastest_version和check in

对应VSS的check out/undo check out的CVS里是edit和unedit

在CVS中,标记自动更新功能缺省是打开的,这样也带来一个潜在的问题,就是不用-kb方式添加binary文件的话在cvs自动更新时可能会导致文件失效。

$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $ $Date: 2003/11/09 07:57:11 $这样的标记在Virsual SourceSafe中称之为Keyword Explaination,缺省是关闭的,需要通过OPITION打开,并指定需要进行源文件关键词扫描的文件类型:*.txt,*.java, *.html…

对于Virsual SourceSafe和CVS都通用的TAG有:
$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $
$Author: chedong $
$Date: 2003/11/09 07:57:11 $
$Revision: 1.9 $

我建议尽量使用通用的关键词保证代码在CVS和VSS都能方便的跟踪。

WinCVS

下载:

cvs Windows客户端:目前稳定版本为1.2
http://cvsgui.sourceforge.net
ssh Windows客户端
http://www.networksimplicity.com/openssh/

安装好以上2个软件以后:
WinCVS客户端的admin==>preference设置
1 在general选单里
设置CVSROOT: username@192.168.0.123:/home/cvsroot
设置Authorization: 选择SSH server

2 Port选单里
钩上:check for alternate rsh name
并设置ssh.exe的路径,缺省是装在 C:\Program Files\NetworkSimplicity\ssh\ssh.exe

然后就可以使用WinCVS进行cvs操作了,所有操作都会跳出命令行窗口要求你输入服务器端的认证密码。

当然,如果你觉得这样很烦的话,还有一个办法就是生成一个没有密码的公钥/私钥对,并设置CVS使用基于公钥/私钥的SSH认证(在general 选单里)。

可以选择的diff工具:examdiff
下载:
http://www.prestosoft.com/examdiff/examdiff.htm
还是在WinCVS菜单admin==>preference的WinCVS选单里
选上:Externel diff program
并设置diff工具的路径,比如:C:\Program Files\ed16i\ExamDiff.exe
在对文件进行版本diff时,第一次需要将窗口右下角的use externel diff选上。

基于CVSTrac的小组开发环境搭建

作为一个小组级的开发环境,版本控制系统和BUG跟踪系统等都涉及到用户认证部分。如何方便的将这些系统集成起来是一个非常困难的事情,毕竟我们不能指望 Linux下有像Source Offsite那样集成度很高的版本控制/BUG跟踪集成系统。

我个人是很反对使用pserver模式的远程用户认证的,但如果大部分组员使用WINDOWS客户端进行开发的话,总体来说使用 CVSROOT/passwd认证还是很难避免的,但CVS本身用户的管理比较麻烦。本来我打算自己用perl写一个管理界面的,直到我发现了 CVSTrac:一个基于WEB界面的BUG跟踪系统,它外挂在CVS系统上的BUG跟踪系统,其中就包括了WEB界面的CVSROOT/passwd文件的管理,甚至还集成了WIKIWIKI讨论组功能。

这里首先说一下CVS的pserver模式下的用户认证,CVS的用户认证服务是基于inetd中的:
cvspserver stream tcp nowait apache /usr/bin/cvs cvs –allow-root=/home/cvsroot pserver
一般在2401端口(这个端口号很好记:49的平方)

CVS用户数据库是基于CVSROOT/passwd文件,文件格式:
[username]:[crypt_password]:[mapping_system_user]
由于密码都用的是UNIX标准的CRYPT加密,这个passwd文件的格式基本上是apache的htpasswd格式的扩展(比APACHE的 PASSWD文件多一个系统用户映射字段),所以这个文件最简单的方法可以用
apache/bin/htpasswd -b myname mypassword
创建。注意:通过htpasswd创建出来的文件会没有映射系统用户的字段
例如:
new:geBvosup/zKl2
setup:aISQuNAAoY3qw
test:hwEpz/BX.rEDU

映射系统用户的目的在于:你可以创建一个专门的CVS服务帐号,比如用apache的运行用户apache,并将/home/cvsroot目录下的所有权限赋予这个用户,然后在passwd文件里创建不同的开发用户帐号,但开发用户帐号最后的文件读写权限都映射为apache用户,在SSH模式下多个系统开发用户需要在同一个组中才可以相互读写CVS库中的文件。

进一步的,你可以将用户分别映射到apache这个系统用户上。
new:geBvosup/zKl2:apache
setup:aISQuNAAoY3qw:apache
test:hwEpz/BX.rEDU:apache

CVSTrac很好的解决了CVSROOT/passwd的管理问题,而且包含了BUG跟踪报告系统和集成WIKIWIKI交流功能等,使用的 CGI方式的安装,并且基于GNU Public License

在inetd里加入cvspserver服务:
cvspserver stream tcp nowait apache /usr/bin/cvs cvs –allow-root=/home/cvsroot pserver

xietd的配置文件:%cat cvspserver
service cvspserver
{
disable = no
socket_type = stream
wait = no
user = apache
server = /usr/bin/cvs
server_args = -f –allow-root=/home/cvsroot pserver
log_on_failure += USERID
}

注意:这里的用户设置成apache目的是和/home/cvsroot的所有用户一致,并且必须让这个这个用户对/home/cvsroot/下的 CVSROOT/passwd和cvstrac初始化生成的myproj.db有读取权限。

安装过程

  1. 下载:可以从http://www.cvstrac.org 下载
    我用的是已经在Linux上编译好的应用程序包:cvstrac-1.1.2.bin.gz,
    %gzip -d cvstrac-1.1.2.bin.gz
    %chmod +x cvstrac-1.1.2.bin
    #mv cvstarc-1.1.1.bin /usr/bin/cvstrac
    如果是从源代码编译:
    从 http://www.sqlite.org/download.html 下载SQLITE的rpm包:
    rpm -i sqlite-devel-2.8.6-1.i386.rpm
    从 ftp://ftp.cvstrac.org/cvstrac/ 下载软件包
    解包,假设解包到/home/chedong/cvstrac-1.1.2下,并规划将cvstrac安装到/usr/local/bin目录下, cd /home/chedong/cvstrac-1.1.2 编辑linux-gcc.mk:
    修改:
    SRCDIR = /home/chedong/cvstrac-1.1.2
    INSTALLDIR = /usr/local/bin
    然后
    mv linux-gcc.mk Makefile
    make
    #make install

  2. 初始化cvstrac数据库:假设数据库名是 myproj
    在已经装好的CVS服务器上(CVS库这时候应该已经是初始化好了,比如:cvs init初始化在/home/cvsroot里),运行一下
    %cvstrac init /home/cvsroot myproj
    运行后,/home/cvsroot里会有一个的myproj.db库,使用CVSTRAC服务,/home/cvsroot/myproj.db /home/cvsroot/CVSROOT/readers /home/cvsroot/CVSROOT/writers /home/cvsroot/CVSROOT/passwd这几个文件对于web服务的运行用户应该是可写的,在RedHat8上,缺省就有一个叫 apache用户和一个apache组,所以在httpd.conf文件中设置了用apache用户运行web服务:
    User apache
    Group apache,
    然后设置属于apache用户和apache组
    #chown -R apache:apache /home/cvsroot
    -rw-r–r– 1 apache apache 55296 Jan 5 19:40 myproj.db
    drwxrwxr-x 3 apache apache 4096 Oct 24 13:04 CVSROOT/
    drwxrwxr-x 2 apache apache 4096 Aug 30 19:47 some_proj/
    此外还在/home/cvsroot/CVSROOT中设置了:
    chmod 664 readers writers passwd
  3. 在apche/cgi-bin目录中创建脚本cvstrac:
    #!/bin/sh
    /usr/bin/cvstrac cgi /home/cvsroot
    设置脚本可执行:
    chmod +x /home/apache/cgi-bin/cvstrac
  4. 从 http://cvs.server.address/cgi-bin/cvstrac/myproj 进入管理界面
    缺省登录名:setup 密码 setup
    对于一般用户可以从:
    http://cvs.server.address/cgi-bin/cvstrac/myproj
  5. 在setup中重新设置了CVSROOT的路径后,/home/cvsroot
    如果是初次使用需要在/home/cvsroot/CVSROOT下创建passwd, readers, writers文件
    touch passwd readers writers
    然后设置属于apache用户,
    chown apache.apache passwd readers writers
    这样使用setup用户创建新用户后会同步更新CVSROOT/passwd下的帐号

修改登录密码,进行BUG报告等,
更多使用细节可以在使用中慢慢了解。

对于前面提到的WinCVS在perference里设置:
CVSROOT栏输入:username@ip.address.of.cvs:/home/cvsroot
Authenitication选择:use passwd file on server side
就可以了从服务器上进行CVS操作了。

CVS的用户权限管理

CVS的权限管理分2种策略:

  • 基于系统文件权限的系统用户管理:适合多个在Linux上使用系统帐号的开发人员进行开发。
  • 基于CVSROOT/passwd的虚拟用户管理:适合多个在Windows平台上的开发人员将帐号映射成系统帐号使用。

为什么使用apache/apache用户?首先RedHat8中缺省就有了,而且使用这个用户可以方便通过cvstrac进行WEB管理。
chown -R apache.apache /home/cvsroot
chmod 775 /home/cvsroot

Linux上通过ssh连接CVS服务器的多个开发人员:通过都属于apache组实现文件的共享读写
开发人员有开发服务器上的系统帐号:sysuser1 sysuser2,设置让他们都属于apache组,因为通过cvs新导入的项目都是对组开放的:664权限的,这样无论那个系统用户导入的项目文件,只要文件的组宿主是apache,所有其他同组系统开发用户就都可以读写;基于ssh远程认证的也是一样。

   apache(system group)
/            |           \
sysuser1   sysuser2     sysuser3

Windows上通过cvspserver连接CVS服务器的多个开发人员:通过在passwd文件种映射成 apache用户实现文件的共享读写
他们的帐号通过CVSROOT/passwd和readers writers这几个文件管理;通过cvstrac设置所有虚拟用户都映射到apache用户上即可。

   apache(system user)
/            |            \
windev1     windev2      windev3             

利用cvs + (WinCVS/cvsweb/cvstrac),构成了一个相对完善的跨平台工作组开发版本控制环境。

相关资源:

CVS HOME:
http://www.cvshome.org

CVS FAQ:
http://www.loria.fr/~molli/cvs-index.html

相关网站:
http://directory.google.com/Top/Computers/Software/Configuration_Management/Tools/Concurrent_Versions_System/

CVS–并行版本系统
http://www.soforge.com/cvsdoc/zh_CN/book1.html

CVS 免费书:
http://cvsbook.red-bean.com/

CVS 命令的速查卡片:
http://www.refcards.com/about/cvs.html

WinCVS:
http://cvsgui.sourceforge.net/

CVSTrac: A Web-Based Bug And Patch-Set Tracking System For CVS
http://www.cvstrac.org

StatCVS:基于CVS的代码统计工具:按代码量,按开发者的统计表等
http://sourceforge.net/projects/statcvs

如何在WEB开发中规划CVS上:在Google上查 “cvs web development”
http://ccm.redhat.com/bboard-archive/cvs_for_web_development/index.html

一些集成了CVS的IDE环境:
Eclipse
Magic C++

原文出处:<a href=”http://www.chedong.com/tech/cvs_card.html”>http://www.chedong.com/tech/cvs_card.html</a>
<<返回

<<返回首页

2004年09月21日

Linux firewall進展過程

 Ipchains (Linux 2.2 Kernel)。
 Iptables (Linux 2.4 kernel)。

 

防火牆工具 iptables

防火牆基本上都是設定防火牆規則( RULES ) 來規範封包的處理。

iptables 將不同的規則集合起來﹐放進不同的鏈( CHAINS )。

iptables 的三個內建鏈 ( Built-in Chains ) 分別是﹕INPUT﹑OUTPUT﹑與 FORWARD。

iptables工具主要具備三個tables, table中包含chains, chains 裡面包含的就是 rules policies

Iptable結構示意圖

IP datagramKernel中處理的狀態圖

The IP datagram is received. (1)
 If the datagram is for this machine, it is processed locally. (2)
 Forward (3)
Datagrams from local processes are sent to the routing software for forwarding to the appropriate interface. (4)
 The IP datagram is transmitted. (5)
Input rule is applied at flow 2.
Output rule is applied at flow 4.

Iptableschine示意圖

OUTPUT 是 local process 產生的封包才會經過。
PREROUTING 就是在 ROUTING 之前﹐通常是封包從一個界面進入之後﹐尚未交給路由判斷之前的處理﹐在這裡進行 DNAT 的 socket 替換
POSTROUTING 則是在路由判斷之後﹐通常是當封包離開界面之前的處理,在這裡進行 SNAT 的 socket 替換。

User-defined Chins

Iptables is the ability for the user to create new chains.
test chain” is a User-defined chain.
User-defined chains can jump to other user-defined chains.
don’t make loops: your packets will be dropped if they’re found to be in loop.
   

iptables main Features

增強網路位址轉換(NAT)

SNAT (Source Network Address Translation) change source address of packet.

DNAT (Destination Network Address Translation) change destination address of packet.

增強的封包檢視 

IPTables可以檢查所有6個TCP旗標。

IPTables可以詳細的指定一連串的來源或目的端埠,而非只有單一埠或單一範圍的埠。

MAC位址過濾 

可以使用MAC Address做為過濾的條件。

一定比例的限制條件

抵擋拒絕服務的攻擊(像是大量syn)和埠的掃描,而且也限制重覆的程序上登入的次數。 

服務的優先順序類型(TOS)

傳輸可以依優先順序到不同的排列裡。效率儘可能在可靠度最大、花費最少及標準的服務。

如何開始使用iptables

  必須將 Firewall 功能編進核心裡面
Networking options ---> [*] Network packet filtering (replaces ipchains) [*] TCP/IP networking IP: Netfilter Configuration ---> <M> Connection tracking (required for masq/NAT) (NEW) <M> FTP protocol support (NEW) <M> IP tables support (required for filtering/masq/NAT) (NEW) <M> limit match support (NEW) <M> MAC address match support (NEW) <M> netfilter MARK match support (NEW) <M> Multiple port match support (NEW) <M> TOS match support (NEW) <M> tcpmss match support (NEW) <M> Connection state match support (NEW) <M> Packet filtering (NEW) <M> REJECT target support (NEW) <M> Full NAT (NEW) <M> MASQUERADE target support (NEW) <M> REDIRECT target support (NEW) <M> Packet mangling (NEW) <M> TOS target support (NEW) <M> MARK target support (NEW) <M> LOG target support (NEW) <M> TCPMSS target support (NEW) <M> ipchains (2.2-style) support (NEW) 

  Load iptables module
(1)   # modprobe ip_tables
(2)   # lsmod (確定 ip_tables 模組存在)

iptables指令格式:

      # iptables [-t table] command [match] [target / jump]

 
  [-t table]

the -t option specifies which table to use, Per default, the filter table is used.

nat table
the nat table is used mainly for Network Address Translation.
mangle table
this table is used mainly for mangling packets. Examples of this would be to change the TTL, TOS or MARK.
filter table
The filter table should be used for filtering packets generally.
 
  command (manage whole chains)
    建立一個新的(自定)鏈 ( -N )。

    刪除一個空的(自定)鏈 ( -X )。

    改變一個內建鏈的原則 ( -P )。

    列出一個鏈中的規則 ( -L )。

    清除一個(內建)鏈中的所有規則 ( -F )。

    在一個鏈的最後面新增( append ) 一條規則 ( -A )。

    在鏈內某個位置插入( insert ) 一條新規則( -I )。

    在鏈內某個位置替換( replace ) 一條規則 ( -R )。

    在鏈內某個位置刪除( delete ) 一條規則 ( -D )。

    刪除(delete) 鏈內第一條符合的規則 (-D)。

 
 
  [match]
Generic matches
  iptables -A INPUT -p tcp
  iptables -A INPUT -s 192.168.1.1
  iptables -A INPUT -d 192.168.1.1
  iptables -A INPUT -i eth0
  iptables -A FORWARD -o eth0
  iptables -A INPUT –fragment
TCP matches
  iptables -A INPUT -p tcp –sport 22
  iptables -A INPUT -p tcp –dport 22
  iptables -p tcp –tcp-flags SYN,ACK,FIN SYN
UDP matches
  iptables -A INPUT -p udp –sport 53
  iptables -A INPUT -p udp –dport 53
ICMP matches
  iptables -A INPUT -p icmp –icmp-type 8
MAC match
  iptables -A INPUT –mac-source 00:00:00:00:00:01
Limit match
iptables -A INPUT -m limit –limit 3/hour
iptables -A INPUT -m limit –limit-burst 5
Multiport match
  iptables -A INPUT -p tcp -m multiport –source-port 22,53,80,110
  iptables -A INPUT -p tcp -m multiport –destination-port 22,53,80,110
  iptables -A INPUT -p tcp -m multiport –port 22,53,80,110
TTL matches
  iptables -A OUTPUT -m ttl –ttl 60
  TOS matches
iptables -A INPUT -p tcp -m tos –tos 0×16
Minimize-Delay 16 (0×10)
Maximize-Throughput 8 (0×08)
Maximize-Reliability 4 (0×04)
Minimize-Cost 2 (0×02)
Normal-Service 0 (0×00)
 
  [target / jump]
  ACCEPT target

   接受這個封包﹐也就是可以通過規則檢驗而放行﹑順利通過這個鏈。

DROP  target
  丟棄這個封包﹐也就不能通過規則檢驗而被擋掉。
QUEUE target
   將封包重導至本機端的佇列 ( queue ) 程式。
  RETURN  target

    進入某一個Chine時,如果符合某種條件,使其回到原來的Chine中。

  TOS  target

iptables -t mangle -A PREROUTING -p TCP –dport 22 -j TOS –set-tos 0×10

  SNAT  target

iptables -t nat -A POSTROUTING -o eth0 -j SNAT –to-source 140.116.164.179

  DNAT  target

iptables -t nat -A PREROUTING -p tcp -d 140.116.164.179 –dport 80 -j DNAT –to-destination 1.2.3.0-1.2.3.9

    修改為特定的 Destination Socket

  MASQUERADE  target

   動態的根據路由判斷後的界面而修改 Source Socket

  REDIRECT  target

iptables -t nat -A PREROUTING -i eth0 -p tcp –dport 80 \
-j REDIRECT –to-port 3128

  TTL  target

iptables -t mangle -A PREROUTING -o eth0 -j TTL –ttl-set 64

  

 

2004年09月20日

GPL协议及其对Linux的影响

  GPL是GNU Public License的缩写,最早是自由软件基金会为了促进开放源代码的发展,而搞出来的一种版权协议。

  GPL对软件产业的发展起到了巨大的促进作用,但是也带来了很多误解。在美国考察期间,我们和GNU的主要负责人进行了广泛交谈,精确地了解了GPL的本质,以及它对软件产业产生的影响。本文就是介绍这方面的情况。

一、GPL和软件版权

  首先需要明确的是,GPL协议只是无数种版权协议其中的一种,它和版权本身是不同的概念。具体的解释如下:

  软件的版权完全属于其作者所有,作者可以自由地选择采取哪一种版权协议来发布自己的软件。

  在传统的商业模式,作者一般采取商业版权协议来发布自己的软件。商业版权协议也有各种不同的方式。例如以前商业版权协议要求用户在每一台计算机上安装一份软件,而微软新的版权协议要求用户不但安装软件,还要同时购买升级资格。这些都是不同的商业版权协议。

  为了促进软件产业的发展,作者也可以选择GPL协议。这样作者将他的源代码开放,供其他人修改,而其他人能够修改的前提是接受作者指定的GPL协议。

  自由软件基金会主持开发了无数的自由软件,特别是C++编译器Gcc,由于采用了GPL协议,无数人的思想可以共享,因此GPL和自由软件迅速地发展起来。

  由于GNU的软件全部采用GPL协议,而GNU的软件影响力又非常之大,因此很多人错误地将自由软件和GPL完全联系在一起,认为所有的自由软件都是采用GPL协议的,这是完全错误的。

  虽然GPL和自由软件的发展紧密地联系在一起,但是这两者并不等同。自由软件的作者并不一定选择GPL来发布他的软件,如果愿意的话,软件的作者甚至可以自己编写一个版权协议。

  这里一定要搞清下面的概念:

1. 软件的版权归其作者所有,其作者有权选择采用哪种版权协议
2. GPL只是众多版权协议中的一种
3. 自由软件不一定要采用GPL协议
4. GNU不拥有所有开放源代码软件的版权,GNU只拥有那些由自由软件基金会开发的软件的版权,以及那些作者自愿交给GNU的版权。

二、GPL的发展

  随着技术的发展,越来越多的公司开始关注开放源代码软件和Linux,这些公司显然不愿意完全开放源代码。

  同时GNU也注意到GPL不利于商业运作。为了促进Linux下商业软件的发展,GNU又公布了LGPL协议。

LGPL协议的核心思想是:

1.用户可以使用开放源代码的“程序库”开发自己的商业软件,而无需开放源代码。(“程序库”指可以完成特定功能的现成的软件代码)

2.如果对“程序库”本身进行了修改,则必须公开修改“程序库”的源代码。

3. 用户如果自己开发“程序库”,如果该程序库是专门针对某个特定的开放源代码软件开发的,则必须公布源代码。如果该程序库具有一定的通用性,则可以不开放源代码。

  第一条的意思是允许在Linux上开发商业软件。因为Linux下的所有软件都必须用到glibc库,所以如果要求遵守GPL协议,那么就不可能存在Linux下的商业软件了。而采取LGPL,则允许商业软件的发展。

  第二条的意思是为了保护程序库的唯一性和一致性。如果用户随意修改程序库而不必开放源代码,则很快程序库就有很多个版本,这样就不能保证其他软件的兼容性。

  第三条的意思是允许用户开发商业用途的程序库。

  LGPL协议大大促进了Linux下商业软件大发展,一些优秀的软件,例如IBM的WebSphere,Borland的Kylix,AW的Maya,都是在LGPL的前提下发展起来的。

  但是,仍然有很多公司认为LGPL协议的规定还是太死板,不能满足其需要,所以又提出了各种各样的“第三方协议”,比较典型的是Sun为OpenOffice提出的SISSL协议。

  SISSL协议规定,在开放源代码项目OpenOffice基础上开发的商业软件,可以不公布源代码。这样,就有一个比LGPL更好支持商业软件的版权协议。

  注意,前面提到过,软件的作者拥有版权,能够决定采取何种版权协议发布自己的软件。而最近一些比较大的开放源代码项目都是由一些大公司公布的源代码,因此他们可以决定对自己的开放源代码项目采取何种版权协议,而不必理会GNU的意见。

  现在一些比较大的开放源代码项目,都是由大公司主持的,因此都支持各种各样的第三方协议。这些协议的目的只有一个,就是保护这些大公司自己的版权,允许他们开发商业软件。

  GNU最近也在考虑制定新的版权协议,进一步增加对商业软件的支持,可见GPL协议也在不断地更新之中。

三、GPL的功与过

  GPL对软件产业的发展到底是好还是坏,这是大家讨论的焦点。

  在1998年以前,GPL对Linux的促进是很显然地,可以说没有GPL就没有Linux。大家都知道,与Linux竞争的,还有FreeBSD项目,这个项目的版权协议允许封闭源代码,并且实际上FreeBSD的某些软件也相当不错。但是现在来看,Linux的到蓬勃发展,FreeBSD却在逐渐消亡。

  原因很简单,基于FreeBSD开发的软件,很快就变得不开放源代码了,因而得不到整个社区的支持,所以也没有可持续发展。一旦其创始人由于某种原因终止了开发,整个项目就不会延续了。

  而基于GPL的项目,由于种种原因,总会有人不断研究,因此有很强的可持续发展能力。

  1998年以后,公司大量介入Linux,因此LGPL又起到了很大的促进作用。由于LGPL的推广,很多公司把自己的商用软件移植到Linux上。在Linux World大会上,我们可以看到,除了微软以外,几乎所有的大型软件公司都把自己的产品移植到了Linux上或者正在移植。

  现在,由于桌面Linux系统的要求,以及对Linux软件商业化的压力,大家开始让Linux真正被普通人接受,而不只限于爱好者。此时,“第三方协议”又起到了很大的作用。Linux下的主要应用软件,例如办公套件、浏览器、电子邮件、IDE编程环境等,都是基于SISSL这样的第三方协议发展起来的。

  在未来的岁月里,显然Linux的发展需要各种协议的综合运用,使得Linux既拥有开放源代码软件的优点,也拥有商业软件的优点。偏重于任何一种版权协议都是片面地,对整个软件产业发展是有害的。

四、国际大公司的做法

  根据在Linux World大会上的考察,目前主要的软件和硬件公司,例如IBM、Sun、RedHat、Borland、Oracle等,都是采用多种软件协议并行的方式。

  最典型的是Sun负责的OpenOffice项目,这个项目就是GPL、LGPL、SISSL三种协议并存,开发者可以根据自己的情况,选择自己最合适的版权协议。

  等到GPL的最新版本出来之后,也许就不会有各种各样的第三方协议,而同一在GPL的某个新的、完全支持商业软件发展的协议之下。

小结:

  我们可以将GPL协议对开放源代码社区的影响情况总结如下:

1. 要历史性地看待GPL 技术发展日新月异,我们对GPL的看法不能停留在1998年。现在单纯采用GPL协议的软件已经不多,大部分是GPL、LGPL和商业版权协议同时采用。开发者可以根据自己的需要自由地选择自己希望遵守的版权协议。这样既保证开放源代码的发展,又保证各公司的利益。

2.不是开放源代码软件必然采用GPL GPL不代表版权,也和Linux没有必然联系,它只是软件作者可以选择的版权协议之一。

3. GPL协议自身也在不断发展之中 必须看到,GPL是相当古老的东西,已经不太适合于现代软件产业的发展。所以国际上很多人正在修订GPL协议,以最大限度地促进软件产业的发展。

4. GPL对商业化的阻碍没有宣传得那么大 在国际社会的共同努力下,GPL协议已经不再成为开放源代码进入商业的阻碍。过分强调GPL的缺点有夸大事实之嫌。必须看到,现在的开放源代码体系是多种版权协议的混合体系,如果我们认为某些项目不适合于GPL,那么选择其他协议就可以了,没有任何问题的。

2004年09月15日

Quarrel, fight. 又一次争吵,上次是在8月份,一个多月没有吵架了,是不是很不容易了?不知道,黑霉日,还有这样的说法~是很准。

 

总结一下,是因为晚上说太多了,伤了她的自尊心。

2004年09月09日

按照vqadmin里面写的INSTALL来做,怎么也无法登录vqadmin来管理。直到修改了/usr/local/httpd/conf/vqpasswd的所有者,所有组以及权限之后才可以登录。

使用sh备份qmail+mysql数据到指定的ftp地址的方法!

转载自http://www.chinaunix.net 作者:peijun.jiang  发表于:2003-10-13 13:46:57

备份vpopmail的domains目录、qmail的control目录和mysql的var数据存放目录,使用crontab定时进行操作。下面是脚本文件,具体的目录视自己的系统更改:

mkdir /var/qmailbakup
cd /var/qmailbakup
touch qmailbakup.sh
chmod 755 qmailbakup .sh

vi qmailbakup.sh
[code:1:d480555598]
#!/bin/sh

DATE=`date +%Y-%m-%d-%H`

cd /var/qmailbakup/

tar cvzf domains.$DATE.tar.gz /home/vpopmail/domains
tar cvzf control.$DATE.tar.gz /var/qmail/control
tar cvzf mysql.$DATE.tar.gz /usr/local/mysql/var

ftp -n 192.168.0.21 << ! //你的ftp服务器的地址
user qmail qmailbakup //ftp用户名和密码,注意要有put权限
binary
put domains.$DATE.tar.gz
put control.$DATE.tar.gz
put mysql.$DATE.tar.gz
bye
!

rm -f domains.$DATE.tar.gz control.$DATE.tar.gz mysql.$DATE.tar.gz //删除本机产生的文件,如果你想在本服务器也保存一份备份,去掉该项即可。
[/code:1:d480555598]

使用crontab定时执行:
vi /etc/crontab
00 20 * * 0-6 /var/qmailbakup/qmailbakup.sh

这样每天晚上8:00执行改备份程序。