Linux 编程提示
大多数 Linux 用户至少听说过 Makefile,但许多人不知道 make 程序有多么强大。它被认为是维护其他程序的工具,但它的功能远不止于此。在任何项目中,只要某些文件是从其他文件创建的,它都可以使混乱变得井然有序,无论最终产品是程序、书籍还是 Usenet 的自动帖子。即使您从未编写过 makefile,本教程也将引导您有效地使用 make。
作者:Michael K. Johnson
许多人对 make 感到困惑:也许您也是。您知道它很难使用,因为它具有与您使用的任何其他程序都不同的怪异语法。如果您幸运的话,您可能已经被警告过在某些地方使用制表符(而不是空格)很重要,并且您知道如果您搞砸了一个 makefile,您将无法修复它。
自己编写 makefile 是不可能的。这很困难,而且,反正您也不是真正的程序员。这个程序员的维护程序能为您做什么?您为什么要学习它那怪异的语法?或者,也许您是一名程序员,并且您不想学习这个名为 make 的工具,它的语法与您学过的任何语言都不同。
怪异语法的原因是 make 所做的工作与普通的编程工具非常不同,并且它非常适合这项非常不同的工作。理解这项工作是理解 make 的第一步。一旦您理解了这项工作,并对 make 有了一些了解,您将能够编写简短而强大的 makefile。
我们暂时假设您正在写一本书,尽管完全相同的想法也适用于编写程序。(我只是想强调 make 不仅仅适用于真正的程序员。)您正在使用 LaTeX。每一章都有几张图。这些图是用 xfig 完成的,需要使用 fig2dev 转换为 PostScript 格式,然后才能包含在您的书中。
问题来了。您偶尔会用 xfig 编辑图形,并忘记在完成后为每个图形制作 PostScript 副本,因此您编写了一个大型 shell 脚本,将每个图形从 xfig 转换为封装的 PostScript (EPS) 格式。它很大、很笨重且不灵活,但每次打印时您都能正确完成工作。不幸的是,将所有文件从 xfig 转换为封装的 PostScript 需要一段时间。即使您只在一个图形中进行了微小的更改,它也会重新转换您的所有图形。这很烦人,并且转换您大约 80 个图形需要一段时间。
make 的智能总结为两个概念:依赖关系和规则。您需要告诉 make,dvi 文件(我们称之为 book.dvi)需要从主 LaTeX 文件(book.tex)和封装的 PostScript 文件 (*.eps) 创建。在 make 术语中,book.dvi 依赖于 book.tex 和 *.eps。此外,每个 .eps 文件都依赖于相应的 .fig 文件。您还需要为 make 提供将 book.tex 转换为 book.dvi 以及将 .fig 文件转换为 .eps 文件的规则。
新手注意:makefile 中包含 shell 命令的所有行都必须以 TAB 字符开头。
这是一个 makefile 的示例,它将完成所有必要的工作
0: # This is a makefile to create book.dvi 1: EPSFIGS = fig1.eps fig2.eps fig3.eps fig4.eps \ 2: fig5.eps fig6.eps fig7.eps fig8.eps fig9.eps \ 3: <... more .eps files, too many to print ...> \ 4: fig78.eps fig79.eps fig80.eps fig81.eps 5: 6: book.dvi: book.tex $(EPSFIGS) 7: latex book.tex 8: 9: fig1.eps: fig1.fig 10: fig2dev -Lps fig1.fig fig1.eps 11: 12: fig2.eps: fig2.fig 13: fig2dev -Lps fig2.fig fig1.eps 14: <many more similar rules that you can imagine> 220:
此文件可以保存为 makefile 或 Makefile;两者都可以工作。如果您在同一目录中同时拥有这两个文件,则将使用 makefile 而不是 Makefile。此外,还有其他名称可以使用以及控制它的规则。如果您在意,请参阅 GNU Make 手册(您通常不会在意)。人们几乎总是使用 Makefile,因为大写字母会显示在目录列表的顶部。
第一行,第 0 行,是注释。第 1 行开始定义变量 EPSFIGS。反斜杠继续该行,因此 EPSFIGS 变量包含从 fig1.eps 到 fig81.eps 的所有 EPS 文件的名称,并且逻辑上所有这些行实际上都是一个长行。第 6 行告诉 make,如果 book.tex 或任何 .eps 文件比 book.dvi 新,则必须重新创建 book.dvi。第 7 行解释了如何做到这一点。非常重要的是,这一行以 TAB 字符开头。make 就是通过这种方式知道这是要执行的 shell 命令,以更新其前面的依赖项。八个空格不起作用。空格可以跟在制表符后面,但是该行的第一个字符必须是 TAB。其余行以相同的方式工作:第 9 行表示 fig1.eps 依赖于 fig1.fig,第 10 行告诉 make 如何在 fig1.fig 自上次创建 fig1.eps 以来已更新的情况下,从 fig1.fig 更新 fig1.eps。
只需键入 make 就会自动制作 book.dvi,因为 makefile 中的第一个目标(此处由第 6 行和第 7 行组成)是默认目标。您可以想象键入 "make fig1.eps" 仅从 fig1.fig 更新 fig1.eps,并且仅在必要时才更新。如果不是必需的,make 将告诉您 "fig1.eps 已是最新版本。"
makefile 的基本语法可以简化为这样
通过使行的最后一个字符为反斜杠字符,可以将任何行继续到下一行。
变量使用包含等号的行定义:FOO=bar。
变量通过将它们括在括号中(或花括号中,但括号是首选且更具可移植性)并在前面加上美元符号来引用:$(FOO)(或 ${FOO})。
通过将要创建的文件放在冒号之前,并将创建或更新该文件所需的文件列表放在同一行上的冒号之后,使文件依赖于其他文件。
用于创建或更新文件的 shell 命令列表直接在该行的下方,在以 TAB 字符作为第一个字符的行上。每行都由 shell 的单独调用运行,因此一行上的 cd 命令仅对该行有效。要使连续的行成为同一 shell 调用的一部分,请在行尾附加“ ;\”,使下一行真正成为同一行的另一部分。
注释以“#”字符开头。
了解这六个非常简单的规则将使您能够维护您在 Internet 上找到的大多数 makefile,并且将使您能够创建几乎任何您需要的 makefile。然而——
此 makefile 可以工作,直到您定义了 fig82.fig 并忘记将其转换为 fig82.eps 为止。它也比需要的长得多。我将重写它,使其更小,同时工作得更好。我将假设您正在使用 GNU make,它是 Linux 和其他一些系统上的标准 make。在您可能拥有的其他大多数系统上,它也很容易构建。
0: # This is an improved version of the makefile for 1: # making book.dvi 2: # FIG is a list of all the native xfig drawings 3: FIG = $(wildcard *.fig) 4: # EPS is a list of EPS files to create from the xfig files 5: EPS = $(subst .fig,.eps,$(FIG)) 6: 7: %.fig : %.eps 8: fig2dev -Lps $< $@ 9: 10: book.dvi: book.tex $(FIG) 11: latex book.tex 12: 13: print: book.dvi 14: dvips -r -Z -q book.dvi
这个 makefile 完成了前一个 makefile 所做的所有事情,甚至更多。它沿途引入了几个新概念,其中一些是 GNU make 独有的。第一个,也许也是最明显的,是我没有显式列出所有 .fig 或 .eps 文件。每次添加图形时,列出所有文件都需要我更新 makefile,这很容易出错。使用 GNU make 通配符函数允许 makefile 自动正确更新文件,无论您添加多少图形。如果您选择使用它,请注意您是否真的要列出所有文件,并注意它在大多数商业供应商的 make 版本下都无法工作。
GNU make 有许多像 wildcard 这样的函数。与变量一样,它们也包含在 $(...)(或 ${...})中,但您可以区分函数和变量,因为函数具有嵌入的空格。函数名在最前面,后面跟着参数或逗号分隔的参数列表。它们都在 GNU Make 手册的第 9 章“转换文本的函数”中记录。wildcard 等效于您每天在命令行上使用的 shell “globbing”(例如 ls *.fig),而 subst 的工作方式类似于 sed 的非常简单的版本(例如 sed 's/\.fig/\.eps/g'),用不同的子字符串替换字符串中一个子字符串的每个实例。
使用的另一个 GNU make 功能称为“模式规则”。这类似于所有 make 版本中使用的“后缀规则”(后缀规则在 GNU make 中仍然有效,请不要担心),但功能更强大。此处给出的示例等效于后缀规则
.fig.eps: fig2dev -Lps $< $@
但是模式规则是更通用的构造。如果您为自己或为可以保证使用 GNU make 的项目编写 makefile,则模式规则可能是首选,如果您尝试为所有平台编写可移植的 makefile,则后缀规则是首选。
模式规则比后缀规则更强大的原因是它们可以匹配任何类型的模式,而不仅仅是后缀。例如
foo.% : bar.%
将是“前缀规则”,这在其他形式的 make 中不存在。模式是比后缀更通用的概念,并且可以以其他方式在其他类型的规则中使用。GNU Make 手册中有这方面的示例。
变量 $< 和 $@ 是可以在规则中使用的特殊变量。$@ 变量代表正在更新的文件,即依赖项中“:”之后的文件所依赖的目标。$< 变量代表目标所依赖的文件。这是一个标准的 make 功能,不需要 GNU make。
最后,此 makefile 使用了一个定义不存在的目标的标准技巧。键入 make print 将打印您的书的副本,其中所有内容都是最新的,即使没有名为 print 的文件。类似地,由于第一个目标是默认目标,许多 makefile 的第一个规则都是一个名为“all:”的规则,当要构建“所有内容”时,该规则会执行任何看起来正确的操作。事实上,键入“make all”而不是仅仅“make”的做法非常普遍,以至于即使默认情况下只有一个程序要构建,有些人也会在他们的 makefile 中添加一个 all 目标,即使这根本没有必要
0: all: foo 1: 2: foo: foo.o bar.o baz.o <and so on> instead of 0: foo: foo.o bar.o baz.o <and so on>
这只是 make 的一点点味道。《GNU Make 手册》是对 GNU make 的温和但更彻底和正确的介绍,并与 GNU make 源代码一起分发。它不假设您已经知道如何使用任何版本的 make。在正确安装的系统上,可以通过 info 系统获得它。可以从 Emacs 中读取 info 文件(在 Emacs 中键入 C-h i)或从独立的 info 阅读器(如 info 或 tkinfo)中读取。或者,可以使用 TeX 排版系统打印该书,或从自由软件基金会订购。
Michael K. Johnson 是 Linux Journal 的编辑,但这并不能阻止他进行黑客编程。