GNU Make 使用手册(中译版)
GNU make Version 3.79
April 2000
Richard M. Stallman and Roland McGrath
目录
1 make概述
了解这份手册的详细阅读方法,请访问此处
1.2 问题和BUG
详细解析Makefile文件的功能与作用
这个项目参考了一个简明扼要的Makefile文档
2.3 使用make工具管理Makefile文件的步骤
第2.4节 [通过引入变量优化Makefile流程](https://www.cnblogs.com/AbeDay/p/5026844.html#_2.4_ʹ�ñ���_BCF2_��makefile_�ļ� "第2.4节 [通过引入变量优化Makefile流程"])
2.5支持make推断功能
2.7 在目录中移除文件的规定
第三步是搭建Makefile
3.2 工作流程文件的命名规范
3.3 除了现有的Makefile文件外
变量MAKEFILES
标题为【Make files variable_make_files
基于Makefile的文件重构流程
该工具通过解析MAKE指令来获取项目依赖项的流程
4 编写规则
4.1规则的语法
在文件名设置方面,请注意应用通配符机制以实现多名称匹配功能
4.2.1通配符例子
4.2.2 常见错误:在实际应用中常遇到的通配符相关问题及解决方法
该野卡工具[...]
4.3 在目录中进行依赖查找
4.3.1VPath:所有的相关搜索目录
4.3.2vpath指令
4.3.4 撰写搜索指定文件夹的Shell指令
4.3.5[目錄搜索與潛規則](https://www.cnblogs.com/AbeDay/p/5026844.html#_4.3.5_�ļFE����Ѱ���� BA AC %B9%E6 %D4=F2 "目錄搜索與潛規則)
4.3.6[连接工具的查找目录](https://www.cnblogs.com/AbeDay/p/5026844.html#_4.3.6_���ӿ����ļ�����Ѱ "连接工具的查找目录)
4.4假想目标
4.5 无外部命令或依赖的规则 https://www.cnblogs.com/AbeDay/p/5026844.html#_4.5_û�������"F 没有命令或依赖的规则
采用空白目錄文件來記錄事件
该对象内部定义的关键字或变量名称是一个专有名词
本节介绍的是多目标规则的设计与实现机制
4.9具有多条规则的目标
4.10 静置形式规范
4.10.1固定格式规范的规范
4.10.2 固定样式与潜在规范
4.10.2 固定样式与潜在规范
4.11双冒号规则
自动生成依赖关系
第五项:按照规定进行操作[在规则中使用命令]https://www.cnblogs.com/AbeDay/p/5026844.html#_5[D4 DA B9 E6 4F 2D 60 CA B9 D3 3C FC C1 EE] '在规则中使用命令'
5.1命令回显
5.2执行命令
5.3并行执行
5.4命令错误
5.5停止运行或终止make
第5.6节通过递归机制实现的make函数调用
5.6.1参数MAKE的操作流程
5.6.2 [与子MAKE进行数据交换的关键参数](https://www.cnblogs.com/AbeDay/p/5026844.html#_5.6.2����MAKE��ͨѶ���� "与子MAKE进行数据交换的关键参数)
5.6.3子make的通信设置
5.6.4 [--print-directory'设置](https://www.cnblogs.com/AbeDay/p/5026844.html#_5.6.4_%A1%AE--print-directory%A1%AF%D1%A1%CF%EE "--print-directory'设置 ")
5.7指定特定顺序指令
调用空命令
6 使用变量
6.1 标识符的操作核心:变量引用基础
6.2 变量的核心特征
6.3 高阶变量引用机制
6.3.1替换引用
6.3.2嵌套变量引用
6.4变量取值
6.5设置变量
用于将变量的值附加文本
6.7override指令
详细阐述多行变量的定义 https://www.cnblogs.com/AbeDay/p/5026844.html#_6.8ª������ %C9%FA%C7%CA
6.9环境变量
6.10 明确的目标变量数值
该明确的目标数值被设定为...
6.11 [该特定格式变量对应的数值](https://www.cnblogs.com/AbeDay/p/5026844.html#_6.11_�ض���ʽ������ֵ "特定格式变量的值)
-
Makefile文件中的条件语句
-
Makefile文件中的条件判断逻辑
7.1条件语句的例子
7.2 条件语句的语法)
7.3测试标志的条件语句
8 [转换函数](https://www.cnblogs.com/AbeDay/p/5026844.html#_8_�ı�ת���� %CAFD "文本 转换 函数")
第8.1节讨论了[代码]
8.2 字符串操作与分析模块
第8.3节名称函数)
8.4 函数 foreach(https://www.cnblogs.com/AbeDay/p/5026844.html#_8.4_并行遍历数组)
8.5函数if
8.6函数call
功能组件
功能组件
8.8函数shell
8.9[实现对Make功能的管理](https://www.cnblogs.com/AbeDay/p/5026844.html#_8.9_����make�ĺ��� "实现对Make功能的管理)
9 运行make
该链接用于指定Makefile文件的相关设置,并提供具体的信息以供参考。其中具体信息包括链接位置的具体信息。
9.2 明确核心目的并设置相关的配置项(D6:B8:6A8:D7:EE:D6:D5:C4:BF:B1:EA:B5:C4:B2:CE:CA:FD "D6:B8:6A8:D7:EE:D6:D5:C4 BF B1 EA B5 C4 B2 CE CA FD")
此功能可替代地完成指令的执行过程
9.4[避免重复进行编译操作](https://www.cnblogs.com/AbeDay/p/5026844.html#_9.4�������±���һЩ�ļ� "避免重复进行编译操作)
9.5变量重载
9.7选项概要
第10项 遵循隐式规则
根据链接中的详细说明应用隐式规则以深入探讨这一策略
第十章第二节《隐含规则目录》
在第十点中使用了隐藏规则来处理变量
第10.4节 隐式的规则链条
10.5定义与重新定义格式规则
10.5.1 格式规范概述
10.5.2格式规则的例子
10.5.3自动变量
10.5.4格式匹配
10.5.5万用规则
第十条第五节第六条规避隐式条款
该系统采用创新的方法来制定适用于所有场景的缺省规则
10.7 不再适用的后缀形式)
该算法旨在通过数据分析自动识别隐藏模式与关联性。
采用make工具优化文档文件更新流程
[档案成员目标] 《档案管理目标》
档案成员的目标中的潜在规定
该文探讨了在实际操作中可能出现的档案使用问题及其潜在风险(https://www.cnblogs.com/AbeDay/p/5026844.html#_11.3_ʹ�õ�����Σ��)
在第11.4节中探讨其使用规范遵循统一的标准格式
12 GNU make的特点
13 [相容性问题与丧失的优势](https://www.cnblogs.com/AbeDay/p/5026844.html#_13_�������Ժ�ȥ����(CC%D8%B5%E3 "相容性问题与丧失的优势"))
遵循一定的规范步骤以确保代码的一致性和可维护性
遵循通用规范
本节将介绍如何利用makefile文件来优化项目管理流程,并附有详细的构建工具链接。
第十四章第三节标识命令的变量名或标识名称
14.4 配置文件位置参数
14.5(用户的预期目标)(https://www.cnblogs.com/AbeDay/p/5026844.html#_14.5�û���Ŀ�� "用户的预期目标")
14.6部署指令分类
15快速参考
16 导致make函数出现错误
第17号引用:较为复杂的Makefile文档案例
Appendix Term-to-Translation Reference Table
1 Make 概述
该工具可自动决定一个大程序中哪些文件需要进行重编译,并能够发布一系列指令来执行这一系列重编译操作。此版本GNU Make使用手册由Richard M. Stallman和Roland McGrath编写,源自Paul D. Smith撰写的V3.76版本。
该工具可自动决定一个大程序中哪些文件需要进行重编译,并能够发布一系列指令来执行这一系列重编译操作。此版本GNU Make使用手册由Richard M. Stallman和Roland McGrath编写,源自Paul D. Smith撰写的V3.76版本。
GNU Make符合IEEE Standard 1003.2-1992 (POSIX.2) 6.2章节的规定。
由于C语言程序更具代表性, 因此我们选择以该类型作为示例进行说明. 然而, Make工具不仅局限于处理C语言源代码, 它还能管理那些能够在Shell脚本中被调用的各种编程语言. 实际上, GNU Make的应用不仅局限于编程领域, 而是可以用于任何一种当某些源文件发生更改时会自动触发相应依赖文件重新编译或更新的任务.
如果要使用Make工具进行构建管理,则需要编写一个名为Makefile的源文件。该源文件的作用是阐述程序内部文件之间的相互依赖关系,并提供每个可执行程序所需的更新指令。在程序设计中,在可执行程序的基础上进行修改时需要考虑以下几点:首先需要确保所有相关的源代码都已经正确编译并生成对应的二进制目标代码;其次在构建过程中需要注意依赖关系的一致性与完整性;最后还需要配置好Makefile中的目标链接规则以保证构建过程顺利进行。
一旦一个合适的Makefile文件存在,在shell命令行下进行简单的操作:
make
就能被系统能够完成所有的必要的重新编译任务。
这个程序被负责决定哪些文档需要重新编译。
对于这些文档, Make负责依据Makefile中的数据和时间戳发布指令来进行。
具体操作请参考** 运行Make** 章节.
1.1怎样阅读本手册
若您目前对此一无所知或者仅需了解关于Make的基本信息,请参考前面几章作为基础入门材料。而前面几章则主要提供基础性的知识,在后面的内容中则深入探讨了更为专业的技术细节。
如果您对其他Make程序有较为深入的理解,请参考《 GNU Make的特点》以及《不兼容性和失去的特点》两章内容,《 GNU Make的特点》一章详细阐述了其对make程序的增强功能,《不兼容和失去的特点》一章则深入分析了其他Make程序所具备特征而GNU Make未能继承的原因。
对于快速浏览者,请参阅** 选项概要、快速参考** 和** 内建的特殊目标名** 部分。
1.2问题和BUG
如您有任何关于GNU Make的问题或认为发现了其缺陷,请告知开发者。我们承诺不保证所有功能都能正常工作但我们力求修复其中存在的问题。在提交BUG前请确认是否确实存在此类问题即使文档未能清晰说明操作步骤也请告知此问题
在您报告或自行更正BUG之前,请将其分离出来;即,在使问题得以暴露的前提下尽可能地缩减Makefile文件。随后,请将此Makefile文件与Make系统提供的精确结果一并发送给我们;同时,请说明您期望得到什么信息。以上操作有助于我们判断问题是否源于文档编写。
一旦您找到一个精确的问题,请给我们发E-mail,我们的E-mail地址是:
请在邮件中提及您所使用的GNU Make软件的版本信息。建议您通过执行命令'make --version'来获取软件 versions. 请您告知有关您的硬件配置及操作系统的信息. 如果方便,请考虑一并提交与软件配置相关的config.h文件(该文件有配置过程产生)。
2 Makefile文件介绍
一个被称为Makefile的Make程序必须要有文件来指示其如何运行。通常情况下,在大多数情况下(省略),该文件告诉编译器如何将源代码转换为目标程序。
在本章中,我们将探讨一个相对简单的Makefile文档。这份文档将阐述如何利用8个C源代码文件与3个包含头信息的头文件来生成一个文本编辑器。通过使用 Makefile 文件结构化的方式管理所需的指令行工具与脚本,在编译阶段实现自动化处理流程。如需深入学习更多关于复杂 Makefile 文件的知识,请参考《复杂 Makefile 文件示例》章节。
每当Make对这个编辑器进行重构建时
2.1 规则的格式
一个简单的Makefile文件包含一系列的“规则”,其样式如下:
目标(target)…: 依赖(prerequiries)…
…
…
目标(target)通常是用于生成的文件名,在这种情况下可能指可执行文件或OBJ格式的对象文件名。此外,目标也可能代表一种执行的操作名称,例如'clean'(有关详细信息,请参阅** 假想目标** 节)。
依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。
Make执行的动作即为命令。一个规则可能包含多个命令且每个单独占据一行。注意:每条指令行必须以Tab字符开头这是操作时容易犯错误的地方
一般情况下,在发生依赖变化时,则必须执行相应的命令序列以操作相关依赖项及服务信息以实现更新或新建目标实体。值得注意的是,并非所有执行更新操作的规则都需要依赖关系支持。举个例子来说,在涉及与特定对象'clern'相关的删除指令时所建立的规则就无需依赖。
一般情况下这些规则旨在说明如何以及何时修复特定文件而这些特定文件通常是该详尽规划的目标对象Make程序首先执行命令来处理所有依赖项以确保能够正确地创建或更新目标文件当然在某些情况下这些规划也可用来描述如何以及何时执行某个操作请参考** 编写规划** 一章获取详细信息
一个Makefile文件不仅可以包含除了规则以外的其他内容,而且一个简单的Makefile文件只需拥有规则即可。尽管这里的示例规则可能不如真实情况复杂得多,但格式却始终如一。
2.2一个简单的Makefile文件
一个相对简单的Makefile实现方案用于构建名为文本编辑器(Edit)的应用程序此方案实现了文本编辑器的基本功能模块由8个编译好的OBJ对象文檔构成这些OBJ对象依赖于8個C源代码文檔以及3個包含頭信息的 header files
在这个例子中,每个C语言源代码都导入了名为'defs.h'的标准库;然而仅用于执行编辑操作的源代码则导入了'command.h';同时为了管理缓存数据仅有的修改操作由特定模块负责。
采用反斜杠分隔符将每个长段落划分为两段或多段;这些部分实际上等效于一个整体;其目的仅在于提高可读性。
使用Makefile文件创建可执行的称为‘edit’的文件,键入:make
使用Makefile文件从目录中删除可执行文件和目标,键入:make clean
在该Makefile示例中描述了一个项目结构体。其中包含可执行程序edit以及多个OBJ对象文件(如main.o与kdb.o)。这些代码资源的构建依赖于基础的C源代码(如main.c)以及相关的头文件(如def.h)。值得注意的是,在本项目中每个OBJ对象文件同时 serves as both a target and a dependency. 因此,在构建过程中会使用cc编译器针对main.c生成main.exe,并针对kbd.c生成kdb.exe.
若目标为单一文件,则其任一依赖发生变化时将导致项目重编译并链接。任何指令的第一字符须为'Tab'符号以便将Makefile中的指令与旁系各行区分开来。(此外,请务必记住:Make并不了解操作指令的具体执行方式它仅能为您提供符合当前设置的最佳更新建议而无法预测特定操作的影响结果因此完全依赖于开发者遵循所设定的操作规范执行相应操作以确保项目顺利运行。)
目标'clean'不是一个文件,仅仅是表示某个操作的动作名称。通常情况下,在规则设置中'clean'这个动作不会被执行执行,并且目标'clean'也不需要任何依赖项。一般情况下,默认情况下make系统不会自动执行'clean'命令。这些规则本身不依赖任何条件或参数,它们存在的主要目的是为了实现特定的操作指令。这些无需依赖的特殊动作被统称为假想操作符或虚拟操作符;详细内容请参阅**假想操作符相关内容;有关如何避免make错误导致rm等命令被忽略的知识,可参阅命令错误**部分。
2.3 make处理makefile文件的过程
在默认情况下, make以第一个假想项目(其名称前需附加'.'号)的形式启动.这一项目通常被称作默认最终项目(即make用于更新项目的那个项目的具体信息,请参阅**指定最终项目的参数**一节).
如上一节所述的简单示例中,默认最终目标设定为此可执行文件名'edit'。因此我们将其列为第一条规则。当您输入以下命令时:
make
make就会读当前目录下的makefile文件,并开始处理第一条规则。在本例中,第一条规则是连接生成‘edit’,但在make全部完成本规则工作之前,必须先处理‘edit’所依靠的OBJ文件。这些OBJ文件按照各自的规则被处理更新,每个OBJ文件的更新规则是编译其源文件。重新编译根据其依靠的源文件或头文件是否比现存的OBJ文件更‘新’,或者OBJ文件是否存在来判断。
其他规则的处理取决于它们的目标是否与默认最终目标存在关联。当某些规则与默认最终目标没有关联,则这些规则通常不会被执行;然而,在特殊情况下(例如通过运行make clean命令),即使没有关联也会被强制执行。
在编译之前进行OBJ文件重新编译时,在编译之前阶段的Make工作流程会先对依赖的C语言源文件和C语言头文件进行核对。如果这些依赖性资源不是由任何特定规则所指定的目标,则Make系统将不会对其做任何处理工作。此外,在编译阶段中,默认情况下Make系统还具备自动生成C语言源程序的能力,并且这需要特定的生成规则来实现(例如基于Bison或Yacc生成相应的C语言源代码)。
完成对 OBJ 文件的重新编译(必要时)后,系统将决定是否创建新的 edit 可执行文件。如果 edit 可行文件尚未创建或者其中任何一个 OBJ 文件较已有的 edit 可行文件更为新,则系统将创建新的 edit 可行文件。
当我们修改了'insert.c'源文件后,并非直接运行编译器进行操作。相反地,在我们进行以下步骤:首先将修改后的源代码保存好后,在终端中输入'make'命令进行操作。这时编译器会自动重新编译该源文件并生成对应的可对象文件。随后会连接这些可对象文件以生成新的可执行文件'edit'。同样的道理适用于对'hkdebug.c'等其他相关源代码的处理:当我们修改了'hkdebug.c'之后,在终端中输入'make'命令会触发一系列操作流程:编译器会重新处理并生成相关的中间目标代码,并最终连接生成新的'db调试版可执行程序'等结果输出。
2.4使用变量简化makefile文件
在我们提供的示例中,在'edit'的生成逻辑中将每一个OBJ文件重复列举了两次,在此再次强调。
在进行两次列举时可能存在错误的可能性。例如,在系统中添加一个新的OBJ文件时,请注意很可能会在一个需要列举的地方不小心添加了它,在另一个地方则可能遗漏了这个内容。为了简化makefile文件并避免上述可能出现的问题,请考虑引入变量来代替重复的内容。通过引入变量来简化makefile文件并能够在多个位置替换相同的字符串(具体内容请参考** 使用变量** 小节)。
在makefile文件中采用名为objects, OBJECTS, objs, OBJS, obj或`OBJ的变量来表示所有OBJ文件是约定俗成的做法。
在这个makefile文件中我们定义了名为objects的变量及其格式说明。
然后,在所有需要列出OBJ文件的地方里
rm edit $(objects)
2.5 让make推断命令
无需显式编写命令即可完成C语言源程序的编译工作。因为make系统会自动推导出相应的操作指令以实现目标。具体而言(make)系统会默认采用类似'cc -c main.c -o main.o'这样的指令模式来处理'CMakeLists.txt'中的说明条目:即通过'CC –c'指定使用的编译器并完成'C'源文件到'O'对象文件的名字转换操作。例如在实际应用中当运行'make'时系统会自动执行上述提到的操作从而生成所需的'OBJ'文件形式的结果状态而无需制定针对'OBJ'文件的具体更新规则等有关配置参数的要求。
只要C语言源程序能自动生成编译指令,则它们也能无需人工干预地加入到项目依赖中。这样我们就可以避免在项目依赖中编写C语言源代码,并无需手动编写相关指令。下面是一个使用隐含规则和变量objects生成完整makefile文件的例子:
这是一个具体编写makefile文件的例子。(其中与目标‘clean’相关的复杂情况在其他地方详细说明)具体内容可参考** 假想目标** 和** 命令错误** 两个章节。虽然隐含规则非常方便但在makefile文件中频繁使用。
这是一个具体编写makefile文件的例子。(其中与目标‘clean’相关的复杂情况在其他地方详细说明)具体内容可参考** 假想目标** 和** 命令错误 **两个章节。
虽然隐含规则非常方便但在makefile文件中频繁使用。
2.6 另一种风格的makefile文件
那时,在运用隐含规则创建OBJ文件时,在makefile中采用另一类配置方式同样是有效的。在这一类配置方式中,则可以通过依赖分组来替代目标分组的方式进行组织。以下是按照这种配置方式编写的makefile示例:
这里的defs.h是一个被所有OBJ文件所共享的依赖项;command.h与bufffer.h则被特定列出的一些OBJ文件所共享作为依赖。
虽然按照这种风格编写makefile文件更有特色:makefile文件更为简洁, 但有人认为将每个目标的信息集中在一起会更加清晰易懂却并不赞成这种做法.
2.7 在目录中删除文件的规则
编译程序并非唯一由make规则控制的任务。通过Makefile文件可以指导make执行除编译之外的任务。例如,在删除OBJ和目标(.obj)以及可执行文件时(如.exe),应确保目录保持整洁。以下将介绍如何利用make规则实现这些操作的具体方法:
rm edit $(objects)
为了避免未能预见的情况发生,在实际应用场景中应编写复杂而细致的规则
-rm edit $(objects)
这种机制能够避免由于存在以'clean'命名的文件而导致系统混乱,并且使得在执行rm命令时出现问题(具体参见**假想目标及命令错误**两节内容)。
此类规则不应放置在makefile文件的开头位置,并可能导致其被默认设为最终目标。为了实现类似我们之前的makefile文件配置方式,在编译更新edit可执行程序时将其设定为此处定义的目标变量。
3 编写makefile文件
make编译系统依据的信息来源于称为makefile文件的数据库。
3.1 makefile文件的内容
makefile文件由五个部分构成:具体规则(详细规定了这些方面的潜在规定)、隐含规则(潜在规定)、定义变量(变量赋值)、指令(操作指令)和注释(备注信息)。这些详细规定与潜在规定及操作指令将在后续章节中被介绍。
l 具体规则 涉及说明其如何重新生成称为规则目标的一个或多个文件的过程。它列出了该规则所依赖的文件,并被称为这些文件中的一个或多个。具体规则还可能包含创建或更新这些依赖项所需的命令。如需了解详细信息,请参阅** 编写规则** 一章
l 隐含规则 是用来说明何时或如何重新生成与原有文件名相同的系列文件的过程及其相关操作指令的机制。其目标在于根据与自身名称相同的其他文件进行创建或更新操作,并为此提供了相应的指令。详细信息可参考前述“使用隐含规则”部分。
将变量赋值为一个固定的字符串以便在后续的文件操作中替换该固定字符串特别指出,在makefile文件中每个变量应单独占一行上一章提供的示例中引入了objects变量(具体内容请参考** 使用变量简化makefile文件** 一节)
l 这是一个负责使用make文件根据指定的操作系统环境配置制作相应软件包的操作指令。这些涵盖了以下几点:
n 读其它makefile文件(详细内容参见** 包含其它的makefile文件** )。
n 确定(基于变量值)是否使用或不考虑makefile文件中的某些内容(详细内容参阅** makefile文件的条件语句** 一节)。
n 注释 多字段或复杂数组类型的变量定义(详细内容参见** 定义多行变量** 一节)。
l 以#开头的行属于注释内容。这些注释行会被make工具在处理时忽略执行操作。特别地,在某些情况下如果一个注释行末尾以\结束,则表示下一行内容也会被视为该注释的一部分而继续作为同一段的延续进行处理。需要注意的是,在define指令所包含的指令块内部是可以包含这样的连续性标记来实现多段式标注效果的。
3.2 makfile文件的命名
在默认情况下, 当程序 make 搜索 makefile 文件时, 该程序试图搜索具有以下名称的文件: 包括名称分别为 'GNUmakefile'、'makefile' 以及 'Makefile' 的文件。
一般情况下,在编写项目时您应该将您的makefile文件命名为'-makefile'或'Makefile'。(我们强烈推荐采用'Makefile'作为文件名因为它通常会出现在目录列表的前面紧随其他重要文件如'README'等)。
尽管首先在搜索目录中发现了一个名为'GNUmakefile'的文件但出于兼容性考虑我们并不推荐使用它除非您的项目是专为GNU make设计的在这种特殊情况下您必须使用该名称。
当make无法识别具有前述名称的任何文件时,则该系统将完全不依赖于任何makefile文件。这意味着在缺乏自动检测的情况下(您必须)通过手动指定命令参数来明确给出构建的目标,并让系统依靠其内部预设规则来推断如何重构这些依赖关系)。详细内容参见** 使用隐含规则** 一节。
建议采用'−f'或'−−file'参数指定make...如果您同时设置多个'−f'或'−−file=...'参数,则表示将按具体顺序依次作用于这些make...若同时设置多个'−f'或'−−file=...'参数,则表示将按具体顺序依次作用于这些make...将不再自动检查是否存在名为GNU---------++++++++++++++++
3.3 包含其它的makefile文件
include指令指示make暂时不读取当前的makefile文件,并在完成指定的makefile文件处理后继续执行后续操作。需要注意的是,在源码编辑器界面中该指令应单独占用一行并遵循特定编码规范。
include filenames...
filenames可以包含shell文件名的格式。
在include命令行界面中,在每行开头允许存在多余的空格;但make处理时会忽略这些空白字符;请注意该行不得以Tab字符开头(因为以Tab开头的一行会被视为命令行);文件名与include命令之间以及两个文件名之间均需留有空格分隔;多余的空白部分make会自动去除;此外,在每行末尾可附加带有‘#’符号开头的注释说明;文件名可能包含变量或函数调用名称,在处理过程中它们会被make系统自动展开(相关内容请参考** 使用变量** 一节)。
举个例子来说吧,在这种情况下
include foo *.mk $(bar)
和‘include foo a.mk b.mk c.mk bish bash’等价。
When encountering an include instruction, make will temporarily halt reading the current makefile file, then proceed to sequentially read each of the listed makefiles. After finishing, it will then resume reading the current makefile file from where the include instruction was located.
一种常见的情况是多个程序各自独立地拥有一个makefile文件,在这种情况下它们必须具备一组相同的变量定义(参见** 设置变量** ),或者一组相同的格式规范(参见** 定义与重新定义格式规则** )。
另外一种使用include指令的场景是当使用include指令时会自动从源文件为目标生成相应的依赖关系。也就是说,在这种情况下这些依赖会被整合到目标目标构建过程中。相比于传统做法——将所有依赖直接添加到主makefile的后端——这种处理方式更加高效
若makefile文件名不以斜杠开头,并且在当前目录中未找到,则应探索其他目录。首先搜索以选项--include-dir指定的目录。然后依次搜索以下目录(若存在):包括prefix子目录中的include文件夹(通常位于/usr/local(include),接着是/usr(gnu)/include、$prefix(include)以及其他位置如/usr(include)下的标准包含路径。
如果指定包含有 Makefiles 的目录经过全面排查仍无法找到目标 Makefiles, 系统将触发警告信息提示, 请注意这不是一种严重的错误。在执行 include 指令时所涉及的所有 Makefiles 均被成功处理完毕后, 系统将转而处理当前这个 Makefiles file. 当完成对所有 Makefiles 的读取操作后, 系统将会尝试创建或更新那些过时或不存在的相关 Makefiles. 详细信息请参阅 ** Makefiles 重生成的具体流程** . 只有当系统穷尽一切努力但仍未能找到丢失的目标 Makefiles 时, 才能断定该缺失行为属于致命性错误.
如果有人希望忽略无法重新创建的makefile文件并避免生成错误信息,则应替代使用-include指令而不是include指令,请参考以下格式
这种指令的功能就是确保在任何不存在的makefile文件情况下都不会报错(即便发出警告信息也不会报错)。建议为了维持与其他版本make系统的兼容性而采用sinclude指令取代-include指令。
3.4 变量MAKEFILES
当定义了环境变量MAKEFILES时,则认为该变量的值被视为一连串附加的makefile文件名(以空格分隔),并且这些附加的makefile应依次被读取)。Make的行为模式与上一节介绍的方法相似(即在指定目录中搜索这些文件)。需要注意的是,默认情况下最终目标不会包含在上述任何一张makefile图示中(同样地,在无法找到相关makefile图示时也不会触发错误信息)。
环境变量MAKEFILES在make递归调用过程中起到关键的沟通作用(参考** 递归调用make)。若在make顶级调用前设置环境变量并非一个好的策略,因为这会令makefile文件与外界的关系变得更加复杂。然而,在缺少makefile文件的情况下运行make时,在MAKEFILES环境中包含缺失的makefile文件能够帮助内置隐含规则更好地发挥作用(参考 在目录中搜寻依赖**)。
很多用户倾向于让系统参数在登录过程中预设一个临时值MAKEFILES。然而,在这种设定下使用的only当相关目录不存在的时候才会生效。这是一个非常糟糕的想法,并最好避免这样做。正确的做法是直接向makefile文件注入明确包含路径的操作指令(具体内容请参阅上一节)。
3.5 makefile文件重新生成的过程
通常情况下, makefile文件可以通过从其他源文件进行重构来实现自动生成, 如基于RCS或SCCS等版本控制系统. 当一个makefile能够基于其他源文件进行重构时, 建议确保在更新后才读取新的makefile内容以避免潜在的问题.
完成对所有MakeFile档案的讀取後, make將檢查並處理每個目標, 將其內容進行 更新並修正相关内容。若某個MakeFile檔案中有明確规定如何進行 更新(无论是現存或其它MakeFile档案中的规定),或是依未明確规定但实际執行時會遵循的默认規則(詳細內容請參見** 使用隱含規則** ),則在必要時系統會自動根據既定規則執行相应動作。當全部MakeFile檔案被檢查過後, 如果發現有任何一個檔案已被修改, 系统將將會清除所有紀錄並重新讀取並處理所有MakeFile檔案(随后再次執行相關操作,通常來說因為這些档案已被更新, system將不會再对其进行進一步處理)。
当您发现某些makefile文件无法重新构建时,请考虑其可能与执行效率相关的问题。为了避免根据隐式规则搜索或重建这些文件,请采用适当的方法阻止基于隐式规则的操作。例如,在某些情况下您可以编写特定的规则将这些makefile标记为目标,并采取措施防止它们被重建。详细内容可参阅** 使用空命令** 的相关内容。
如果在makefile中指定了基于双冒号规则构建命令但未提供依赖项,则每次运行make都会生成该文件。(具体细节可参阅** 双冒号规则 **)同样地若基于双冒号规则指定了构建一个makefile文件且未提供依赖项则每次运行make都会重新生成该makefile并再生读所有makefile文件从而导致系统陷入无限循环状态以至无法完成其他任务为了避免上述问题发生必须确保不基于双冒号规则构建任何makefile而不论是否提供依赖项
若未指定-f或--file选项时,默认的build file会被选用(参考第3.2小节)。若不采用特定选项设置,则系统将无法确认构建所需的 make file 是否存在。默认情况下若缺失 build file 却可依据 make 的自动管理规则生成该文件,请确保已启用构建相关的功能配置项。
如果默认情况下生成的makefile文件不存在,则Make系统会按照搜索顺序尝试建立相应的Makefile文件,并会继续这个过程直至成功生成Makefile文件或者已经为所有可能存在的Makefile进行了尝试。需要注意的是,在Make系统中无法生成或找到Makefile文件并不构成错误信息,并非必须依赖于Make系统的正常运行。
由于即使您指定使用'-t'或'--touch'选项来特别指定某个特定的目标(即不会阻止makefile文件被更新),但makefile$文件仍然会更新其依赖项以反映最新的变化。因此,在您选择使用这些选项时,请避免以过时的makefile$文件来确定哪些目标会被触达(具体细节请参阅** 代替执行命令** )。类似地,在使用带有参数的选项如'-q' (即 '-q'") 或者 '-n'" (即 '-n') 时,请注意这些选项不会阻止 makefile$ 文件被更新;然而这可能会导致过时的 makefile$ 文件对其他目标产生错误的行为模式。
在实际使用过程中, 您可能会遇到想要阻止更新makefile文件的情况. 如果是这样, 您可以在makefile文件的命令行中将需要被更新的makefile文件指定为目标, 这样就能阻止对它的更新. 当makefile文件名明确被指定为一个目标时, 选项'-t'等会对此起作用. 如果是这样设置,'make -f mfile -n foo'这个命令将会执行:输入'mfile', 生成更新'foo'所需的指令及其依赖项, 但不会执行对'foo'的更新操作. 'foo'的更新指令会包含在现有的'mfile'中.
3.6 重载其它makefile文件
有时两个close的makefile files之间存在相似性同样具有实用价值。通过使用the include instruction, one can incorporate additional makefiles into the current system, thereby enabling the definition of more targets and variables. The consequence of providing different commands for the same targets in two makefiles would be fatal errors.
在源makefile文件(遵循包含其他makefile文件的规定)中,默认情况下仅当依赖当前makefile中的信息无法重新构建目标时才会搜索其他makefile文件。详细说明请参阅** 定义与重新定义格式规则**
举个例子:如果您有一个说明怎样创建目标'foo'(和其他目标)的Makefile文件称为Makefile,则您可以编写另一个名为GNUmakefile的Makefile文件并包含以下内容。
当输入‘make foo’时, 系统会自动找到对应的‘GNUmakefile’文件, 并执行相应的操作以生成指定文件foo. 如果尝试处理另一个目标bar, 系统会根据Makefile中的规则自动启动相应的处理流程. 在Makefile中为某个目标设置更新规则后, 系统将严格按照该规则进行处理. 对于那些未在Makefile中定义的目标, make也会采用同样的处理方式. 该系统通过使用匹配符‘%’来实现灵活的目标匹配功能. 这种匹配机制允许系统强制执行某个指令, 无论目标文件是否存在. 在这种模式下, 用户可以通过空命令避免不必要的依赖关系生成, 避免陷入无限递归的危险状态.
3.7 make读取makefile文件的过程
GNU make被明确划分为两个主要阶段来执行其功能。在第一个阶段中, make解析makefile文件, 包括它自身的信息, 内置变量和它们的值, 以及隐含规则和具体规则等要素。接着, 在第二个主要阶段中, make依据构建好的依赖关系图表确定哪些目标需要重新构建, 并根据必要的运算规则完成相应的计算过程以实现最终目标。
掌握make双阶段的操作模式具有重要意义
对于这一部分内容, 您可能还不够了解. 建议您先完整阅读后续章节以便更好地掌握相关知识后再回来看看本节内容.
变量赋值
变量的定义语法形式如下:
关于+=运算符,在右侧使用(:=)时将被定义为简单扩展的变量,则这些情况属于立即变量子类;其他情况则属于延时变量子类。
条件语句
就而言,在编译器设计中,条件语句通常按照语法顺序进行解析。常见的有:ifdef、ifeq、ifndef 和 inneq。这些通常是用来实现特定的控制流程功能的。
定义规则
规则不论其形式如何,都按相同的方式扩展。
在目标与依赖部分均进行迅速展开时(针对构造目标而言),这些命令往往会在后续阶段逐步展开。这一普遍的原则同样适用于具体规范、格式规范以及相关的后缀规范等基本要素。
4编写规则
makefile中的规则用于说明何时及如何构建特定文件。这些特定的文件称为该规则的目标(通常情况下,每个条目仅有一个目的)。在条目中列出的其他文件则作为目的依赖列出,并给出构建或更新相应目的的操作指令。一般情况下各条之间顺序无关紧要,在确定默认目的时却大有关系。默认目的是指当您未特别指定最终目的时,默认会采用第一条有定义的目的。当未特别指定最终目的时,默认会采用第一条有定义的目的。这一默认值会在以下两种特殊情况下失效:一种是以句点开始的目的不是默认值(但如果该目的包含一个或多个斜杠,则可能被视为默认值);另一种情况是格式化法则所规定的目的不会被默认选作默认值。(请参阅** 重新定义格式化法则** 部分)
因此,在编写makefile文件时, 一般将第一个规则的目标设定为编译全部程序或是由makefile文件所包含的所有程序(通常设置一个称为' all '的目标)。参见** 指定最终目标的参数**.
4.1规则的语法
通常一条规则形式如下:
或:
目标标识符(target identifier)用于标识特定的文件名称。允许在文件名称中引入通配符符号(参阅附录_A_关于_在_文件名_中使用_通配符_的部分),其中采用'a(m)'格式说明表示成员m存在于相应于a的目标档案中(参阅附录_B_关于_档案成员目标_的相关说明)。通常情况下,每条规则仅对应一个单一的目标,但在某些特殊情况下,一条规则可能会被配置为具有多个不同的操作目标(参阅附录_C_关于_具有多个操作目的的规则_.)
遵循Tab键启动的命令行界面设计中,默认情况下允许起始位置与依赖项在同一行操作。具体来说,在某些情况下允许将第一个命令与其相关联的依赖项放在同一行,并用分号将其分开以便于后续的操作步骤衔接。另外一种方式则是在完成当前操作后换行并按Tab键启动下一个操作流程。这两种方式均具有相同的执行效果即确保了操作流程的连贯性与稳定性,并且都可参考以下标记** 在规则中使用命令** 以获取进一步的操作说明。
因为美元符号已被用作变量引用的起始符(参阅**使用变量**部分)。如果您确实希望在规则中使用美元符号,则需连续写出两次'$$'。此外,在长条目中间插入转义字符'\ '即可将其分为多行:例如,在一行末尾添加'\ '表示下一行系本行之续篇。然而并非非此不可:Make并未对makefile文件中的行长度设定任何限制。一条规则可同时告知Make两个信息:何时目标已过时;以及当有必要时如何更新它们。
判断一个目标是否过时的标准与其所依赖的关系极为密切;其中这些依赖关系通常基于文件名,并以空格分隔;此外,在构建这些依赖关系时还可以引入通配符以及档案成员名称。
如果一个目标不存在或者其最新修改时间早于某个已存在的依存项,则该依存项被认为是过时的。
这一原则源于将独立的目标状态视为其依存项状态函数的事实。
具体来说,默认情况下所有规则都是通过shell解释器来处理的。
4.2 在文件名中使用通配符
通过使用通配符符号可以简洁地表示多个文件名称。两者在通配符符号的选择上完全一致,在Make系统与Bourne shell系统中都采用相同的三个基本字符:即星号()、问号(?)以及方括号内的字符序列([...])。举例而言,在当前目录中使用通配符 '.C'可以匹配所有以'.C'后缀命名的文件。
字符''在文件名前同样具有特殊意义。当字符''单独放置或后面跟着斜杠'/'时,默认指向用户的home目录位置。例如,在Unix/Linux系统中,'/bin'等价于'/home/bin'路径。若字符''后接一个汉字,则表示该汉字作为子目录名存储于home目录下, 如'~John/bin'相当于'/home/John/bin'结构。值得注意的是, 在某些操作系统如ms-dos和ms-windows中没有明确的home目录概念, 但可以通过设置环境变量名为"home"来模拟相应的存储空间配置
在目标、依赖和命令中使用通配符会自动展开其匹配内容。在其他语境下,默认情况下通配符不会展开匹配内容,并需特别指定调用通配符函数才能实现匹配
通配符另一个重要特点是如果通配符前面是反斜杠「\」则该通配符将失去匹配功能例如在表达式「foo*bar」中可以看到一个特定的文件名由三个部分组成:'foo' '*'以及'bar'
4.2.1通配符例子
用于规则命令中时, 通配符将由 shell 扩展。例如, 下述规则将删除所有 OBJ 文件:
clean:
rm –f *.o
通配符在模式依赖中同样具有重要作用。对于下面列出的makefile指令:"make print"会生成一份包含自上次修改以来发生变动的所有.c源文件的清单。
本规则将‘ptint’设为空目标文件(参考相关说明);该自动变量‘$?’用于显示已被更改的文件(参考相关内容)。
当您定义一个变量时通配符不会扩展,如果您这样写:
objects = *.o
变量objects的本质实际上是字符串‘*.o’。然而,在您将变量objects的值应用于一个目标、依赖或命令时...
objects=$(wildcard *.o)
详细内容参阅** 函数wildcard** 。
4.2.2使用通配符的常见错误
下面有一个初级使用通配符扩展的例子,在实际情况中该示例无法实现您所期望的目标。其中可执行文件名为'foo'的,则其依赖所有位于当前目录下的OBJ文件。
因为变量objects的值被定义为字符串‘*.o’的原因导致了一个特定的操作机制——该机制会在目标‘foo’规则下展开。因此,在必要时每个OBJ文件会被转换为目标‘foo’的依赖,并会主动地进行自身重编译以满足相关需求。
假如您的所有OBJ文件已被彻底删除,则会遇到怎样的问题呢?因为没有与通配符匹配的源代码对象存在, 所以目标标识符'foo'就必须依赖一个具有非标准名称的对象'.o'. 由于该源代码对象对应的源代码不在目录中, make命令将输出无法生成'.o'对象的信息. 显然这不是预期中的操作流程.
事实上正确结果的实现可以通过通配符来实现然而要实现这一目标需要采取更为复杂的技术手段这些技术手段主要依赖于wildcard函数以及替换字符串的操作具体说明将 deferred至下一节部分
微软的操作系统(MS-DOS、MS-WINDOWS)使用反斜杠分离目录路径,如:
C:\foo\bar\bar.c
遵循 Unix 风格文件路径表示法(其中 'c:' 代表驱动器字母)。当 make 在这些操作系统上运行时不仅允许路径中包含反斜杠还支持 Unix 样式的前导反斜杠。然而这一做法并未涵盖通配符扩展功能 因为在这种情况下反斜杠被用作引用字符。因此在这种情况下您必须采用 Unix 风格的路径表示法。
4.2.3函数wildcard
通配符在规则设计中通常会实现其目标,并非仅限于简单的匹配功能;然而,在某些特定情况下(如设置于变量或函数参数时),通配符可能会遇到限制或无法正常展开。为了满足这些特殊需求并确保功能完整性,在这些场景下建议调用wildcard函数,并遵循以下具体使用方法:
在makefile中的任意位置都可以使用这个字符串。在应用时,这个字符串会被替换为一整列位于指定目录下的所有符合条件的文件名称列表。这些符合条件的内容是:它们与给定名称匹配,并且各名称之间以空格分隔符分隔开。如果没有满足上述条件的具体情况出现,则函数wildcard将不会输出结果。需要注意的是,在本规则中采用的是逐字符展开的方法而非省略处理。(参见上节)
调用函数wildcard获取指定目录结构下的全部C语言源程序文件名的命令格式如下所示。
我们可以通过编译器将具有'.c'扩展名的C语言源代码转换为具有'.obj'扩展名的目标文件名称。
在当前项目中采用了其他的一个辅助功能:patsubst;具体功能细节可参考** 字符串替换与分析功能**
这样,在特定目录下编写所有C语言源代码并将其整合到一个makefile文件中的做法是可以采用的。
这里应用编译C语言源程序时所遵循的隐含规则。因此无需为每个文件单独制定具体的编译规则。符号 ' := ' 是符号 ' = ' 的变体。关于符号 ' := ' 的定义,请参阅 ** 两种风格的变量** 。
4.3在目录中搜寻依赖
在大型系统环境中, 将源代码文件组织在一个独立的项目目录下, 而将可执行文件放置到另一个专门的目录也是常见的做法。
Make 的强大 directory 搜索功能使得在一个项目内查找依赖变得非常简便。
如果您需要对多个 directories 进行微调以优化构建流程, 无需修改任何配置规则就可以轻松完成这一操作.
4.3.1 VPATH:所有依赖的搜寻路径
通过设置变量VPATH为其值,确定了make进行搜索的方向。通过设置变量VPATH为其值,确定了make进行搜索的方向。通常使用的都是那些包含了依赖项的 directories,并非仅限于当前 directory;而 VPATH 则定义了一个适用于所有 files 的通用搜索路径序列
如果一个作为目标或依赖的文件不在当前目录中存在,则make就会在其指定的目标 directory 中进行搜索。一旦这些 target 目录中有该所需 file,则其行为等同于这些 file 已经存在于 current working directory 中。参阅** 编写 search directory 的 shell command**
在VPATH变量定义中对目录名称采用冒号或空格分隔的方式其排列顺序也与make进行搜索时的一致。需要注意的是在此过程中特别是在MS-DOS及MS-WINDOWS系统中对于VPATH变量定义中的目录名称采用了分号作为分隔符的原因在于其中冒号被用作路径的一部分(通常位于驱动器字母之后)。例如:
指定了一组目录,在-src和.../headers处设置好了参数。基于该VPATH设置下,在此顺序中运行make命令将有助于优化编译过程。详细说明如下:
在执行时就象如下写法一样会被中断:
foo.o : src/foo.c
然后在src目录下搜寻foo.c。
4.3.2 vpath指令
该指令(特别强调字母小写)与VPATH变量相似,并相比而言更加灵活。该指令赋予该指令根据特定格式类型定义搜索路径的能力。具体来说包含三种不同形式的vpath指令:
**vpath**pattern****directories
为特定格式类型的文件名指定了一个搜索路径。该搜索路径由多个目标目录组成,在这些目录之间使用冒号(在MS-DOS及MS-WINDOWS系统中常用分号分隔)或空格分割,并与VPATH变量定义的搜索路径格式相同。
**vpath**pattern
清除和一定类型格式相联系的搜寻路径。
**vpath**
清除所有前面由vapth指令指定的搜寻路径。
一个vpath的格式pattern是一个包含一个'%'符号的字符串。这个字符串必须与一个正搜索的目标依赖文件名对应(关于具体规则,请参阅** 定义与重新定义格式规则** )。例如,在模式中使用%.h时会匹配所有具有.h扩展名的文件名。如果没有使用%,则模式必须与依赖文件名完全一致(这种情况很少被采用)。
在vpath指令格式中使用字符'%'时可以通过前面添加一个反斜杠来进行引用。对于其他同样需要引用'%'符号的位置也可以选择使用更多的反斜杠来进行引用操作。值得注意的是,在与文件名进行比较或格式处理时'%'符号与其他反斜杠符号之间是分开处理的。只要'%'所引用的内容没有语法错误那么多余的反斜杠并不会对程序运行造成任何潜在风险
当vpath指令格式与一个依赖的文件名匹配的同时,在当前目录中该依赖不存在时,则vpath指令所指定的目录与VPATH变量中的相应的目录也会被搜索到。举个例子:
让make若当前目录中不存在以.h结尾的文件,则向父目录headers搜索所有具有.h扩展名的依赖项。
当存在几个遵循特定格式的vpath指令及其对应的依赖文件名时,则依次处理这些指令,并搜索每个指令所指明的目标目录。随后依据这些命令在makefile中的顺序来管理各个独立运行的任务。以下代码:
表示搜寻`.c'文件先搜寻目录‘foo'、然后‘bar',最后‘blish'。
4.3.3目录搜寻过程
当使用目录搜索功能检索到某个文件时(该文),该文件可能不在您的项目所列出的依赖关系中;有时使用 directories 搜索检索到的道路也可能被弃用。决定对通过 directory 搜索并被弃用的道路采取保留或放弃处理的方法将基于以下算法进行操作
当目标文件不在makefile所在的目录中时,则会触发相应的目录搜索流程。
2、如果目录搜寻成功,则路径和所得到的文件暂时作为目标文件储存。
3、所有该目标的依赖用相同的方法考察。
4、把依赖处理完成后,该目标可能需要或不需要重新创建:
如果该目标无需重新构建,则在进行目录搜索时所得文件路径将被用作作为该目标所有依赖项所需的路径,并包含该目标自身文件。换句话说,则可采用通过目录搜索获得的路径来满足需求
若要重建此目标,则在此过程中所找到的文件路径将成为弃用状态;重建的目标将在位于makefile所在目录的位置上进行构建。
换句话说,在构建make时重建的目标将在位于makefile所在目录的位置上进行构建;
而不是在此过程中所找到的文件路径上的位置。
该算法似乎比较复杂,但它却可十分精确的解释实际您所要的东西。
替代版本中的make采用基于贪心策略的一种高效可靠的算法:若目标文件不在当前目录中存在,则依赖于目录搜索获取的有效路径。
具体来说,在GNU make中如果您有一些或全部目录表现出这种特性,请问您是否可以通过设置GPATH变量来指定这些目录?
当使用目录搜索获取到一个已失效的目标时(即该目标所在的文件夹或目录已经存在于GPATH变量中),则该路径不会被丢弃,并将对该目标进行重新构建
4.3.4编写目录搜寻的shell命令
即使通过目录搜索在其他目录中找到一个依赖,并且不更改规则的命令仍然遵循原有的方式执行。因此建议谨慎编写这些命令以确保它们能够在Make能够处理依赖的情况下继续运行。
通过诸如‘^’这样的自动变量可以更方便地应用shell命令(参见** _自动变量_** )。比如,在Shell编程中,“^”变量表示所有依赖项的列表,并提供用于搜索这些依赖项的目录路径;而“$@”则直接存储目标文件或目录的名字。
该变量CFLAGS便于您通过隐含规则指定编译C语言源程序的选项。在这里我们采用它主要是为了确保编译后的C语言源程序具有统一性。参阅** 隐含规则使用的变量** 。
任何依赖通常也会包含相应的头文件。因为自动变量‘$<’的值被定义为第一个依赖项的原因在于此;因此您无需在编译命令中特意列出这些头文件。
4.3.5 目录搜寻和隐含规则
由变量VPATH或隐含规则引导的vpath指令确定(可参考相关指南)。例如,在缺乏特定编译规则时,请采用以下方法:若文件'foo.o'无对应规定,则make将应用默认规则生成相应的二进制代码;即使文件'foo.c'位于其他目录中找到也会同样应用内置编译规则生成相应的二进制代码。
暗藏规则的命令运用自动变量是必要的,并因此使得隐含规则能够方便地通过目录获取文件
4.3.6 连接库的搜寻目录
对于连接库文件,在目录搜索时采用了一种特殊方式。这种特殊方式源自一个玩笑:您定义一个依赖关系时,请以'-|name'的形式命名它。(这里可以编写一些奇怪的字符,因为通常情况下依赖关系名称是基于合理命名规范的(libname.a形式),而库文件名多为类似'libname.a'的形式)。
当一个依赖的名字是'-|name'形式时,在当前目录以及其他与vpath匹配的目录中(包括VPATH指定的目录、/lib、/usr/lib以及prefix/lib(通常为/usr/local/lib但要注意MS-DOS/MS-Windows版本可能以DJGPP安装树根目录为前缀))中搜索名为libname.so的文件,并进行相应的处理。如果无法找到该.so文件,则继续在上述目录中搜索名为libname.a的静态对象文件。
例如,如果在您的系统中有‘/usr/lib/libcurses.a'的库文件,则:
如果‘foo’比‘foo.c’更旧,将导致命令‘cc foo.c /usr/lib/libcurses.a -o foo'执行。
在缺省状态下会搜索'libname.so' 和 'libname.a'文件;具体来说,在搜索这些文件及其类型时可以通过$ LIBPATTERNS变量来指明模式;当需要查找名为'-|name'依赖时,MK首先会用_name_来代替列表中第一个模式中的格式部分,从而生成要搜索的目标库文件名;然后根据生成的库文件名,在上述指定目录中进行搜索;如果没有找到所需的库文件,则会继续尝试列表中的下一个名称;依此类推。
.LIBPATTERNS变量默认设置为' 'lib%.so lib%.a' `。这一设定可确保前面所述的基础行为得以实现。通过将此变量设置为空可彻底阻止对附加库的链接。
4.4假想目标
假想目标并非真实存在的文件名,而是您制定的具体规则所执行命令的名称。使用假想目标有两个主要原因:一是为了避免与文件名冲突;二是为了提升性能。
如果您的某个命令未预先指定以创建目标文件,则在因重构而提及该目标时(即涉及此目标时),该规则将导致相应的操作执行。以下是一个示例:
由于rm命令在执行Clean模式时不会生成名为‘clean’的文件,并且为了避免潜在的问题设置人员故意设置了该限制条件;因此每当运行make clean指令时,默认情况下也会触发此操作序列以确保系统状态的一致性与稳定性。
虚拟目标能够停止任何在目录下生成名为‘clean’的文件的操作。
然而,
即使目录中存在文件 clean 也是如此,
由于该 target clean 没有依赖关系,
因此其 update 命令永远无法执行。
为了避免这种情况,
请使用像以下特别的.PHONY 目标格式将该 target 具体声明为一个虚拟(假想)的目标:
这样可以确保 clean target 不会因意外而触发 update 命令
一旦进行这样的声明,在运行‘make clean’命令时无论目录下是否存在名为‘clean’的文件时
由于make意识到假想目标并不是必须基于其他文件而重建的实际文件之一,因此它会跳过隐含规则来搜索假想目标的步骤(具体细节参见** 使用隐含规则** )。这是因为将一个目标声明为假想目标能够提高执行效率的原因,因此如果您采用假想目标的方式,就无需担心目录中是否存在实际文件的问题。这样一来,对于前面提到的例子,您可以直接以假想目标的形式写出它的格式如下:
此外,在某些情况下也可以通过假想目标实现连接功能:例如,在Makefile文件中通常会定义一系列需要生成子目录的目标变量。如果想要避免使用假想目标来完成这一任务,则可采用另一种方式——即定义一条规则(rule),其对应的命令类似于在一个一个子目录下循环执行某个shell指令的操作序列,请参见下面的例子:
然而,在采用这种方法时存在以下几个关键问题:首先指出的一个问题是:这一规则在处理子目录创建过程中存在一个问题:未能及时识别由错误所引发的问题,并导致了一个连锁反应。具体而言,在某个子目录无法成功生成的情况下尽管如此,在这种情况下它仍会尝试生成尚未成功的其他子目录。虽然可以通过在脚本中添加监控错误出现并终止执行的相关代码来规避这一缺陷;然而需要注意的是,在某些情况下即使采用了这样的措施也难以完全避免这个问题;例如,在某些特定条件下即使采用了上述补救措施仍可能存在缺陷导致脚本无法正常运行。其次需要强调的是:这一点尤其值得重视在于它不仅限制了我们利用make的强大特性来进行批量处理工作
采用假想目标(若无已存在的子目录,则需创建新的;否则无需创建新的)则能有效规避上述问题:
若子目录‘baz’未能成功生成,则无法生成子目录’foo’;在尝试进行多线层构建时这种关联性的声明显得尤为重要。
假设性目标不应依附于实际的目标文件。如果这种依赖关系存在,则每次运行此命令都会更新相关的目标文件。当假想的目标并非基于真实的目标构建时,在非特别任务模式下其相关命令将不会被触发(参考有关指定最终参数的技术细节)。
示例:
示例:
这样,您能够重建全部程序;也可以通过参数形式来重建其中的一个或多个(例如,在命令行中使用类似'make prog1 prog3'的方式)。
当一个假想的目标基于另一个假想目标而存在时,则该前一个假想目标将被视作后一个假想目标的一个子例程。例如,在本例中,“make cleanall”这个命令被用于清除OBJ文件、diff文件以及程序文件:
4.5 没有命令或依赖的规则
如果没有依赖也没有命令,并且其目标文件也不存在的话,则make则认为只要执行该规则就会使其目标文件被更新完成。这表明基于这种类型的依赖关系的所有相关项目都会自动执行相应的操作。
这里的目标' FORCR '符合上面的特殊条件,在以目标' clean '为依赖的情况下,总将强制其执行相应的命令。关于' FORCR '的名字没有特别的要求;但通常情况下会选择使用' FORCR '作为名字。
也许您已有所了解,在采用FORCR方法时所获得的效果与设置(.PHONY: clean)相同。然而,在具体实施上前者显得略显笼统不够灵活可调。考虑到某些MAKE版本缺乏对假想目标的支持,在许多MAKE文件中仍可见到FORCR的身影。参考有关** 假想目标** 的详细说明会助您更好地理解这一概念。
4.6使用空目标文件记录事件
空目标是一个虚拟目标变量,用于控制一系列特定命令的操作,并完成一些常规性任务。然而与真正的虚拟目标变量不同的是其对应的目标文件可以在实际环境中存在,并且这些文件的内容与当前操作无关,在一般情况下是空白的。
空目标文件的作用在于保存规则指令最后一次执行的时间信息以及该文件上次被修改的时间戳。其之所以能够实现这一功能是因为在规则指令中包含了一条专门用于更新目标文件的操作指令——'touch'命令的存在。此外为了确保其有效性和可靠性空目标文件必须具备一定的依赖关系(否则就会失去存在的意义)。具体来说如果空目标文件比其依赖项要更新得更老则只有当重新构建该空目标文件时相关的操作才会被执行以确保数据的一致性和完整性下面是一个具体的实施示例:
遵循这一规定,在任何情况下当任何一个源文件自上次运行「make print」以来发生变动时,则输入「make print」将导致执行lpr命令以实现打印功能。该自动变量「$?」用于追踪并显示哪些文件已发生更改(参考有关「自动变量」的部分)。
4.7 内建的特殊目标名
一些名字作为目标使用则含有特殊的意义:
l .PHONY
特殊的目标中, PHONY依赖于假想的目标. 假想的目标是指那些无论目录中文件是否存在或它们上次更新的时间如何, make都会无条件地执行它们命令的目标. 具体内容可参考**假想目标**部分.
l .SUFFIXES
特殊目标.SUFFIXES的功能构成一列用于后缀规则检查的后缀,请参考** 过时的后缀规则** 详细内容。
l .DEFAULT
.DEFAULT指定了某些命令用于处理那些未发现可更新的目标(包括具体规则或隐含规则)。详细内容请参考** 定义最新类型的缺省规则** 。如果这些指定的命令被设置,则涉及的相关文件仅能作为依赖使用而不被任何规则目标引用;此外,在执行时,这些指定的命令将遵循其特定的方式。详细内容请参考** 隐含规则搜索算法** 。
l .PRECIOUS
该特殊依赖关系将遵循以下规定的处理方式:当运行涉及这些特定目标的任务时, 如果make命令被终止或关闭, 相关依赖项将无法被删除:请参考** 关闭和中断make** 一节:若该依赖关系为中间文件, 即使不再有使用价值, 它仍需保留:具体情况与常规处理相同,请参见** 隐含规则链** 部分:此特殊依赖关系与其他属性(如SECONDARY)存在关联:当相关规则的目标格式与指定文件名相匹配时, 您可以采用类似‘%.O’的方式列出
l .INTERMEDIATE
特殊目标.INTERMEDIATE的依赖被转换为中间文件。详细信息请参阅** 隐含规则链** 。.INTERMEDIATE如果缺少依赖文件,则无法发挥作用。
l .SECONDARY
特殊目标.SECONDARY的所有依赖都被视为中间文件,并且这些中间文件不会自动删除。详细内容包括在**隐含规则链**中进行查看。如果一个Secondary目标没有依赖文件,则该目标的所有makefile中的目标都将被视为中间文件进行处理。
l .DELETE_ON_ERROR
DELETE_ON_ERROR作为一个目标被引用,并且当其相关条件变化或其命令未能正确执行而导致退出时,则make会将此规则的目标从当前目录中删除。
l .IGNORE
如果您特别指定目标.IGNORE,则MAKE会忽略处理这些依赖文件时执行命令所产生的错误。若_IGNORE 被作为无依赖的目标提出,则 MAKE 会完全避免在处理所有文件时产生任何错误。.IGNORE 命令本身并无特殊意义;它仅是为了与早期版本兼容而存在的功能模块。由于 IGNORE 影响所有操作, 因此其作用范围有限; 我们建议您采用其他方式来实现只针对特定操作忽略出错的情况(详情请参阅 _ 命令出错 _)。
l .SILENT
如果特别为.SILENT指定依赖,则MAKE在执行前不会显示重新构建文件的指令。当.SILENT被用作无依赖的目标时,在执行任何指令前都不会输出结果。.SILENT本身没有特殊意义,默认情况下与早期版本兼容。我们建议您采用其他方式处理不会输出结果的指令。更多细节请参阅** 命令回显** 。如果您希望所有指令都不输出结果,请使用‘-s’或‘-silent’选项(详细参阅** 选项概要**)。
l .EXPORT_ALL_VARIABLES
如果某个特殊的单一目标被提及,默认情况下会传递所有变量。请参考与子MAKE通信的变量...
l .NOTPARALLEL
当.NOTPARALLEL被作为目标提及时,在即使指定了--jobs参数的情况下(make也不会启动多线程执行)。然而,在嵌套函数调用场景下(即在被NOTPARALLEL目标引用的makefile中),递归make命令仍能实现多线程执行(除非其中某个NOTPARALLEL的目标不在排除列表中)。此外,在构建过程中将完全忽略所有依赖项。
所有定义的隐含规则后缀若作为目标出现都将被视为特殊类别而无需特别处理即便将两个相关联的目标符号串联起来也应遵循同样的原则举例说明如‘.c.o’这样的组合形式就被视为一种特殊的复合型目标名称这些特定的目标则被称为复合型前缀或复合型postfix尽管如此但这种分类方法仍属于较旧的技术框架(尽管仍被广泛采用)就原则而言如果您希望将其拆分为单独项并添加到列表中每个目标名称都可以按照此方式来指定值得注意的是所有的前缀及postfix都应以‘.’开头因此即使是那些特殊的复合型前缀同样必须遵循这一命名规范进一步详细信息请参阅** 过时的复合型前缀与postfix**
4.8 具有多个目标的规则
将具有多个目标的规则视为独立的条目,在结构和内容上完全一致。它们在操作上会应用同样的指令,在结果上可能会有所不同。然而,在指令中可以利用‘$@’语法来指定不同的实际操作对象。这条规则进一步表明所有的目标都依赖于同一个基础结构。
在以下两种情况下具有多个目标的规则相当有用:
l 您仅仅需要依赖,但不需要任何命令。例如:
kbd.o command.o files.o: command.h
为三个提及的目标文件给出附加的共同依赖。
所有目标均采用同一指令。然而该指令的运行结果可能不完全一致。由于自动变量' $@ '在重建期间可指定目标(见前述相关内容),因此具体情况需依实际情况而定。例如如下所示:
等同于:
假定程序会产生两种类型的输出文件:其中一种会生成带有'-'big'的文件名, 另一种则会生成带有'-'little'的文件名. 参考** 字符串代替和分析函数** 对函数subst的作用进行说明.
如果您愿意遵循基于目标的依赖转换策略,则类似于通过变量 '$@' 执行转换操作。无需采用涉及多个复杂条件的目标相关规则。可参考 静态格式规则 作为基础。详细内容见下文。
4.9 具有多条规则的目标
一个目标文件允许允许多个规则。所有规则中的依赖都会整合到该目标的依赖列表中。如果该目标比任何一个依赖"旧",将导致重建该目标。
若有多条指令针对同一文件,则make会遵循最后一条指令并输出相关信息。(例如,在特殊情况下如文件名以点开头时(当文件名以点开头时),该行为不会触发提示信息。这种设计特色仅出于与其它版本的make保持兼容)。因此无需在makefile中添加这些指令, 这正是...原因.
一条独特的依存规则能够迅速为多个目标文件提供额外依存关系;举例而言,在项目管理中常会遇到此类需求;具体来说,在软件开发中经常会遇到这种情况;比如当某个源代码头文件如‘config.h’发生更改时,则会导致所有的输出文件都需要重新编译;这可能带来一定的挑战性;因此,在这种情况下可参考以下简便方法编写相关的代码块
这些既可自由插入也可取出,并不会干扰实际指定的目标文件生成规则。如果您希望间断性地为目标添加依赖,则是简便的方法。
另一种实现依赖的方法是为依赖创建一个全局变量,并将其传递给make命令作为选项使用。详细内容参阅** 变量重载** 。举例如下:
命令make extradeps=foo.h表示将foo.h作为所有OBJ文件的依赖;仅执行make命令时不会产生这种依赖关系。
当未对生成任务给出明确指令时, make会寻找适用的隐含规则, 并根据这些规则自动生成所需的命令以完成任务.
4.10 静态格式规则
静态格式规则是明确指定多个目标并能依据每个目标名生成对应的依赖名的规则。当应用于多个目标时更为常见,因为各目标所需依赖的关系只需大体相似即可。
4.10.1 静态格式规则的语法
这里是静态格式规则的语法格式:
目标列表明确该规则应用的目标
阐述了计算每个目标及其相关依赖的具体方法;通过分析匹配的目标名称结构,在所需信息中提取出关键部分;随后将这些关键部分分别对应到各个相关结构中生成相应的名称
每个格式一般都会包含字符'%'
每个目标的依赖名都是通过采用径替代各个依赖中的'%'来实现生成。例如,在一个依赖格式为'%.c'的情况下,则用径'foo'替代其中的'%'以生成文件名为'foo.c'的依赖名称。需要注意的是,在某些情况下(如当所有目标共享相同的路径结构时),这种统一的路径设置可能更为方便。
注
这里有一个例子,它将对应的‘.c’文件编译成‘foo.o’和‘bar.o’。
在这里使用‘<’作为自动变量来管理依赖项的名称,在这里使用‘@’作为另一个自动变量来管理目标项的名称。具体内容请参见**自动变量**
每个指定的目标都应与目标格式相匹配;如果不符合将触发警告信息。如果有某一列的数据存在,则可以通过调用Filter函数来筛选并去除不符合条件的数据行。(参阅** 字符串替代和分析函数 )
在此例中,'(filter %.o,(files))'运算的结果是'bar.o lose.o';第一条静态格式规则的作用是将对应的C源代码文件进行编译生成相应的对象文件;而'(filter %.elc,(files))'运算的结果则是'foo.elc';该结果是由'foo.el'构建而成的。
另一个例子是阐明怎样在静态格式规则中使用‘$*’:
当命令generate执行时,$*扩展为径,即‘big’或‘little’二者之一。
4.10.2静态格式规则和隐含规则
显式规范结构与隐式规范结构在许多方面存在诸多相似之处;(详细参阅** 定义与重新定义规范结构** )。双方在遵循目标时都采用了基于名称的方式进行规范;其区别在于采用时机的不同。
隐含规则适用于任何与之匹配的目标;然而,在目标既未明确指定了操作命令且依赖项可被探测到的情况下才得以应用。当存在多条符合规定的隐含规则时,请仅需执行其中之一,并根据其定义次序确定优先级。
相反,在规定中明确的目标只能由静态格式规则来处理。该规定无法应用于除其自身外的任何其他目标,并且其应用模式为固定模式。如果两个带有命令的规定相互之间产生矛盾,则会导致错误。
静态格式规则因为如下原因可能比隐含规则更好:
对于那些无法依据句法进行分类却可提供列表的文件名,在应用静态格式规则时能够重新加载隐含规则链
如果不可以精确明确所使用的路径,则无法确认某些看似无关紧要但可能影响make运行错误的隐含规则;因为它们的选择顺序取决于它们在定义中的先后。而采用静态格式化规范就不会存在这些不确定性:每一项规范都会被精准地指派到特定的目标。
4.11双冒号规则
用于在目标名后将":"替换为"::"的规范是双冒号规则。当同一个目标在一个以上的规范中出现时(即多条规范),其处理方式与常规不同。
每当某个目标同时存在于多个不同类型的系统控制指令中时(即多条控制指令涉及同一个操作目的),这些控制指令必须全部属于同一种类型:要么全部采用双冒号格式表示关联关系的方式进行定义;要么全部采用非双冒号的方式进行定义)。在这种情况下:
- 如果这些控制指令全部采用了双冒号格式表示关联关系的方式进行定义,则它们彼此互不影响;
- 如果某一条特定的目标较早地被某个双冒号关联式指令所定义,则该关联式指令将被优先处理;
综上所述,在系统设计中应确保所有涉及同一操作目的的控制指令保持一致的一致性原则
双冒号规则实际上就是用来将具有相同目标的一组规则分成多个独立的部分。每个双冒号分隔符都负责独立地执行其对应的逻辑。这类似于这些分隔符各自都有不同的处理目标。
对于一个目标而言,双冒号规则将遵循它们在makefile文件中出现时的顺序。然而,在双冒号规则与执行顺序无关的情况下,这些规则具有重要意义。
双冒号规则不够清晰易懂,在这种情况下通常仅基于引起更新的依赖文件类型不同而采取不同的更新方式以实现目标变更。实际上,在大多数项目中使用双冒号规则的情况极为少见。
每个双重冒号规则都需要明确指定对应的命令;若未指定相关命令,则系统将默认应用隐式规则。如需了解更多信息,请参阅** 使用隐含规则** 。
4.12 自动生成依赖
在编写makefile脚本时...通常是用来描述各个目标文件所需依赖的基础头文件信息...例如,在main.c源代码中仅包含一次#include <defs.h>的情况下...则需遵循以下规则
这个规则告诉Make,在发生某些情况下,则会要求再构建相应的目标文件。这样就能理解,在处理大型程序时,开发者通常会在Makefile中编写大量类似的规则。此外,在添加或删除包含头文件(#include)的指令时也需要格外小心地修改Makefile。
以消除这种烦恼;现代C编译器通过分析源程序中的#include指令自动生成相应的规则;如果希望启用此功能,则可以在编译时使用选项‘-M’;例如,在以下命令中;
产生如下输出:
这样您就不必再亲自写这些规则,编译器可以为您完成这些工作。
因为makefile中提到创建了'main.o'这个目标文件, 所以系统不会默认将其视为中间文件而进行隐式处理, 这也意味着该目标不会自动被清除;请参阅** 隐含规则链** 。
在旧版make程序中,在编译器的支持下通过一个请求命令如'make depend'实现依赖关系的传统方法是一种传统的方法。这些命令会产生一个名为‘depend’的文件,在其中包含了所有自动生成的依赖项;随后可以通过include指令将这些自动生成的内容导入到当前的makefile中(参阅** 包含其它makefile文件** )。
在GNU make中,在重做makefile文件时所体现的特点使得这一做法逐渐成为过去的事情——无需告知make如何重建依赖项, 因为GNU make会自动处理所有过时的makefile文件. 参考** Makefile文件的重建过程**
我们习惯上采用自动生成依赖的做法是将makefile与源程序一一对应起来。例如,在每一个源程序文件' name.c '对应的makefile中为' name.d '列出该目标对象文件所需的全部来源代码。这种做法的一个显著优点在于只需在源程序发生更改时就重新扫描以生成新的依赖关系即可。
该系统遵循C语言源文件' name.c '生成对应名称为' name.d '的依赖文件,并按照特定规范流程制定相关格式规则。
有关格式规则的定义,请参阅** 定义与重新定义格式规则** 。
‘-e’选项指示当$(CC)命令不成功(导致非零终止状态)时立即终止程序。
通常情况下,在管道中使用lastcomm显示上一命令的状态后,默认情况下Shell会包含该信息;然而,在某些环境中如make工具中,默认情况下无法检测到编译器导致的非零终止状态。
在GNU C编译器中建议使用选项‘-MM’替代‘-M’
命令Sed的作用是翻译(例如):
到:
基于相同的源代码库和包含头文件的项目结构设计中,在编译过程中Make机制能够根据任何一个源代码库的变化情况来触发构建。这意味着每当任意一个包含头文件的目标发生变化时(即任意一个.d目标),相关的目标必须重新构建以保持项目的正确性。
一旦您制定了并定义了重构‘.d’文件的规则,则可以通过include命令直接读取它们,并参考** 包含其他makefile文件 **作为示例。
因此,在必要的情况下,.d的目标Makefile将与所有其他Makefiles一样,并且即使没有您发出任何进一步指令(例如make, make clean等),它也会在需要时进行重建。请参阅** Makefile文件的重新生成过程 **以获取详细信息。
5在规则中使用命令
该指令集由多个shell指令序列构成。
这些指令依次按照特定顺序运行。
除第一条指令可用分号开头并附着于目标依赖后外,
所有指令均须以Tab键启动。
空格和注释可以在指令中间出现,
并被忽略。
但需注意:
虽然看似像空白的部分是空格与注释会被忽略(即被忽略),
但实际是以Tab起始的那一部分被认为是真正的空操作,
而非真正的空白。
用户可以采用多种不同的shell工具,在makefile文件中未明确指定其他shell的情况下,默认会以缺省方式解析其中的命令。请参考** 命令执行** 相关内容。
shells 的类型会影响我们在终端界面是否能够添加命令行脚本上的注释以及编写这些脚本时所采用的标记语言. 当选择 '/bin/sh' 作为 shell 时, 带有 ' # ' 标记的注释会从起始位置一直到末尾. 此外, ' # ' 不在开头位置, 并且它本身并不是一个注释内容.
5.1 命令回显
通常情况下,在执行一个新命令之前, make会先显示该命令的路径信息. 我们之所以能够做到这一点, 是因为这种操作不会改变您编写的原始指令内容, 所以我们将其称为'回显'.
以带有@符号开头的行无法显示,在传递给shell时@符号会被丢弃。常见的情景是,在makefile文件中使用一个仅用于打印特定内容的命令——例如通过echo命令可以查看makefile文件执行的状态。
若使用make指令并提供'-n'或'--just-print'选项,则仅显示相应指令而不执行它们。可参考选项概要部分了解详细信息。此时才会发生的是所有输入都会被显示出来——包括那些以@开头的行。此标志尤其在无需实际运行任何指令就能了解make视为必需的指令时大有帮助。
-s
5.2执行命令
在操作过程中每条命令行都会启动一个专用子shell环境以确保其正确运行.(实际上 make 可能会忽略某些选项而无法影响结果.)
请仔细阅读以下内容并确认无误后再操作
请仔细阅读以下内容并确认无误后再操作
如果您希望将一个单独的指令拆分为多行文本,并且希望这些文本行能够按顺序连接起来形成一个新的完整指令,则需要在每一行末尾使用反斜杠符号(\),但需要注意的是最后一行除外。通过删除反斜杠后按顺序连接起来形成一个新的完整指令后再将其传递给shell系统即可实现功能与前一种方法相同
用作shell的程序由变量SHELL指定,在缺省情况下,默认选择程序‘/bin/sh’来充当shell。
在MS DOS环境中执行程序时,在变量SHELL未被赋值的情况下,默认 shell会被设定为COMSPEC的值。
在Windows 98等其他操作系统上运行与采用其他操作系统的机器相比,在Makefile文件中对SHELL变量进行赋值的行为处理存在差异。由于MS-DOS环境中默认配置下的BATCH文件仅包含简单的命令行执行功能(即'command.com'),导致许多Make项目人员倾向于安装替代性的Shell程序以改善效率。因此,在MS-DOS环境下运行时系统会根据所定义的Unix风格或DOS风格来调整其行为模式;即使将SHELL变量指向'command.com'也会导致系统仍会检测该变量并相应地进行响应行为调整。
如果变量SHELL被设置为采用Unix风格的Shell,在MS-DOS环境中运行make时会执行额外检查以确认指定的Shell是否存在;若不存在则会忽略该Shell。在MS-DOS环境中使用GNU make时,则会遵循以下步骤来确定合适的Shell选项:
在由SHELL环境变量所指设置的目录中。例如,在makefile文件指定`SHELl = /bin/sh'的情况下,则使make命令根据当前工作目录搜索子文件夹‘/bin’中的内容。
2、在当前路径下。
3、按顺序搜寻变量PATH指定的目录。
在预设的所有搜索目录下,默认情况下,MK系统会优先查找指定类型的应用程序(例如,示例中的'data'目录)。当目标程序未被发现时,MK系统将切换至预设的所有搜索目录,并按照固定模式检查具有固定扩展名或其他特定后缀名的应用程序,以实现完整的程序查找功能。其中常见后缀包括:'.exe'、'.com'、'.bat'、'.btm'以及与项目相关的其他特定后缀如'.sh'等。
在成功检索到任何 shell 的情况下(其中一种情况),变量 SHELL 将被赋值为该 shell 的完整路径(全路径文件名)。然而若所有尝试均未成功,则变量 SHELL 的值保持不变(其设置会失效)。相应地,在未找到合适的 shell 时其相关命令行指令的有效性也会受到影响(尤其是针对 Unix 风格系统的情况)。这是因为 make 命令在其运行环境中仅支持特定类型的 Unix 风格 shell 设备特性(如 shells 命令)。
请务必注意这种针对shell的扩展搜索仅限于makefile文件中对变量SHELL进行配置的情形。若要在环境或命令行环境中进行设置,请确保指定shell的全路径文件名,并且所指明的全路径文件名应与在Unix系统中运行时一样精确无误。
经过上述DOS特色的处理后,此外您还需要将sh.exe安装到指定目录中,或者在makefile文件中设定SHELL环境变量为/bin/sh(与多数Unix系统的makefile文件配置方式一致)
不像其它大多数变量那样遵循常规模式, 变量 SHELL 通常不会根据环境条件来设置. 它的作用是指示您自己选择交互使用的 shell 程序. 如果在环境中对 SHELL 进行设置, 将会直接影响 makefile 文件的功能, 这并不是一种理想的做法, 参阅 ** 环境变量** . 在 MS-DOS 和 MS-WINDOWS 系统中,默认情况下不设置 SHELL 变量以避免影响 makefile 功能, 因为这些系统的大规模用户群普遍认为无需自行配置 shell 程序. 不过, 如果发现 set SHELL 在默认配置下不适合您的工作流程, 您可以选择在环境中定义一个替代的 make 使用 shell 程序, 例如 set MAKE=MAKESHELL; 这种做法会使得 SHELL 的默认值失效
5.3 并行执行
段落保持不变
段落保持不变
段落保持不变
若-j选项后紧跟整数,则该整数值代表每次执行所处理的指令数量;此数值即为job slots值。若-j选项后未指定数值,则表明并未限定job slots的数量上限。默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认槽位值为一,在这种情形下会依次处理每一条指令(即单个指令串行处理)。当允许多条指令并发处理时(即多条指令同时并行运行),虽然可提高处理效率但这一做法的一个不是很理想的结果是各指令输出呈现时间对应关系(即各指令输出可能会显得杂乱无章)。
另一个问题是两个进程无法在同一设备上实现标准输入同步,因此必须确保在每次操作中仅允许一条终端指令进行标准输入. make命令仅能保证当前执行中的指令的标准输入通道有效,而其他所有标准输入通道将失去有效性.这意味着如果多个用户同时向标准输出设备提交指令时,对于绝大多数子进程而言将会导致系统出现严重的错误(例如引发'Broken pipe'异常).
程序对一个有效的标准输入流(它从终端运行或您为make设备进行调整后的设备上使用)的需求是不可预知的。执行第一条操作的程序总是会首先接收这个标准输入流。在处理完第一条指令后开始执行第二条指令时会接收下一个标准输入流。依此类推。
当我们在寻找更好的替换方案时遇到了挑战,则会决定对make的工作流程进行调整。
请注意,在此期间,请您务必避免使用任何依赖于标准输入的命令。如果不遵守这些 instructions ,某些命令在正常情况下将无法正常运行。
那么在并行处理未被启用的情况下,默认情况下所有依赖于标准输入的命令都能正常运行。
最后,在make内部嵌套调用也会引发问题。更详细的内容参阅** 与子make通讯的选项**
若某指令因中断或非零退出而失败,并因该指令导致的错误不可忽视(参阅** 指令错误** ),则构建同一目标的所有后续指令行将终止。若某指令因' -k ' 或' --keep-going '选项未提供而导致失败,则 make 将停止继续处理。若因为某种原因(如中断), make 需终止操作时,则当前正在运行中的子进程将在所有它们都完成之后才真正退出。
当系统处于高负载状态时
-| 2.5
禁止make在平均负荷超过2.5时执行某项任务。如果未提供数据,则撤销前面指定的负荷限制。
更详细地说,在make启动一个新任务的过程中(即当make开始执行一个新任务),而在此之前系统已经有一个以上的现有任务在运行中,则系统会评估当前系统的负载情况;如果发现此时系统的负载水平不小于指定的阈值(由'-|'选项设置),则系统会保持静止状态,并待上述条件满足后才会继续执行下一个可用的任务。
缺省情况下没有负荷限制。
5.4命令错误
每当某个shell命令执行完毕时, make会检查该command的exit status. 如果该command成功地完成, 随后的新command会被置于一个新的subshell环境中. 当最后一个command完成后, 这条规则也随之结束. 如果出现错误(非零exit status), make将放弃当前这条规则, 或者说可能会放弃所有相关的规则.
有时个别特定的命令出现故障并不一定是问题本身。举个例子来说吧:如果您尝试使用mkdir命令来创建一个已经存在的目录的话,mkdir将会报错。但是,在这种情况下您或许应该继续尝试用make来执行后续操作。
要忽略一个命令执行产生的错误,请使用字符‘-’(在初始化TAB的后面)作为该命令行的开始。当通过shell传递该命令时,字符‘-’会被丢弃,并且该命令行将被视为一个独立的指令块进行处理。例如:
这条命令即使在不能删除一个文件时也强制rm继续执行。
在运行make指令时若采用'-i'或'--ignore-errors'选项,则会忽略所有涉及此命令执行所产生的错误信息。对于位于makefile中的那些没有依赖项的特殊目标来说, 通过设置IGNORE标志同样能达到这一目的. 然而, 由于字符'-''的灵活性较高, 因此这条指令如今已很少被采用.
当采用‘-’或‘-i’选项时,在运行过程中产生的错误会被忽视;这种情况下与成功运行并返回结果的命令相仿地处理带有返回误差的任务;主要区别在于会输出一条信息告知您该操作导致程序退出的具体编码状态以及确认此误操作已被忽视;若出现这样的情况即发生此类操作而系统未明确指出误操作已被忽视,则暗示当前目标无法重新构建成功;此外与其直接相关的或其他间接相关的项目也将无法进行重建;由于前一步骤未能完成任务进而导致后续的操作无法继续进行下去
在上述情况下,在大多数情况下(通常是默认情况),make程序会在遇到无法完成的任务时停止执行当前操作并返回一个非零状态代码以指示错误。然而,在指定包含'--keep-goning'或'-k'选项参数的情况下(即告诉make继续尝试完成剩余的任务),它会继续处理其他依赖项直到所有可能的任务都被处理完毕或者再次遇到无法完成的任务为止。例如,在编译一个OBJ文件时出现错误后(即使知道无法将所有OBJ文件一次性连接编译成功),使用'--keep-goning'或'-k'选项可以让make继续处理其他未受影响的OBJ文件以避免报错并终止整个编译过程。详细内容参见**选项概要**部分)。通常情况下,在告诉make您希望更新特定的目标之后(一旦发现这是不可能的),它会立即报错并终止整个编译过程以避免不必要的工作量)。'--keep-goning'或'-k'选项的作用是告诉make真正想要测试的是程序中所有潜在问题的变化可能性(或者寻找几个独立的问题以便可以在下次编译之前纠正它们)。这是Emacs编译命令缺省情况下传递这个特殊选项的原因所在
一般情况下,在某个命令运行失败的情况下:
如果导致了目标发生更改,
可能导致目标无法正常使用或仅部分完成更新。
然而,
时间戳表明,
在下次运行时,
系统将不再重新生成该修改过的文档。
这种状况类似于命令被强制终止的情况,
参考有关中断或关闭make的讨论部分。
因此,
在修改完文档后发现出现错误,
通常建议删除已修改的文档副本。
特别地,
如果.DELETE_ON_ERROR选项被指定为目标对象之一,
则会触发某些特定行为:
这是您应明确要求make执行的行为,
而非以往的习惯做法;
特别是在考虑兼容性问题时,
这一步骤应予以特别强调。
5.5中断或关闭make
每当在执行一条命令时出现错误信息,则系统将基于最初检查的时间戳以及最后一次修改的时间戳是否有变化来决定是否删除该命令的目标文件。
删除目标文件的主要目的是保证当make下次运行时能够获得最新的更新信息以确保项目能够顺利进行。原因在于,在程序中断时若未及时清除部分完成的二进制代码(如OBJ文件),则会导致所生成的目标代码(如OBJ时间戳)较新从而影响最终项目的正确性与稳定性
您可以将某个特定的目标作为特殊的.PRECIOUS依赖项来配置,并利用此机制阻止Make意外地清除该特定的目标。当重新构建项目时,Mak会首先检查当前的目标是否存在在特定的依赖列表中, 特别是.PRECIOUS项中, 这一机制有助于决定何时清除或保留相关的目标。可能的原因包括:因为每次更新都是单独的操作(即原子化操作), 或者是为了单纯记录修改时间(虽然无用但记录方便), 或者必须始终存在以防其他潜在错误等
5.6递归调用make
递归调用允许在 make 中将 make 作为一个可执行命令使用。这一技术特别适用于将大型系统的 make 分解为多样化的子系统。例如:
- 假设您有一个名为 'subdir' 的子目录。
- 您希望在该子目录运行 make 并使用其中的 makefile 文件吗?
- 可以通过以下方式编写:
- 在 subdir 目录下创建一个与主 project 相关的 Makefile 文件。
- 在主 project 的 Makefile 中引用这个 subdir 的 Makefile,并将其作为 build 所需的依赖项。
或, 等同于这样写 (参阅** 选项概要**):
您只需要复制上述示例中的代码片段即可生成make函数的递归调用,并且请确保理解其工作原理。同时,请弄清楚它们为什么会这样运行以及子_make与上层_(make)之间的相互作用机制。
为了提高使用便捷性, GNU make将变量CURDIR赋值为当前工作目录. 当采用'-C'选项时, 该变量的内容将是新目录而非原有目录. 该变量与makefile中的设定具有相同的优先级(默认情况下, 环境变量CURDIR无法重载). 注意, 在执行make命令时无法重新指定该变量.
5.6.1 变量MAKE的工作方式
在嵌套调用的情况下,默认情况下递归执行make命令时会自动使用系统变量MAKE来替代直接引用'make'命令名称。
该变量存储的是用于调用make程序的文件路径。若目标文件路径指定为'/bin/make'时,则运行命令应为cd subdir && /bin/make。当在父级makefile中选择特定版本的make工具时,在递归调用过程中也会采用同一版本。
通过在命令行中引入变量MAKE,可以调整诸如「-t」(即「--touch」)、「-n」(即「--just-print」)以及「-q」(即「--question」)等选项的作用。若在一个以变量MAKE开头的命令行前添加字符「+」也会达到类似效果。参阅** 替代执行命令** 。
设想在示例中使用make -t命令时会发生什么情况。通常情况下,在示例中使用make –t只会生成一个名为subsystem的文件,并不执行其他操作。当您运行cd subdir && make –t时,您的意图是什么?您希望启动子目录吗?
Make的一个显著特点是:当命令行中包含变量MAKE时,在选项-t、-n和-q的情况下这些选项不会对该行产生任何影响。尽管存在这些选项能够阻止命令执行的情况(例如包含变量MAKE的命令行),但仍然可以通过这种方式运行包含MAKE变量的命令行程序。Make实际上通过传递MAKEFLAGS变量将这些设置传递给子make程序(参阅** 与子make通讯的选项** )。因此,在验证文件、打印请求等操作都能成功传递给子系统进行处理
5.6.2与子make通讯的变量
基于明确的要求,在上层_make变量中的值可通过环境传递至子_make;在该情况下,在下层环境中可默认进行定义;当不采用\-e开关时,“参阅\texttt{\_\_command\_summary\_}”内容无法替代下层所使用的\texttt{Makefile}文件中的指定参数设置。
在向下方传递或输出一个变量时,在执行每个命令的过程中都会将其以及其值加入到所使用的环境中。子make则利用其环境来初始化其变量值表。参阅** 环境变量**。
除了明确指定外,在环境中定义并初始化的或在命令行中设置的环境变量仅被make向下输出,并且这些环境变量的名称必须仅限于字母、数字及下划线字符。有些shell对于含有非字母数字及下划线字符的环境变量无法处理。然而例外的是像SHELL及MAKEFLAGS这样的特殊变量(除非有意隐藏),它们通常都会被显示出来。即使将(makefile)设定为非默认值也不会影响这一行为
Make自动传入命令行中预设好的变量值的方式是将这些值存入MAKEFLAGS变量中。具体信息请参见下节。Make默认创建的变量值无法向下传递,在子 make中可以自行定义它们。如果希望将指定变量传递给子 make,请使用 export 命令。
您要将阻止一些变量输出给子make,请用unexport指令,格式如下:
为方便起见,您可以同时定义并输出一个变量:
下面的格式具有相同的效果:
以及
同样,
亦同样:
参阅** 为变量值追加文本** 。
您可能会发现,在make与shell中执行export和unexport指令的行为是一致的;例如,在sh中。
如果您要将所有的变量都输出,您可以单独使用export:
export
告诉make输出所有未被export或unexport处理的变量;即使这些在unextract中被提到的变量也无法获得输出结果。如果仅以extract作为默认输出方式使用,则包含非字母数字及下划线字符的变量将无法通过这种方式获得输出;除非您明确指出并使用extract指令来指定这些特定变量应被包含在内,则它们将无法通过这种方式被排除在外
单独执行export操作是老板本GNU make的默认行为。如果您在makefile中依赖这些操作,并且希望与老板本 GNU make兼容,则可以在.EDIT/Makefile中添加一个规则即可。该规则将不被.EDIT/Makefile所影响,并且如果同时使用export指令将会导致错误。
同样地,在某些情况下您可以仅通过单行命令指定make行为以避免输出变量。由于这是缺省设置的做法只有当您在某个包含文件中的makefile中首次调用时才需要特别处理无需额外操作即可正常工作因此无需单独配置这一点通常不会引起混淆或者问题出现。需要注意的是不应同时尝试用单独的 export 和 unexport 指令来控制特定命令的行为因为这样做只会导致混乱而无法实现预期效果最终由最末尾的 export 或 un export 指令将完全定义 make 的运行模式。
作为一个显著特征,在层次传递过程中变量MAKELEVEL的值会发生变动。其一数值类型为字符型,在层级结构中以十进制数字表示层级深度。其中'0'对应顶层make,'1'对应子make,'2'则代表子-子make以此类推。每次命令创建新环境时会增加此数值
该变量的主要功能是判断在一个条件指令中是否执行(参考** _makefile文件中的条件语句**)。通过这种技术手段后,您将能够构建一个makefile,并根据需要选择不同的运行方式。
建议您在构建过程中使用环境变量MAKEFILES来指定所有子构建所需的附加makefile文件。请注意,在此配置中,默认情况下仅包含当前目录下的目标目标目标目标目标目标目标目标目标目标目标target target target target target target target target target target target targettargettargettargettargettargettargettargettargettargettargettargettarget. 如果希望包括其他目录中的目标,则应在Makefile中指定完整的目标路径。例如:$ cat Make/Make/Make/Make/Make(target)/Make/Make/Make/Make/Make(target)/Make/Make/Make. 参考** 变量MAKEFILES **部分以获取更多信息并完成配置设置。
5.6.3与子make通讯的选项
诸如' -s '与' -k '标志会自行传递至子 make 并经由变量 MAKEFLAGS 接收。此变量会由 make 自行建立并内含着其接收的所有标志字母。因此,在使用' make –ks '时, 变量 MAKEFLAGS 将获取值' ks '。
其结果为:任意一个make进程在其运行环境中为其MAKEFLAGS变量赋值;在响应中, make将根据这些设置进行处理,类似于它们被当作参数传递。参阅** 选项概要 。
同样地,在命令行中定义的变量也会通过传递给子make来使用MAKEFLAGS这一特殊机制进行处理。MAKEFLAGS值中的字符可以包含等于号'='。子make在接收MAKEFLAGS参数时会按照相应的规则进行解析与应用操作。需要注意的是,在这种机制下所执行的操作与直接在命令行中指定参数的行为是相同的
选项-C', -f', `-o', 和 ‘-W’不能放入变量MAKEFLAGS中;这些选项不能向下传递。
‘-j’选项是一种特殊的情况(参见** 并行执行** )。如果您将其设置为某个数值N,并且您的操作系统的相应功能得到实现(大多数Unix系统已实现这一配置;其他操作系统通常不支持),那么父make与所有子make之间会确保仅在它们之间同时运行不超过N个任务。需要注意的是,在这种情况下,“代替执行命令”的概念可能会导致某些复杂性——具体来说,“代替执行命令”的任务不会计入总任务数内。(大多数Unix系统支持这一配置;其他操作系统的相关功能通常未实现)
当您的操作系统无法支持上述通信机制时
为了避免其他标志向下传递的效果,请通过调整变量MAKEFLAGS的值来遵循以下规定。
在命令行执行过程中定义的变量会直接出现于MakeOVERRIDES中,并且MakeFLAGS这一目标变量会包含上述定义过的引用值。如果希望仅向下传递标志而不传递命令行参数,则建议将MakeOVERRIDES字段设置为空值
这个功能的实际作用有限。然而,在某些情况下(如环境容量设定有一个固定的上限)以及当数据量较大时(如该值相对较小),将如此多的信息存储到MAKEFLAGS变量中可能导致超出其容量限制。(根据严格的POSIX.2标准规定,在makefile文件定义特殊目标‘.POSIX’时修改MAKEOVERRIDES变量不会影响MAKEFLAGS变量)或许这并不是一个特别重要的考虑因素(也许您不需要过于关注这个问题)。
为了与早期版本兼容,并且提供相同功能的变量MFLAGS也存在。需要注意的是,在不包含命令行定义变量的情况下,默认情况下这些参数的行为会有所不同。值得注意的是,在不包含命令行定义变量的情况下,默认情况下这些参数的行为会有所不同。特别地,在不是空值的情况下,默认情况下这些参数的行为会有所不同;其值与MAKEFLAGS相同,并且除非它是空值,默认情况下这些参数的行为会有所不同;其值总是以短线开头(MAKEFLAGS只有在与多字符选项一起使用时才会以短连接线开头),例如:默认情况下这些参数的行为会有所不同;其值总是以短线开头(MAKE(FLAGS只有在与多字符选项一起使用时才会以短连接线开头),例如:默认情况下这些参数的行为会有所不同;其值总是以短线开头)。默认情况下这些参数的行为会有所不同;其值总是以短线开头。(MAKE FLAGS只有在与多字符选项一起使用时才以短连接线开头)例如:
现在由于变量MAKEFLAGS的存在这种用法不再必要。如果您希望您的makefile文件与老版本的make程序兼容请采用这种方法这种方法在现代make系统中同样有效
如果您需要在每次运行make命令时都设置特定选项(例如 '-k' 选项,请参阅 选项概要 ),那么变量MAKEFLAGS将非常有用。您可以简单地在环境中为该变量赋值(或在makefile文件中指定附加标志),这些标志会对整个makefile文件产生影响。(请注意,在某些情况下无法通过这种方式设置MFLAGS变量)。
当解释变量MAKEFLAGS赋值时(无论是在环境中定义还是在makefile文件中定义),若该赋值不以短划开头,则系统会默认为其附加一个短划;随后将其分解为单词(单词之间由空格分隔),并对其进行语法解析(如同命令行参数一般处理)。值得注意的是,“–C”, “–f”, “–h”, “–o”以及它们对应的长名称版本会被忽略;对于无效或未定义的参数则不会产生错误提示。
当在一个环境中设置变量MAKEFLAGS时,请避免使用会影响make运行的关键指令,并破坏makefile文件本身以及影响make自身运行的选择。例如指定选项-t、-n和-q中任何一个到MAKEFLAGS变量中可能会导致严重的后果或至少带来令人不快的结果。
5.6.4 ‘--print-directory’选项
当您进行多层make递归调用时
说明进入目录中,还没有进行任何任务。下面的信息:
说明任务已经完成。
一般情况下,默认情况下无需明确指定该参数值即可完成操作:因为make会自动生成相应的设置参数值(如-W),因此当在命令行中执行'make -C某目录'时,在子make操作中也会启用-W参数设置;然而如果采用'-s'开关来选择 silent模式(即禁止输出日志信息),则相应的-W参数就不会被开启(除非同时使用 '--no-print-directory' )
5.7定义固定次序命令
在创建各种目标时(特别是当您希望保持一致操作顺序)(建议)使用define指令来指定一组具有固定执行顺序的操作(并根据目标规则引用该序列)。由于此序列本质上是一个变量(其名称必须与现有其他变量名称不重复),因此必须为其分配独特的标识符以避免冲突。
下面是定义固定次序命令的例子:
run-yacc被用作一个变量名称;endef标志用于表示结束;中间的部分属于命令。该指令在执行时不会扩展对已引用变量或函数的引用;涉及的各种符号(如'$$')、括号以及变量名称等都被整合为您所定义的相应变量值的一部分;该部分详细介绍了如何进行多行变量的**定义**。
在这个例子中,在处理任何遵循固定顺序的规则时,在第一条指令阶段会启动对第一个依赖进行Yacc操作,并使由Yacc生成的输出文件统一命名为‘y.tab.c’;随后在第二条指令阶段,则会将这些输出内容转移到规则的目标文件中。
当我们按照固定顺序应用时,在遵循规则中所涉及的命令使用的具体数值进行操作时
当按照指定数值应用相关指令并执行运算操作时
当按照指定数值应用相关指令并执行运算操作时
当按照指定数值应用相关指令并执行运算操作时
当固定次序‘run-yacc’运行时,‘foo.y’将代替变量‘^’,‘foo.c’将代替变量‘@’。
这是一般情况下适用的一个案例,但它并非必要条件。例如,在make系统中,默认会根据文件名类型来决定要执行的操作,请参阅**使用隐含规则**部分。
当运行命令时,在固定的顺序中执行的每一行与直接出现在规则中的命令相同。为了实现这一点,在每条目前面添加一个Tab键会使make工具启动一个独立的子shell环境。另外一种方法是,在每条目前面可以附加一些影响后续操作前缀字符(如@, -, 和 +),请参阅** 在规则中使用命令** 。例如,请参考以下固定的顺序:
make将不回显第一行,但要回显后面的两个命令行。
此外,在按固定顺序引用的命令行中使用前缀字符时,则该前缀字符将在每一行上应用这一规则。例如这个规则如下:
将不回显固定次序的任何命令。具体内容参阅** 命令回显** 。
5.8 使用空命令
定义无功能或无关紧要的命令有时很有用;可以通过简单的指令创建一个仅包含空格而没有其他内容的命令;例如:
该程序为字符串‘target’创建了无操作命令。此命令行操作采用了Tab开头的方式设定无操作指令。然而这种做法可能因视觉上显得过于空旷而引发混淆。
也许有些用户会对此感到疑惑:为什么我们需要定义一个空命令?唯一的理由是为了防止当目标进行更新操作时,默认调用由隐含规则提供的相关命令。(参阅** 使用隐含规则** 以及** 定义最新类型的缺省规则** )
或许您更偏爱定义针对真实不存在的目标文件的空命令?因为这样其依赖关系能够被重新构建。然而这种方法并不理想——毕竟如果目标文件实际上存在,则其依赖关系可能不会被重新构建。因此在这种情况下推荐采用假想目标作为替代方案——参阅** 假想目标**
6 使用变量
在makefile中被定义为名字的变量用于替代一段特定文本;这段特定文本被称为该变量的值;根据需求,在makefile的不同部分使用这些值来替代目标、依赖项或命令;为了实现这一功能,在其他版本中的Make系统中将这种机制称为宏(macros)。
当读取makefile文件时,在遵循以下规则的情况下进行处理:首先排除shell语句、右侧赋值的变量以及通过define指令定义的部分;而对于其余所有变量和函数,则会进行扩展处理。
变量可替代文件列表;编译器接收的相关选项;执行的任务;获取源文件所在的目录位置;目标输出路径;或您可以想象的各种文本。
变量名排除了以‘:’,‘#’,‘=’开头或结尾,并且不能带有前导或尾随空格的任何字符串。然而,在变量名中包含除了字母、数字以及下划线之外的其他字符时,则应尽量避免这样做。这是因为这些特殊字符可能在未来被赋予特定含义,并且对于某些shell来说无法通过环境传递给子make(参阅** 与子make通讯的变量** )。此外,请注意变量名对大小写的敏感性:例如,在不同的大小写形式下(如‘foo’, ‘FOO’, 和 ‘Foo’),同一个变量将代表不同的实体。
习惯上,在程序设计中,默认采用大写字母作为变量名称。然而,在makefile文件内部实现时,默认采用小写字母作为变量名称更为合理。为了便于管理隐含规则和其他功能模块的需求,在程序设计中,默认优先分配大写字母用于定义这些特殊功能相关的变量名称。参阅相关文档中的** 变量重载** 部分了解详细信息。
一小部分变量采用了标点符号或其他几个字符作为名称,在这种情况下这些变量子为自动生成的标识符并具有特定功能参考有关详细信息
6.1 变量引用基础
使用美元符号后跟圆括号或大括号包裹变量名即可表示其值:这两个表达式都可以表示变量foo的值。这是因为单独使用会导致在命令或文件名中需要双倍使用以获得单个的效果。
在程序的不同部分或上下文中均可使用变量引用:目标对象、其他依赖项、命令行参数以及其他大多数指令中的参数和新定义的变量名等。举个常见的例子,在编程逻辑中我们通常会使用一个变量来存储所有OBJ文件的名称:
变量的引用按照严格的文本替换进行,这样该规则
该程序支持对文件名带有单引号的C语言源代码'prog.c'进行编译。在内存地址分配过程中,默认情况下变量名前后不带空格会被视为同一地址;因此,在编译后的运行环境中,变量foo对应的实际内存地址存储的是大写字母'C'。(请注意,在您的makefile中不应这样操作!)
当美元符号后紧跟一个非美元符号且非圆括号或大括号的字符时,则该字符被视为单个变量名称。因此可以采用‘$x’的方式引用对应的变量。然而,在仅限于自动变量的情形下应用这种做法,在其他实际工作场景中则应予以全面禁止。参阅上述所述的内容中的** 自动变量** 部分。
6.2 变量的两个特色
在GNU make中支持将变量赋值为两种不同的形式,在这种编程环境中称作变量的两个独特表现(two flavors)。它们之间的主要区别体现在它们如何进行定义以及在扩展时的行为上。
该类型变量子重其主要特点是以递归调用来实现数据延伸。具体而言,在命令行界面中可通过等于号将所需数值赋给目标位置(参阅** 设置单点赋值** ),或者借助define指令完成多点数值的一并赋值(参阅** 批量赋值操作** )。值得注意的是,在执行上述操作时,默认会对您输入的具体数值进行逐一处理;若其中包含其他变量子引用,则会在该处展开处理。我们将这一机制统称为基于递归调用的数据延展机制,并提供相应的示例说明以便理解其工作原理。
将回显‘Huh?':‘(foo)’扩展为‘(bar)’,进一步扩展为‘$(ugh)’,最终扩展为‘Huh?’。
这种特殊的变量与其他MAKE版本支持的不同类型的变量存在差异。它既有不足之处也有其优势。普遍认为这类特殊的变量的优势在于它们能够提供更高的灵活性和可扩展性。
即旨在执行其所需任务的标志符是'CFLAGS';当它被展开到命令中时,最终会被展开为'-Ifoo -Ibar'." 其主要缺陷在于无法在其后附加内容,例如,在以下场景中:
当在变量扩展过程中可能出现无限循环时(事实上,在检测到无限循环时系统会生成相应的错误提示)。
它还存在一个主要缺陷在于其在定义阶段对所有涉及**文本转换函数**的使用会导致变量提前展开并触发即时执行的行为。这种行为不仅会导致make程序效率降低、整体性能水平下降;还可能导致通配符操作与shell内建函数之间出现难以预测的相互影响效果。
为了避免此问题以及递归调用扩展型变量带来的不便, 引入了一种新的特殊变量: 简单扩展型变量.
简单扩展型变量通过‘:=’进行定义(参阅** 设置变量** )。其值经过一次扫描就会永久保持不变,在它们被引用时,在定义阶段已经进行了展开。当它们被引用时,在定义阶段已经进行了展开。其数值直接反映您所写的代码扩展内容。因此这种类型不会包含任何其他变量或函数的引用;一旦在其定义时就完整地包含了相关的内容。
等同于:
当引用一个简单的扩展型变量时(即非全局扩展),它的值也会进行逐字符替换操作。以下是一个较为复杂的示例:具体展示了' := '运算符及其与shell脚本连接实现之间的关系(参阅函数shell)。此外还表明了另一个相关变量MAKELEVEL的作用机制:当该变量用于跨层级传递数值信息时其行为会发生变化。(参阅与子make通讯相关的文档可获取关于MAKELEVEL的详细信息)
按照这种方法使用‘:=’的优点是看起来象下述的典型的‘下降到目录’的命令:
由于在绝大多数程序设计语言中这些变量的行为类似于传统变量的行为,并且能够通过自身的属性直接进行赋值操作。这些特性使得复杂程度较高的makefile编译过程变得更加高效可靠,并且减少了手动干预的可能性。此外,在一些特殊场景下通过编写自定义脚本还可以进一步增强其功能限制以及性能指标的表现能力
注:改动说明:
- 将"您"改为"您可以"以增强指令性
- "使用"改为"采用"
- "将"改为"引导"
- "一般"改为"通常"
- "这个特点意味着"改为"这一特性表明"
- "象这样:"改为"例如:"
此处变量space赋值为一个单个空格。为了便于理解与后续操作相关联,在代码中加入了注释标记‘# end of the line’。需要注意的是,在代码末尾添加一个额外的单个空间并不会影响功能实现(然而这显得有些笨拙)。如果将一个额外的空白字符放置在变量值之后,则可以在代码末尾添加注释以明确指示其用途(这是一个不错的选择)。相反地,在不添加额外空间的情况下处理则更为高效与简洁。请务必注意不要在其后的几行末尾随意放置多个空白后再添加注释。以下是一些具体的示例供参考:
此处变量dir赋值为' /foo/bar '(包含四个尾随空格),不符合预期。
其中预设正确值应为' /foo/bar '。
另一个用于给变量赋值的操作符是‘?=’;我们将其称为条件变量赋值操作符;因为该操作符仅在变量尚未被定义时起作用。此声明为:
和下面的语句严格等同(参阅** 函数origin** )
注意,一个变量即使是空值,它仍然已被定义,所以使用‘?=’定义无效。
6.3变量引用高级技术
本节内容介绍变量引用的高级技术。
6.3.1替换引用
替换引用即按照您所指定的变量替代同一个变量中的特定位置。
其表示方法通常写作(var:a=b)或{\rm var: a = b}。
具体而言,则意味着将该变量的所有以a结尾的位置替换成b。
在变量值表示为单个字符的情况下,在变量值以单一字符形式呈现时,并规定,在变量值以单一字符形式呈现时
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
例如:在类似的情况中
**_变量设置_**
**_字符串替换和分析函数_**
**_字符串替换和分析函数_**
社值变量‘bar'的值为‘a.c b.c c.c'。
6.3.2嵌套变量引用(计算的变量名)
**_文本转换函数_**** _字符串替换和分析函数_ ):**
a_dirs
1_dirs
a_files
1_files。
**_变量的两个特色_**
6.4变量取值
**_变量重载_**
**_设置变量_**** _定义多行变量_**
**_环境变量_**
**_自动变量_**
**_隐含规则使用的变量_**
6.5设置变量
**_变量的两种特色_**
**_隐含规则使用的变量_**** _自动变量_**
**_函数origin_**
和
6.6 为变量值追加文本
变量objects 设置为‘main.o foo.o bar.o utils.o another.o'。
使用 `+=' 相同于:
**_变量的两种特色_**
**_设置变量_**
等同于:
粗略等同于:
**_隐含规则目录_**
6.7 override指令
**_变量重载_**
或
**_为变量值追加文本_**
除了在define指令中应用override指令外,请注意以下是一个示例:
6.8定义多行变量
**_定义固定次序命令_**
**_变量的两个特色_**
**_执行命令_**
**_override指令_**
6.9 环境变量
**_选项概要_**
**_递归调用make_**** _与子make通讯的变量_**
**_执行命令_**
6.10 特定目标变量的值
**_自动变量_**
或这样:
6.11 特定格式变量的值
或这样:
7 makefile文件的条件语句
7.1条件语句的例子
7.2条件语句的语法
ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"
ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"
ifdef variable-name
**注意ifdef仅仅测试变量是否有值。它不能扩展到看变量是否有非空值。因而,使用ifdef测试所有定义过的变量都返回‘真’,但那些象‘foo=’情况除外。测试空值请使用ifeq($(foo),)。例如:**
设置‘frobozz'的值为‘yes', 而::
设置‘frobozz' 为‘no'。
ifndef variable-name
**_自动变量_**
7.3测试标志的条件语句
**_字符串替换和分析的函数_**
**_递归调用make_**
8 文本转换函数
8.1函数调用语法
或这样:
8.2字符串替换和分析函数
$(subst from,to,text)
$(patsubst pattern,replacement,text)
寻找‘text’中符合格式‘pattern’的字,用‘replacement’替换它们。这里‘pattern’中包含通配符‘%’,它和一个字中任意个数的字符相匹配。如果‘replacement’中也含有通配符‘%’,则这个‘%’被和‘pattern’中通配符‘%’匹配的文本代替。在函数patsubst中的‘%’可以用反斜杠(‘\’)引用。引用字符‘%’的反斜杠可以被更多反斜杠引用。引用字符‘%’和其它反斜杠的反斜杠在比较文件名或有一个stem(径)代替它之前从格式中移出。使用反斜杠引用字符‘%’不会带来其它麻烦。例如,格式‘the%weird\ %pattern\ '是‘the%weird' 加上通配符‘%'然后和字符串‘pattern\ '连接。最后的两个反斜杠由于不能影响任何统配符‘%’所以保持不变。在字之间的空格间被压缩为单个空格,前导以及结尾空格被丢弃。例如:
等同于 :
等同于:
要得到这些文件的源文件,您可以简单的写为:
代替规范的格式:
$(strip string)
**_makefile中的条件语句_**
$(findstring find,in)
**_测试标志的条件语句_**
$(filter pattern...,text)
$(filter-out pattern...,text)
下面产生不包含在变量‘mains’中的OBJ文件的文件列表:
$(sort list)
**_VPATH:依赖搜寻路径_**
**_override指令_**
8.3文件名函数
$(dir names...)
产生的结果为 ‘src/ ./’。
$(notdir names...)
$(suffix names...)
$(basename names...)
产生的结果为‘src/foo src-1.0/bar hacks’。
$(addsuffix suffix,names...)
结果为‘foo.c bar.c’。
$(addprefix prefix,names...)
结果为‘src/foo src/bar’。
$(join list1,list2)
$(word n,text)
返回 ‘bar’。
$(wordlist s,e,text)
返回`bar baz'。
$(words text)
$(word $(words
),
)
$(firstword names...)
生成结果为'foo'。 尽管 $$(firstword ``text``) 和 $$(word 1,``text``)的作用相同(即它们均执行相同的操作),然而第一个函数因其简单的实现得以保留下来。
$(wildcard pattern)
**_在文件名中使用通配符_**
8.4函数foreach
**_变量的两个特色_**
8.5函数if
**_条件语句的语法_**
8.6函数call
该函数call是唯一用于创建带有参数的新函数的方式。
您可以通过将一个复杂的表达式赋值为变量,
进而使用call以不同的参数调用该表达式。
函数call的语法为:
在make展开此函数的过程中,在每个参数'param'被赋值之前都需要为它们分配独立的临时存储空间1、2等。其中独立的临时变量$0则继承自全局变量'variable'。值得注意的是,在调用此类函数而不提供任何参数时,默认行为并无实际意义。
其中,在这些临时变量中定义了一个名为'variable'的新类型。该引用直接指定了当调用函数call时的第一个参数应取名为'param'。
请注意:'variable'代表一个标识符而非引用。
因此建议避免使用'$'符号及圆括号来表示该标识符。
同时建议避免在直接引用时混用双引号与单引号,
否则会导致解析错误。
当一个变量名被识别为内置函数名称时,在某些编程环境中(即使存在名为相同且由make生成的自定义变量),该内置函数会被触发调用)。在某些编程环境中(即使存在名为相同且由make生成的自定义变量),内部函数call会在分配临时变量之前先展开其参数(意味着,在涉及内部函数call的行为中使用特定术语可能会导致与预期不同的结果)。例如,在涉及内部函数call的行为中使用特定术语可能会导致与预期不同的结果。以下实例可以帮助理解这一机制。
该例子时使用宏将参数的顺序翻转:
这里变量foo的值是‘b a’。
以下是一个引人注目的案例:该宏被定义为一个用于操作文件的工具;通过该宏的操作符可以在变量PATH涵盖的所有目录中快速定位到第一个满足条件的程序。
现在变量LS的值是‘/bin/ls’或其它的类似的值。
在函数调用中可以采用嵌套结构。每一次递归调用都可以为其自身的局部变量(如$(1))进行初始化操作,并替代上一层函数调用所赋予的值。例如,在这种情况下实现了映像功能。
现在您可以使用映射(mapped)仅接收一个参数的函数例如 origin 一次性获取多个数据点:
最后变量o包含诸如‘file file default’这样的值。
在调用function call时,在参数中的空格必须谨慎处理。由于在其它函数中,后续参数未去除空间可能会导致非常不预期的结果,请确保在调用function call时,请检查并清除所有多余的空间
8.7函数origin
该函数与常规函数有所不同, 它不会干预或修改任何变量的值. 该函数仅提供有关单个变量的基本信息. 特别地, 该函数会揭示变量是如何取得其值的.
函数origin的语法:
请注意,“variable”被视为查询变量名称。因此它并非对该变元进行引用。因此不能使用'$'或圆括号格式书写该变元。如果有必要使用非静态文件名,则可以在文件名中插入相应变元引用。
请注意,“variable”被视为查询变元名称而非直接引用。因此它并非直接指涉该变元本身。因此在书写时不能采用 '$' 或圆括号形式来表示此变元。然而如果有必要采用非静态文件名,则可在文件名中适当位置插入相应的变元引用来实现所需效果。
函数origin的结果是一个字符串,该字符串变量是怎样定义的:
‘undefined'
如果变量‘variable’从没有定义。
‘default'
该变量被预设为缺省状态,并常与诸如CC类的命令协同工作;参阅** 隐含规则使用的变量** 。需要注意的是,在对同一个缺省变量进行新的赋值时,请确保不会覆盖之前的定义;因为在这种情况下, 函数origin将返回后续所赋予的新值。
‘environment'
**_选项概要_**
‘environment override'
**_选项概要_**
‘file'
‘command line'
变量‘variable’在命令行中定义。
‘override'
变量‘variable’在makefile中用override指令定义(参阅** override指令** )。
‘automatic'
**_自动变量_**
**_字符串替换和分析函数_**
8.8 函数shell
8.9 控制make的函数
$(error text...)
$(warning text...)
9 运行make
**_代替执行命令_**
9.1 指定makefile文件的参数
**_编写makefile文件_**
9.2指定最终目标的参数
**_自动产生依赖_**
**_假想目标_**** _使用空目标记录事件_**** _用户标准目标_ :**
‘all'
创建makefile文件的所有顶层目标。
`clean'
删除所有make正常创建的文件。
`mostlyclean'
`distclean'
`realclean'
`clobber'
`check'
`test'
9.3 代替执行命令
`-n'
`--just-print'
`--dry-run'
`--recon'
`-t'
`--touch'
`-q'
`--question'
`-W file'
`--what-if=file'
`--assume-new=file'
`--new-file=file'
使用标志‘-n’,make打印那些正常执行的命令,但却不执行它们。
指定标志为't'后,在遵循相应规则的情况下运行'make'会忽略遵循这些规则的命令。针对需要进行更新的目标位置运行'touch'指令。如果不采用'-s'选项或者.SILENT设置,则触达打印操作以确认状态变化。这样处理可以显著提升系统的运行效率,并且不会真正调用系统程序'touch'而是让其直接执行相应的操作以节省资源并加快处理速度。
指定标志‘-q’后运行make命令时不会显示日志也不会执行相应操作。当所有目标已更新至最新版本时会返回状态码0若有部分目标需更新则返回状态码1若在运行过程中发生错误则返回状态码2因此建议您先检查未被修改的目标再排查问题。
当运行make指令时,在使用这三个标志(即-n、-t及-s)若同时选择了两个或全部三个将会导致错误。这三个标志仅影响那些以+号开头的命令行以及包含(MAKE)或{MAKE}字符串的命令行。请注意,只有当遇到以+号开头的命令或者包含(MAKE)或{MAKE}的情况时才无需考虑这些选项。参阅有关变量MAKE的工作原理部分。
‘-W’标志有一下两个特点:
l 如果同时使用标志‘-n’或‘-q’,如果您更改一部分文件,看看make将会做什么。
未采用标志符「-n」和「-q」的情况下,在make运行时选择标志「-W」则会认为所有文件已更新。然而实际上并未修改任何文件内容。
请特别注意选项‘-p’和‘-v’所提供的详细信息(参阅** 选项概要** )
9.4避免重新编译文件
有时候修改一个源文件时,并不会导致所有的依赖它而被修改的目标程序都需要重新编译。举个例子来说,在某个包含大量依赖程序的包含头文库中插入一个宏或者声明符时,默认遵循保守原则(make),所以它会认为一旦这个头文库被修改就会导致所有的依赖程序都需要重新编译;然而实际上这种情况可能是不必要的行为
如果在修改包含头文件的源代码之前提前了解相关问题,则可以使用'-t'选项来指示编译器忽略不必要的时间戳更新。这一标记会阻止make执行原本计算时间戳的命令,并将所有目标的时间戳更新为最新的版本。按照以下步骤操作即可完成这一设置:
1、用make命令重新编译那些需要编译的源文件;
2、更改头文件;
通过采用‘make –t’命令更新所有目标文件的时间戳信息,在下次运行make指令时就不会因头文件的变化而影响任何文件的编译。
一旦修改了包含于程序中的头文件,则无法再按照正常流程完成任务;为了避免因修改包含于程序中的头文件而导致无法完成后续任务的情况发生,请您采用以下方法:使用带有‘-o file’选项的编译命令——这个命令能够将指定的选项的时间戳假装设置为旧版本的时间戳(参阅** 选项概要**)。这样就能避免因时间戳错误导致的问题。请按照以下步骤操作:
建议采用'make -o file'命令以对那些未因修改包含头文件而需更新的项目进行重新编译操作。当处理多个包含头文件时,请确保您可对每个包含头文件应用'-o'选项进行编译设置。
2、使用‘make –t’命令改变所有目标文件的时间戳。
9.5变量重载
在使用等于号(=)进行变量定义时,默认情况下会将变量v赋值为x。当您按照此方法为某个变量做出定义时,在makefile文件之后执行任何对这一变量常规的操作都会被系统忽略。若希望这些操作能在命令行中生效,则需在适当的位置重新加载该操作。
主流的做法是赋予编译器增强指令(即辅助指令)来提升灵活性。例如,在Makefile文件中,在变量CFLAGS中已经定义了运行C编译器所需的每一个命令。因此,在仅仅输入make命令时(即不指定具体程序),文件'foo.c'将按照以下方式被构建:
当您在makefile文件中修改CFALAGS这一变量时,默认会传递给编译器运行所需的选项;不过您可以随时重新设置该变量以重新启用特定选项;举例来说,在命令行输入如make CFLAGS='-g -O'时;其中任意一个C编译器都将采用cc -c -g -O的方式进行处理;这也揭示了如何利用shell脚本引用包含空格和其他特殊字符在内的变量值。
CFALAGS仅仅是一个您可以通过这种方式进行重写的标准变量之一,并且其中详细说明了您可以通过这种方式进行重写的所有相关标准变量。
您还可以创建一个makefile来查看您的自定义变量,并且以便用户可以通过修改这些变量来控制make运行时的其他配置。
当您在命令参数中进行重载操作时,请注意区分两种类型的扩展型变量:递归调用扩展型变量与简单扩展型变量。具体而言,在本例中我们采用了递归调用扩展型变量这一实现方式;若选择创建简单扩展型变量,则应采用赋值符' := '进行赋值(而非等号' =')。值得注意的是,在这种情况下除非您的代码中涉及变量子程序或函数引用等高级功能,默认情况下这两种类型在功能上并无明显区别。
通过这种方式可以在makfile文件中实现对变量的重载。在mk_make脚本中进行变量重载的操作通常会采用 override 指令。类似于 'override variable = value' 这种命令行格式。详细内容参阅 override指令 。
9.6 测试编译程序
在正常的shell命令执行过程中出现错误时, make会立即终止并返回非零状态; 不会继续为任何目标执行shell命令. 出现的错误表明make无法正确地构建最终的目标, 并且在发现任何误差后都会立即报告.
在您重新编译修改后的程序时,请确认是否得到了预期的效果。建议让make工具尽可能多地尝试编译每个程序,并尽量清晰地展示任何出现的错误信息。
当遇到这种情况时
一般情况下, make的行为基于以下假设:您的目标是要使最终目标更新;一旦发现无法实现这一目标,则立即报告错误。
选项'-k'就是:您可以通过测试改变对程序的影响来发现现有问题,并在此前版本中进行修正。
这是Emacs M-x compile命令缺省传递'-k'选项的原因.
9.7 选项概要
下面是所有make能理解的选项列表:
`-b'
`-m'
和其它版本make兼容时,这些选项被忽略。
`-C dir'
`--directory=dir'
在导入makefile之前,请切换至目标路径' dir'。若连续使用多个'-C'选项,则每个都基于前一个的设置:连续使用'-C'等同于'-C/etc'。此功能常用于递归执行make指令,请参见有关递归调用make的详细说明
‘-d’
在正常处理完成后生成调试信息供程序查阅。这些调试信息将列出用于更新的文件,并指出哪个文件被用作时间戳基准来比较不同版本的变化情况;同时也会记录比较结果以及哪些文件实际发生过修改需遵循哪些内部规定等细节问题——所有与make命令最终执行目的相关的方面都将在其中得到体现。' -d '选项等价于设置 --debug=a 选项(参见以下内容)。
`--debug[=options]'
在常规处理流程完成后输出常规调试信息
Default
a (_all_)
显示所有调试信息,该选项等同于‘-d’选项。
b (_basic_)
基本调试信息打印每一个已经过时的目标,以及它们重建是否成功。
v (_verbose_)
高级别的调试信息包括对makefile文件语法结构的具体解析结果以及无需关注或更新的关键依赖项,并且该设置同时提供基础级别的调试信息支持
i (_implicit_)
打印隐含规则搜寻目标的信息。该选项同时包含基本调试信息。
j (_jobs_)
打印各种子命令调用的详细信息。
m (_makefile_)
这些选项均不涉及重新创建makefile文件的信息。这些选项也提供了相关的信息。请注意,在这些设置中,并没有提供与某些方面信息相关的内容。这些设置还提供了基本调试相关的配置参数。
`-e'
`--environment-overrides'
在环境中继承的变量配置其优先权高于makefile文件中的相应变量配置。参阅关于** 环境变量** 的详细说明。
`-f file'
`--file=file'
`--makefile=file'
将名为‘file’的文件设置为makefile文件。参阅** 编写makefile文件** 。
`-h'
`--help'
向您提醒make 能够理解的选项,然后退出。
`-i'
`--ignore-errors'
忽略重建文件执行命令时产生的所有错误。
`-I dir'
`--include-dir=dir'
请指示程序去搜索位于dir目录下的所有makefile文件,并注意其中可能存在的其他makefile文件。特别地,在您希望处理多个makefile时,请确保将它们按优先级排序以便正确搜索。
`-j [jobs]'
`--jobs[=jobs]'
确定能够同时运行的任务数量。
如果没有make参数,则会尽可能多地执行任务;如果有多个‘-j’选项,则只有最后一个选项起作用。
详细内容请参考** 并行执行** 。
请记住,在MS-DOS环境下此选项会被忽略。
`-k'
`--keep-going'
在出现问题时尽力维持执行。一旦某个操作无法完成,则所有依赖该操作的对象都将受到影响;不过这些对象的所有其他关联仍可正常处理。参阅** 测试编译程序** 。
`-l [load]'
`--load-average[=load]'
`--max-load[=load]'
当存在其他正在运行的任务且系统的平均负载已接近或达到了预设的load值(一个浮点数)时,则在此时不会启动新的任务。如果缺少必要的参数,则会取消先前对负载的限制。参阅**并行执行**。
`-n'
`--just-print'
`--dry-run'
`--recon'
打印要执行的命令,但却不执行它们。参阅** 代替执行命令** 。
`-o file'
`--old-file=file'
`--assume-old=file'
即使file与其依赖物处于‘旧’的状态也不进行重编译为了避免因file的变化而导致其他所有相关联的项目被误认为需要更新选择不进行重编译以保护项目的稳定性和安全性其实质是假设置了时间戳为与原有版本同步的状态从而阻止其按照新的变化触发更新机制参阅** 避免重新编译文件** 。
`-p'
`--print-data-base'
在处理过程中会先读取并解析makefile文件的内容作为基础数据源;随后将按照常规流程进行操作或根据特定选项进行处理。若同时输入' -v '参数,则会显示版本信息(参考下文)。采用' make –qp '命令时会打印出结果却不重新构建文件;而通过使用' make –p –f/dev/null '选项会生成预设好的规则与变量的数据库。输出结果中包含了文件路径以及参与命令与变量引用的具体行号信息;这也是一种在复杂系统环境中非常有用的调试工具。
`-q'
`--question'
问题模式标记
`-r'
`--no-builtin-rules'
避免调用内置默认的格式化指令(参阅** 使用隐含规则** )。然而,在这种情况下,默认提供的扩展功能依然可供调用(参阅** 定义和重新定义格式规则** )。通过选择此选项(-r),将彻底清除现有的默认后缀及其相关配置项(参阅** 过时的后缀规则** )。不过,在这种情况下,默认提供的扩展功能依然可供调用(参阅** 隐含规则使用的变量** );参阅下述的选项‘-R’。
`-R'
`--no-builtin-variables'
不要使用内置规则变量(参阅** 隐含规则使用的变量** )。然而,在此场景下您仍然可以选择创建属于自己的变量系统。当采用选项‘-R’时,默认启用选项‘-r’的功能;但由于去除了用于隐式规则所依赖的自定义变量设定这一机制,在这种情况下隐式规则也就无法继续发挥作用了。
`-s'
`--silent'
`--quiet'
沉默选项。不回显那些执行的命令。参阅** 命令回显** 。
`-S'
`--no-keep-going'
`--stop'
使了导致了其失效。然而,仅此一种情况下,即当递归调用make函数时,MAKEFLAGS变量是从其上级make函数中继承配置项 -k,或者用户已经将此配置项设置在其工作环境中,则无需考虑此参数的作用;其他情况下,此参数将不起作用并可忽略不计。
`-t'
`--touch'
标志文件已达到最新的版本状态, 但实际并未对它们进行修改或更新。这种做法旨在模拟这些命令的运行, 其目的在于欺骗将来的make调用程序。参阅** 代替执行命令** 。
`-v'
`--version'
打印make程序的版本信息,作者列表和没有担保的注意信息,然后退出。
`-w'
`--print-directory'
显示在执行makefile文件时涉及的所有工作目录对于追踪其递归调用过程中复杂嵌套所导致的错误非常有帮助。参阅《递归调用Make》, 实际上,默认情况下您几乎不需要手动指定此选项, 因为默认情况下, Make系统已经为您实现了这一功能
`--no-print-directory'
当使用选项‘-w’时,在运行程序后不会显示工作目录。这个选项通常会在默认情况下启用,并且当您不想查看过多信息时非常有用。参阅** ‘--print-directory’ 选项**.
当使用选项‘-w’时,在运行程序后不会显示工作目录。这个选项通常会在默认情况下启用,并且当您不想查看过多信息时非常有用。参阅** ‘--print-directory’ 选项**.
`-W file'
`--what-if=file'
`--new-file=file'
`--assume-new=file'
假设目标文件已更新。当采用标志'n``时, 它会告知您更改该文件会产生什么结果. 如果不带'-n``选项, 则等效于直接运行make命令前用touch操作对该文件. 然而, 在这种情况下, 使用make只是假设地修改了该文件的时间戳. 参阅代替执行命令
`--warn-undefined-variables'
当\make\遇到未定义的变量时会发出提示信息。\如果在调试\makefile\文件时采用复杂的方式引用变量\this feature将非常有用。\
10 使用隐含规则
一些常用的标准方法用于重新创建目标文件。比如,在创建OBJ文件时, 通过C编译器如cc来编译C语言源程序.
隐含规则可向make指示如何利用传统技术完成任务。这样,在您应用它们时就无需详细指定每个细节。例如说编译C语言源程序时会自动执行某些操作而无需手动配置参数;比如编译C语言程序一般会生成“.o”扩展名的目标文件。因此可以说make凭借对文件后缀的理解能自动选择适用的相关隐藏规则进行处理。多个隐藏规则可依次应用;比如从一个“.y”的源文件借助“.c”的中间文件重建出“.o”的目标文件具体内容请参阅** 隐藏式流程衔接** 。内置的隐藏命令需依赖变量来控制其行为方式;通过调整这些变量值make能够灵活配置各种隐藏功能设置相关内容可在参考文档中找到完整说明参见** 默认隐藏指令的操作原理** 。若要自定义新的隐藏命令则需编写专门的形式化则规定并将其存储在一个特定的位置里这样就能根据需求动态添加更多功能模块具体内容请参考** 定制特殊隐藏指令** 。
附加项规则是对定义内含规则最严格性的规定。格式规定通常既通用又清晰明了。然而附加项则需特别注意兼容性问题。参阅** 过时的后缀 规则**
10.1 使用隐含规则
使make能够在单个目标文件中查找传统更新方式,并通过不设定任何命令行来实现这一功能。你可以选择不添加带有命令行的操作规则或者完全省略相关规则设置。这样系统将在现有源文件类型以及需生成的目标文件类型间推断并应用相应的默认隐式规则。
例如,假设makefile文件是下面的格式:
由于您指出文件'foo.o'的存在,并未提供相应的规则信息,则系统会依靠内在机制推导出相应的更新策略以指导make如何更新该目标文件。不论该目标文件是否存在这种行为会持续进行下去。
一旦发现存在这样的一条隐含规则,则它就具备为命令及其相关源文件提供支持的能力。
如果需要为编译操作增加额外依赖项,请注意该隐含规则无法直接支持这些情况。
因此,在这种情况下,
必须向目标‘foo.o’编写相应的编译指令,
并将其配置为不带命令行参数的形式。
每个隐含规则都具有目标格式以及所需依赖格式;其中有些隐含规则可能具有相同的扩展名称(例如)通常会产生无数个.o文件:通过C编译器处理.C文件;通过Pascal编译器处理.p文件;依此类推)。实际应用中使用的扩展类型通常是那些确实存在或者能够被构建起来的特定扩展类型
显然,在制作一个makefile文件时,请注意以下几点:首先,请明确使用哪条默认隐藏规则,并了解make系统将如何选择这条规则的原因在于该文件中假设存在相应的依赖文件。有关预定义隐藏规则列表的具体内容,请参考**隐含规则目录**以获取详细信息。
首先指出了一条可应用的隐式法则;这一法则的有效性条件是其前提要素必须具备存在状态或是可通过某种机制生成状态。其中一种情况是参与法则的应用对象——一个文件——若被包含在makefile中作为目标或依存项出现,则被视为可生成状态;另一种情况则是指通过某一特定法则经由递归调用过程而得以生成的状态;若某条法则的应用导致其依存项变为其他法则的应用结果,则形成了一种称为"链"的情况;参阅** 显式法则链**
就整体而言,在构建系统时, make工具会分别针对每个目标进行隐式规则的搜索. 其中, 特别针对那些不带有命令行参数的双冒号语法进行隐式模式的寻找. 需要注意的是, 仅限于那些在声明中被引用或依赖的对象, 将被视为一个独立的目标. 如果某个目标未明确定义任何操作符或模式, make则会主动去探索这些潜在的操作符或模式. 对于详细的搜寻过程, 可参阅** 隐含规则的搜寻算法** .
注意,在任何具体依赖的情况下都不会影响到隐含规则的搜索过程。举例而言,则可将其视为一条具体的规则:
并非必要存在。该机制表明系统会依据预先设定的默认法则自动生成与原始Pascal源代码相对应的OBJ实体。进一步说明的是,在这种机制下,默认情况下编译后的可执行程序能够基于原始的Pascal源码及时更新。特别地,在此方案中,并非必须同时具备foo.p这一条件即可运行;例如,在这种情况下,默认优先考虑是否存在对应的C语言开发文档或者其他相关资源?如需了解详细信息,请参阅** 隐含规则目录** 。
当您希望避免通过默认规则创建无命令行界面的目标时,请在目标前后添加分号以指定其为空命令,并参阅** 使用空命令**
10.2隐含规则目录
此处列出了预设的内建规则清单。这些内建规则通常是频繁使用的标准配置项。需要注意的是,在makefile文件中如果需要重新配置或完全移除后,请确保相应设置已做调整以避免影响系统正常运行,并参考** 删除内建规则** 一节以确保操作正确无误。使用选项‘-r’或‘--no-builtin-rules’可彻底清除所有预先定义好的默认设置项以释放资源并优化性能。
并不是所有的隐含规则都是预先设定好的,在make系统中,默认设置下的许多隐含规则实际上是基于后缀规则进行扩展。因此这些预先设定好的隐含规则与后缀规则密切相关(特别是特殊目标.SUFFIXES所依赖的列表)。缺省的后缀列表如下:.out,.a,.ln,.o,.c,.cc,.C,.p,.f,.F,.r,.y,.l,.s,.S,.mod,.sym,.def,.h,.info,.dvi、.tex、.texinfo、.texi、.txinfo、.w、.ch、.web、.sh、.elc和.el等项。如果您更改这个后uffix列表,则只有那些由一个或两个出现在您指定的新列表中的后续名所对应的预定义后续规会起作用;而那些没有出现在新后的后续名将不再生效。有关详细关于后续规的规定请参阅**过时后的后续规说明**
Compiling C programs(编译C程序)
‘n.o' 自动由‘n.c' 使用命令 ‘(CC) -c (CPPFLAGS) $(CFLAGS)'生成 。
Compiling C++ programs (编译C++程序)
'n.o'通常通过命令'(CXX) -c (CPPFLAGS) $$(CXXFLAGS)'自动生成。我们建议您优先选择'.cc'作为C++源文件的扩展名。
Compiling Pascal programs (编译Pascal程序)
‘n.o'自动由‘n.p'使用命令‘(PC) -c (PFLAGS)'生成。
Compiling Fortran and Ratfor programs (编译Fortran 和 Ratfor程序)
‘n.o'自动由‘n.r', ‘n.F'或‘n.f' 运行Fortran编译器生成。使用的精确命令如下:
`.f'
`(FC) -c (FFLAGS)'.
`.F'
`(FC) -c (FFLAGS) $(CPPFLAGS)'.
`.r'
`(FC) -c (FFLAGS) $(RFLAGS)'.
Compilation stage involves compilation by compilers for Fortran and Ratfor programs.
Compilation stage involves compilation of Fortran and Ratfor programs.
n.f会被自动地从n.r或n.F中获取。
仅限于处理器将Ratfor程序或可预处理的Fortran程序转化为标准Fortran程序。
使用的精确命令如下:
`.F'
`(FC) -F (CPPFLAGS) $(FFLAGS)'.
`.r'
`(FC) -F (FFLAGS) $(RFLAGS)'.
Compiling Modula-2 programs(编译Modula-2程序)
'n.sym'按照'n.def'使用指定命令'\$(M2C)\$\$(M2FLAGS)\$\$(DEFFLAGS)'自动产生。'n.o'则基于'n.mod'产出;其生成命令为:'$(M2C)$$(M2FLAGS)$$(MODFLAGS)$'。
Compiling and undergoing preparatory processing of assembler programs
‘n.o'自‘n.S'运行C编译器,cpp,生成。命令为:‘(CPP) (CPPFLAGS)'。
Linking a single object file (连接一个简单的OBJ文件)
n 会通过 n.o 执行 C 编译器中的链接程序 linker(通常称为 ld),生成目标可执行文件。编译指令为: $$(CC) $$(LDFLAGS) n.o $$(LOADLIBES) $$(LDLIBS)。此规则适用于单一源文件的简单程序或多个 OBJ 文件(可能源自不同源文件)的情况。当处理多个 OBJ 文件时,其中必定存在一个 OBJ 文件名称与目标可执行文件名称一致的情况。
当‘x.c', ‘y.c' 和‘z.c' 都存在时则执行:
在处理更为复杂的情况时,请确保 OBJ 文件名称与目标执行程序名称完全一致。对于无需使用特定选项即可生成“.o”扩展名的文件,请直接利用编译器(如 (CC)、(FC) 或 (PC),其中 (CC) 也可用于汇编程序)进行连接操作。此外,在必要时也可选用多个 OBJ 文件作为中间构建阶段所需的临时存储介质。需要注意的是,在这种情况下完成编译与连接操作的速度将会显著加快。
Yacc for C programs (由Yacc生成C程序)
‘n.c'自动由‘n.y'使用命令‘(YACC) (YFLAGS)'运行 Yacc生成。
Lex for C programs (由Lex生成C程序)
‘n.c'自动由‘n.l' 运行 Lex生成。命令为:‘(LEX) (LFLAGS)'。
Lex for Ratfor programs (由Lex生成Rator程序)
n.r默认由lex运行n.l生成,默认情况下会将所有lex文件不论生成c代码还是ratfor代码都将采用相同后缀.l来进行转换。然而,在某些情况下无法让make自动判断您使用的编程语言。若make试图利用.l文件来重构一个.obj,则必须假设它采用的是c编译器这一更为普遍的选择。为了方便切换语言,在ratfor环境中请确保其相关配置包含于makefile中;否则如果您仅依靠ratfor而不涉及c语言,则隐式规则列表中需排除.c扩展名以避免潜在冲突。
Generating Lint libraries from C, Yacc, or Lex programs(由C, Yacc, 或 Lex程序生成Lint库)
‘n.ln’ 是通过 ‘Lint’ 编译器对 ‘n.c’ 进行代码检查并生成 lint 输出文件的结果。具体的编译命令是:(LINT) (LINTFLAGS) $(CPPFLAGS) –i。此命令与处理 n.y 或 n.l 类型程序时使用的命令完全相同。
TeX and Web(TeX 和 Web)
可以通过运行LaTeX编译器将'n.dvi'生成自'n.tex'文件。具体而言,'.tex'文件可以通过运行WEAVE脚本转换为'.dvi'格式的文档。此外, '.tex'文件还可以通过执行Tangle脚本转换为'.w'文件,或者如果'n.ch'存在或能够构建的话,也可以利用CWEAVE工具将其转换为'.wch'文件。.p文件可以通过运行Tangle脚本自'n.web'd自动生成,而.c文件则可以通过CTANGLE工具自'n.w'(以及可选的'n.ch')构建。
Texinfo and Info(Texinfo和Info)
通过编写脚本可以自动生成相应的dvi文件。
此外,
可以通过编写脚本自动生成相应的info文件。
RCS
在需要的情况下(即当必要时),文件'n'可以从名为'n,v'或'RCS/n,v'的RCS文件中提取。具体命令为:'$$(CO) (COFLAGS)'。如果文件'n'已存在,则即便其版本号较新(尽管其版本号较新),仍无法从该RCS文件中提取内容。针对RCS而言的规定最为严格(参阅** 万用规则** ),因此可知RCs无法由任何源文件生成;它们必须事先独立存在。
SCCS
在某些情况下, 文件'n'可以从名为's.n'或'SCCS/s.n'的SCCS文件中提取. 具体命令是:'(GET) (GFLAGS)'. 用于SCCS的规则是最终的规则, 参阅** 万用规则 **, 所以SCCS不能够从任何源文件产生, 它们必须存在. SCCS的优点是可以从文件'n.sh'复制并生成可执行文件(任何人都可以). 该功能用于shell脚本, 该脚本位于SCCS内部进行检查. 因为RCS支持保持文件的可执行性, 所以您没有必要将其特性应用于RCS文件. 我们建议您避免使用SCCS, 因为RCS不仅应用广泛而且免费. 选择自由软件替代收费软件(或低劣软件)是对自由软件的支持.
通常情况下,您要仅仅改变上表中的变量,需要参阅下面的文档。
Make遵循常规方式处理这些隐含规则。这些值包含如上所述的一系列命令。它实际上涉及等其他变量。例如,在编译.x源文件时会利用变量.x$\_compile;生成可执行文件则会调用.x$\_link;而预处理操作则会运用.x$\_preprocess等其他变量。
所有生成OBJ文件的规则均需设置变量OUTPUT_OPTION。make根据编译选项将该变量设为' -o @ '或空值。如果源代码分散在不同目录中,则应采用' -O '选项以确保目标位置正确;同时若采用VPATH环境变量,则同样适用。(参阅** _依赖搜寻目录_** 。)某些编译器对OBJ文件不支持' -o '开关;在这些情况下若启用VPATH环境变量,则可能导致编译目标位置错误。解决方案在于将 OUTPUT_OPTION 设置为:mv *.o $@。
10.3隐含规则使用的变量
内置隐含规则的行为允许命令对预定义变量进行灵活操作;通过修改makefile文件中的相关设置或者通过指定make命令的相关参数或在工作环境中直接调整;从而无需重新定义这些规则即可调整它们的行为;特别地,“-R”或“--no-builtin-variables”选项可彻底移除所有由隐含规则使用的预定义变量。
例如,在编译C程序时所使用的命令本质上就是'(CC) -c (CFLAGS) (CPPFLAGS)'这一指令串;其中变量缺省设置为'cc'或未赋值状态时,默认行为即执行'cc –c'这一操作。若我们对变量'CC'进行再定义,并将其值设定为'ncc'(如前所述),则所有隐含规则都将采用'ncc'作为指定用于处理C语言源代码的编译工具;类似地,在设定变量'CFLAGS'值为'-g'后,则可使每个编译步骤均能正确传递'-g'这一选项参数;值得注意的是,在处理C程序的过程中,默认情况下所有隐含规则都会通过'CC'获取当前设置下的编译器名称,并在传递给各个编译工具所需参数时始终包含'$CFLAGS'这一引用。
隐含规则使用的变量主要分为两大类:一类是用于标识符(如cc),另一类则涉及具体的运行时所需参数(如CFLAGS)。需要注意的是,在这种情况下'program name'可能会附加一些命令行相关选项;然而,在实际应用中它必须以一个合法有效的可执行文件名称开头。特别地,在某个'variable value'内如果有多个独立的元素存在,则这些元素之间应当使用空格字符进行分隔。
这里是内建规则中程序名变量列表:
AR
档案管理程序;缺省为:‘ar'.
AS
汇编编译程序;缺省为:‘as'.
CC
C语言编译程序;缺省为:‘cc'.
CXX
C++编译程序;缺省为:‘g++'.
CO
从RCS文件中解压缩抽取文件程序;缺省为:‘co'.
CPP
带有标准输出的C语言预处理程序;缺省为:‘$(CC) -E'.
FC
Fortran 以及 Ratfor 语言的编译和预处理程序;缺省为:‘f77'.
GET
从SCCS文件中解压缩抽取文件程序;缺省为:‘get'.
LEX
将 Lex 语言转变为 C 或 Ratfor程序的程序;缺省为:‘lex'.
PC
Pascal 程序编译程序;缺省为:‘pc'.
YACC
将 Yacc语言转变为 C程序的程序;缺省为:‘yacc'.
YACCR
将 Yacc语言转变为 Ratfor程序的程序;缺省为:‘yacc -r'.
MAKEINFO
将Texinfo 源文件转换为信息文件的程序;缺省为:‘makeinfo'.
TEX
从TeX源产生TeX DVI文件的程序;缺省为:‘tex'.
TEXI2DVI
从Texinfo源产生TeX DVI 文件的程序;缺省为:‘texi2dvi'.
WEAVE
将Web翻译成TeX的程序;缺省为:‘weave'.
CWEAVE
将CWeb翻译成TeX的程序;缺省为:‘cweave'.
TANGLE
将Web翻译成 Pascal的程序;缺省为:‘tangle'.
CTANGLE
将Web翻译成C的程序;缺省为:‘ctangle'.
RM
删除文件的命令;缺省为:‘rm -f'.
此处存储着根据上述程序增补的参数列表。当未特别指定时,默认所有变量均被赋以空值。
ARFLAGS
用于档案管理程序的标志,缺省为:‘rv'.
ASFLAGS
用于汇编编译器的额外标志 (当具体调用‘.s'或‘.S'文件时)。
CFLAGS
用于C编译器的额外标志。
CXXFLAGS
用于C++编译器的额外标志。
COFLAGS
用于RCS co程序的额外标志。
CPPFLAGS
用于C预处理以及使用它的程序的额外标志 (C和 Fortran 编译器)。
FFLAGS
用于Fortran编译器的额外标志。
GFLAGS
用于SCCS get程序的额外标志。
LDFLAGS
用于调用linker(‘ld’)的编译器的额外标志。
LFLAGS
用于Lex的额外标志。
PFLAGS
用于Pascal编译器的额外标志。
RFLAGS
用于处理Ratfor程序的Fortran编译器的额外标志。
YFLAGS
用于Yacc的额外标志。Yacc。
10.4 隐含规则链
通常情况下, 生成一个文件会涉及一套或多套内置于该过程的机制. 例如, 在将文件从扩展名'y'转换为'o'的过程中, 首先执行Yacc相关的处理步骤, 然后是cc相关的操作步骤. 这种机制组合被称为内置于该过程的隐式规范集合.
当'file' 'n.c'存在或者在'makefile'中被提及时,则无需特别搜索:make首先通过C编译器编译'n.c'生成该OBJ文件。接着,在处理'n.c'时,则采用了运行Yacc规则的做法。从而最终更新了这两个关键的源代码文件'n.c'和'n.o'。
即使在 file 'n.c' 不存在 或者不在 makefile 中被提及 的情况下, make 系统 也能 推断 出 在 file 'n.y' 和 'n.o' 之间 存在 缺失 连接 的 情况. 在这种 情况 下, file 'n.c' 被 称作 中间 文件 . 一旦 决定 采用 中间 文件, make 系统 将 导入 该 中间 文件 到 数据库 中, 类似于 middle file 在 makefile 中 被 引用 的 情形; 根据 隐式 规则 生成 相应 的 中间 files.
中间文件和其他文档一样遵循特定的重建规则进行构建过程;然而相对于其他所有文档而言,在处理流程上存在明显差异
在处理方式上存在差异的是当中间文件不存在时make的行为表现不同。通常情况下,在没有普通 files b 存在时, make 会生成这些 files b 并基于每个 files b 更新相应的 targets. 而当中间 file b 是依赖关系中的普通节点时, make 则不会生成 file b 并且也不会更新最终的目标. 这种行为模式仅在 file b 的依赖更新状态与 final targets 的最新状态不符, 或者存在其他特殊原因时, make 才会对 final targets 进行更新.
当make完成对某个目标的更新并创建文件b后(second distinct point),若该文件不再被所需要,则会将其删除(delete operation))。因此,在start-up前及start-up完成后均不会存在该临时(temporary)file(file)。当delete操作发生时会输出一条类似'rm –f'的信息(message)。
一般而言,在makefile中所指的目标与依赖通常并非被视为间接受处理的对象。然而,在某些特殊情况下或需求下,则允许您选择特定的文件作为间接受处理的对象。具体来说,您只需将被指定为间接受处理的那些特定内容标记为特殊的. INTERMEDIATE目标即可实现这一做法。这一做法不仅适用于那些直接使用其他方法所提到的内容,并且也适用于那些间接涉及的内容。
通过将文档标记为SECONDARY类型来避免被系统自动删除中间生成的临时文件。当这样做时,请确保所要保留的关键中间文件被指定为目标依赖项,并标记为SECONDARY类型。SECONDSARY类型的文档在Make构建过程中不会因为缺少而被创建出来,并且也不会被意外地清除或删除。此外,在此机制下使用的SECONDSARY文档也必须被视为中间临时文档。
您可以列出一个隐含规则的目标格式(如%.o),将其作为特殊目标 .PRECIOUS的一个依赖项。这样您就能保存那些由隐含规则生成并符合该格式的中间文件名。参阅** 中断和关闭make**
一个隐式规则序列最少由两个隐式规则组成。例如,在构建名为foo的项目时,从源代码路径'RCS/foo.y,v'开始会生成文件foo。为此过程需依次执行以下步骤:使用RCS进行编译、使用Yacc进行解析以及使用cc进行汇编;其中,.y和.c文件是在源代码生成阶段产生的源代码和目标代码文件,在构建完成后会被删除。
不存在任何一条隐含规则能够在隐含规则链中重复出现超过两次(包括恰好两次)。这意味着, make不会简单地认为从文件‘foo.o.o’创建文件foo仅仅是运行linker两次。这从而使得make在搜索一个隐式规则链时能够有效避免无限循环。
某些特定情况下的特殊隐式规则能够通过规则链实现优化。例如,在编译器中,创建名为foo的共享对象文件时(通过cc),该过程由支持编译和链接的规则链管理。其中会生成一个中间目标文件 named foo.o. 然而,在这种情况下有一条特殊规则(如cc命令),它能够同时执行编译和链接操作。
10.5定义与重新定义格式规则
按照制定规范的方式建立隐式模式。此规范与常规模式相似,在设计上存在显著差异:其目标是识别并解析特定类型的标记符号。其中指定的特殊符号仅限于一个类型,并且能够精准捕获这些标记的作用域范围。特别地,在命名实体识别过程中,默认情况下会将标记符号视为独立存在的单元,并通过引用机制将其与相应的实体名称相关联。
格式‘%.o : %.c’是说将任何‘stem.c’文件编译为‘stem.o’文件。
在格式规则中,“%”扩展用于对所有变量和函数进行扩展,在这些操作完成后发生。这些操作是通过从makefile文件中读取数据来实现的。参阅有关使用变量以及转换文本函数的相关说明。
10.5.1格式规则简介
在目标中包含一个 % 符号的格式规则下进行操作是合理的设定。其余方面则遵循常规规定以确保一致性与稳定性。其中 % 能够对应任何非空字符串的内容,并且能够与其他特定字符组合使用以实现复杂的匹配需求。
例如,在Markdown中,“%.c”将匹配任何以“.c”结尾的文件名;而“s.%.c”则用于匹配以“s.”开头且带有“.c”的文件名(其中至少包含5个字符的原因是%,代表至少一个字符)。在依赖关系中,“%”被用来表示它们的名字中含有与目标名称相同的茎(radix)。为了遵循特定格式规则,在处理依赖时需要确保目标名称符合相关格式要求,并且相关的依赖项必须存在或能够被创建出来。以下规则:
需生成一个标记为‘n.o’的文件,并将其作为其所需依赖项引入另一个文件标记为‘n.c’;该文件必须预先存在或在运行时能够自动生成以满足需求
在格式规范中, 依赖偶尔不包含'%'符号. 这意味着依照该规范建立的所有文件都将共享相同的依赖关系. 这类仅限于特定场景下的固定依赖的规范往往非常有用.
虽然格式规则的依赖通常不包含字符‘%’这些特殊符号,默认情况下它们也不会被包含进去]这样的设置使得这类通配符往往具有强大的灵活性]此类通配符特别适用于能够自动识别并处理各种符合该模式的文档]参阅** 定义最新类型的缺省规则**
规范可以同时设置多个参数。然而这样的规范不会同时处理多条基于相同依赖或命令的不同指令。如果一个规范设置有多个参数(make知道所使用的指令对每个指定的目标都是有效的),那么它就不能同时处理多条基于相同依赖或命令的不同指令(因为这些指令只有在创建各自的目标时才执行)。特别地,在运行该文件后(注意该文件的命令一旦执行...)
在makefile文件中遵循特定顺序的格式规定同样重要;因为它们排列顺序也会影响最终效果;特别地,在处理依赖关系时,默认会按照其内在逻辑进行排序
10.5.2格式规则的例子
这里有一些在make中预定义的格式规则实例,在第一处,请您注意编译.c文件生成.o文件的具体规则:
制定了一项用于将'x.c'转换为'x.o'的操作指导方针;该指导方针被设计为采用自动变量'@'与'<'来取代任何适用该指导方针的目标文件及其源文件的相关引用。参阅**自动变量**.
第二个内建的例子:
该系统在子目录RCS中基于文件x.v自动生成文件x的规定被正式制定。其中目标设定为%号;只要对应的相关依赖文件存在即可应用此规定于任意文件;双冒号表明此规定为最终版本,在此之前可能有中间版本;如需进一步了解,请参阅*万用规则*部分
下面的格式规则有两个目标:
该工具告知make运行命令 'bison -d x.y', 会创建两个目标文件:'x.tab.c' 和 'x.tab.h'. 如果源程序中的某个模块foo依赖于这两个目标文件, 而且其中'scan-o'又反过来依赖于另一个目标文件'scan-h', 那么每当源代码中的某个部分'y'发生变化时, make指令会自动运行一次'bison -d parse-y'. 因此,'parse-tab-o'和'scan-o'的目标也会随之更新. 即假设'scan-o'是由'scan-c编译而成,'parse-tab-o', 则是由其对应的源代码编译而来; 当构建整个项目时, 需要同时处理这些编译目标以及它们之间的相互依赖关系, 上述规则就能很好地发挥作用.
10.5.3自动变量
假设您想要为“.c”文件生成“.o”目标创建编译规则:请问您如何设计命令“CC”,使其能够正确处理目标文件名?显然直接将目标名称包含在命令中是不可行的。由于编译器通常会根据隐式的规则来确定目标名称。
建议您使用make的一个特性:自动变量。这些自动生成的变量在每次规则执行时都会根据目标和依赖更新其值。例如,在替换目标文件名时可以使用自动生成的变量@;而替换依赖文件名则采用<这样的自动生成方式。
下面是自动变量列表:
$@
该规则指定的默认名称。当目标是档案成员时,则变量'$$'指示该档案成员对应的文件名。对于采用格式化的扩展语法定义的格式化字符串(参阅**格式化字符串定义**),则会生成相应的字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用字符实体引用
$%
假设当前操作涉及档案中的成员,请参考** 使用make更新档案文件** 以获取相关信息。举例而言,在实际操作中可观察到:当变量被赋值时(即$%),其具体含义如下——
- 其中,在实例中可观察到:当变量被赋值时(即$%),其具体含义如下——
- 当目标不是档案成员,则上述两个变量将保持为空值。
$<
第一个依赖的文件名标识符。如果目标更新命令源自隐式规则,则该变量表示由隐式规则所定义的第一个依赖项。参阅** 使用隐含规则**
$?
所有比目标新定义的依赖项名称,请以空格分隔开来。针对档案成员的相关依赖项,在引用时必须引用已命名的成员名称。请参考**使用make更新档案文件**部分
$^
所有依附于同一目标的名字需以空格分隔开。对于仅与档案相关的依附项必须具有明确标识。参阅有关通过make更新档案文件的技术说明。在同一目标下对应的一个文件最多只能列出一次作为依附项。无论该文件名在依附列表中重复出现多少次变量$^所记录的内容始终仅为该单一文件名。
$+
该变量名被称为‘$^'。然而,在makefile文件中依次列出多个依赖项时会按出现顺序复制。主要作用在于,在特定排列方式下重复引用库文件名时使用连接命令。
$*
与隐含规则相匹配的stem(径),参见** 格式匹配** 。对于一个目标路径为‘dir/a.foo.b'的目标来说,在静态格式规则中定义的茎是匹配该路径中字符的一部分。当使用变量‘*'来获取目标名中的特定部分时,请注意以下几点:首先,在没有茎的具体规则的情况下,默认情况下变量‘*'将被赋值为空值;其次,在某些特定情况下(如目标名包含推荐后缀时),可以通过去掉后缀来获取相应的部分;最后,在某些特殊情况下(如目标路径包含多个层级),需要根据具体的规则来确定如何提取相关的文件名部分。
当您希望仅仅操作那些发生改变的依赖时(即只处理会受到影响的对象),变量 '$?' 仍然具有重要的作用(尤其是在具体的规则设计中)。例如,在名为 'lib' 的档案文件中包含了多个 OBJ 文件副本的情况下(比如通过某种打包方式生成),则该规则仅会复制出发生变化的所有 OBJ 文件副本到目标档案文件中
在上述列出的变量中,有四个变量仅存储单个文件名;三个变量存储的是文件名列表;这七个变量都具有仅存于路径名或仅存于目录下的文件名变体。这些变体名称是在原变量名称后追加字母'D'或'F'得到的;这些变体目前处于半废状态;参阅** 文件名函数** 。值得注意的是,在dir函数中总是输出结尾斜杠的情况下,'F'变体省略了这一部分;以下是一些相关说明:
`$(@D)'
目标文件名中的路径部分已去除斜杠。当变量@的值为'dir/foo.o'时,则该变体将取值'dir';若变量@不含斜杠,则该变体默认为点号。
`$(@F)'
目标文件名中的真正部分反映了实际文件名称的情况。当变量@的值为'dir/foo.o'时,则其变体(@F) 的值是 'foo.o'。值得注意的是,在这种情况下,(@F) 等同于 (notdir $@)。
`$(*D)'
`$(*F)'
stem(径)中的路径名和文件名;在这个例子中它们的值分别为:dir' 和 foo' 。
`$(%D)'
`$(%F)'
其中的路径名称和文件名称;这些仅在采用archive(member)形式的目标中具有特殊意义,并且只有当目标包含特定目录结构时才会被识别出来;请参阅有关_档案成员目标_的详细说明。
`$(<D)'
`$(<F)'
第一个依赖名中的路径名和文件名。
`$(^D)'
`$(^F)'
所有依赖名中的路径名和文件名列表。
`$(?D)'
`$(?F)'
所有比目标‘新’的依赖名中的路径名和文件名列表。
在讨论自动变量时,我们采用了特殊的表示惯例;具体而言,在其值以符号'<'表示的同时,默认将该符号视为单独的元变量而非常规字符。与普通变量如objects和CFLAGS类似,在这种情况下我们可以省略外层括号以避免歧义(例如写作(<)而非(<))。这一约定在某些情况下显得更加直观且易于理解;它允许我们在不影响可读性的前提下显著简化表达式书写(例如写作(CFLAGS)'而非CFLAGS)。这一习惯在编程社区中已逐渐形成共识,并被广泛接受为其带来了诸多便利
10.5.4格式匹配
目标格式是由前缀、后缀及中间的通配符%组成,在这种情况下其中任何一个或两个都可以为空值。这种格式下只有当一个文件名同时满足以下条件时才算匹配:该文件名以指定的前缀开头,并以指定的后缀结束,并且前后部分之间没有任何重叠部分存在的情况下才算匹配。符合上述条件的前后部分之间的中间区域即被定义为径(stem)。举个例子,在这种情况下如果一个依赖规则为‘%.o’那么它将扩展名为‘test.o’其中径(stem)为‘test’。类似地如果一个依赖规则为‘%.c’则扩展后的文件名为‘test.c’
当目标格式中没有斜杠(实际上并非如此)时,则会先去除文件名中的路径部分。随后将其与前缀和后缀进行对比分析,在生成相应的依赖关系后,在根据规则产生的依赖中添加带有斜杠的部分。需要注意的是,在推断隐式规则时不考虑中间的路径部分,在实际应用中则必须考虑这一部分的影响。例如,“e%t”模式匹配文件名为“src/eat”的情况,“stem径”的提取结果是“src/a”。当将依赖转化为文件名称时,“stem径”中的前缀部分会被附加到相应的基础名称之前。“c%r”的转换结果则是“src/car”,其中茎中的路径部分已经被移除并替换掉了所有的"%"
10.5.5万用规则
在Markdown中,一个格式规则的目标仅包含‘%’符号。其匹配任何文件名,并因此被广泛应用于项目管理中。然而,在实际应用中发现这类通用命令虽然实用且广泛应用于项目管理中,但在实际应用中发现这类通用命令虽然实用但效率却不高。原因在于像make这样的工具必须同时处理每个目标及其依赖项。
在makefile中提到文件foo.c的情况下, make工具会尝试通过多种方式进行目标生成.具体来说,它会首先尝试链接一个obj对象文件'foo.c.o'来生成目标.此外,它还会考虑使用单步C编译连接程序处理文件foo.c.c进行操作,以及由Pascal编译连接器处理文件foo.c.p进行操作等可能性.
人们普遍认为make在处理某些情况时表现得很滑稽,这是因为foo.c只是一个未编译过的C语言源代码文件,而非已经生成了最终执行文件.假如系统尝试处理此类问题即使发现相关文件如foo.c.o和foo.c.p等均缺失也会选择予以拒绝.然而这类情况数量庞大往往会导致编译效率低下
为提升效率起见,在优化make匹配万用规则的过程中,我们对其中的一组特定限制进行了设置。这组限制分为两个不同的类别,在您每次定义一个万用规则时,请您为您的规则在这两个类别中进行选择。
一种方式是通过标识该万用规则作为最终规则,在定义阶段采用双冒号进行规范。如果一个规则被确定为最终规则,则只有当它具备相应的依赖关系时才能被应用生效;即使这些依赖可以通过隐含的机制建立也不可 bypass这一限制条件。简而言之,在这种情况下不会存在进一步的连接链存在。
例如,在基于RCS和SCCS文件提取原代码中的内建规则作为最终规则的情况下,则如果源代码'foo.c,v'不存在,
make绝不会尝试使用中间目标如'foo.c,v.o'或位于路径上的RCS/SCCS对象's.foo.c,v'进行构建。
因此,在构建时也不会尝试使用这些中间目标。
此外,
RCS 和 SCCS 文件通常被视为最终源代码,
因此,在生成时只会记录修改时间而不必追踪如何重建这些源代码。
如果您的标记不是最终通用地,则该标记属于非最终通用地。不属于最终通用地的任何特殊通用地都不能用于指定了特定类型的文件。如果存在其它标记(即非通用地)的目标匹配一项文件名,则该文件名就是指定了特定类型数据的文件名。
在编译器配置中指定名为$file的源代码时(此处$file采用.c扩展名),并配合指定编译指令如%c:%y(其中 %y 指令由Yacc生成)。其潜在影响仅限于与目标匹配的情况。从而确保不会在源代码名为.c的项目中引入非最终状态下的编译指令。
该内置特殊伪格式指令用于识别特定类型的文件名称;此类文件在处理时不得采用非最终通用规则;而这类伪格式指令无依赖项或命令;它们通常仅在指定用途下应用;例如内置隐式指令:
有一种机制能够确保Pascal源程序如'foo.p'匹配特定的目标格式从而避免去寻找对应的可执行文件或编译后的代码
在处理后缀时,在指定的后缀列表中为每个符合标准的有效后缀设置了伪格式模式,并参考了文档中的相关内容
10.5.6删除隐含规则
通过创建与现有功能相似但带有独特指令的新规范,您可以重定向默认功能(亦可重定向您自选的功能)。一旦创建新的规范,则会驱使原有的功能退出。 新规范在功能顺序表中的排列将基于您的规范设定位置。
按照新规定设定不带命令的新规范条目,在不保留原有隐式指引的情况下, 您可以让系统自动跳过内置默认操作. 例如, 下述这些规范条目将会取消运行汇编编译器相关的默认行为:
10.6 定义最新类型的缺省规则
您可通过构建不带依赖关系的通用格式来定义新的缺省规则,并将其命名为最新类型。参阅** 万用规则** ,这种新制定的规定与其常规形态相似,在其独特之处在于它能够匹配任意目标对象。因此这样的规范将适用所有未拥有独立命令的目标及其相关项,并且在缺乏其他内建规范的应用场景下也适用。
在实际操作中进行编译测试时
这导致所有必需的源文件(作为依赖)都自动创建。
您可为此类无规范的目标及未明确指明指令的目标设定指令。完成此任务时,请特别针对特殊目标.DEFAULT 制定相应规定。这些规定可在所有具体规定中应用于既未被指定为目标及无法调用隐式规定的依赖情况。自然地,在未对.DEFAULT 规则进行明确定义时,默认情况下将不存在相关规范。
如果您使用特殊目标.DEFAULT 而不带任何规则和命令:
如果之前为.DEFAULT目标定义了命令,则这些命令将被清除。这样,在使用make时,您将不会遇到因误操作而产生的问题。
如果不希望某个特定的目标通过万用规则或.DEFAULT 目标获取到命令,并且不希望为该目标执行任何操作,则可以在定义时设置为空命令。参阅** 使用空命令**
建议采用最新的策略对另一个makefile文件进行部分更新,请参考有关重载其他makefile文件的详细说明
10.7 过时的后缀规则
隐含规则的传统定义方式已被弃用。隐含规则因格式更为普遍且简洁的原因而被淘汰。在GNU make系统中获得支持的目的在于与早期使用的makefile文件保持兼容性。这些规则被划分为单字符和双字符后缀规则两类处理。
双后置规则由一对特定的前后置组成:一个是目的前后置、另一个是源文件前后置。它能匹配所有以目的前后置结尾的文件名。其隐含依赖关系则是通过将目的前后置替换成源文件前后置的方式建立起来的。其中一个实例是当目的前后置与源 file 前后的前后置分别为'.o' 和 '.c' 时的情况:这种情况下形成的双前后fix 规则等价于指定格式 '%.o : %.c'。
单个文件名的扩展规则由单个扩展名来定义;这个扩展名属于源文件类型;它匹配所有文件名称,并确定它们相应的依赖名称为通过在这些名称末尾添加源文件的扩展名而获得;仅当源file的exten suffix为‘.c’时,默认使用此rule作为formatting rule ‘% : %.c’
依据已知后缀列表判断后的策略:单个目标结尾对应单符号模式;多个目标结尾对应双符号模式
比如,在缺省列表中已知的后缀有.o和.c。因此, 如果有人想要创建一个规则,其目标为.c.o, Make就会将其视为双级后缀规则, 其中源文件的后缀通常是.c, 而目标的后缀通常是.o。请注意以下是一个已经过时的方法,用以编译C语言程序:
任何后缀****规则都不能拥有属于自身的依赖项。一旦存在这样的依赖项(即被这些依赖项影响),则这些工具将不再是作为后缀****规则使用的工具。例如以下规则:
告诉从依赖foo.h生成文件名为‘.c.o’的文件,并不是象格式规则:
说明如何通过.c文件生成.o文件的方法:该过程不仅能够创建所有.o文件,并且遵循特定的格式规则;同时,在构建过程中引用外部资源如foo.h进行辅助操作
无任何后缀规则也无用处。 它们不按照命令格式规则的方式去除先前设定(参阅** 删除隐含规则** )。 它们仅将它们作为目标存储于数据库中。
该术语中的已知后缀属于特殊目标「.SUFFIXES」的简单依赖结构。基于这些规则和这些约束关系,在此框架中引入额外的约束关系从而让用户自定义新的独立后缀。例如:
把‘.hack' 和‘.win'添加到了后缀列表中。
为了使特殊目标‘.SUFFIXES’不受预定义后缀的影响而被独立处理,请您创建不依赖于其他规则的特定规则来处理这些目标。通过这种方法,请您考虑以下步骤:首先消除现有相关设置;其次检查是否所有必要的条件都已设置好;最后确保所有参数都已正确赋值并处于可执行状态。
标志‘-r'或‘--no-builtin-rules'也能把缺省的后缀列表清空。
变量SUFFIXES$在make处理时会默认加载一组扩展名列表。您也可以通过指定特殊目标‘.SUFFIXES$’来修改这一设定,默认情况下不可更改。
10.8隐含规则搜寻算法
该make过程旨在为指定的目标变量't'挖掘隐式规则,并应用于那些未伴随明确指令的标准模式的目标;同时也可以应用于那些非标准模式依赖的情况;此外,在隐式模式驱动下通过递归调用自身的方式继续搜索隐藏的关联模式。
在本算法中没有提及所有后缀规则,因为它们在makefile文件读入时被转换为格式规则。
对于每个是archive(member)类型的档案成员目标,该算法将重复运行两次.首先,系统会尝试使用整个目标名t进行匹配.如果在第一次运行中未找到相关规则,则系统将在第二次运行时将(target=(member))设为搜索依据.
从文件路径变量't'中拆分出一部分作为主目录路径,并将其命名为'd';剩下的则定义为文件名变量'n'。例如取值如't=src/foo.o'时,则'd=src/'; 'n=foo.o'。
创建所有目标名称的格式规则列表。如果目标格式中包含斜杠符号,则归类为类型t;否则归类为类型n。
当列表中存在一个非万用规则时,则应移除所有最终万用规则。
4、 将没有命令的规则也从列表中移走。
5、 对每个列表中的格式规则:
1、 寻找stem‘s’,也就是和目标格式中%匹配的‘t’或‘n’部分。
- 采用stem's来计算dependency names。若目标格式中无斜杠,则应在每个dependency前面添加字母d。
3、 检查所有依赖项的存在性与可构建性。(若任何文件在makefile中被指定为目标或依赖,则认为其存在。)若所有依赖均存在并可构建,或者无依赖,则适用此规则。
6、 如果到现在还没有发现能使用的规则,进一步试。对每一个列表中的规则:
1、 如果规则是最终规则,则忽略它,继续下一条规则。
2、 象上述一样计算依赖名。
3、 测试所有的依赖是否存在或能够创建。
针对未存在的依赖项,通过该算法递归地进行查找操作,是否能借助隐含规则来进行创建?
5、 如果所有依赖存在或能使用隐含规则创建,则应用该规则。
如果没有任何内建规则可用,则如果有用于目标' . DEFAULT '的内建默认值配置,则应用该默认值配置。在这种情况下,在这种情况下赋予目标' . DEFAULT '的命令值为't'。
一旦发现可应用规则后,则对每个匹配的目标格式(不论是't'还是'n')实施stem's替换%的操作会产生相应的文件名并将其存储于数据库中;在执行这些命令之后将所有生成的文件名存入数据库中并标记该操作已完成其时间戳与目标文件t相同
当遵循某种格式规则的操作时,在创建't'时执行,在这种情况下自动生成的目标将被赋值为其相应的依赖项(参阅*_自动生成*)
11使用make更新档案文件
档案文件内部嵌入了多个子文件,并且每个子文件都有独特的名称标识;常见做法是将这些称为成员;档案文件与程序ar常被同时引用,并且它们的主要用途是作为连接的例程库。
11.1档案成员目标
独立的档案文件成员可以在make中用作目标或依赖. 按照以下方式, I can in the archive file 'archive' to specify the member named 'member'.
这种结构仅限于目标与依赖领域内使用,并绝对不可以用于命令语境。大部分程序都缺乏对这一语法的支持,并且无法直接作用于档案成员。仅限于那些专门用于处理档案文件的操作者能够采用此方法进行更新操作。例如,在以下规则中可观察到此类情况:借助拷贝文件'hack.o'可以在'foobb'档案中创建名为'hack.o'的新成员。
通常情况下,“大多数档案成员目标都会按照这种方式进行更新”,并且,“专为更新档案成员目标而设计的一条隐性规则”。注意: 如果档案文件不存在,则程序ar必须设置‘c’标志。
在同一份文件中一并设置多个成员
等同于:
另外,在引用档案成员时可以采用shell类型通配符。参考**在文件名中使用通配符**。例如,“$ foobb(*.o)”会扩展到包含所有以“.o”结尾的文件名的档案$ foobb。类似于:“$ foobb(hack.o) 和 \$ foolib(kludge.o)”。
11.2 档案成员目标的隐含规则
对目标‘a(m)’表示名为‘m’的成员在档案文件‘a’中。
When Make is used for searching implicit rules of this target, it is because it possesses an additional distinctive feature. It holds that when a rule matches '(m)', it inherently also matches 'a(m)'. This is due to the inherent nature of the rule.
其特点引致一个特殊的规则。()该规则通过复制文件m至档案中设置为a(m))。例如它通过复制bar.o至foo.a中的过程设置为foo.a(bar.o).
如果该规则与其他规则构成链条,则其功能极为强大。为了保护圆括号能够被shell解析,“make "foo.a(bar.o)"”(特别地即使缺少makefile文件而仅有文件"bar.c"也同样可以确保以下命令得以顺利执行:
这里make假设文件‘bar.o’是中间文件。参阅** 隐含规则链** 。
诸如这样的隐含规则是使用自动变量‘$%’编写的,参阅** 自动变量** 。
档案成员名称不能有路径名称,在makefile文档中路径名称是重要的用途。如果有人想要创建一个档案成员规则'foo.a(dir/file.o)' ,该工具会自动执行以下命令以完成更新
它的内容是将文件' dir/file.o '复制到名为' file.a '的档案中。在执行此操作时会用到自动变量%D和%F。
11.2.1更新档案的符号索引表
用作库的档案文件一般会包含一个特别指定为'__SY
MDEF' 的特殊部分,在此部分中记录了由整个系统内所有其他部分所定义的所有外部符号名称及其对应位置信息。每当修改了系统的其他部分之后,在修改相关的'__SY
MDEF' 部分之前,请务必确保该特殊部分已经正确地反映了当前系统的状态以备后续使用需求。为了实现这一更新过程,请按照使用 ranlib 程序的操作步骤进行操作:
正常情况下,您应该将该命令放到档案文件的规则中,把所有档案文件的成员作为该规则的依赖。例如:
该程序的作用体现在对档案成员‘x.o'、‘y.o'以及后续其他相关成员的更新上。随后,在执行程序ranlib的过程中,“__”号下的SYMDEF表项也被相应地进行了更新。需要注意的是,默认情况下,“新增”、“删除”、“修改”等操作通常遵循隐含的规则。一般而言,您无需手动指定这些默认行为,在系统会默认将文件复制到相应的档案中。这部分内容在之前的章节中有详细说明。
使用GNU ar程序时这不是必要的,因为它自动更新成员‘__.SYMDEF’。
11.3 使用档案的危险
同时采用并行执行方式(通过指定-j选项启动,并参考相关文档)。当多个命令同时作用于同一份档案文件时,可能会导致数据损坏。未来版本可能引入机制来解决这一问题,但目前仍需谨慎操作以避免出现此类情况。
11.4 档案文件的后缀规则
用于处理档案文件, 您可以选择创建一种特殊的后缀规则. 有关这一主题, 请参阅** 过时的后缀规则** . 在GNU make中, 这种档案后缀规则已弃用, 因为其针对文档的标准更为通用(如见** 档案成员目标的隐含规则** ). 然而为了确保与其他版本make程序兼容性, 它们仍被保留使用以保证兼容性.
为档案创建扩展功能提供支持的策略和方法
这和下面的格式规则工作完全一样:
实际上就是make观察到一种特定情况:当它看到以'.a'作为附加标记的某种标记时的行为方式。对于任何一个双层标记模式'.x.a'来说,在这种情况下系统会将其转换为相应的处理逻辑。
由于您可能需要将‘.a’用作文件类型后缀,在make工具中会按照常规方法将档案的后缀规则转换为格式规范,并可参考**过时的文件类型规则**以获取相关信息。这样的双重后缀模式‘.x.a’会产生两个格式模式:一个是(%.o): %.x, 另一个是%.a: %.x。
12 GNU make的特点
此处归纳了 GNU make 的核心特性,并用于对比其他版本的 make 工具。为了实现可移植性目标,请避免参考 4.2 BSD 核心团队开发的 make 工具特性中的相关内容。同时建议避免参考 不兼容性和失去的特点 中所列内容。
许多特点在System V 中的make也存在。
- 变量
VPATH具有特殊意义,在目录中搜寻依赖(参见相关内容)。这一特性存在于System V中的make系统中(但未被证实)。4.3 BSD版本的make也具备这一特性(据称模仿了System V中的VPATH变量特性)。 - 包含其他makefile文件的功能(参见相关内容)。这是GNU make的一个扩展功能。
- 环境变量具有读取和通信的功能(参见相关内容)。
- 在递归调用make时可以通过
MAKEFLAGS参数传递选项(参见相关内容)。 - 在档案引用中自动变量
$%会被设置为成员名(参见相关内容)。 - 自动变量有多种变体形式如
$(@F)与$(@D)等(参考相关内容)。我们在此基础上对自动变量进行了扩展处理,并将其应用于自动变量$^上(参考相关内容)。 - 变量引用的相关知识请参考相关内容。
- 命令行选项‘-b’与‘-m’分别执行接受与忽略操作(在System V make系统中这些选项实际发挥了作用)。
- 即使指定‘-n’‘-q’或‘-t’等选项也能通过MAKE指令实现递归调用make系统(参考相关内容)。
- 在后缀规则中支持‘.a’后缀格式(参考相关内容)。这一特性在GNU make系统中基本未被使用因为隐式规则链机制已经足够强大(参考相关内容)。
- 命令行排版与反斜杠换行结合仍能保持原样功能当打印输出时它们的表现形式基本一致但去除了初始化空白部分。(参考相关内容)
其中一些特点被多个不同版本的make吸收,目前尚不清楚具体是哪些版本吸收了哪些特点。
- 该功能已在多个不同版本的make中实现。我们尚不清楚是谁最先发明了这一特点,并且它发展得非常迅速。请参阅** 定义与重新定义格式规则** 。
- 这一特性最初是由Stu Feldman在其make版本中实现,并在AT&T第八版Unix研究项目中得到了应用(这里将其称为"传递闭合")。我们不确定这一特性是源自于他们还是我们自己独立开发出来的。请参阅** 隐含规则链 **。
- 自动变量包含了当前目标所需的所有依赖项的列表。我们对此一未知开发者表示敬意(此处可能指同一人)。自动变量
$+是变量$^的一种扩展形式。 - Andrew Hume在其mk程序中首次提出并命名为"what if"标志(该标志在GNU make中被采用)。请参阅** 代替执行命令 **。
- 并行执行的概念已体现在许多make版本中(尽管System V或BSD并未实际支持这一概念)。请参阅** 执行命令 **。
- 格式替换功能起源于SunOS4系统(后被集成到GNU make中)。关于这一技术的具体起源信息尚不明确,请参阅** 变量引用基础 **。
- 在命令行前加上的"+"符号具有特殊意义(详情请参考** 代替执行命令 **)。这一规定由IEEE Standard 1003.2-1992 (POSIX.2)首次确立。
- 对于+=语法用于向变量追加赋值的功能起源于SunOS4 make系统(后被整合到GNU make中)。请参阅** 为变量值追加文本 **。
- SunOS4 make系统提供的语法"archive(mem1 mem2...)"允许在一个档案文件中列举多个成员文件,请参阅** 档案成员目标 **。
- -include指令可将makefile文件包含进来,并且即使文件不存在也不会报错信息(此功能源自于SunOS4 make系统)。(但需注意的是,在单个指令中SunOS4 make系统可指定多个makefile文件的做法与SGI make中的sinclude指令相同),
剩余的特点是由GNU make发明的:
- 使用‘-v'或`--version'选项打印版本和拷贝权信息。
- 使用‘-h' 或‘--help' 选项总结make的选项。
- 简单扩展型变量。参阅** 变量的两特特色** 。
- 在递归调用make时,通过变量MAKE自动传递命令行变量。参阅** 递归调用make** 。
- 使用命令选项‘-C' 或‘--directory'改变路径。参阅** 选项概要** 。
- 定义多行变量。参阅** 定义多行变量** 。
- 使用特殊目标.PHONY声明假想目标。AT&T 贝尔实验室Andrew Hume 使用不同的语法在它的mk程序中也实现了该功能。这似乎是并行的发现。参阅** 假想目标** 。
- 调用函数操作文本。参阅** 用于转换文本的函数** 。
- 使用‘-o'或‘--old-file'选项假装文件是旧文件。参阅** 避免重新编译文件** 。
- 条件执行。该特点已在不同版本make中已经实现很长时间了;它似乎是C与处理程序和类似的宏语言的自然扩展,而不是革命性的概念。参阅** makefile文件中的条件语句** 。
- 指定包含的makefile文件的搜寻路径。参阅** 包含其它makefile文件** 。
- 使用环境变量指定额外的makefile文件。参阅** 变量MAKEFILES** 。
- 从文件名中去除前导斜杠`./' ,因此,‘./file' 和‘file' 是指同一个文件。
- 使用特别搜寻方法搜寻形式如‘-lname’的库依赖。参阅** 连接库搜寻目录** 。
- 允许后缀规则中的后缀包含任何字符(参阅** 过时的后缀规则** )。在其它版本的make中后缀必须以‘.’开始,并且不能包含‘/’字符。
- 包吹跟踪当前make级别适用的变量MAKWFILES的值,参阅** 递归调用make** 。
- 将任何在命令行中给出的目标放入变量MAKECMDGOALS。参阅** 指定最终目标的参数** 。
- 指定静态格式规则。参阅** 静态格式规则** 。
- 提供选择性vpath搜寻。参阅** 在目录中搜寻依赖** 。
- 提供可计算的变量引用。参阅变 量引用基础 。
- 更新makefile文件。参阅重建makefile文件。System V
make中有非常非常有限的来自于该功能的形式,它用于为make检查SCCS文件。 - 各种新建的隐含规则。参阅** 隐含规则目录** 。
- 内建变量`MAKE_VERSION' 给出make的版本号。
13 不兼容性和失去的特点
其他版本的make程序也包含一些特定功能,在GNU make中并未实现。该标准由IEEE制定,并规定无需实施这些特性。
- ‘file((entry))' 形式的目标代表一个档案文件的成员file。选择该成员不使用文件名,而是通过一个定义连接符号enty的OBJ文件。该特点没有被GNU
make吸收因为该非标准组件将为make加入档案文件符号表的内部知识。参阅** 更新档案符号索引表 。** - 在后缀规则中以字符‘~’结尾的后缀在System V
make中有特别的含义;它们指和文件名中没有‘~’的文件通讯的SCCS 文件。例如,后缀规则‘.c~.o'将从名为‘s.n.c'的SCCS文件中抽取文件‘n.o'。为了完全覆盖,需要这种整系列的后缀规则,参阅** 过时的后缀规则** 。在GNUmake中,这种整系列的后缀规则由勇于从SCCS文件抽取的两个格式规则掌管,它们可和通用的规则结合成规则链,参阅** 隐含规则链** 。 - 在System V
make中, 字符串‘@'又奇特的含义,在含有多个规则的依赖中,它代表正在处理的特殊目标。这在GNU `make没有定义,因为字符串`‘'代表一个平常的字符‘$'。使用静态格式规则可以实现该功能的一部分(参阅** 静态格式规则** )。System Vmake中的规则:
在 GNU make 中可以用静态格式规则代替:
- 在System V以及4.3 BSD make系统中,在借助于VPATH参数(参考用于依赖目录搜索的部分)的情况下被搜索到的文件,在修改其文件名后会被包含进相应的命令行参数。我们倾向于认为采用内置变量更为直观简洁,并因此选择不引入此特性。
- 对于某些Unix make工具而言,在规则定义中的自动变量*会以一种令人难以理解的独特方式发挥作用:它最终会生成的是该规则的目标完整名称而非通常定义下的*变量。我们尚不清楚Unix make是如何进行这种考虑的。
- 隐含规则搜寻机制(参阅默认情况下使用的隐式搜索机制)并非仅限于那些未预先指明特定目标的任务;相反地,则是为了确保所有目标都被考虑到。
Unix make能够直观地识别文件’foo.o'所依赖的文件’foo.c'。我们相信这种做法容易产生混乱。The dependencies in Make are already well-defined (at least for GNU Make).These practices are not in accordance with the rules.
- GNU
make不包含任何编译以及与处理EFL程序的内在规则。
如果我们了解某人使用了EFL程序组别,并且愿意的话,请将其加入到我们的支持范围内。
在 SVR4make中, 一条后缀规则可省略命令, 其处理方法与包含空命令时的情形一致(参阅**使用空命令**)。例如:
针对内置的C语言后缀规则“.c.a”,我们可以认为其主要作用是对无命令的目标进行复制操作。我们发现,在处理无命令的目标时,默认为其添加依赖会比仅复制简单的规则更为高效。与GNU make中的类似情况一致。
某些版本的make调用shell采用了-e标志而非-k标志(参阅《测试程序编译》)。-e指示一旦程序运行返回非零状态就会立即退出。我们主张根据每条命令是否需要特殊处理直接将其写入命令行更为清晰明了。
14 makefile文件惯例
本章阐述了遵循GNU make规范撰写makefile文件的标准和流程。借助Automake工具能有效指导您依照既定的标准构建makefile文件。
14.1 makefile文件的通用惯例
任何makefile文件都应该包含这行:
防止系统层面变量VARSHELL意外继承环境值所带来的问题。(在一个像GNU make这样的环境中这种情况通常不会发生。)
各具特色的make项目都有各自带有特定的扩展名和隐藏的规则,这种差异可能导致混乱或执行错误的行为。因此最理想的方式就是建立一个专门的后缀列表,在该列表中仅列出您在当前makefile文件中所使用的扩展名。例如:
该first line removes all unnecessary items from the suffix list. The second line clearly specifies the suffixes that may be implicitly referenced by default rules in this makefile.
请不要将‘.’视为命令运行的执行路径。当您在构建应用程序时,请确认:如果该程序是项目组成部分,则应使用‘./’;而如果该程序在源代码中处于固定位置,则应使用‘$(srcdir)’。若无指定前缀,则仅在当前目录下搜索。
在设置目录结构时,默认使用当前目录 './' 与源代码目录 '$(srcdir)'' 的区别至关重要。由于用户可以在配置界面中通过 '--srcdir' 选项指定单独的目标目录
只有当所创建的存储结构与主项目源码路径不一致时才会导致系统错误。由于涉及的相关资源文件均位于该主项目的根库路径中
当运行GNU make工具时,
依靠变量VPATH来搜索源文件,在单一属性文件存在的情况下能够有效地运行,
由于make中的自动变量<包含着源文件存在的路径。
(例如,在许多版本的make系统中,默认仅会在隐含规则中定义值变量<。)
例如一个典型的makefile目标结构:
将被替换为:
为了确保变量VPATH能够正常工作。当目标涉及多个依赖时,默认情况下会使用$(srcdir)作为路径最为简便的方式以确保该规则能够有效工作的方法。例如,在上述示例中,默认的目标路径应设置为:
GNU的分类中常见包含一些非源文件——如Info文档等。这些非源文件位于src代码目录中,并应保留在该位置上而非放置于构建目錄中。因此应将makefile规则更新至src代码目錄
然而
旨在构建创建和安装目标,并且其中包含(包括其子目标)能够在并行的框架中正确工作。
14.2 makefile文件的工具
分别适用于shell sh环境下的makefile文件命令及其相关的脚本(如configure),不涉及ksh或bash特有的功能。避免使用ksh或bash特有的功能。
该配置相关的脚本(如 configure)及其 Makefile 文件 除下面所列工具外应避免使用其他任何工具
压缩程序gzip可在dist规则中使用。
应避免采用特定于单一系统的命令,在这些程序中推荐采用通用操作符以确保兼容性
应当在makefile中尽量不建立符号链接是非常有帮助的提醒, 因为存在这样的问题.
为创建及安装Makefile而制定的规则能够利用编译器及其相关工具实现功能目标;然而建议通过make变量来实现这一点以提高效率与灵活性;我们基于以下思路编写了一系列工具:
请使用下述make变量运行这些程序:
在使用ranlib或ldconfig时,请确保如果系统中不存在要使用的程序将不会产生副作用。请设置过滤掉这些命令产生的错误,并持续监控其运行状态以确保正常操作。(Autoconf‘AC_PROG_RANLIB'宏可以帮助您实现此功能。)建议如果采用符号连接的方式,请为不支持符号连接的操作系统准备一个低效但安全的工作流程。
附加的工具也可通过make变量使用:
该系统位于源代码文件(或其他脚本文件)中。您了解这些工具在某些特定的系统中有良好的表现吗?
14.3 指定命令的变量
Makefile文件应该为重载的特定命令、选项等提供变量。
特别地,在您运行大多数工具时都应合理地使用变量。如果希望调用程序Bison,则该名为 BISON 的全局变量已默认配置为:"( BISON )=bison"。当您需要调用程序 BISON 时,请合理地进行如下引用:"( \texttt{ BISON } )=bison;"
文件管理工具如ln, rm, mv等用途广泛,在实际操作中无需采用这种方式引用;因为用户无法更换其他替代程序。
每个程序变量应与其用于向该程序提供选项的选项变量一起存在。对于命名该程序的变量之后附加一个'FLAGS'标记即可实现这一目标——例如,在BISON中使用BISONFLAGS(这种命名方式在C编译器中采用CFLAGS,在Yacc中则采用YFLAGS;尽管Lex的标准性特征使得我们仍需保留其单独处理的方式)。特别需要注意的是,在执行预处理阶段时应使用CPPFLAGS;而在执行链接阶段时应使用LDFLAGS;这些设置与直接调用ld命令的行为一致。
当使用C编译器处理特定文件时必须使用的那些选择项不应该存储于变量**`** CFLAGS $ 中
在变量CFLAGS设置中有选项‘-g’ ,虽然在某些编译环境中不需要它 ,因此通常建议将其设为默认值 。当构建数据包时选择GCC作为编译器 ,并且通常将其设为默认值 ,则会包含选项‘-o’
将变量CFLAGS置于编译指令序列末尾,在包含编译选项和其他变量之后的位置上这样就使得用户能够通过设置CFLAGS对其他相关参数进行重定义
每次调用C编译器都用到变量CFLAGS ,无论进行编译或连接都一样。
在所有Makefile中都会定义变量_INSTALL$是一个用于将文件安装到系统的基本命令.
在所有Makefile文件中,明确变量 INSTALL_PROGRAM 和 INSTALL_DATA,其默认值均为 (INSTALLED).当进行实际安装操作时,无论遇到可执行程序还是不可执行程序,通常会将它们用作指令.下面提供了一些示例来说明如何利用这些变量化简流程
您无需将变量DESTDIR预先指定为目标文件名, 这样的操作将使安装程序能够在实际目标文件系统中生成相应快照. 无需在makefile或其他安装相关的地方定义DESTDIR变量. 通过变更DERSTDIR可修改上述示例:
通常,在安装命令中会采用文件名而非路径名作为第二个参数。每个安装文件均对应一条独立的安装命令。
14.4安装路径变量
通常会以变量命名的安装目录,在非标准位置进行安装也非常简单;这些标准名称将在下面进行介绍;安装目录是基于标准文件系统布局设计的;其中使用的变体已经广泛应用于SVR4、4.4BSD、Linux、Ultrix v4等现代操作系统中
以下两个变量配置了安装文件的主目录位置,默认情况下所有其他安装路径都会基于这两个主目錄构建,并且系统不允许在这些主目錄之外直接配置任何可执行文件
`prefix'
前缀被定义为构建一系列变量时使用的默认值。在软件工程领域中,默认情况下prefix会被设定为/usr/local路径。当进行完整的GNU系统构建操作时,默认情况下prefix会被设置为空(即未指定)。其中 / 表示根目录,并且是一个符号链接。(如果采用Autoconf配置方式,则应将其表示为 @prefix@ 。)与常规构建过程使用的 prefix 值不同,在运行 make install 时,默认不会重新编译程序。
`exec_prefix'
前缀被定义为构建一系列变量时的缺省值。其中$(exec_prefix)专门用于存储包含特定于机器文件的目录路径(例如可执行文件以及程序库)。而$(prefix)则直接指向其他相关目录。通常情况下,在运行'make install'命令时应避免使用与创建程序期间相同的$(exec_prefix)值。
可执行程序安装在以下目录中:
`bindir'
在该目录中配置用于使安装用户的程序能够正常运行的可执行文件。其默认设置为' /usr/local/bin ' 。建议将其表示为' $(exec_prefix)/bin ' 。如果采用Autoconf ,则建议将其配置为' @bindir@ ' 。
`sbindir'
该目录用于配置能够通过shell脚本运行的执行文件。该配置项仅适用于系统管理员。其常规设置位于' /usr/local/sbin '路径上,在实际应用中建议将其设置为' $(exec_prefix)/sbin '格式;建议采用Autoconf时则需将其配置为' @sbindir@ '的形式。
`libexecdir'
该目录中用于调用其它程序的可执行文件。其默认值通常为' /usr/local/libexec ' ,但在实际应用中应当将其配置为' $(exec_prefix)/libexec ' 。若采用Autoconf构建,则建议将其配置为' @libexecdir@ ' 。
程序执行时使用的数据文件可分为两类:
- 程序支持修改的部分与无法修改的部分(尽管用户能够编辑其中一部分)。
- 体系结构无关的文件是指这些文件可被所有设备访问;而体系相关的文档仅限于同一类型设备及操作系统下才能访问;除此之外则无法在两台设备间共享。
会产生六种不同的可能性。我们坚决不建议采用体系相关的方法(然而OBJ格式和其他库文件被允许使用)。除了上述两种情况之外(选择其他非体系相关数据会更为便捷),这一做法相对容易实现。
所以,这里有 Makefile变量用于指定路径:
`datadir'
该目录专门用于安装非关键系统数据文件。其中默认情况下会指向prefix下的share目录,在实际操作中建议将其设置为(prefix)/share以避免潜在问题。需要注意的是,请参考相关的变量设置包括infodir和includedir。如果采用Autoconf工具进行配置,则建议将其设置为@datadir@形式以确保正确性。
`sysconfdir'
在该目录中安装属于单个机器的只读数据文件,在于配置主机的各种文件。这些文件包括邮件服务程序、网络配置相关的文档等。所有该目录下的文件都是普通的ASCII文本文件。通常设置为‘/usr/local/etc’但实际操作中应将其写成‘(prefix)/etc’(若采用Autoconf则应设为‘@sysconfdir@’)。在此处不应放置可执行程序(因为它们通常位于‘(libexecdir)’或‘(sbindir)’))。同样也不建议在此处安装需要修改后使用的程序(这些程序负责调整系统拒绝的相关设置)。这些程序通常位于‘(localstatedir)’)
`sharedstatedir'
该目录下存在一些在安装程序运行过程中需要变化的无关数据文件。这些文件的默认设置为‘/usr/local/com’。然而,在实际应用中应当将其配置为‘$(prefix)/com’。(如果您采用Autoconf配置,则建议将其设置为‘@sharedstatedir@’)
`localstatedir'
该目录中的数据文件涉及安装程序时会做相应调整。但它们都属于特定的机器。
无需在该目录内修改配置程序包选项。
将这些配置信息存储于独立的文件中。
其中,
其常规值设置为'/usr/local/var',
但在实际应用中,请将其表示为'$(prefix)/var'。
若采用Autoconf,则应将其表示为'@localstatedir@'。
`libdir'
该目录中存储着用于存放OBJ文件和相关库的OBJ代码。建议在此目录中不应安装可执行文件,因为它们可能属于'(libexecdir)'路径下的位置。变量`libdir`的默认设置通常是' /usr/local/lib '路径指向的地方,在实际应用中可能会根据需求修改为'(exec_prefix)/lib'的形式。(如果采用Autoconf配置方式,则建议将其表示为'@libdir@'形式。)
`infodir'
该目录下的Info文件用于安装软件包。其缺省值通常设置为/usr/local$info/。但在实际应用中建议将其配置为$(prefix)/info。 (如果采用Autoconf配置,则应将其设置为@infodir@)
`lispdir'
注意:以上内容可能与某些系统不兼容
最后两句
注意
`includedir'
在这个目录下用于安装用户的程序中包含C预处理指令所需的头文件位置。这些通常位于标准路径' /usr/local/include '。但根据具体需求,在实际应用中建议将其配置为' $(prefix)/include '的形式。如果采用Autoconf构建工具,则通常建议将其配置为' @includedir@ '的位置。需要注意的是,除GCC编译器外的大多数其他编译器不会在默认包含目录中搜索头文件路径。因此这种方法仅适用于GCC这样的编译器环境。然而有时候这也并非问题所在,因为一些库文件确实依赖于GCC来进行构建。但也有部分库文件会依赖于其他编译器,并会在includedir指定位置以及另一个特定位置存储相关的头文件以满足不同需求
`oldincludedir'
该目录下用于包含‘#include'的头文件,在除GCC外的其他C语言编译器中被用作包含标准库头文件的标准位置。其默认值通常为‘/usr/include’。(如果使用Autoconf构建工具,则应将其设置为 @oldincludedir@。)Makefile中的变量oldincludedir是否为空?如果是空值,则系统会避免尝试使用该目录下的头文件,并会删除第二次安装的相同头文件。一个软件包在该目录下替换已有的头文件时,默认情况下不会进行替换(除非所有头文件都来自同一个软件包)。例如,在软件包Foo中提供了一个名为‘foo.h'的头文件,则该系统会在变量oldincludedir指定的位置安装此头文件(满足以下任一条件:1)此处尚未包含过‘foo.h';2)此处已有过来自软件包Foo的标准库头文件)。要确认某特定头文件是否属于软件包Foo管理,请在其所在文档中放置一个magic字符串作为注释内容,并利用grep命令查找此字符串。
Unix风格的帮助文件安装在以下目录中:
`mandir'
在安装该软件包后,默认情况下顶层帮助(如果存在)位于 '/usr/local/man'。其中 normals 设置为 ' /usr/local/man' 。但实际应用中建议将其设置为 '$(prefix)/man'’ 。如果采用 Autoconf 系统,则建议设置成 '@mandir@'。
`man1dir'
这个目录下用于安装第一层帮助。其正常的值是‘$(mandir)/man1'。
`man2dir'
这个目录下用于安装第一层帮助。其正常的值是‘$(mandir)/man2'。
`...'
避免将任何GNU软件的主要文档用作参考材料;建议编写用户指南以替代帮助页面的功能。作为辅助工具仅限于协助用户在Unix系统上运行和管理这些软件
`manext'
文件名称代表对已安装的帮助页面的扩展。它包含一定的时间间隔,并在后面紧跟合适的数字;通常情况下为'1'
`man1ext'
文件名表示对已安装的帮助页第一部分的扩展。
`man2ext'
文件名表示对已安装的帮助页第二部分的扩展。
`...'
采用这些文件名替代`manext'。当该软件包的帮助页面需要安装相应的帮助文档的不同章节时。
最后您应该设置一下变量:
`srcdir'
在该目录中存放着需要进行编译的原始文件。该变量通常由脚本configure在其执行过程中赋值。(如果您使用Autoconf, 应将它写为‘srcdir = @srcdir@'.)
例如:
为了在标准用户的指定期目录中安装大量文件,将其程序文件放置在特别指定的子目录内是必要的.如果您打算这样做,请编写安装规则以建立这些子 directories.
请避免假设用户会在列出的变量值范围内包含这些子目录。通过一套变量名设置安装目录中的软件包, 以便于不同类型的GNU软件包能够被精确指定。因为这种设计使得所有软件包都需具备在用户操作时自动处理的能力
14.5用户标准目标
所有的GNU程序中,在makefile中都有下列目标:
`all'
进行完整的程序编译。这应作为默认目标存在。此目标无需重建文档文件,在发布版本中通常会包含Info文件,在特殊需求下才会构建DVI文件的操作仅限于特定情况下的处理方式。当采用默认配置时, 编译与连接过程会使用选项‘-g', 因此,在非调试模式下不会进行详尽的调试操作。对于那些并不介意外存管理问题的用户而言, 如果他们希望将独立运行的一份可执行程序与辅助信息区分开来, 就可以从源代码中提取出相应的可执行程序部分存储起来即可完成这一操作步骤
`install'
编译程序后将可执行程序和库文件复制至专为实际使用保留的文件夹中。如果测试仅需确认安装效果,则可以直接运行验证程序即可。需要注意的是,在安装过程中不要剥离可执行程序,默认情况下'安装'操作不会涉及此过程(参见**指定命令的变量)。为了方便管理和分发,请确保编写的'安装'目标仅负责构建所有依赖项并提供完整的功能集合——即运行'make all'命令即可完成所有构建工作。这样做的好处在于简化了用户命名空间,并方便在不同系统上进行软件包部署。如果当前目录不存在,则该操作应自动创建所需目录结构——包括由变量prefix和exec_prefix指定的独特目录及其子目录结构(参阅指定命令的变量)。实现这一功能的方法是通过下面所述的目标 installdirs 。在所有安装帮助页命令前添加'-'可以使 make 命令忽略这些输出错误(参见指定命令的变量),从而确保即使在没有Unix帮助页支持的情况下也能顺利完成软件包安装过程)。有关Info文件的具体操作,请参考变量(INSTALL_DATA)将 Info 文件复制到(infodir)目录中(参阅 指定命令的变量 );如果存在名为 install-info 的程序,则应运行其后端脚本以完成操作(参见 指定命令的变量 **)。这个示例展示了如何配置并运行 install-info 程序:
install-info
在组装install目标时,请确保将所有的命令归位为三类:正常操作指令、安装前指令以及安装后指令。参阅** 安装命令分类** 题目。
`uninstall'
移除所有具有'install'目标复制行为的文件,并且该规则应仅排除编译所产生的目录,请确保只进行操作即可无需进行其他处理。此外,请注意反编译命令与之类似地划分为三类,请参阅**反编译命令分类**获取详细信息
`install-strip'
和目标install相仿,在安装过程中仅提取可执行文件。 在绝大多数情况下,该目标的定义通常非常简单:
在常规情况下我们并不推荐从系统中剥离可执行程序来进行安装。仅当您确信这些程序在使用时不会导致问题时才允许这样做。从一个正在运行的实际可执行文件中剥离并安装后,同时需要保存那些在这种操作可能导致BUG出现的情况下的相关文件。
`clean'
清空当前目录中的所有文件
`distclean'
清空当前目录下的所有文件,并非所有的操作都会产生预期效果。通常情况下,“初始化系统”或“配置参数”会生成特定类型的配置信息。如果未解压原始代码库,“初始化系统”操作将无法生成其他类型的信息。“make distclean"命令则会在最终发布的目录中仅保留原有的那些信息。
`mostlyclean'
类似于一个名为"clean"的目标, 该目标特意不将那些通常无需重新编译的程序或文件从代码库中删减。例如, 在使用GCC时,"mostly clean"不会移除包含在内联汇编中的共享对象如libgcc.a. 因为在这种常见情况下,"libgcc.a"不需要重新编译.
`maintainer-clean'
基本上,在当前目录中删除所有可以由该Makefile重建的文件。这些被此目标所移除的文件包括通过distclean目标进行删除的操作,并且还包括从Bison生成的C源代码以及相关标志列表和Info文档等。我们称"几乎所有"的原因是因为运行命令make maintainer-clean并不会移除脚本"configure"这一特殊情形;实际上,在运行make maintainer-clean时,并不会移除那些用于执行脚本"configure"以及构建整个项目所需的基础架构的所有相关文档。请注意:这是在仅此一次情况下无法被消除的一类特定文档——它们与脚本"configure"相关并且用于构建整个项目的基础架构。
为了帮助所有Make用户的理解,请注意以下事项:
`TAGS'
更新该程序的标志表。
`info'
产生必要的Info文件。最好的方法是编写象下面规则:
您必须在makefile文件中定义变量MAKEINFO。该程序将被运行以生成Makeinfo脚本,并位于源代码目录下。通常情况下,在GNU发布程序中,Info文件与源代码文件一起生成并存储在同一目录中。当用户构建一个软件包时,默认情况下无需重新更新Makeinfo脚本即可满足发布需求。
`dvi'
创建DVI文件用于更新Texinfo文档。例如:
您必须在makefile文件中定义TEXI2DVI变量。该程序将执行texi2dvi命令,这属于发布中的Texinfo组件。为了实现目标,请选择以下两种方式之一:或者仅编写依赖于文件的项目;或者允许GNU make提供相应的命令来处理构建流程。
`dist'
建立一个供程序访问的数据压缩包。
通过建立适当的存储结构,在每个存储单元中按特定方式命名。
这些名称可以基于软件发布时使用的标准命名 convention。
此外,在名称中也支持包含版本信息。
例如, GCC 1.40版本的数据块将被解压到位于 ./gcc-1\.40 的存储空间中。
最方便的做法是采用适当的方式设置父级存储位置。
将 tar 文件分解后存放在相应的父级存储空间中。
为了提高效率, 建议对 tar 文件进行gzip压缩处理, 其结果即被称为 gcc-1\.40\.tar\.gz 数据块。
目标位置应基于所有发布的非源代码部分, 因此你需要确保所发布的数据已及时更新。
请参阅上述文档中的相关部分:如何正确构建发布集.
`check'
启动自我检查流程。建议用户在进行测试前无需进行额外的安装步骤即可开始程序,并编写一个自定义测试框架,在系统已配置完成而尚未部署应用时运行它。
以下目标建议使用习惯名,对于各种程序它们很有用:
installcheck
进行自我验证。用户应在运行测试前首先建立并安装这些程序。您不应该假设'$(bindir)'位于搜索路径中。
installdirs
配置名为 'installdirs' 的模块对于管理文件及其父目录特别有帮助。脚本 'mkinstalldirs' 是专门为此目的设计的;在 Texinfo 软件包中找到它,并按照以下方式使用规则:
该规则并不更改编译时创建的目录,它仅仅创建安装目录。
14.6 安装命令分类
配置已安装的目标,请务必将其所有操作分为三类:常规操作指令、预设操作指令以及后期操作指令
通常情况下,在运行该命令时会将文件转移至适当位置,并赋予其相应的属性或模式。这些操作仅涉及从软件包中提取文件本身,并不涉及对原有文件内容的更改。
安装之前的命令以及安装之后的命令可能会修改某些配置文件进而影响数据库。
安装前命令在正常命令之前执行,安装后命令在正常命令执行后执行。
运行最常见的操作通常是执行install-info指令。这种操作无法通过常规指令完成的原因是修改了(Info目录),这个目录无法完整地从软件包中复制出来。因此属于一类特殊的系统任务:即必须先执行正常的install-info操作才能完成相关功能。
许多程序不依赖安装前命令而运行正常,但我们特意呈现这一特点,以满足用户在特定场景下的便利需求
在安装规则的命令中划分三种类型时,在这些命令中间插入category lines(分类行) 。 分类行标识了下面列出的命令所属类别。
分类行为常包含Tab键、特殊指定的make引用符及伴随行末尾的随意注释项。建议采用三组独立的元数据字段(variables),每组字段代表不同的数据类型;每个字段名称明确标识其用途。需要注意的是这类分组标记不应嵌入常规工作流程脚本中。特别地这些元数据字段通常仅在高级配置选项页面或特定工具组件内部进行配置避免干扰常规操作环境。
分类行为常包含Tab键、特殊指定的make引用符及伴随行末尾的随意注释项。建议采用三组独立的元数据字段(variables),每组字段代表不同的数据类型;每个字段名称明确标识其用途。需要注意的是这类分组标记不应嵌入常规工作流程脚本中。特别地这些元数据字段通常仅在高级配置选项页面或特定工具组件内部进行配置避免干扰常规操作环境
这里有三种分类行,后面的注释解释了它的含义:
如果在安装时起始未启用分类管理,则在第一个类目启动前的所有指令均为常规指令。若完全未配置类目管理,则所有指令均为常规指令。
这是反安装的分类行
反安装前命令的典型用法是从Info目录删除全部内容。
建议在处理需要安装或去安装程序时,请确保每个子程序(即依赖)都已成功运行后才开始主目标的操作。这种方法确保了所有操作都被正确分类。
安装前命令和安装后命令除了对于下述命令外,不能运行其它程序:
按照这种方式区分命令的主要目的是为了创建一个独立且完整的独立环境。典型的二进制软件包通常包含所有的可执行文件、必要的附加文件以及其自带的安装文件——因此,在这种情况下不需要运行任何常规的脚本或命令。然而,在进行实际的装入操作时,则需要先执行准备阶段的任务以及完成阶段的任务以确保一切顺利进行。
该流程采用自动化脚本处理,在软件部署阶段发挥重要作用。该方法用于提取安装前指令:首先从机器环境获取并存储所有预装软件包相关的安装前指令;随后在系统启动时自动执行这些指令以初始化运行环境。该过程包括从机器环境获取并存储所有预装软件包相关的安装前指令,并通过配置文件指导其执行顺序。
这里文件‘pre-install.awk'可能包括:
在安装前运行的命令产生的结果文件类似于在安装二进制软件包时所采用的一部 shell 脚本的做法
15 快速参考
该段落是对指令集、支持的操作函数以及GNU make程序所理解的各种变量进行汇总介绍。如需了解更多的细节,请参阅以下内容:特殊内建目标名列表(见特殊内建目标)、隐含规则目次(见隐含规则目录)以及选项概述(见选项概要)。
这里是GNU make是别的指令的总结:
define variable
endef
定义多行递归调用扩展型变量。参阅** 定义固定次序的命令** 。
ifdef variable
ifndef variable
ifeq (a,b)
ifeq "a" "b"
ifeq 'a' 'b'
ifneq (a,b)
ifneq "a" "b"
ifneq 'a' 'b'
else
endif
makefile文件中的条件扩展,参阅** makefile文件中的条件语句** 。
include file
-include file
sinclude file
包含其它makefile文件,参阅 包含其它makefile文件 。
override variable=value
override variable:=value
override variable+=value
override variable?=value
override define variable
endef
创建变量,并对原有的变量定义进行重载操作;此外,请在命令行环境中重新定义变量以实现上述功能。请参阅**override指令**以完成相关设置。
export
告诉make缺省向子过程输出所有变量,参阅** 与子make通讯的变量** 。
export variable
export variable=value
export variable:=value
export variable+=value
export variable?=value
unexport variable
告诉make是否向子过程输出一个特殊的变量。参业** 与子make通讯的变量** 。
vpath pattern path
制定搜寻匹配‘%’格式的文件的路径。参阅** vpath指令** 。
vpath pattern
去除以前为‘pattern’指定的所有搜寻路径。
vpath
去除以前用vpath指令指定的所有搜寻路径。
这里是操作文本函数的总结,参阅** 文本转换函数** :
$(subst from,to,text)
在‘text’中用‘to’代替‘from’,参阅** 字符串替换与分析函数** 。
$(patsubst pattern,replacement,text)
将字符在指定位置进行替换操作,并参考相关的字符串操作函数
$(strip string)
从字符串中移去多余的空格。参阅** 字符串替换与分析函数** 。
$(findstring find,text)
确定‘find’在‘text’中的位置。参阅** 字符串替换与分析函数** 。
$(filter pattern...,text)
在‘text’中选择匹配‘pattern’的字。参阅** 字符串替换与分析函数** 。
$(filter-out pattern...,text)
在‘text’中选择不匹配‘pattern’的字。参阅** 字符串替换与分析函数** 。
$(sort list)
将列表中的字符按字母顺序排列,并去除重复字符。参阅** 字符串替换与分析函数** 。
$(dir names...)
从文件名中抽取路径名。参阅** 文件名函数** 。
$(notdir names...)
从文件名中抽取路径部分。参阅** 文件名函数** 。
$(suffix names...)
从文件名中抽取非路径部分。参阅** 文件名函数** 。
$(basename names...)
从文件名中抽取基本文件名。参阅** 文件名函数** 。
$(addsuffix suffix,names...)
为‘names’中的每个字添加后缀。参阅** 文件名函数** 。
$(addprefix prefix,names...)
为‘names’中的每个字添加前缀。参阅** 文件名函数** 。
$(join list1,list2)
连接两个并行的字列表。参阅** 文件名函数** 。
$(word n,text)
从‘text’中抽取第n个字。参阅** 文件名函数** 。
$(words text)
计算‘text’中字的数目。参阅** 文件名函数** 。
$(wordlist s,e,text)
返回‘text’中s到e之间的字。参阅** 文件名函数** 。
$(firstword names...)
在‘names…’中的第一个字。参阅** 文件名函数** 。
$(wildcard pattern...)
寻找匹配shell文件名格式的文件名。参阅** wildcard函数** 。
$(error text...)
该函数执行时,make产生信息为‘text’的致命错误。参阅** 控制make的函数** 。
$(warning text...)
该函数执行时,make产生信息为‘text’的警告。参阅** 控制make的函数** 。
$(shell command)
执行shell命令并返回它的输出。参阅** 函数shell** 。
$(origin variable)
返回make变量‘variable’的定义信息。参阅函数origin 。
$(foreach var,words,text)
将列表words与var中的每个元素进行映射连接。参考** 函数foreach** 。
$(call var,param,...)
通过变量(1), (2)...来确定出变量var,并使这些变量分别代表参数param的第一个、第二个…的值。参见关于函数调用的相关说明
这里是对自动变量的总结,完整的描述参阅** 自动变量** 。
$@
目标文件名。
$%
当目标是档案成员时,表示目标成员名。
$<
第一个依赖名。
$?
所有相对于目标'新'的依赖名称,在列举时应以空格分隔。针对档案成员的相关依赖关系,则仅允许引用已命名的成员。参阅**使用make更新档案文件**
$^
$+
所有依赖项的名称,在列举时需以空格分隔。对于属于档案成员的依赖关系,则仅允许引用已被命名的对象。参阅** 使用make更新档案文件** 。在版本控制中,默认情况下会跳过了重复引用的情况;然而,在某些情况下(如需要完整的历史记录),我们仍然可以选择完整地保留所有重复引用项以供参考。
$*
和隐含规则匹配的stem(径)。参阅** 格式匹配** 。
$(@D)
$(@F)
变量$@.中的路径部分和文件名部分。
$(*D)
$(*F)
变量$*中的路径部分和文件名部分。
$(%D)
$(%F)
变量$%中的路径部分和文件名部分。
$(<D)
$(<F)
变量$<中的路径部分和文件名部分。
$(^D)
$(^F)
变量$^中的路径部分和文件名部分。
$(+D)
$(+F)
变量$+中的路径部分和文件名部分。
$(?D)
$(?F)
变量$?中的路径部分和文件名部分。
以下是GNU make使用变量:
MAKEFILES
每次调用make要读入的Makefiles文件。参阅** 变量MAKEFILES** 。
VPATH
对于当前目录中无法定位到的文件进行查找操作所使用的路径。参阅** _VPATH_**** : 所有依赖的搜寻路径 。
SHELL
默认的命令解析程序名称通常是 /bin/sh。您可以在 makefile 文件中设置环境变量 SHELL 配置运行时使用的 shell。参阅执行命令[1]
默认的命令解析程序名称通常是 /bin/sh。您可以在 makefile 文件中设置环境变量 SHELL 配置运行时使用的 shell。参阅执行命令[1]
MAKESHELL
变量仅限于MS-DOS环境内使用;make语句解析并执行相应的操作;其值在优先级上胜过SHELL变量
MAKE
被调用为make名称,在命令行环境中使用该变量具有独特的功能;参考有关MAKE操作流程的信息将有助于理解其工作原理。
MAKELEVEL
递归调用的层数(子makes)。参阅** 与子make通讯的变量** 。
MAKEFLAGS
向MAKE提供一个标志项。您可以在环境配置文件或MAKEFILE文件中使用该变量来设置相应的标志项。参阅相关资料中的详细说明。由于其内容无法正确地被shell引用,在命令行环境中直接引用该变量不可行……但是,在递归调用MAKE时可以通过环境传递的方式将其内容传递给子MAKE使其得以应用。
MAKECMDGOALS
该系统旨在通过命令行界面向make传递指令。通过设置此变量不会对make的行为产生任何影响。参阅** 特别目标的参数** 。
该系统旨在通过命令行界面向make传递指令。通过设置此变量不会对make的行为产生任何影响。参阅** 特别目标的参数** 。
CURDIR
设置当前工作目录的路径名,参阅** 递归调用make** 。
SUFFIXES
在读入任何makefile文件之前的后缀列表。
.LIBPATTERNS
定义make搜寻的库文件名,以及搜寻次序。参阅** 连接库搜寻目录** 。
16 make产生的错误
这里是您可以看到由make生成大部分常见的错误列表及其相关信息
通常情况下, make可能会产生一些非致命的错误. 例如, 在命令脚本行前面添加某种前缀时, 或者在命令行中选择选项 -k 时所导致的错误通常是不严重的. 不过, 如果你使用字符串***作为前缀, 则会导致严重的后果.
在每个错误信息之前插入一个前缀以明确其来源,并包含导致该错误的原因如程序名称、文件路径及其出现的位置编号
在下述的错误列表中,省略了普通的前缀:
`[foo] Error NN'
`[foo] signal description'
这些错误不是make所导致的错误。它们表明调用的程序返回了非零状态码值,并带有Error NN编码。这种情况通常被解释为失败或非正常退出(某些类型的信号),参考** 命令错误** 一节。如果信息中没有附加***条目,则表示子过程失败;然而,在makefile文件中的这一规则带有特殊前缀,则使make忽略该错误。
`missing separator. Stop.'
`missing separator (did you mean TAB instead of 8 spaces?). Stop.'
该工具无法正确解析输入的命令行参数。GNU make 程序通过识别不同类型的命令行分割符(如:、=, Tab字符等),来判断并报告解析过程中的问题所在。本程序未能识别到有效的分割符模式。通常情况下,在这种错误信息中出现的主要原因是:您或其使用的文本编辑器(尤其是基于Windows的操作系统)在设置代码缩进时误将空格当作Tab字符使用了。在这种情况下, GNU make 程序会按照第二种错误类型来生成错误提示信息。(请务必注意:所有命令行都应该以Tab开头,并非八个空格也视为有效缩进)参考_语法规范_。
`commands commence before first target. Stop.'
`missing rule before commands. Stop.'
看起来像是一个无效的命令行(例如,在进行变量赋值操作时)
No rule to make target xxx'.'
No rule to make target xxx', needed by `yyy'.'
为了实现决策制定的目标, make系统必须建立一个明确的目标, 但这个目标不能在makefile文件中找到任何与之相关的指令, 包括具体的制定规则和潜在的隐式规则. 如果您希望系统能够创建该目标, 您需要额外编写相应的规则来完成这一功能. 其他可能的原因包括: makefile文件可能存在错误(例如文件名书写错误), 或者导致源代码树被破坏(即无法按照预期重建某个文件, 这通常由单一依赖关系引起).
`No targets specified and no makefile found. Stop.'
`No targets. Stop.'
前者表明您未在命令行指定目标名称;因此运行make时无法读取任何makefile文件。另一方面则表示虽然某些makefile文件被识别出来但它们并未定义默认目标也没有接收来自命令行的参数信息。在这种情况下GNU make自然无能为力建议参阅相关文档以了解如何正确配置make脚本参数设置方法
Makefile xxx' was not found.'
Included makefile xxx' was not found.'
在命令行中明确配置一个makefile文件的位置(前者)或其所在目录(后者)均未被正确识别
warning: overriding commands for target xxx''
warning: ignoring old commands for target xxx''
GNU make规定在一个规则中每个命令只能出现一次(除非使用双冒号),这样可以避免重复;当您给一个目标指定一个指令时(如果该指令已经在目标的定义中被预先设置了),系统会发出提示信息;另外一条规则会覆盖之前对同一指令的设定。参阅** 具有多条规则的目标** 。
`Circular xxx <- yyy dependency dropped.'
这表明make识别出一种相互依存的关系:当追踪目标xxx所依赖的yyy时注意到,yyy所依仗的对象之一又反过来以xxx为其依仗的对象。
Recursive variable xxx' references itself (eventually). Stop.'
您需要为一个具有递归调用性的make变量xxx进行创建,在展开过程中会自动进行自我引用操作。这种情况下应当避免采用简单赋值(:=)或追加赋值(+=)的方式进行初始化,请参考** 使用变量** 部分获取详细指导
`Unterminated variable reference. Stop.'
这意味着您在变量引用或函数调用时忘记写右括号。
insufficient arguments to function xxx'. Stop.'
这意味着您在使用函数时是您的同伴提供所需数量的一致参数。
有关如何设置这些参数的详细信息,请参阅**Text to Function**。
`missing target pattern. Stop.'
`multiple target patterns. Stop.'
target pattern contains no %'. Stop.'
这些错误信息源自畸形的静态格式规范所导致的结果
`warning: -jN forced in submake: disabling jobserver mode.'
这些警告信息表明,在支持与子make进行通信的系统中发现并行处理相关的错误(参阅** 与子make通讯选项** )。具体来说,当递归调用一个make进程时,并且使用了–j选项(此处指定值n>1),这种情况下可能会导致问题的发生。例如,在某些操作系统的配置下可能会出现此情况:如果您将环境变量MAKE设置为‘make –j2’这样的形式,则会导致子make进程无法与其他任何(make)进程进行通信,并且还会错误地声称自己承担了两个任务。
warning: jobserver unavailable: using -j1. Add +' to parent make rule.'
为了确保各个make之间的通信顺畅进行。父层的make会将信息发送给子层的make,并可能会引发问题。因为子过程可能并非真正的make,在这种情况下仅根据其为一个make这一前提来发送所有信息。如前所述,在** MAKE变量的工作原理** 中有详细说明。如果一个makefile包含了这样的父层结构,则无法确切判断其是否正确识别了所有的child make进程。
17 复杂的makfile文件例子
这是一个针对GNU tar程序的makefile文件;这是一个有一定难度的makefile文件。
由于所有(all)被定义为第一个目标,并因此成为缺省目标。值得注意的是,在此makefile中存在一个有趣的特性:.h头文件来自于同一个名为testpad的源代码文件,并且这个项目通过将.c源码文件进行编译来实现。
如果您输入 make 或 make all ,则会创建一个名为 tar 的执行文件,并提供远程访问磁带的进程 rmt ,以及一个名为 tar.info 的信息文件。
如果按照命令行输入make install的话,则make不仅生成tar, rmt, 和tar.info文件,并完成它们的安装操作。
当您输入'make clean'时,则该命令会清除所有的.o文件以及其他如.tar、.rmt、.testpad、.testpad.h和.core文件。
如果您运行命令'make distclean', 那么此操作不仅会执行'make clean'所删减的所有项(即从构建目录中删减所有目标),而且还会进一步删减与构建相关的TAGS、Makefile以及config.status等关键文件。(尽管这一行为并不直观可见,在实际运行中,默认情况下系统会将'makefile'(以及'config.status')视为由'tarball'生成后由用户配置过的成果;不过这些细节在此不做深入探讨。)
如果您输入' make realclean' , 则 make执行后会删除由'make distclean'生成的所有文件,并包含来自'tar.texinfo'产生的Info文件。
另外,目标shar和dist创造了发布文件的核心。
脚注
(1)
在MS-DOS和MS-Windows上编译的GNU Make的行为模式与将前缀定义为DJGPP树体系根的行为一致
(2)
基于MS-DOS环境下, 其当前工作目录设为全局值, 因此修改该值将导致后续命令受影响
(3)
texi2dvi 使用TeX 进行真正的格式化工作. TeX没有和 Texinfo一块发布。
本文档使用版本1.54的texi2html 翻译器于2000年7月19日产生。
本文档的版权所有,不允许用于任何商业行为。
名词翻译对照表
archive 档案
archive member targets 档案成员目标
arguments of functions 函数参数
automatic variables 自动变量
backslash () 反斜杠
basename 基本文件名
binary packages 二进制包
compatibility 兼容性
data base 数据库
default directries 缺省目录
default goal 缺省最终目标
defining variables verbatim 定义多行变量
directive 指令
dummy pattern rule 伪格式规则
The command will display its execution results.
editor 编辑器
empty commands 空命令
empty targets 空目标
environment 环境
explicit rule 具体规则
file name functions 文件名函数
file name suffix 文件名后缀
flags 标志
flavors of variables 变量的特色
functions 函数
goal 最终目标
implicit rule 隐含规则
incompatibilities 不兼容性
intermediate files 中间文件
match-anything rule 万用规则
options 选项
parallel execution 并行执行
pattern rule 格式规则
phony targets 假想目标
prefix 前缀
prerequisite 依赖
recompilation 重新编译
rule 规则
shell command shell命令
static pattern rule 静态格式规则
stem 径
sub-make 子make
subdirectories 子目录
suffix 后缀
suffix rule 后缀规则
switches 开关
target 目标
value 值
variable 变量
wildcard 通配符
word 字
