Linux 版 (精华区)

发信人: AA (振奋,成长中的AA), 信区: Linux
标  题: 无限使用make(zz)
发信站: 哈工大紫丁香 (2002年06月27日19:24:44 星期四), 站内信件

摘要 
文章除介绍make工具是如何运行的以外,还涉及到make工具除了具有软件开发作用
外的许多别的用途。 


介绍 

几乎每个使用Linux的用户都会使用到make工具。当然我们也知道只有在程序和内
核建立于源代码的基础上及软件包安装完成后,make工具才能运行。Make对于软件
开发是一个非常重要的操作工具。然而,make还能做更多你先前没有注意过的事。
 
你所将要了解的是,make工具将成为你以后处理日常工作如写文章、著书或建立一
个Web站点的一个非常有效的工具。你会学到许多‘unix窍门’,它会让你在以后
的工作中更熟练的操作make工具。要注意的是:在此我们谈及的虽然只是Linux,
但原则上我们可以在任何操作系统中使用make工具。 


示例: 建立一个Web站点 

一个Web站点必须由许多分工不同的人共同维护。Jan负责两个栏目,Piet负责布局
。 

我们需要一个简单的系统去区分布局和内容,一个有效的措施是:从数据库中读取
内容,这对于页面是必须的。例如,PHP和Microsoft Active Server Pages 就可
以实现这个功能。然而,我们仅可能储存静态的HTML(HyperText Markup 
Language)。而且,内容不会改变以便有效的维护数据库。 

通过简单的命令建立Web站点: 
◆ 布局设计 
Jan的文章都保存在他的根目录:offer.htem和indew.html中。而所有文件都保存
在目录/hone/jan/Docs/webbsite/中。 
index.html包括一些新闻和一个欢迎辞。offer.html提供每周的what-do-I-know 
potatos 的照片。Piet负责布局设计和建立web站点。最后,页面在根目录
/home/piet/public-html中公布。 
◆ 模板 
Piet要求Jan在HTML中写他的页面,这需要一个与之相配的布局。 
例如,Piet将站点的header文件放于header.html中,将footer文件放在footer.
html中。header.html如下: 

〈html>〈!--the header--〉 
〈head> 




 
This is our website

Some rubbish is written down here.

We are very interactive

so this is our telephone number:

0123-456789 
 

footer.html设置如下: 

 


再如,下面unix系统命令从Jan的index.html中建立: 

cat header.html /home/jan/Docs/website/index.html 
echo -n ' 


------------------------------------------------------------------------
-------- 
Last modification: ' 
date '+%A %e %B' 
cat footer.html 

请查阅命令使用手册。执行完以上命令的结果是最终的文件将被输送到标准输出端
中,被放置于一个文件里。 


cat header.html /home/jan/Docs/website/index.html 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' 
date '+%A %e %B' 
cat footer.html 
} > /home/piet/public_html/index.html 

这个程序将和其他的文件一起被再三重用,offer.html。事实上,我们生成了一个
小小的脚本使web站点得以建立。 
然而,用人工操作这些命令是不实际的。我们可以设立一个脚本程序,这样当Jan
更新他的指数的时候脚本会自动发挥作用。而且,当Piet想更改header文件或
footer文件的时候这个脚本同样可以发挥作用!另一方面,假如Jan 不做修改,那
么这个脚本不会发挥作用。 


第一次亲密接触make 

GNUD的make工具的信息手册是一个让人费解的文献。从一开始它就着重于一个程序
环境,出于这个原因,我将简要的说明一下make工具的功能: 
make确定一组命令是否需要执行,是依赖于源文件的创建时间和目标文件的创建时
间的对比。 
换句话说就是当源文件比目标文件新的话,这一组命令将被执行。目的是更新该目
标文件。 
目标文件是对象,源文件是先决条件(第一命令)。如果先决条件之一比目标文件
要新(或着目标不存在)那么命令将被执行。而如果所有的先决条件都比目标要旧
或着和目标一样旧,那么命令不被执行,目标将被更新。 
在当前的工作目录下,将产生一个名为Makefile的文件。make工具工作的时候,这
个文件中的信息将被使用到。一旦我们有了makefile文件,那么我们唯一要做的就
是:键入‘make’和相关命令,生成目标文件,这些都将被自动执行。 
使用下面的命令行来调用make: 

make target1 target2…… 

目标是可选择的(如果目标文件没有指定,那么将使用Makefile中的第一个目标)
Make习惯在当前目录中搜寻Makefile文件。很有可能会提供多个目标。 

Makefile的衔接 

Makefile将由一个编辑器生成: 

# This is an example of a Makefile. 
# Comments can be put after a hash (#). 

target: prerequisites 
command 

target: prerequisites 
commando 

# and so on and so on. 

我们把目标文件放在最前面,后边紧接着一个“:”及所需的先决条件。在先决条
件存在的情况下,用一个反斜线符号(/)结束一个命令行然后开始另一个命令行
。 
在接下来的命令行中,将具有一个或多个命令。每一行都被认为是一个标准命令。
假如你想使用多行来定义一个命令,那么你可以在行末使用反斜线符号(//)。
Make工具将把这些命令衔接起来。在这种情况下,我们用分号来分离这些命令,以
便在运行shell的时候避免出现错误。 
注意:命令用TAB缩进,而不是用8个空格。 
Make读取Makefile文件而且确定每一个目标命令是否被执行。目标文件、先决条件
、标准被指示为规则(rule)。 
如果make工具在没有参数的情况下使用,那么只有第一个目标文件被执行。 



我们例子中的Makefile文件 

编辑Makefile文件内容如下: 

# This Makefile builds Piets' and Jans' website, the potato-eaters. 
all: /home/piet/public_html/index.html /home/piet/public_html/offer.html
 
/home/piet/public_html/index.html: header.html footer.html 
/home/jan/Docs/website/index.html 

cat header.html /home/jan/Docs/website/index.html ; 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' ; 
date '+%A %e %B' ; 
cat footer.html ; 
} > /home/piet/public_html/index.html 

/home/piet/public_html/offer.html: header.html footer.html 
/home/jan/Docs/website/offer.html 

cat header.html /home/jan/Docs/website/index.html ; 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' ; 
date '+%A %e %B' ; 
cat footer.html ; 
} > /home/piet/public_html/offer.html 

# the end 

现在,我们有了三个目标文件:’all’、index.html和offer.html。’all’唯一
的作用就是将其他两个文件作为先决条件。因为’all’本身不是文件名,所以目
标文件’all’将被执行。(随后我们将介绍一种更别致的方法用来定义并非文件
的目标) 
如果header文件和footer文件被修改,那么整个页面都将被更新。但如果只改变其
中的一页,那么仅这一页被更新。 
但是,Makefile文件有一个缺点:它不容易被监查。所幸的是,有许多方式可以有
效而简单的解决这个问题! 


Makefile的制作流程 

☆变量 

使用变量,Makefile将得到很大的简化。变量定义如下: 
variable = value 
我们使用表达式$(variabele)来引用变量。如果我们将变量并入Makefile中,操作
如下: 
# This Makefile builds Piets' and Jans' website, the potato-eaters. 

# Directory where the website is stored: 
TARGETDIR = /home/piet/public_html 

# Jans' directory: 
JANSDIR = /home/jan/Docs/website 

# Files needed for the layout: 
LAYOUT = header.html footer.html 

all: $(TARGETDIR)/index.html $(TARGETDIR)/offer.html 

$(TARGETDIR)/index.html: $(LAYOUT) $(JANSDIR)/index.html 

cat header.html $(JANSDIR)/index.html ; 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' ; 
date '+%A %e %B' ; 
cat footer.html ; 
} > $(TARGETDIR)/index.html 

$(TARGETDIR)/offer.html: $(LAYOUT) $(JANSDIR)/offer.html 

cat header.html $(JANSDIR)/index.html ; 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' ; 
date '+%A %e %B' ; 
cat footer.html ; 
} > $(TARGETDIR)/offer.html 

# the end 

要养成用大写字母来书写变量的好习惯。这样改动起来的时候会容易得多。 
如果你愿意,你可以定义另外一种方法将各个文档放在恰当的布局。但如果你想将
许多变量放在一个布局中,那又该怎么做呢?当有许多副本的时候makefile文件将
变得非常庞大。但是,所有这些问题也可以变得很简单。 

☆模式规则 

模式规则使我们对不同的目标文件使用同样的一系列命令。 
使用模式规则,将改变行的衔接;加入一个额外的pattern field: 

Multiple targets: pattern : prerequisite prerequisite ... 
command 

这个模式是一个表达式,它适用于任何目标文件。百分号被用于合并目标文件名的
不同部分。 
例如: 

/home/bla/target1.html /home/bla/target2.html: /home/bla/% : % 
commands 

当make工具读到这的时候,行将扩张到两行。在此,模式将确定目标文件的哪一个
部分将用百分号合并。 
当百分号出现在先决条件区段(prerequisites-field)中时,表示这一部分已经
被百分号复制了下来。 
Make工具发展如下: 

/home/bla/target1.html: target1.html 
commands 

/home/bla/target2.html: target2.html 

模式‘/home/bla/%’中的百分号和目标‘/home/bla/target1.html’中的‘
target1.html’,从而‘%’发展为‘target.html’的必备条件。 
适用我们的web站点,下面的规则合并为: 

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: 
$(TARGETDIR)/% : $(JANSDIR)/% 
$(LAYOUT) 

现在我们还剩一个问题:在命令中如何使用这些变量?命令和目标文件都有一些不
同吗? 

☆自动变量 

幸运的是,make能自动定义变量。这些变量中的某些被称为自动变量。这些变量在
命令执行过程中(最好:刚好在执行命令之前)包含目标的数量及(或者)先决条
件。 
简单的变量$< 象征第一先决条件,变量$@ 为目前的目标。 
使用这些变量可将下全部的规则归纳如下: 

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : 
$(JANSDIR)/% 
$(LAYOUT) 

cat header.html $< ; 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' ; 
date '+%A %e %B' ; 
cat footer.html ; 
} > $@ 

为了完整性,要呈现整个Makefile文件,包括一些optimalisations: 

# This Makefile builds Piets' and Jans' website, the potato-eaters. 

# Directory where the website is published: 
TARGETDIR = /home/piet/public_html 

# Jans' directory: 
JANSDIR = /home/jan/Docs/website 

# Files needed for the layout: 
LAYOUT = header.html footer.html 

# These are the webpages: 
DOCS = $(TARGETDIR)/index.html $(TARGETDIR)/offer.html 


# Please change nothing below this line;-) 
# ------------------------------------------------------------- 

all: $(DOCS) 

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT) 

cat header.html $< ; 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' ; 
date '+%A %e %B' ; 
cat footer.html ; 
} > $@ 

# the end 

就算要加入更多的文件,并将它们合并到Makefile文件中,只要使用DOCS变量还是
很简单的,无须很多的键入。 
你看,维护Makefile文件是一件多么简单的事,你根本不用再为它是怎么工作的而
感到困惑! 

☆最后的小小优化 

我们更喜欢在DOCS中mension文档,这样不用包含整个目录。操作如下(我们在
TEXTS中的makefile文件的开头改变DOCS): 

# Please change nothing below this line;-) 
# ------------------------------------------------------------- 
DOCS = $(addprefix $(TARGETDIR)/,$(TEXTS)) 

all: $(DOCS) 

# and so on 

我们在此看到的是make工具的一个独特的功能:它可以在括号间使用一个完全的表
达式来代替变量名。这样,我们可以通过很多方式来修改文本。 
这个独特的命令$(addprefix prefix,list)为列在列表上的每个元素加上一个前缀
。例如,TARGETDIR变量加上一个斜线(/)。 
所列条目用空格分开。出于这个原因,使用make 工具加工带有空格的文件名将会
出现问题。 
在前面我们提到目标文件’all’并不能产生一个名为’all’的文件(这行并不包
括任何命令 ),结果是该目标文件将被始终执行。但是当自动生成一个以此文件
名命名的文件,而且它比其它文件新的时候,我们又该怎样操作? 
有一种很简单的方式定义make 始终执行一个特定的目标文件,而且这个目标文件
并不指定硬盘中的文件。操作起来是将这个目标文件标示为’phony’(假名):
 

.PHONY:all 

这样,整个MakeFile文件变成如下所示: 

# This Makefile builds Piets' and Jans' website, the potato-eaters. 

# Directory where the website is published: 
TARGETDIR = /home/piet/public_html 

# Jans' directory: 
JANSDIR = /home/jan/Docs/website 

# Files needed for the layout: 
LAYOUT = header.html footer.html 

# These are the names of the webpages: 
TEXTS = index.html offer.html yetanotherfile.html 

# Please change nothing below this line;-) 
# ------------------------------------------------------ 
DOCS = $(addprefix $(TARGETDIR)/,$(TEXTS)) 
.PHONY: all 

all: $(DOCS) 

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT) 

cat header.html $< ; 
echo -n ' 
------------------------------------------------------------------------
-------- 
Last modification: ' ; 
date '+%A %e %B' ; 
cat footer.html ; 
} > $@ 

# the end 

保存这个文件然后忽略它!从现在开始它将通过使用你的contab来维持你的web布
局,并且将你的布局和内容完全分开来。 


最后的忠告 

当然我们在很多情况下要修改这个例子。 
产生文献的简单方式并不是没有错的:假如Jan用结束他的文章,多数浏览器不能
显示Piet已有的footer文件。如果我们使用grep,perl或tcl程序,那么在站点的
header文件中我们能以一种很更聪明的方式从Jan的文献中提出一些标题。 
当然,Jan可以书写一些简单的文本并使用sed命令用〈p〉来更改所有的空白行(
回车状态): 

sed -e 's/^s*$/
/g' 

此外,Jan可用Lyx写文本文件(http;//www.lyx.org/),还可以用程序如
lyx2html将它改写为HTML语言。很有可能会用到。 
另一个template的建立也是有可能的。 
我们没有考虑如何将图片传送(测量、修改、压缩)到web根目录下。这一过程也
是可以自动进行的。 
在这个例子中,Piet必须在Jan的WEB站点目录下有读的权限。在区分这些工作时有
趣的是它们可以在很大的范围里应用。甚至可能的是Piet可以在世界的另一头注册
或NFS系统中登录他的根目录。这个例子也可以使用在由一个用户正在完成的工作
中。 
幸好,Makefile工具原理是如何运行的已经弄明白,而且当你写了一个好用的
Makefile文件后,你的日常工作将变得轻松得多。 

技巧提示 

◆Make做为用户的接口 
一个文件包可以做为完成一件作品的原始资料或者可以产生很多别的用途。 
使用’phony’目标文件(.PHONY:target),它很容易捆绑一些简单的功能。例
如配置Linux内核。 
键入make menuconfig 用一个交互式的菜单开始配置。在X系统中,键入make 
xconfig 用一个Tcl/Tk接口开始配置。 
以上所提的目标文件对真正的建立一个内核毫无用途。它们只是对必需的功能(如
配置内核)建立了一个简单的接口。 

例子 

想象一下;你将收集的大批文件作为一个整体保存、维持、分配。你想写一个摘要
,将它用草稿打印出来,然后再做修改,发布在网站上,等等。所有这些都来源于
一个源文件。(如LaTeX!)。 
你可以用下列的PHONY文件来生成Makefile文件: 

[help:] Print a simple overview of the functions (targets) that are 
incorporated in this Makefile. 
[print-draft:] Print, e.g., a zoomed-out version of the document 
(using the ps-utils) to a printer with a low resolution. 
[print-full:] Print the complete document. 
[ps:] Export the complete document to a PostScript file. 
[report:] Summarize title, abstract, and summary of a document and 
mail that to someone who supervises your work. 
[html:] Update the html-version of your work automatically. 

除此以外,还有很多。这种方式可以在Makefile中定义复杂的操作。这些复杂的操
作可以通过简单的调用一个非常容易维护的接口来实现。 

◆先决条件也可以作为目标文件 
作为指定目标文件的先决条件的文件,在下一步中也可以作为目标文件。 
这种方式也适用于从文本文件中归纳得到HTML文件,并改进HTML文件的布局。如:
 

TEMPLATE = layout1/Template1.txt 
/home/httpd/sales/sales.html: sales.html $(TEMPLATE) 
perl Scripts/BuildPage.pl -template $(TEMPLATE) $< > $@-new 
mv -f $@-new $@ 
sales.html: sales.txt 
aptconvert -toc $@ $< 

如果Template.text修改,那么文件也将被更新。 

◆Echo命令、错误信息及文本 
如果命令以’@’,那么它不会被make命令显示出来: 

target: prerequisite 
@cc -o target prerequisite 

如果命令以'-'开头,那么即使这个命令产生了错误(如删除了一个不存在的文件
),make工具也不会终止。 

PHONY: clean 
clean: 
-rm -r $(tempdir) 

如果你想看一个确切的make命令是如何运行的,如安装make工具,但你又不愿命令
真的执行,那么可以在命令中使用-n选项。 

wilbert@nutnix:~ > make -n install 
install -m 755 program /usr/local/bin 
install -m 644 program.1 /usr/local/man/man1 
wilbert@nutnix:~ > 

◆避免make变量的代入 
如果你想使用符号($) ,如作为文件的一部分或程序命令的一部分,你可以双写它
($$): 

# A Makefile 
# Don't try this at home! :-) 
source = menu.txt help.txt 
target: $(source) 
for i in $(source) ; 
do 
if [ "$$i" = "menu.txt" ] ; 
then 
doThis $$i ; 
else 
doThat $$i ; 
fi ; 
done > target 

make 命令将在输送命令到达程序执行以前取代变量,并改写双写符号($$)为单
一符号($)。 


更多信息 

有关make 工具是如何运行的及其他的可开发的用途的信息可以在GNU Make手册中
查阅。你在你的Linux中可以使用下面的命令阅读这份手册: 

info make 

当然,你也可以使用GNOME 、KDE浏览器或便利的tkinfo程序阅读GNU Make 手册。
 
make的有关信息: 
(Dutch!) review(http://nl.linux.org/boeken/lees.php?recensie_id=25) 
Google search (http://www.google.com/search?q=make+tutorial) 




--
                 Activity & Advanced

健康的自信,一种脱离优越感、盛气凌人、自以为是和傲慢无礼的自信。这种自信不但
让对方感到很得体,也会使与之交往的人产生一种信赖。

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 211.94.161.128]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:204.687毫秒