我们需要使用Makefile将所有内容整合到我们的项目中,但我们的教授从未向我们展示过如何操作.
我只有一个档案a3driver.cpp.驱动程序从某个位置导入类"/user/cse232/Examples/example32.sequence.cpp".
就是这样,其他一切都包含在内.cpp.
我如何制作一个简单的Makefile来创建一个名为a3a.exe?的可执行文件?
dmc*_*kee 531
复制自我为物理专业研究生写的维基文章.
由于这是针对unix的,因此可执行文件没有扩展名.
需要注意的一点是,它root-config是一个提供正确编译和链接标志的实用程序; 以及用于根据root构建应用程序的正确库.这只是与本文档原始受众相关的详细信息.
或者你永远不会忘记你的第一次
关于make的介绍性讨论,以及如何编写简单的makefile
什么是Make?我为什么要关心?
名为make的工具是构建依赖项管理器.也就是说,它需要知道需要执行哪些命令才能从源文件,目标文件,库,标题等集合中获取软件项目的顺序.-其中一些最近可能已更改---并将它们变成一个正确的最新版本的程序.
实际上你也可以将make用于其他事情,但我不会谈论这个.
一个简单的Makefile
假设你有一个目录包含:tool tool.cc tool.o support.cc support.hh,并且 support.o它依赖于root并且应该被编译成一个被调用的程序tool,并且假设你一直在攻击源文件(这意味着现有tool的已经过时)并希望编译程序.
你自己可以做到这一点
1)如果任一检查support.cc或support.hh比较新的support.o,并且如果是这样运行像一个命令
g++ -g -c -pthread -I/sw/include/root support.cc
Run Code Online (Sandbox Code Playgroud)
2)检查是否比support.hh或者tool.cc更新tool.o,如果是,则运行如下命令
g++ -g -c -pthread -I/sw/include/root tool.cc
Run Code Online (Sandbox Code Playgroud)
3)检查是否tool.o比新的更新tool,如果是,则运行命令
g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
Run Code Online (Sandbox Code Playgroud)
唷!太麻烦了!有很多值得记住的错误和几次出错的机会.(顺便说一句 - 这里展示的命令行的细节取决于我们的软件环境.这些在我的计算机上工作.)
当然,您每次都可以运行所有三个命令.这样做可行,但不能很好地扩展到大量的软件(比如DOGS需要超过15分钟从我的MacBook上完全编译).
相反,你可以写一个这样的文件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
Run Code Online (Sandbox Code Playgroud)
然后只需make在命令行输入.这将自动执行上面显示的三个步骤.
这里的非缩进行具有"target:dependencies"形式,并告诉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
Run Code Online (Sandbox Code Playgroud)
它更具可读性,但仍需要大量输入
制作功能
GNU make支持各种函数,用于从文件系统或系统上的其他命令访问信息.在这种情况下,我们感兴趣的$(shell ...)是扩展到参数的输出,并$(subst opat,npat,text)替换文本中的所有opatwith的实例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
Run Code Online (Sandbox Code Playgroud)
这更容易键入,更易读.
请注意
隐式和模式规则
我们通常希望所有c ++源文件都应该以相同的方式处理,并且make提供了三种方式来说明这一点
内置隐式规则,下面将讨论一些规则.模式规则以类似的形式指定
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
Run Code Online (Sandbox Code Playgroud)
这意味着通过运行显示的命令从c源文件生成目标文件,其中"自动"变量$<扩展为第一个依赖项的名称.
内置规则
Make有一大堆内置规则,这意味着很多时候,一个项目可以通过一个非常简单的makefile进行编译.
GNU make c源文件的内置规则是上面展示的.同样,我们使用类似规则从c ++源文件创建目标文件$(CXX) -c $(CPPFLAGS) $(CFLAGS)
单个目标文件使用链接$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS),但在我们的情况下这不起作用,因为我们想要链接多个目标文件.
内置规则使用的变量
内置规则使用一组标准变量,允许您指定本地环境信息(例如在何处查找ROOT包含文件),而无需重写所有规则.我们最感兴趣的是:
CC - 要使用的c编译器CXX - 要使用的c ++编译器LD - 要使用的链接器CFLAGS - c源文件的编译标志CXXFLAGS - c ++源文件的编译标志CPPFLAGS - c和c ++使用的c-preprocessor的标志(通常包括命令行中定义的文件路径和符号)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
Run Code Online (Sandbox Code Playgroud)
我们还添加了几个执行特殊操作的标准目标(比如清理源目录).
请注意,如果在没有参数的情况下调用make,它将使用文件中找到的第一个目标(在本例中为all),但您也可以命名目标以获取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
Run Code Online (Sandbox Code Playgroud)
请注意
make,然后ls -A你看到一个文件名为.depend它包含的东西,看起来像化妆依赖行其他阅读
知道错误和历史记录
make的输入语言是空格敏感的.特别是依赖关系后的操作行必须以制表符开头.但是一系列空格看起来可能相同(实际上有些编辑器会将选项卡静默转换为空格,反之亦然),这会导致make文件看起来正确并且仍然无效.这在早期被确定为一个错误但是(故事发生)没有修复,因为已经有10个用户.
Rei*_*ica 54
我一直认为通过一个详细的例子更容易学习,所以这就是我对makefile的看法.对于每个部分,您有一行不缩进的行,它显示该部分的名称,后跟依赖项.依赖项可以是其他部分(将在当前部分之前运行)或文件(如果更新将导致下次运行时再次运行当前部分make).
这是一个快速示例(请记住,我使用4个空格,我应该使用选项卡,Stack Overflow不会让我使用选项卡):
a3driver: a3driver.o
g++ -o a3driver a3driver.o
a3driver.o: a3driver.cpp
g++ -c a3driver.cpp
Run Code Online (Sandbox Code Playgroud)
当您键入时make,它将选择第一部分(a3driver).a3driver依赖于a3driver.o,因此它将转到该部分.a3driver.o依赖于a3driver.cpp,所以它只会在a3driver.cpp自上次运行以来发生变化时运行.假设它已经(或从未运行过),它会将a3driver.cpp编译为.o文件,然后返回a3driver并编译最终的可执行文件.
由于只有一个文件,它甚至可以简化为:
a3driver: a3driver.cpp
g++ -o a3driver a3driver.cpp
Run Code Online (Sandbox Code Playgroud)
我展示第一个例子的原因是它显示了makefile的强大功能.如果需要编译另一个文件,可以添加另一个部分.这是一个带有secondFile.cpp的示例(在名为secondFile.h的头文件中加载):
a3driver: a3driver.o secondFile.o
g++ -o a3driver a3driver.o secondFile.o
a3driver.o: a3driver.cpp
g++ -c a3driver.cpp
secondFile.o: secondFile.cpp secondFile.h
g++ -c secondFile.cpp
Run Code Online (Sandbox Code Playgroud)
这样,如果你在secondFile.cpp或secondFile.h中更改某些东西并重新编译,它只会重新编译secondFile.cpp(而不是a3driver.cpp).或者,如果你在a3driver.cpp中更改某些内容,它将不会重新编译secondFile.cpp.
如果您对此有任何疑问,请与我们联系.
包括名为"all"的部分和名为"clean"的部分也是传统的."all"通常会构建所有可执行文件,而"clean"将删除像".o"文件和可执行文件这样的"构建工件":
all: a3driver ;
clean:
# -f so this will succeed even if the files don't exist
rm -f a3driver a3driver.o
Run Code Online (Sandbox Code Playgroud)
编辑:我没注意到你在Windows上.我认为,唯一的区别是改变-o a3driver来-o a3driver.exe.
小智 35
为什么每个人都喜欢列出源文件?一个简单的find命令可以轻松地处理这个问题.
这是一个简单的C++ Makefile的例子.只需将其放在包含.C文件的目录中,然后键入make...
appname := myapp
CXX := clang++
CXXFLAGS := -std=c++11
srcfiles := $(shell find . -name "*.C")
objects := $(patsubst %.C, %.o, $(srcfiles))
all: $(appname)
$(appname): $(objects)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)
depend: .depend
.depend: $(srcfiles)
rm -f ./.depend
$(CXX) $(CXXFLAGS) -MM $^>>./.depend;
clean:
rm -f $(objects)
dist-clean: clean
rm -f *~ .depend
include .depend
Run Code Online (Sandbox Code Playgroud)
小智 13
老问题,我知道,但对于后代.你有两个选择.
选项1:最简单的makefile = NO MAKEFILE.
将"a3driver.cpp"重命名为"a3a.cpp",然后在命令行上写入:
nmake a3a.exe
Run Code Online (Sandbox Code Playgroud)
就是这样.如果你正在使用gnu-make使用"make"或"gmake"或其他什么.
选项2:2行makefile.
a3a.exe: a3driver.obj
link /out:a3a.exe a3driver.obj
Run Code Online (Sandbox Code Playgroud)
瞧.
您的make文件将具有一个或两个依赖关系规则,具体取决于您是使用单个命令编译和链接,还是使用一个用于编译的命令和一个用于链接的命令.
依赖关系是一个规则树,如下所示:
main_target : source1 source2 etc
command to build main_target from sources
source1 : dependents for source1
command to build source1
Run Code Online (Sandbox Code Playgroud)
有必须是一个目标的命令后一个空行,而且必须不能是命令之前,一个空行.makefile中的第一个目标是总体目标,仅当第一个目标依赖于它们时才构建其他目标.
所以你的makefile看起来像这样.
a3a.exe : a3driver.obj
link /out:a3a.exe a3driver.obj
a3driver.obj : a3driver.cpp
cc a3driver.cpp
Run Code Online (Sandbox Code Playgroud)
我建议(请注意,缩进是TAB):
tool: tool.o file1.o file2.o
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@
Run Code Online (Sandbox Code Playgroud)
要么
LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o
Run Code Online (Sandbox Code Playgroud)
后一种建议稍好一些,因为它重用了GNU Make隐式规则。但是,为了正常工作,源文件必须与最终的可执行文件具有相同的名称(即:tool.c和tool)。
注意,没有必要声明源。中间目标文件是使用隐式规则生成的。因此,这Makefile适用于C和C ++(以及Fortran等)。
还要注意,默认情况下,Makefile $(CC)用作链接器。$(CC)不适用于链接C ++对象文件。我们LINK.o仅因此修改。如果要编译C代码,则不必强制使用该LINK.o值。
当然,您也可以添加带有变量的编译标志,CFLAGS并在中添加库LDLIBS。例如:
CFLAGS = -Wall
LDLIBS = -lm
Run Code Online (Sandbox Code Playgroud)
附注:如果必须使用外部库,建议您使用pkg-config来正确设置CFLAGS和LDLIBS:
CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)
Run Code Online (Sandbox Code Playgroud)
细心的读者会注意到,Makefile如果更改了一个标头,则无法正确重建。添加以下行以解决问题:
override CPPFLAGS += -MMD
include $(wildcard *.d)
Run Code Online (Sandbox Code Playgroud)
-MMD允许构建.d文件,其中包含有关头文件依赖项的Makefile片段。第二行仅使用它们。
可以肯定,一个写得很好的Makefile还应该包括clean与distclean规则:
clean:
$(RM) *.o *.d
distclean: clean
$(RM) tool
Run Code Online (Sandbox Code Playgroud)
请注意,$(RM)等效于rm -f,但是不rm直接调用是一个好习惯。
该all规则也受到赞赏。为了起作用,它应该是文件的第一条规则:
all: tool
Run Code Online (Sandbox Code Playgroud)
您还可以添加一条install规则:
PREFIX = /usr/local
install:
install -m 755 tool $(DESTDIR)$(PREFIX)/bin
Run Code Online (Sandbox Code Playgroud)
DESTDIR默认为空。用户可以将其设置为在其他系统上安装您的程序(交叉编译过程必选)。多个发行版的软件包维护者也可能会更改PREFIX,以便在中安装软件包/usr。
最后一句话:不要将源文件放在子目录中。如果您确实要这样做,请将其保留Makefile在根目录中,并使用完整路径来标识您的文件(即subdir/file.o)。
总而言之,完整的Makefile应该如下所示:
LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)
all: tool
tool: tool.o file1.o file2.o
clean:
$(RM) *.o *.d
distclean: clean
$(RM) tool
install:
install -m 755 tool $(DESTDIR)$(PREFIX)/bin
Run Code Online (Sandbox Code Playgroud)
我用过friedmud的答案.我对此进行了一段时间的研究,这似乎是一个开始的好方法.此解决方案还有一个定义良好的添加编译器标志的方法.我再次回答,因为我做了更改,使其在我的环境,Ubuntu和g ++中工作.有时,更多工作实例是最好的老师.
appname := myapp
CXX := g++
CXXFLAGS := -Wall -g
srcfiles := $(shell find . -maxdepth 1 -name "*.cpp")
objects := $(patsubst %.cpp, %.o, $(srcfiles))
all: $(appname)
$(appname): $(objects)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)
depend: .depend
.depend: $(srcfiles)
rm -f ./.depend
$(CXX) $(CXXFLAGS) -MM $^>>./.depend;
clean:
rm -f $(objects)
dist-clean: clean
rm -f *~ .depend
include .depend
Run Code Online (Sandbox Code Playgroud)
makefile似乎非常复杂.我正在使用一个,但它产生了一个与不链接g ++库有关的错误.这种配置解决了这个问题.