跳转到内容

GNU make/打印版

维基教科书,自由的教学读本

声明,本文翻译的是GNU make 3.8.1版用户手册;本手册的原作者是司徒文(Richard M. Stallman),麦家府(Roland McGrath),史密斯(Paul D. Smith)。原手册发布于2006年4月。此为原文下载地址

make概述

[编辑]

make工具能自动判断一个大程序的哪些部分需要再编译,并且能用命令来执行再编译操作。本文档讨论的GNU make原来是由司徒文与麦家府开发制作的,从3.76版开始由史密斯接管开发工作。

GNU make遵从IEEE 1003.2-1992标准(POSIX.2)第6.2条。

在本文例子中,笔者用的是C语言程序,这是因为C语言最为流行,但读者也可以用make来完成其他编程语言的编译工作,前提是这种语言的编译器能在 shell下运行。当然,make的应用不局限于程序;如果遇到一些任务,这些任务的文件必须随着其他文件或数据的变化而更新,那么读者就可以用make 来应对这些任务。

在使用make之前,请读者先写好Makefile文件,该文件须描述清楚程序文件之间的关系,还要给出更新每个文件的命令。通常,一个程序的可执行文件由OBJ文件来更新,而这些OBJ文件则由源代码编译产生。

只要有合适的Makefile文件,在修改了一些源代码后,只要在Shell命令行下输入:

make

就足以完成所有必要的再编译。make程序通过Makefile的数据库以及诸文件的最后修改时间来判断哪些文件需要更新。对于每个需要更新的文件, make会用数据库中记录的命令来对其操作。

读者可以给make加上命令行参数,以此告诉make哪些文件需要再编译、怎样再编译。参见第九章如何运行make

如何使用本文档

[编辑]

如果读者是个make新手,或者想阅读大体介绍,那么在阅读每章时只需少量的看前几节,后面几节可以略过。每章的前几节会有该章介绍以及该章的大体信息,而后几节则会包括一些特殊的或技术性的信息。但第二章 make简介是个例外,全章都是介绍性的内容。

如果读者对其他make程序比较熟悉的话,请参阅第十二章 GNU make的特色功能,该章列出了GNU make的增强功能;另外请参阅第十三章 GNU make与标准make不兼容的部分,该章举出GNU make在少数功能上逊色于其他make的例子。

如果需要阅读摘要,请看第九章第七节 选项摘要附录 A 快速参考以及第四章第八节所讲的特殊目标

问题与漏洞

[编辑]

如果读者发现了GNU make的一些问题或者认为发现了漏洞,那么请与开发者联系;我们不作任何承诺,但我们会尽力解决问题。

在提交漏洞报告之前,请核实漏洞的真实性。另外请认真反复阅读文档,确定文档介绍的应用方法在实际操作中有效,如果文档对功能上的一些问题没有阐述清楚,那么也请提交报告,这些都是文档的“漏洞”!

在提交漏洞报告或者自己解决漏洞之前,请将Makefile文件简化至最小且能体现漏洞问题的程度。然后把Makefile文件连同出错和警告信息一齐发给我们。请勿改动原信息,最好将其剪切复制到漏洞报告中。请确保用以生成最简Makefile文件的命令中不使用非自由软件或者不常用的程序(其实对于这种工具,读者随时都可以用简单的几句Shell命令来测试)。最后,请详述Makefile本应有的预期结果,以便于我们判断问题是否出在文档上。

如果读者发现了一个如假包换的错误,那么可以通过以下两种途径提交报告:一、通过发送电子邮件到:

bug-make@gnu.org

与我们联系;
二、使用我们的在线项目管理来提交报告,网址是:

http://savannah.gnu.org/projects/make/


除了上述的信息以外,请将使用的make完整的版本号一并发给我们;读者可以通过使用命令“make --version”来获取版本信息。请确保报告中包含make所运行机器的硬件信息与操作系统信息;顺便提一句,获取这些信息的一种方法是使用命令 “make --help”,一切尽在命令的最后一行输出中。

make简介

[编辑]

首先要有一个名为“Makefile”的文件告诉make需要作什么。绝大多数情况下,Makefile主要让make完成编译与链接程序的工作。

本章我们讨论一个简单的Makefile,这个Makefile会编译与链接一个由八个C源代码文件和三个头文件组成的文本编辑器程序。该 Makefile还会告诉make如何在有明确指示的时候运行各种各样的命令(比如说接到清除指示时,make运行删除某些文件的命令)。要阅读更复杂的 Makefile代码,翻至附录C 一个复杂的Makefile实例

当make重新编译该文本编辑器时,所有的C源代码文件都会被重新编译。如果一个头文件被修改,那么为了保证整体程序无损,包含该头文件的所有C源代码文件都必须要重新编译。源代码文件每经过一次编译都会有一个与自己对应的OBJ文件生成。最后,若有源代码文件被重新编译,所有OBJ文件不管是以前生成的还是新生成的,都会被用来链接生成新的可执行文本编辑器程序。

规则大概是什么样的

[编辑]

一个简单的Makefile所包含的“规则”形式如下

目标 ...:先决条件
     命令
     ...
     ...

目标的名称一般与生成的文件名称一致(像可执行程序和OBJ文件)。目标的名称 也可以是要进行的动作,比如说“clean”(参见第四章第五节 伪目标)。

先决条件是用以生成目标的输入文件,而一个目标往往依赖于多个文件。

命令是make要执行的动作,而一个规则往往要多行命令。请注意:在每个命令前都必须输入一个Tab!一般粗心大意的人容易在这个问题上犯错。

通常有先决条件规则的命令会在先决条件被改动之后执行。但也有一些执行特殊命令的规则没有先决条件,比如说目标“clean”所在的规则就没有先决条件,该规则含有删除命令。

接下来规则会说明与特定规则关联的文件应该何时被操作,如何被操作。然后make执行先决条件中的命令,用以生成或更新目标。此外,规则还可以对何时与如何进行一个动作下令。参见第四章 编写规则

除了规则之外,Makefile文件也可以有其他内容,但一个简单的Makefile只需有规则即可。规则写出来会比模板中所写的略显复杂,但或多或少含有与其一致的部分。

一份简单的Makefile文件

[编辑]

下面有一份简单易懂的Makefile文件,该文件描述了各种依赖关系:可执行程序edit依赖于八个OBJ文件,八个OBJ文件依赖于八个C源代码文件和三个头文件。

在本例中,所有C源代码文件都包含了“defs.h”头文件,只有定义了编辑命令的C源代码文件才包含“command.h”头文件,而只有只有能改变编辑器缓冲区的低层C源代码文件才包含“buffer.h”头文件。

     edit : main.o kbd.o command.o display.o \
            insert.o search.o files.o utils.o
             cc -o edit main.o kbd.o command.o display.o \
                        insert.o search.o files.o utils.o
     
     main.o : main.c defs.h
             cc -c main.c
     kbd.o : kbd.c defs.h command.h
             cc -c kbd.c
     command.o : command.c defs.h command.h
             cc -c command.c
     display.o : display.c defs.h buffer.h
             cc -c display.c
     insert.o : insert.c defs.h buffer.h
             cc -c insert.c
     search.o : search.c defs.h buffer.h
             cc -c search.c
     files.o : files.c defs.h buffer.h command.h
             cc -c files.c
     utils.o : utils.c defs.h
             cc -c utils.c
     clean :
             rm edit main.o kbd.o command.o display.o \
                insert.o search.o files.o utils.o


本例用反斜线“\”将每个长行都分成两行,如此功能上与长行相同,而且减少了阅读长行的难度。

要用此Makefile生成可执行程序edit,请键入:

make

要用此Makefile删除目录中所有OBJ文件和可执行程序“edit”,请键入:

make clean

在本例中,目标包括,可执行程序“edit”以及OBJ文件“main.o”与“kdb.o”;先决条件也有许多,如“main.c”与“defs.h”;而每个“.o”文件都既为目标,又是先决条件;命令包括“cc -c main.c”与“cc -c kbd.c”。

若目标为文件,则该文件会在其先决条件被改动后重编译与链接。另外,会自动生成的先决条件首先更新。本例中,“edit”依赖于八个OBJ文件;OBJ文件“main.o”依赖于源代码文件“main.c”与头文件“defs.h”。

Shell命令跟随在目标与先决条件的下一行,该命令控制更新目标文件。每个命令行首必须要有Tab键入,以此区分命令行与Makefile中的其他行。(想想看,make全然不知命令是如何工作的,必须由程序员提供命令来更新目标。make所能干的只有执行程序员给出命令,然后再根据命令判断目标文件是否需要更新;如需要,更新之)。

目标“clean”不是一个文件,仅仅是动作的名称而已。因为这个规则不包含其他动作,而且也不是其他任何规则的先决条件,所以,make不会执行此处操作,除非被特别指定。请注意,此规则既非其他规则之先决条件,亦无先决条件,故此规则唯一目的为运行特定命令。目标若只含命令而与其他文件不相关联,则称此目标为伪目标。可参阅第四章第五节 伪目标,了解详情。另外,请参阅第五章第五节 命令中的报错信息,了解如何忽略rm或者其他命令的报错信息。

make如何执行一个Makefie的指令

[编辑]

在默认情况下,make的工作始于第一个目标(只要这个目标不是以“。”开头的)。此目标称为默认最终目标(如果不想使用这个默认规定,可以在命令行中输入“make 目标名”(参见第九章第二节 用以确定最终目标的参数)或者更改变量(参见第三章第六节 其它的特殊变量)“.DEFAULT_GOAL”以改变默认最终目标)。

上一节简单示例中,默认最终目标是更新可执行程序“edit”,故将其列入规则首位。

因此,输入以下命令:

make

make首先读取本目录的Makefile,而后开始运行第一个规则。在此例中,规则用以重链接“edit”;但在执行这条规则之前,make必须完成所有“edit”依赖的规则,这些规则就是那些OBJ文件;而每个OBJ文件都会运行自己的一套规则,这些规则会通过编译各自源代码的方式来更新每个“.o”文件。如果头文件或者源代码文件的修改时间比依赖其的OBJ文件要晚,或者OBJ文件不存在,那么重新编译就必须要进行。

那些非最终目标之所以会被运行,是因为最终目标与之相关。而与最终目标无关的规则就不会被执行,除非被特指(如“make clean”)。

在重编译一个OBJ文件之前,make会先更新其先决条件,先决条件包括源代码文件与头文件。此例中的Makefile并没有针对源代码文件与头文件的操作,另外这些“.c”与“.h”文件也不是规则的名称,故make在此不做任何事情。但make会自动更新自动生成的C程序,比如说由Bison或Yacc生成的,由此次规则生成的。

在编译完所有需要的OBJ文件后,make会决定是否重链接“edit”。如果“edit”文件不存在或者OBJ文件比“edit”文件要新,则重链接工作一定要作。如果OBJ文件是刚编译出的,其时间晚于“edit”文件,故此时“edit”文件要被重链接。

因此,如果更改了“insert.c”文件再运行make;make会先编译“insert.c”文件生成“insert.o”文件,再链接生成“edit”文件。若更改的是“command.h”,再运行make;make会编译生成“kdb.o”、“command.o”、“files.o”再链接生成“edit”。

用变量简化Makefile

[编辑]

在上例中,生成“edit”的OBJ文件组被列举了两次(这里在重复一次):

     edit : main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o
             cc -o edit main.o kbd.o command.o display.o \
                        insert.o search.o files.o utils.o


如此重复书写,容易造成错误;另外,若有新OBJ文件加入系统,本应可以只添加新文件而不考虑原有的OBJ文件。若采用变量协助工作,则既可减少出错几率,亦能简化Makefile。变量允许先对一段文本字符串定义,以后需要书写诸OBJ文件时,可以只输入变量而不需要把所有OBJ文件列出(参见第六章 如何使用变量)。

此处,一个习惯性做法是,在每个Makefile中加入名为“objects”、“OBJECTS”、“objs”、“OBJS”、“obj”或者“OBJ”的变量,用以列出全部OBJ文件的名称。在Makefile中,可以用如下方式定义变量“objects”:

 objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o


定义之后,文件时需要列出诸OBJ文件处可用“$(objects)”代替(参见第六章 如何使用变量)。

下面就是用变量来描述诸OBJ文件的Makefile,简单而完整:

objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     main.o : main.c defs.h
             cc -c main.c
     kbd.o : kbd.c defs.h command.h
             cc -c kbd.c
     command.o : command.c defs.h command.h
             cc -c command.c
     display.o : display.c defs.h buffer.h
             cc -c display.c
     insert.o : insert.c defs.h buffer.h
             cc -c insert.c
     search.o : search.c defs.h buffer.h
             cc -c search.c
     files.o : files.c defs.h buffer.h command.h
             cc -c files.c
     utils.o : utils.c defs.h
             cc -c utils.c
     clean :
             rm edit $(objects)

让make自己推算出要执行的命令

[编辑]

不必将每个编译C源代码文件的命令全部写出,因为make可以构造出这些命令:make有一个隐规则可通过命令“cc -c”生成与“.c”文件前缀名一致的“.o”文件的命令,比如将“main.c”编译成“main.o”的命令“cc -c main.c -o main.o”。此处可以把命令中写“.o”的部分省略。参阅第十章 使用隐规则

当“.c”文件以下例方法自动被使用时,“.c”文件会自动加入到先决条件中。在先决条件中省略“.c”文件的同时,编译命令也被省略了。

下面是个完整的例子。例子中包括了上面两种省略方法,而且也使用了上一节所说的“objects”变量。

  objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     
     main.o : defs.h
     kbd.o : defs.h command.h
     command.o : defs.h command.h
     display.o : defs.h buffer.h
     insert.o : defs.h buffer.h
     search.o : defs.h buffer.h
     files.o : defs.h buffer.h command.h
     utils.o : defs.h
     
     .PHONY : clean
     clean :
             rm edit $(objects)


本例与实际应用中Makefile相差无几(复杂化的“clean”规则在其他地方有所讨论。参见第四章第五节 伪目标第五章第五节 命令中的报错信息)。

隐规则使用简便,故尤为重要。读者会频繁见到隐规则的使用。

另一种格式的Makefile

[编辑]

当组成Makefile的全为规则时,可以用另一种形式的Makefile代替前面的。下面这种形式的Makefile全部以先决条件代替,此为全文:

     objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
     
     edit : $(objects)
             cc -o edit $(objects)
     
     $(objects) : defs.h
     kbd.o command.o files.o : command.h
     display.o insert.o search.o files.o : buffer.h


这里,“defs.h”为所有OBJ文件的先决条件;“command.h”与“buffer.h”为特定OBJ文件的先决条件。

不知这样紧凑的写法是否为一些人的爱好,但还是有一些人不喜欢这种写法,认为将一个目标跟与之对应的信息写在一起显得比较清楚。

清理目录的规则

[编辑]

编写Makefile所要写的不仅仅编译程序。Makefile还能做一些编译外的事情,比如说,如何将一个目录里的OBJ文件与可执行文件清理干净。

下面就是一例,演示如何用make以完成清除工作:

     clean:
             rm edit $(objects)


在实际应用中Makefile可能会写得更复杂,以应对突发事件,下为例:

.PHONY : clean
     clean :
             -rm edit $(objects)


本例的Makefile,可在有文件名为“clean”时避免混淆,还可在rm报错时仍旧执行。(参见第四章第五节 伪目标第五章第五节 命令中的报错信息

此类规则不可置于Makefile文首,这是由于一般不愿让其作为默认规则执行!故在前几节的完整Makefile示例中,用“edit”为默认规则,以便编译与重编译。

因为“clean”并非“edit”的先决条件,故此规则在make没有参数时不会运行。若要运行此规则,键入命令“make clean”(参见第九章 如何运行make)。

编写Makefile

[编辑]

make重编译系统的信息源于Makefile构建的数据库。

Makefile裡包含的是什么

[编辑]

“Makefile”文件应该取什么样的名字

[编辑]

包含其它的Makefile文件

[编辑]

“MAKEFILES”变量

[编辑]

“MAKEFILE_LIST”变量

[编辑]

其它的特殊变量

[编辑]

Makefile如可被make再执行

[编辑]

调用其它Makefile的重要技巧

[编辑]

make如何理解Makefile的内容

[编辑]

编写规则

[编辑]

规则的语法结构

[编辑]

先决条件的种类

[编辑]

在文件名中使用通配符

[编辑]

通配符示例

[编辑]

使用通配符的陷阱

[编辑]

“wildcard”函数

[编辑]

为先决条件搜索目录

[编辑]

“VPATH”:为所有先决条件搜索路径

[编辑]

“vpath”指令

[编辑]

目录搜索是如何进行的

[编辑]

编写带目录搜索功能的Shell命令

[编辑]

目录搜索与隐规则

[编辑]

为连接库而做的目录搜索

[编辑]

伪目标

[编辑]

无先决条件与命令的规则

[编辑]

用目标对应的空文件记录事件

[编辑]

特殊内建目标的名称

[编辑]

单规则使用多目标

[编辑]

单目标对应多规则

[编辑]

静态模式规则

[编辑]

静态模式规则的语法结构

[编辑]

静态模式规则与隐规则的比较

[编辑]

带双冒号的规则

[编辑]

自动生成先决条件

[编辑]

编写规则中的命令

[编辑]

命令的语法结构

[编辑]

将命令分为多行的方法

[编辑]

在命令中使用变量

[编辑]

输出信息命令

[编辑]

命令的执行

[编辑]

选择Shell

[编辑]

并行执行

[编辑]

命令中的报错信息

[编辑]

中断或强制结束make

[编辑]

递归使用make

[编辑]

make的变量是怎样工作的

[编辑]

与低层的make互传变量

[编辑]

与低层的make互传选项

[编辑]

“--print-directory”选项

[编辑]

定义封装的命令组

[编辑]

使用空命令

[编辑]

如何使用变量

[编辑]

变量的基本参考

[编辑]

变量的两种风格

[编辑]

变量高级功能参考

[编辑]

替换功能参考

[编辑]

可运算的变量名

[编辑]

变量如何获取其值

[编辑]

设置变量

[编辑]

向变量追加文本内容

[编辑]

“override”指令

[编辑]

逐字定义变量

[编辑]

来自环境变量的变量

[编辑]

目标特定变量的赋值

[编辑]

模式特定变量的赋值

[编辑]

Makefile的条件控制部分

[编辑]

条件示例

[编辑]

条件的语法结构

[编辑]

测试标志位的条件

[编辑]

文本操控函数

[编辑]

函数调用的语法结构

[编辑]

对字符串做替换与分解的函数

[编辑]

对文件名操作的函数

[编辑]

条件控制函数

[编辑]

“foreach”函数

[编辑]

“call”函数

[编辑]

“value”函数

[编辑]

“eval”函数

[编辑]

“shell”函数

[编辑]

控制make的函数

[编辑]

如何运行make

[编辑]

用以确定Makefile文件的参数

[编辑]

用以确定最终目标的参数

[编辑]

用其它功能来取代执行命令的参数

[编辑]

避免重新编译某些文件

[编辑]

初始变量

[编辑]

测试编译程序

[编辑]

选项摘要

[编辑]

使用隐规则

[编辑]

使用隐规则

[编辑]

隐规则章目

[编辑]

隐规则使用的变量

[编辑]

隐规则链

[编辑]

定义与重定义模式的规则

[编辑]

模式的规则介绍

[编辑]

模式的规则示例

[编辑]

自变变量

[编辑]

模式是如何匹配的

[编辑]

匹配一切的模式规则

[编辑]

取消隐规则

[编辑]

定义去除依赖的默认规则

[编辑]

过时的附加规则

[编辑]

隐规则的搜索算法

[编辑]

使用make更新归档文件

[编辑]

归档成员作为目标

[编辑]

为归档成员目标而设的隐规则

[编辑]

更新归档字符目录

[编辑]

使用归档的风险

[编辑]

归档文件的附加规则

[编辑]

GNU make的特色功能

[编辑]

GNU make与标准make不兼容的部分

[编辑]

GNU make的一些不成文规定

[编辑]

Makefile的通用习惯

[编辑]

Makefile的工具

[编辑]

指定变量的命令

[编辑]

安装目录的变量

[编辑]

为用户设计的标准目标

[编辑]

安装命令的条目

[编辑]

附录

[编辑]

A 快速参考

[编辑]

B 常见错误

[编辑]

C 一个复杂的Makefile实例

[编辑]

下面是一段为GNU tar程序设计的Makefile代码,相当复杂。

由于“all”是第一个目标,所以它也是默认目标。下面这个程序段有一个非常有趣的特色,这个特色就是源代码程序中的“testpad.h”文件是一个由名叫testpad程序自动生成的,而这个testpad又是又是由“testpad.c”这个程序经过编译产生的。

如果您输入“make”或者“make all”,则make会让名为“tar”的目标运行从而开始编译指令,会让名为“rmt”的目标运行从而生成一个远程磁带数据存取的守护程序,会让名为 “tar.info”的目标运行从而生成INFO文档。

如果您输入“make install”,那么make就执行的就不仅仅只是“tar”、“rmt”和“tar.info”这三个目标了,还会执行相应的安装操作。< /br>
如果您输入“make clean”,那么make会清除所有的“.o”文件,以及“tar”、“rmt”、“testpad”、“testpad.h”和“core”这些文件。

如果您输入“make distclean”,那么make除了会清除“make clean”会清除的文件外,还会清除“TAGS”、“Makefile”和“config.status”这些文件(虽然不明显,但下面这个 makefile和“config.status”都是用户通过tar发行版中的“configure”文件生成的,这个“configure”文件这里没有列出)。

如果您输入“make realclean”,那么make除了清除“make distclean”会清除的文件外,还会清除由“tar.info”生成的INFO文档。

# Generated automatically from Makefile.in by configure.
     # Un*x Makefile for GNU tar program.
     # Copyright (C) 1991 Free Software Foundation, Inc.
     
     # This program is free software; you can redistribute
     # it and/or modify it under the terms of the GNU
     # General Public License ...
     ...
     ...
     
     SHELL = /bin/sh
     
     #### Start of system configuration section. ####
     
     srcdir = .
     
     # If you use gcc, you should either run the
     # fixincludes script that comes with it or else use
     # gcc with the -traditional option.  Otherwise ioctl
     # calls will be compiled incorrectly on some systems.
     CC = gcc -O
     YACC = bison -y
     INSTALL = /usr/local/bin/install -c
     INSTALLDATA = /usr/local/bin/install -c -m 644
     
     # Things you might add to DEFS:
     # -DSTDC_HEADERS        If you have ANSI C headers and
     #                       libraries.
     # -DPOSIX               If you have POSIX.1 headers and
     #                       libraries.
     # -DBSD42               If you have sys/dir.h (unless
     #                       you use -DPOSIX), sys/file.h,
     #                       and st_blocks in `struct stat'.
     # -DUSG                 If you have System V/ANSI C
     #                       string and memory functions
     #                       and headers, sys/sysmacros.h,
     #                       fcntl.h, getcwd, no valloc,
     #                       and ndir.h (unless
     #                       you use -DDIRENT).
     # -DNO_MEMORY_H         If USG or STDC_HEADERS but do not
     #                       include memory.h.
     # -DDIRENT              If USG and you have dirent.h
     #                       instead of ndir.h.
     # -DSIGTYPE=int         If your signal handlers
     #                       return int, not void.
     # -DNO_MTIO             If you lack sys/mtio.h
     #                       (magtape ioctls).
     # -DNO_REMOTE           If you do not have a remote shell
     #                       or rexec.
     # -DUSE_REXEC           To use rexec for remote tape
     #                       operations instead of
     #                       forking rsh or remsh.
     # -DVPRINTF_MISSING     If you lack vprintf function
     #                       (but have _doprnt).
     # -DDOPRNT_MISSING      If you lack _doprnt function.
     #                       Also need to define
     #                       -DVPRINTF_MISSING.
     # -DFTIME_MISSING       If you lack ftime system call.
     # -DSTRSTR_MISSING      If you lack strstr function.
     # -DVALLOC_MISSING      If you lack valloc function.
     # -DMKDIR_MISSING       If you lack mkdir and
     #                       rmdir system calls.
     # -DRENAME_MISSING      If you lack rename system call.
     # -DFTRUNCATE_MISSING   If you lack ftruncate
     #                       system call.
     # -DV7                  On Version 7 Unix (not
     #                       tested in a long time).
     # -DEMUL_OPEN3          If you lack a 3-argument version
     #                       of open, and want to emulate it
     #                       with system calls you do have.
     # -DNO_OPEN3            If you lack the 3-argument open
     #                       and want to disable the tar -k
     #                       option instead of emulating open.
     # -DXENIX               If you have sys/inode.h
     #                       and need it 94 to be included.
     
     DEFS =  -DSIGTYPE=int -DDIRENT -DSTRSTR_MISSING \
             -DVPRINTF_MISSING -DBSD42
     # Set this to rtapelib.o unless you defined NO_REMOTE,
     # in which case make it empty.
     RTAPELIB = rtapelib.o
     LIBS =
     DEF_AR_FILE = /dev/rmt8
     DEFBLOCKING = 20
     
     CDEBUG = -g
     CFLAGS = $(CDEBUG) -I. -I$(srcdir) $(DEFS) \
             -DDEF_AR_FILE=\"$(DEF_AR_FILE)\" \
             -DDEFBLOCKING=$(DEFBLOCKING)
     LDFLAGS = -g
     
     prefix = /usr/local
     # Prefix for each installed program,
     # normally empty or `g'.
     binprefix =
     
     # The directory to install tar in.
     bindir = $(prefix)/bin
     
     # The directory to install the info files in.
     infodir = $(prefix)/info
     
     #### End of system configuration section. ####
     
     SRC1 =  tar.c create.c extract.c buffer.c \
             getoldopt.c update.c gnu.c mangle.c
     SRC2 =  version.c list.c names.c diffarch.c \
             port.c wildmat.c getopt.c
     SRC3 =  getopt1.c regex.c getdate.y
     SRCS =  $(SRC1) $(SRC2) $(SRC3)
     OBJ1 =  tar.o create.o extract.o buffer.o \
             getoldopt.o update.o gnu.o mangle.o
     OBJ2 =  version.o list.o names.o diffarch.o \
             port.o wildmat.o getopt.o
     OBJ3 =  getopt1.o regex.o getdate.o $(RTAPELIB)
     OBJS =  $(OBJ1) $(OBJ2) $(OBJ3)
     AUX =   README COPYING ChangeLog Makefile.in  \
             makefile.pc configure configure.in \
             tar.texinfo tar.info* texinfo.tex \
             tar.h port.h open3.h getopt.h regex.h \
             rmt.h rmt.c rtapelib.c alloca.c \
             msd_dir.h msd_dir.c tcexparg.c \
             level-0 level-1 backup-specs testpad.c
     
     .PHONY: all
     all:    tar rmt tar.info
     
     .PHONY: tar
     tar:    $(OBJS)
             $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
     
     rmt:    rmt.c
             $(CC) $(CFLAGS) $(LDFLAGS) -o $@ rmt.c
     
     tar.info: tar.texinfo
             makeinfo tar.texinfo
     
     .PHONY: install
     install: all
             $(INSTALL) tar $(bindir)/$(binprefix)tar
             -test ! -f rmt || $(INSTALL) rmt /etc/rmt
             $(INSTALLDATA) $(srcdir)/tar.info* $(infodir)
     
     $(OBJS): tar.h port.h testpad.h
     regex.o buffer.o tar.o: regex.h
     # getdate.y has 8 shift/reduce conflicts.
     
     testpad.h: testpad
             ./testpad
     
     testpad: testpad.o
             $(CC) -o $@ testpad.o
     
     TAGS:   $(SRCS)
             etags $(SRCS)
     
     .PHONY: clean
     clean:
             rm -f *.o tar rmt testpad testpad.h core
     
     .PHONY: distclean
     distclean: clean
             rm -f TAGS Makefile config.status
     
     .PHONY: realclean
     realclean: distclean
             rm -f tar.info*
     
     .PHONY: shar
     shar: $(SRCS) $(AUX)
             shar $(SRCS) $(AUX) | compress \
               > tar-`sed -e '/version_string/!d' \
                          -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \
                          -e q
                          version.c`.shar.Z
     
     .PHONY: dist
     dist: $(SRCS) $(AUX)
             echo tar-`sed \
                  -e '/version_string/!d' \
                  -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \
                  -e q
                  version.c` > .fname
             -rm -rf `cat .fname`
             mkdir `cat .fname`
             ln $(SRCS) $(AUX) `cat .fname`
             tar chZf `cat .fname`.tar.Z `cat .fname`
             -rm -rf `cat .fname` .fname
     
     tar.zoo: $(SRCS) $(AUX)
             -rm -rf tmp.dir
             -mkdir tmp.dir
             -rm tar.zoo
             for X in $(SRCS) $(AUX) ; do \
                 echo $$X ; \
                 sed 's/$$/^M/' $$X \
                 > tmp.dir/$$X ; done
             cd tmp.dir ; zoo aM ../tar.zoo *
             -rm -rf tmp.dir

D GNU 自由文档许可证

[编辑]

GNU 自由文档许可证