我们需要使用 Makefile 为我们的项目整合所有内容,但我们的教授从未向我们展示过如何做。
我只有一个文件,a3driver.cpp
.驱动程序从一个位置导入一个类,"/user/cse232/Examples/example32.sequence.cpp"
.
而已。其他所有内容都包含在 .cpp
中.
我将如何制作一个简单的 Makefile 来创建一个名为 a3a.exe
的可执行文件?
最佳答案
由于这是针对 Unix 的,可执行文件没有任何扩展名。
需要注意的一件事是 root-config
是一个提供正确编译和链接标志的实用程序;以及用于针对 root 构建应用程序的正确库。这只是与本文档的原始受众相关的详细信息。
让我宝贝
否则你永远不会忘记你第一次被制造
make 的介绍性讨论,以及如何编写一个简单的 makefile
什么是制造?我为什么要关心?
该工具名为 Make是一个构建依赖管理器。也就是说,它负责了解需要以何种顺序执行哪些命令才能从源文件、目标文件、库、头文件等的集合中获取您的软件项目——其中一些可能已更改最近---并将它们转换为程序的正确最新版本。
实际上,您也可以将 Make 用于其他用途,但我不打算谈论它。
一个简单的 Makefile
假设您有一个包含以下内容的目录:tool
tool.cc
tool.o
support.cc
support.hh
, 和 support.o
取决于 root
并且应该被编译成一个名为 tool
的程序,并假设您一直在攻击源文件(这意味着现有的 tool
现在已过时)并想要编译该程序。
要自己做这件事,你可以
support.cc
或 support.hh
比 support.o
新,如果是这样运行命令g++ -g -c -pthread -I/sw/include/root support.cc
support.hh
或 tool.cc
比 tool.o
新,如果是这样运行命令g++ -g -c -pthread -I/sw/include/root tool.cc
tool.o
比 tool
新,如果是这样运行命令g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
呼!多麻烦啊!有很多东西要记住,也有很多机会犯错误。 (顺便说一句——这里展示的命令行的细节取决于我们的软件环境。这些在我的电脑上工作。)
当然,您可以每次都运行所有三个命令。这会起作用,但它不能很好地扩展到大量软件(例如 DOGS,它在我的 MacBook 上从头开始编译需要超过 15 分钟)。
相反,您可以编写一个名为
makefile
的文件。像这样:tool: tool.o support.o
g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
tool.o: tool.cc support.hh
g++ -g -c -pthread -I/sw/include/root tool.cc
support.o: support.hh support.cc
g++ -g -c -pthread -I/sw/include/root support.cc
然后输入
make
在命令行。这将自动执行上面显示的三个步骤。此处未缩进的行具有“目标:依赖项”的形式,并告诉 Make 如果任何依赖项比目标更新,则应运行关联的命令(缩进行)。也就是说,依赖行描述了需要重新构建以适应各种文件中的更改的逻辑。如
support.cc
更改意味着 support.o
必须重建,但 tool.o
可以单独留下。当support.o
变化 tool
必须重建。与每个依赖行相关联的命令由一个选项卡(见下文)引出,应该修改目标(或至少触摸它以更新修改时间)。
变量、内置规则和其他好东西
在这一点上,我们的 makefile 只是记住需要做的工作,但我们仍然必须弄清楚并完整地键入每个需要的命令。不一定是这样:Make 是一种强大的语言,具有变量、文本操作函数和大量内置规则,可以使我们更容易地做到这一点。
制作变量
访问 make 变量的语法是
$(VAR)
.分配给 Make 变量的语法是:
VAR = A text value of some kind
(或 VAR := A different text value but ignore this for the moment
)。您可以在类似我们 makefile 的改进版本的规则中使用变量:
CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
-Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
-lm -ldl
tool: tool.o support.o
g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
这更具可读性,但仍然需要大量输入
制作函数
GNU make 支持从文件系统或系统上的其他命令访问信息的各种函数。在这种情况下,我们对
$(shell ...)
感兴趣它扩展到参数的输出,和 $(subst opat,npat,text)
它替换了 opat
的所有实例与 npat
在文本中。利用这一点,我们可以:
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
tool: $(OBJS)
g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
这更容易打字,也更易读。
请注意
隐式和模式规则
我们通常希望所有 C++ 源文件都应该以相同的方式处理,Make 提供了三种方式来说明这一点:
隐式规则是内置的,下面将讨论一些。模式规则以类似的形式指定
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
这意味着目标文件是通过运行显示的命令从 C 源文件生成的,其中“自动”变量
$<
扩展为第一个依赖项的名称。内置规则
Make 有很多内置规则,这意味着实际上,一个项目通常可以由一个非常简单的 makefile 编译。
C 源文件的 GNU make 内置规则是上面展示的规则。类似地,我们使用类似
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
的规则从 C++ 源文件创建目标文件。 .使用
$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
链接单个目标文件,但这在我们的情况下不起作用,因为我们想要链接多个目标文件。内置规则使用的变量
内置规则使用一组标准变量,允许您指定本地环境信息(例如在哪里可以找到 ROOT 包含文件),而无需重新编写所有规则。我们最有可能感兴趣的是:
CC
-- C 编译器使用 CXX
-- 要使用的 C++ 编译器 LD
-- 要使用的链接器 CFLAGS
-- C 源文件的编译标志 CXXFLAGS
-- C++ 源文件的编译标志 CPPFLAGS
-- C 预处理器的标志(通常包括在命令行上定义的文件路径和符号),由 C 和 C++ 使用 LDFLAGS
-- 链接器标志 LDLIBS
-- 要链接的库 一个基本的 Makefile
通过利用内置规则,我们可以将我们的 makefile 简化为:
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
support.o: support.hh support.cc
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) tool
我们还添加了几个执行特殊操作(如清理源目录)的标准目标。
请注意,当不带参数调用 make 时,它使用在文件中找到的第一个目标(在本例中为全部),但您也可以命名要获取的目标,这就是使
make clean
的原因。在这种情况下删除目标文件。我们仍然对所有依赖项进行了硬编码。
一些神秘的改进
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
depend: .depend
.depend: $(SRCS)
$(RM) ./.depend
$(CXX) $(CPPFLAGS) -MM $^>>./.depend;
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) *~ .depend
include .depend
请注意
make
然后 ls -A
你会看到一个名为 .depend
的文件其中包含看起来像 make 依赖行的内容 其他阅读
知道错误和历史记录
Make 的输入语言对空格敏感。特别是,依赖项后面的操作行必须以选项卡开头。但是一系列的空格看起来是一样的(确实有一些编辑器会默默地将制表符转换为空格,反之亦然),这会导致 Make 文件看起来正确但仍然无法工作。这在早期被确定为错误,但( the story goes )并未修复,因为已经有 10 个用户。
(这是从我为物理研究生写的维基帖子中复制的。)
关于c++ - 如何制作一个简单的 C++ Makefile,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2481269/