简介
Make 是一个用于构建其他程序或文档的程序。Make 适用于任何具有可能完成或未完成的中间步骤的过程。
Make 不会重新构建已更新的项目。此外,它知道如何构建中间目标。您可以使用 Make 文件来指示 Make。
Make 程序不止一个。C/C++ 开发人员最常遇到的是 GNU Make。我将首先用示例描述 GNU Make,然后介绍 BSD Make,最后对它们之间的区别进行一些评论。
GNU Make 入门
GNU Make 内置了许多规则。它们被称为内置规则。让我们看一个示例,hello.c
1 2 3 4 5 6 7
|
#include <stdio.h>
int main()
{
printf("hello world\n:");
return 0;
}
|
要构建程序,只需键入
它会运行
因此,GNU Make 已知道可以从 .c 文件构建程序,并且知道如何编译和链接它。
让我们再试一个,这次包含更多文件。这次我们需要一个 makefile。GNU Make 文件可以命名为 GNUmakefile、Makefile 或 makefile。在这些示例中我将使用 GNUmakefile,因为稍后我们将介绍 BSD Make。
calc.c
1 2 3 4 5 6 7 8
|
#include <stdio.h>
#include "engine.h"
int main()
{
printf("%d\n", calc("2+3"));
return 0;
}
|
engine.h
1 2 3
|
#pragma once
int calc(const char* expression);
|
engine.c
1 2 3 4 5 6
|
#include "engine.h"
int calc(const char *expression)
{
return 5;
}
|
GNUmakefile
1 2 3 4 5
|
OBJ = calc.o engine.o
calc: $(OBJ)
$(OBJ): engine.h
|
我们定义了一个宏 OBJ,它包含我们的目标文件列表。请记住,GNU Make 知道如何从 C 程序创建目标文件。
接下来,我们告诉 GNU Make 我们的程序 calc 由这些 OBJ 文件组成。当我们运行 make 时,它会执行
cc -c -o calc.o calc.c
cc -c -o engine.o engine.c
cc calc.o engine.o -o calc |
最后,我们告诉 GNU Make,如果 engine.h 发生更改,我们必须重新编译目标文件。这称为依赖项。
现在,我们来介绍 C++。让我们定义一个新文件:calcpp.cc
1 2 3 4 5 6 7 8 9 10 11
|
#include <iostream>
extern "C"
{
#include "engine.h"
}
int main()
{
std::cout << calc("2+3") << std::endl;
return 0;
}
|
这是我们修改后的 GNUmakefile
1 2 3 4 5 6
|
OBJ = calcpp.o engine.o
calc: $(OBJ)
$(LINK.cc) $^ -o $@
$(OBJ): engine.h
|
那么有什么改变?我们需要告诉 GNU Make 我们正在链接一个 C++ 程序。我们使用 GNU Make 宏 LINK.cc 来告诉它我们正在链接一个 C++ 程序。
$^ 表示所有依赖项,在此情况下为 calcpp.o 和 engine.o。
$@ 表示我们正在构建的目标,在此情况下为 calc。
当我们运行 make 时,它会执行
c++ -c -o calcpp.o calcpp.cc
c++ calcpp.o engine.o -o calc |
那编译 engine.c 呢?嗯,我们之前已经编译过它了,make 知道它不需要重新编译。
BSD Make
BSD Make 早于 GNU Make,并且它有所不同。BSD Make 没有很多内置规则。它需要在 make 文件中被告知很多东西。正如您在上面看到的,GNU Make 知道很多。
BSD Make 知道依赖项和构建这些依赖项的规则,但它没有所有那些内置的默认规则,因此需要被告知。一个使用 BSD Make 的系统,而不是被告知所有内容,而是提供一组包含文件,这些文件定义了在该系统上如何完成事物。例如,在 FreeBSD 上,如果您想编写程序,则包含 <bsd.prog.mk>。这包含了编译、安装、卸载、归档……开发程序所需的所有规则。
GNU Make 和 BSD Make 的比较
请记住,GNU 是自由软件基金会的工具集。GNU 的意思是 GNU 不是 Unix。这很重要。当 GNU 项目启动时,它的目标是提供 Unix 程序的免费可用替代品。并且它们通过添加有用的、一致的行为来改进它们。
BSD Make 只知道依赖项、目标、规则和宏。GNU Make 将内置规则添加到其中,以便为开发人员提供便利。
事实证明,系统的细节是冲突的,并且无法提前知道。BSD Make 通过让系统定义其参数来处理系统依赖项。GNU Make 的内置规则被证明是不够的。Auto Tools 项目(AutoConf、AutoMake、LibTools)是为了弥补这一点而开发的。它通过在配置时发现系统来工作,然后生成 makefiles。
我更喜欢 BSD 的解决方案。很明显,在这个例子中它的设计更好。我发现 BSD 通常都是如此。
示例
此示例使用了 3 个 C++ 源文件(main.cc、config.cc、dispatch.cc)和 2 个头文件(config.hpp、dispatch.hpp)。我没有添加依赖项,在现实世界中,它们是自动生成的。
GNUmakefile
1 2 3 4 5 6
|
CXXFLAGS = -g
all: goodlistener
goodlistener: main.o config.o dispatch.o
$(LINK.cc) -o $@ $^
|
BSDmakefile
1 2 3 4 5 6 7
|
PROG_CXX = goodlistener
SRCS = main.cc config.cc dispatch.cc
MAN = goodlistener.1
MAKEOBJDIR = work
CXXFLAGS=-g
.include <bsd.prog.mk>
|