备注这是user4076675采用的问题“What is the purpose of linking object files separately in a Makefile?”的变体 略有不同的观点。另请参阅相应的META discussion。
让我们考虑C项目的经典案例。 gcc
编译器
能够一步编译和链接程序。我们可以轻松地
用shell脚本描述构建例程:
case $1 in
build) gcc -o test *.c;;
clean) rm -f test;;
esac
# This script is intentionally very brittle, to keep
# the example simple.
但是,描述构建过程似乎是惯用的
使用Makefile
,包含编译每个编译的额外步骤
单元到目标文件并最终链接这些文件。该
相应的GNU Makefile将是:
.PHONY: all
SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
%.o: %.cpp
g++ -c -o $@ $<
all: default
default: $(OBJECTS)
g++ -o test $^
clean:
rm -rf *.o
这个第二个解决方案比简单的shell更容易参与 我们之前写的脚本。这也是一个缺点,因为它混乱了 带有目标文件的源目录。那么,为什么我们要描述构建 使用Makefile而不是shell脚本的程序?在手中 在前面的例子中,它似乎是一种无用的复杂功能。
答案 0 :(得分:2)
在我们编译和链接三个中等大小的简单情况下 文件,任何方法都可能同样令人满意。我会 因此,考虑一般情况,但使用的许多好处 Makefile只对大型项目很重要。一旦我们学会了 最好的工具,让我们掌握复杂的案例,我们想要使用 它也是在简单的情况下。
让我强调一下“好处”&#39;&#39;&#39;使用make
而不是简单的
用于编译作业的shell脚本。但首先,我想做一个
无害的观察。
编写Makefile类似于编写带有轻微的shell脚本 改变观点。在shell脚本中,我们描述了一个过程 问题的解决方案:我们可以开始描述整个过程 使用未定义函数的非常抽象的术语,我们对此进行了改进 描述直到我们达到最基本的描述, 其中一个过程只是一个普通的shell命令。在Makefile中,我们这样做 不介绍任何类似的抽象,但我们专注于我们的文件 想要生产以及我们如何生产它们。这很好用,因为 在UNIX中,一切都是文件,因此每个处理都是 由程序完成,该程序从输入读取其输入数据 文件,做一些计算并在一些输出中写入结果 文件
如果我们想要计算复杂的东西,我们必须使用很多 由输出用作的程序处理的输入文件 输入其他程序,依此类推,直到我们完成决赛 包含我们结果的文件。如果我们翻译计划准备我们的 最终文件放入shell脚本中的一堆程序,然后 处理的当前状态是隐式:计划执行者 知道“它在哪里”,因为它正在执行给定的程序, 这隐含地保证了这样和那样的计算 已经完成,也就是说,这样的中间文件是 已经准备好了现在,哪些数据描述了“计划执行者的位置 在“?
无害的观察 描述“计划所在地的数据” 执行者在“恰好是中间文件的集合 已经准备好了,这正是所做的数据 在我们编写Makefile时显式。
这种无害的观察实际上是概念上的差异 shell脚本和Makefile之间解释了所有优点 Makefile在编译作业和类似作业中的shell脚本上。 当然,要充分体会这些优势,我们必须写出来 正确 Makefile,这对初学者来说可能很难。
当我们使用Makefile描述编译作业时,我们可以轻松地完成
中断并稍后恢复。这是一个结果
无害的观察。类似的效果只能通过以下方式实现
在shell脚本中做了大量工作,而它只是内置的
make
。
您发现Makefile会使对象混乱源树 文件。但Makefile实际上可以参数化来存储这些 目标文件在专用目录中。我与BSD Owl合作 bsdmake的宏并使用
MAKEOBJDIR='/usr/home/michael/obj${.CURDIR:S@^/usr/home/michael@@}'
以便所有目标文件都在~/obj
下结束,并且不会污染我的
源。看到这个
answer
了解更多详情。
高级Makefile允许我们同时拥有多个目录 包含具有不同编译的项目的多个构建 选项。例如,启用或调试不同的功能 版本等。这也是无害观察的结果 Makefiles实际上围绕着一组中间人 文件。 testsuite of BSD Owl。
中说明了这种技术我们可以轻松地并行构建程序,因为这是一个标准
make
的许多版本的功能。这也是后果
无害的观察:因为“计划执行者在哪里”是一个
在Makefile中显式数据,make
可以推理
它。在shell脚本中实现类似的效果需要a
努力。
任何版本的make
的并行模式只有在以下情况下才能正常工作
正确指定了依赖性。这可能是相当的
很难实现,但bsdmake具有 功能
从字面上来说,这是一个问题。它被称为
META模式。它
使用编译作业的第一个非并行传递进行计算
通过监视文件访问来实现实际的依赖关系,并使用它
以后并行构建中的信息。
由于特殊的观点 - 也就是另一种结果 无害观察 - 用于编写Makefile,我们可以 通过挂钩构建系统的所有方面,可以轻松扩展它们。
例如,如果我们决定所有数据库I / O样板代码
应该用自动工具编写,我们只需写在
Makefile自动工具应该将哪些文件用作写入的输入
样板代码。没有更少,仅此而已。我们可以添加这个
描述几乎我们喜欢的地方,make
会得到它
无论如何。在shell脚本构建中执行此类扩展即可
比必要的更难。
这种可扩展性易用性是Makefile代码重用的一个很好的动力。