本章简要介绍两个功能强大的程序开发工具 make 和 SCCS,这两个工具可以非常成功地用于 Fortran 编程项目。
目前,许多论述如何使用 make 和 SCCS 的优秀商品书籍已经出版面市,其中包括 Andrew Oram 和 Steve Talbott 合著的《Managing Projects with make》,还有 Don Bolinger 和 Tan Bronson 合著的《Applying RCS and SCCS》。这两本书均由 O’Reilly & Associates 出版。
make 实用程序使用智能化方法执行程序编译和链接任务。通常,大型应用程序由一组源文件和 INCLUDE 文件组成,需要与许多库进行链接。修改任何一个或多个源文件,都需要对那一部分程序进行重新编译,并且要重新链接。指定组成应用程序的各文件间的相互依赖性以及重新编译和重新链接每一程序块所需的命令,可以自动执行这一过程。指令文件中有了这些说明,make 便会确保只重新编译那些需要重新编译的文件,并确保重新链接时使用生成可执行文件所需要的那些选项和库。以下讨论内容提供了一个如何使用 make 的简单示例。有关摘要信息,请参见 make(1S)。
名为 makefile 的文件以结构化方式告知 make,哪些源文件和目标文件依赖其他文件。它还定义了编译和链接文件所需的命令。
例如,假设您的程序有四个源文件以及相应的 makefile 文件:
demo% ls makefile commonblock computepts.f pattern.f startupcore.f demo% |
假设 pattern.f 和 computepts.f 都有一个名为 commonblock 的 INCLUDE,并且您希望编译每个 .f 文件并将这三个可重定位的文件与一系列库一起链接成一个名为 pattern 的程序。
这时,makefile 将会如下所示:
demo% cat makefile pattern: pattern.o computepts.o startupcore.o f95 pattern.o computepts.o startupcore.o -lcore95 \ -lcore -lsunwindow -lpixrect -o pattern pattern.o: pattern.f commonblock f95 -c -u pattern.f computepts.o: computepts.f commonblock f95 -c -u computepts.f startupcore.o: startupcore.f f95 -c -u startupcore.f demo% |
makefile 的第一行表明 pattern 的创建取决于 pattern.o、computepts.o 和 startupcore.o。下一行及其后续各行给出了由可重定位的 .o 文件和库创建 pattern 的命令。
makefile 中的每一条目都是一项规则,它描述了目标对象的依赖性以及创建该对象所需的命令。规则的结构为:
target: dependencies-listTAB build-commands
依赖性-每一条目的头一行均是为目标文件命名,其后是目标依赖的所有文件。
命令-每一条目随后还有一行或多行,这些行指定将生成本条目相应的目标文件的 Bourne shell 命令。这些命令行中的每一行都必须用一个制表符缩进。
make 命令可以进行无参数调用,只需键入:
demo% make |
make 实用程序在当前目录中查找名为 makefile 或 Makefile 的文件并从该文件中获取指令。
make 实用程序:
读取 makefile,确定其必须处理的所有目标文件、这些目标文件依赖的文件以及生成这些目标文件所需的命令。
查找每个文件的最后更改日期和时间。
如果有任何目标文件比其依赖的任一文件的生成时间更早,则使用 makefile 中与该目标相应的命令重新生成该目标文件。
make 实用程序的宏功能允许进行简单的无参数字符串替换。例如,可将组成目标程序 pattern 的可重定位文件的列表表示为单个宏字符串,使其更易于更改。
宏字符串定义具有以下格式:
NAME = string
宏字符串的使用方式如下所示:
$(NAME)
make 会用宏字符串的实际值来替换它。
以下示例将命名所有目标文件的宏定义添加到 makefile 的开头:
OBJ = pattern.o computepts.o startupcore.o |
现在便可在依赖性列表以及与 makefile 中的目标 pattern 相应的 f95 链接命令中同时使用宏了。
pattern: $(OBJ) f95 $(OBJ) -lcore95 -lcore -lsunwindow \ -lpixrect -o pattern |
对于名称为单个字母的宏字符串,可以省略括号。
make 宏的初始值可以用 make 的命令行选项进行覆盖。例如:
FFLAGS=–u OBJ = pattern.o computepts.o startupcore.o pattern: $(OBJ) f95 $(FFLAGS) $(OBJ) -lcore95 -lcore -lsunwindow \ -lpixrect -o pattern pattern.o: pattern.f commonblock f95 $(FFLAGS) -c pattern.f computepts.o: f95 $(FFLAGS) -c computepts.f |
现在,简单的无参数 make 命令将会使用上面设置的 FFLAGS 值。不过,这可以通过命令行来覆盖:
demo% make "FFLAGS=–u -O" |
这里,make 命令行中的 FFLAGS 宏定义会覆盖 makefile 的初始值,并且会将 -O 标志和 -u 标志一起传递给 f95。请注意,也可以在命令中使用 "FFLAGS=",将宏重置为空字符串使其不再有效。
为使 makefile 更易编写,make 将根据目标文件的后缀,使用自身的缺省规则。
缺省规则在文件 /usr/share/lib/make/make.rules 中。在识别缺省的后缀规则时,make 会将 FFLAGS 宏指定的任何标志、-c 标志以及要编译的源文件名都作为参数进行传递。此外,make.rules 文件还使用 FC 宏赋予的名称作为要使用的 Fortran 编译器的名称。
以下示例两次说明了这一规则:
FC = f95 OBJ = pattern.o computepts.o startupcore.o FFLAGS=–u pattern: $(OBJ) f95 $(OBJ) -lcore95 -lcore -lsunwindow \ -lpixrect -o pattern pattern.o: pattern.f commonblock f95 $(FFLAGS) -c pattern.f computepts.o: computepts.f commonblock startupcore.o: startupcore.f |
make 使用缺省规则编译 computepts.f 和 startupcore.f。
.f90 文件存在缺省的后缀规则,这些规则将会调用 f95 编译器。
然而,除非将 FC 宏定义为 f95,否则 .f 和 .F 文件的缺省后缀规则会调用 f77 而非 f95。
而且,当前没有为 .f95 和 .F95 文件定义后缀规则,.mod Fortran 95 模块文件将会调用 Modula 编译器。要对此进行补救,需要在调用 make 的目录下为 make.rules 文件创建您自己的本地副本,同时对该文件进行修改,添加 .f95 和 .F95 后缀规则,删除 .mod 的后缀规则。有关详细信息,请参见 make(1S) 手册页。
使用特殊目标 .KEEP_STATE 检查命令的依赖性及隐藏依赖性。
当 .KEEP_STATE: 目标有效时,make 会根据状态文件检查用于生成目标的命令。如果自上次 make 运行以来命令已更改,make 会重新生成此目标。
当 .KEEP_STATE: 目标有效时,make 将从 cpp(1) 以及其他编译处理器中读取任何“隐藏”文件(例如 #include 文件)的相应报告。如果目标相对于这些文件中的任何文件已过期,make 会重新生成它。
SCCS 代表源代码控制系统 (Source Code Control System)。SCCS 为实现以下目标提供了途径:
跟踪源文件的演变-即其更改历史
防止源文件被其他开发人员同时更改
通过提供版本标记来跟踪版本号
SCCS 的三项基本操作是:
将文件置于 SCCS 控制下
签出文件进行编辑
签入文件
本部分以上一程序为例向您展示如何使用 SCCS 来执行这些任务。只对基本的 SCCS 进行了说明,并且只介绍了三个 SCCS 命令:create、edit 和 delget。
建立 SCCS 目录
在文件中插入 SCCS ID 关键字(这是可选的)
创建 SCCS 文件
首先,必须在正在开发程序的目录下创建 SCCS 子目录。使用以下命令:
demo% mkdir SCCS |
SCCS 必须采用大写字母。
有些开发人员会在每个文件中放入一个或多个 SCCS ID 关键字,但这是可选的。以后,每次用 SCCS get 或 delget 命令签入文件时,都会用版本号来标识这些关键字。有三种可能的位置可以放置这些字符串:
注释行
参数语句
初始化数据
使用关键字的优点是版本信息会出现在源列表和已编译的目标程序中。如果其前面有字符串 @(#),可用 what 命令打印目标文件中的关键字。
只含有参数和数据定义语句的已包含头文件不会生成任何初始化数据,因此这些文件的关键字通常置于注释或参数语句中。在某些文件中,如 ASCII 数据文件或 makefile,SCCS 信息将会出现在注释中。
SCCS 关键字以 %keyword% 形式出现,并通过 SCCS get 命令扩展成各自的值。最常用的关键字有:
%Z% 扩展为 what 命令识别的标识字符串 @(#)。%M% 扩展为源文件名。%I% 扩展为本 SCCS 维护文件的版本号。%E% 扩展为当前日期。
例如,可以用包含以下关键字的 make 注释来标识 makefile。
# %Z%%M% %I% %E% |
源文件 startupcore.f、computepts.f 和 pattern.f 可以通过以下格式的初始化数据来标识:
CHARACTER*50 SCCSID DATA SCCSID/"%Z%%M% %I% %E%\n"/ |
用 SCCS 处理该文件,进行编译,然后用 SCCS what 命令处理目标文件,显示如下:
demo% f95 -c pattern.f ... demo% what pattern pattern: pattern.f 1.2 96/06/10 |
您还可以创建名为 CTIME 的 PARAMETER,无论何时用 get 命令访问文件,该参数都会自动进行更新。
CHARACTER*(*) CTIME PARAMETER ( CTIME="%E%") |
INCLUDE 文件可以用含有 SCCS 标记的 Fortran 注释加以注解:
C %Z%%M% %I% %E% |
在 Fortran 95 源代码文件中使用单字母派生类型组件名可能会与 SCCS 关键字识别产生冲突。例如,当通过 SCCS 传递时,Fortran 95 结构组件引用 X%Y%Z 在执行 SCCS get 后会变成 XZ。在 Fortran 95 程序中使用 SCCS 时,应注意不要用单个字母定义结构组件。例如,假如 Fortran 95 程序中的结构引用是 X%YY%Z,SCCS 并不会将 %YY% 解释为关键字引用。或者,SCCS get -k 选项在检索文件时将不会扩展 SCCS 关键字 ID。
现在,可以用 SCCS create 命令将这些文件置于 SCCS 控制之下:
demo% sccs create makefile commonblock startupcore.f \ computepts.f pattern.f demo% |
一旦源代码处于 SCCS 控制之下,便可用 SCCS 执行以下两项主要任务:签出文件以便对其进行编辑;签入已编辑完的文件。
使用 sccs edit 命令签出文件。例如:
demo% sccs edit computepts.f |
然后,SCCS 会在当前目录下创建 computepts.f 的可写副本,并记录您的登录名。当文件已签出时,其他用户不能再签出该文件,但可以查出是谁签出了该文件。
在您完成编辑后,使用 sccs delget 命令签入已修改的文件。例如:
demo% sccs delget computepts.f |
该命令会使 SCCS 系统做以下事情:
通过比较登录名确保您就是签出文件的用户
提示您对更改做注释
记录本次编辑会话所更改的内容
从当前目录中删除 computepts.f 的可写副本。
用扩展了 SCCS 关键字的只读副本替换可写副本
sccs delget 命令是两个简单 SCCS 命令(delta 和 get)的复合命令。delta 命令执行上述列表中的前三项任务;get 命令执行后两项任务。