如何制作一个简单的 C++ Makefile

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2481269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:35:50  来源:igfitidea点击:

How to make a SIMPLE C++ Makefile

c++makefile

提问by Befall

We are required to use a Makefile to pull everything together for our project, but our professor never showed us how to.

我们需要使用 Makefile 为我们的项目整合所有内容,但我们的教授从未向我们展示过如何做。

I only have onefile, a3driver.cpp. The driver imports a class from a location, "/user/cse232/Examples/example32.sequence.cpp".

我只有一个文件,a3driver.cpp. 驱动程序从一个位置导入一个类,"/user/cse232/Examples/example32.sequence.cpp"

That's it. Everything else is contained with the .cpp.

就是这样。其他所有内容都包含在.cpp.

How would I go about making a simple Makefile that creates an executable called a3a.exe?

我将如何制作一个简单的 Makefile 来创建一个名为 的可执行文件a3a.exe

回答by dmckee --- ex-moderator kitten

Since this is for Unix, the executables don't have any extensions.

由于这是针对 Unix 的,可执行文件没有任何扩展名。

One thing to note is that root-configis a utility which provides the right compilation and linking flags; and the right libraries for building applications against root. That's just a detail related to the original audience for this document.

需要注意的一点是,这root-config是一个提供正确编译和链接标志的实用程序;以及用于针对 root 构建应用程序的正确库。这只是与本文档的原始受众相关的详细信息。

Make Me Baby

让我宝贝

or You Never Forget The First Time You Got Made

否则你永远不会忘记你第一次得到

An introductory discussion of make, and how to write a simple makefile

make 的介绍性讨论,以及如何编写一个简单的 makefile

What is Make? And Why Should I Care?

什么是制造?我为什么要关心?

The tool called Makeis a build dependency manager. That is, it takes care of knowing what commands need to be executed in what order to take your software project from a collection of source files, object files, libraries, headers, etc., etc.---some of which may have changed recently---and turning them into a correct up-to-date version of the program.

名为Make的工具是一个构建依赖项管理器。也就是说,它负责了解需要以何种顺序执行哪些命令才能从源文件、目标文件、库、头文件等的集合中获取您的软件项目——其中一些可能已更改最近---并将它们转换为程序的正确最新版本。

Actually, you can use Make for other things too, but I'm not going to talk about that.

实际上,您也可以将 Make 用于其他用途,但我不打算谈论它。

A Trivial Makefile

一个简单的 Makefile

Suppose that you have a directory containing: tooltool.cctool.osupport.ccsupport.hh, and support.owhich depend on rootand are supposed to be compiled into a program called tool, and suppose that you've been hacking on the source files (which means the existing toolis now out of date) and want to compile the program.

假设您有一个包含: 的目录tooltool.cctool.osupport.ccsupport.hh,并且 support.o它依赖于root并且应该被编译成一个名为 的程序tool,并且假设您一直在对源文件进行黑客攻击(这意味着现有文件tool现在已经过时)并且想要编译程序。

To do this yourself you could

要自己做这件事,你可以

  1. Check if either support.ccor support.hhis newer than support.o, and if so run a command like

    g++ -g -c -pthread -I/sw/include/root support.cc
    
  2. Check if either support.hhor tool.ccare newer than tool.o, and if so run a command like

    g++ -g  -c -pthread -I/sw/include/root tool.cc
    
  3. Check if tool.ois newer than tool, and if so run a command like

    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
    
  1. 检查support.ccsupport.hh是否比 更新support.o,如果是,则运行类似的命令

    g++ -g -c -pthread -I/sw/include/root support.cc
    
  2. 检查support.hhtool.cc是否比 更新tool.o,如果是,则运行类似的命令

    g++ -g  -c -pthread -I/sw/include/root tool.cc
    
  3. 检查是否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
    

Phew! What a hassle! There is a lot to remember and several chances to make mistakes. (BTW-- the particulars of the command lines exhibited here depend on our software environment. These ones work on my computer.)

呼!多麻烦啊!有很多东西要记住,也有很多机会犯错误。(顺便说一句——这里展示的命令行的细节取决于我们的软件环境。这些在我的电脑上工作。)

Of course, you could just run all three commands every time. That would work, but it doesn't scale well to a substantial piece of software (like DOGS which takes more than 15 minutes to compile from the ground up on my MacBook).

当然,您可以每次都运行所有三个命令。这会起作用,但它不能很好地扩展到大量软件(例如 DOGS,它在我的 MacBook 上从头开始编译需要超过 15 分钟)。

Instead you could write a file called makefilelike this:

相反,您可以编写一个如下所示的文件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

and just type makeat the command line. Which will perform the three steps shown above automatically.

只需make在命令行中输入即可。这将自动执行上面显示的三个步骤。

The unindented lines here have the form "target: dependencies"and tell Make that the associated commands (indented lines) should be run if any of the dependencies are newer than the target. That is, the dependency lines describe the logic of what needs to be rebuilt to accommodate changes in various files. If support.ccchanges that means that support.omust be rebuilt, but tool.ocan be left alone. When support.ochanges toolmust be rebuilt.

此处未缩进的行具有“目标:依赖项”的形式,并告诉 Make 如果任何依赖项比目标更新,则应运行关联的命令(缩进行)。也就是说,依赖行描述了需要重新构建以适应各种文件中的更改的逻辑。如果support.cc更改意味着support.o必须重建,但tool.o可以不理会。何时必须重建support.o更改tool

The commands associated with each dependency line are set off with a tab (see below) should modify the target (or at least touch it to update the modification time).

与每个依赖行相关联的命令由一个选项卡(见下文)引出,应该修改目标(或至少触摸它以更新修改时间)。

Variables, Built In Rules, and Other Goodies

变量、内置规则和其他好东西

At this point, our makefile is simply remembering the work that needs doing, but we still had to figure out and type each and every needed command in its entirety. It does not have to be that way: Make is a powerful language with variables, text manipulation functions, and a whole slew of built-in rules which can make this much easier for us.

在这一点上,我们的 makefile 只是记住需要做的工作,但我们仍然必须弄清楚并完整地键入每个需要的命令。不一定是这样:Make 是一种强大的语言,具有变量、文本操作函数和大量内置规则,可以使我们更容易地做到这一点。

Make Variables

制作变量

The syntax for accessing a make variable is $(VAR).

访问 make 变量的语法是$(VAR).

The syntax for assigning to a Make variable is: VAR = A text value of some kind(or VAR := A different text value but ignore this for the moment).

分配给 Make 变量的语法是:(VAR = A text value of some kindVAR := A different text value but ignore this for the moment)。

You can use variables in rules like this improved version of our makefile:

您可以在类似我们 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

which is a little more readable, but still requires a lot of typing

这更具可读性,但仍然需要大量输入

Make Functions

制作函数

GNU make supports a variety of functions for accessing information from the filesystem or other commands on the system. In this case we are interested in $(shell ...)which expands to the output of the argument(s), and $(subst opat,npat,text)which replaces all instances of opatwith npatin text.

GNU make 支持从文件系统或系统上的其他命令访问信息的各种函数。在这种情况下,我们感兴趣的是$(shell ...)which 扩展到参数的输出,以及$(subst opat,npat,text)替换文本中所有opatwith 的实例npat

Taking advantage of this gives us:

利用这一点,我们可以:

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

which is easier to type and much more readable.

这更容易打字,也更易读。

Notice that

请注意

  1. We are still stating explicitly the dependencies for each object file and the final executable
  2. We've had to explicitly type the compilation rule for both source files
  1. 我们仍然明确说明每个目标文件和最终可执行文件的依赖关系
  2. 我们必须明确输入两个源文件的编译规则

Implicit and Pattern Rules

隐式和模式规则

We would generally expect that all C++ source files should be treated the same way, and Make provides three ways to state this:

我们通常希望所有 C++ 源文件都应该以相同的方式处理,Make 提供了三种方式来说明这一点:

  1. suffix rules (considered obsolete in GNU make, but kept for backwards compatibility)
  2. implicit rules
  3. pattern rules
  1. 后缀规则(在 GNU make 中被认为已过时,但为了向后兼容而保留)
  2. 隐含规则
  3. 模式规则

Implicit rules are built in, and a few will be discussed below. Pattern rules are specified in a form like

隐式规则是内置的,下面将讨论一些。模式规则以类似的形式指定

%.o: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c $<

which means that object files are generated from C source files by running the command shown, where the "automatic" variable $<expands to the name of the first dependency.

这意味着目标文件是通过运行显示的命令从 C 源文件生成的,其中“自动”变量$<扩展为第一个依赖项的名称。

Built-in Rules

内置规则

Make has a whole host of built-in rules that mean that very often, a project can be compile by a very simple makefile, indeed.

Make 有很多内置规则,这意味着实际上,一个项目通常可以由一个非常简单的 makefile 编译。

The GNU make built in rule for C source files is the one exhibited above. Similarly we create object files from C++ source files with a rule like $(CXX) -c $(CPPFLAGS) $(CFLAGS).

C 源文件的 GNU make 内置规则是上面展示的规则。类似地,我们从 C++ 源文件创建目标文件,规则类似于$(CXX) -c $(CPPFLAGS) $(CFLAGS).

Single object files are linked using $(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS), but this won't work in our case, because we want to link multiple object files.

使用 链接单个目标文件$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS),但这在我们的情况下不起作用,因为我们想要链接多个目标文件。

Variables Used By Built-in Rules

内置规则使用的变量

The built-in rules use a set of standard variables that allow you to specify local environment information (like where to find the ROOT include files) without re-writing all the rules. The ones most likely to be interesting to us are:

内置规则使用一组标准变量,允许您指定本地环境信息(例如在哪里可以找到 ROOT 包含文件),而无需重新编写所有规则。我们最有可能感兴趣的是:

  • CC-- the C compiler to use
  • CXX-- the C++ compiler to use
  • LD-- the linker to use
  • CFLAGS-- compilation flag for C source files
  • CXXFLAGS-- compilation flags for C++ source files
  • CPPFLAGS-- flags for the c-preprocessor (typically include file paths and symbols defined on the command line), used by C and C++
  • LDFLAGS-- linker flags
  • LDLIBS-- libraries to link
  • CC-- 要使用的 C 编译器
  • CXX-- 要使用的 C++ 编译器
  • LD-- 要使用的链接器
  • CFLAGS-- C 源文件的编译标志
  • CXXFLAGS-- C++ 源文件的编译标志
  • CPPFLAGS-- c 预处理器的标志(通常包括在命令行上定义的文件路径和符号),由 C 和 C++ 使用
  • LDFLAGS-- 链接器标志
  • LDLIBS-- 要链接的库

A Basic Makefile

一个基本的 Makefile

By taking advantage of the built-in rules we can simplify our makefile to:

通过利用内置规则,我们可以将我们的 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

We have also added several standard targets that perform special actions (like cleaning up the source directory).

我们还添加了几个执行特殊操作(如清理源目录)的标准目标。

Note that when make is invoked without an argument, it uses the first target found in the file (in this case all), but you can also name the target to get which is what makes make cleanremove the object files in this case.

请注意,当不带参数调用 make 时,它​​使用文件中找到的第一个目标(在本例中为全部),但您也可以命名要获取的目标,make clean在这种情况下,它是删除目标文件的原因。

We still have all the dependencies hard-coded.

我们仍然对所有依赖项进行了硬编码。

Some Mysterious Improvements

一些神秘的改进

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

Notice that

请注意

  1. There are no longer any dependency lines for the source files!?!
  2. There is some strange magic related to .depend and depend
  3. If you do makethen ls -Ayou see a file named .dependwhich contains things that look like make dependency lines
  1. 源文件不再有任何依赖行!?!
  2. .depend 和depend 有一些奇怪的魔法
  3. 如果你这样做make,然后ls -A你看到一个文件名为.depend它包含的东西,看起来像化妆依赖行

Other Reading

其他阅读

Know Bugs and Historical Notes

了解错误和历史记录

The input language for Make is whitespace sensitive. In particular, the action lines following dependencies must start with a tab. But a series of spaces can look the same (and indeed there are editors that will silently convert tabs to spaces or vice versa), which results in a Make file that looks right and still doesn't work. This was identified as a bug early on, but (the story goes) it was not fixed, because there were already 10 users.

Make 的输入语言对空格敏感。特别是,依赖项后面的操作行必须以 tab 开头。但是一系列的空格看起来是一样的(确实有一些编辑器会默默地将制表符转换为空格,反之亦然),这会导致 Make 文件看起来正确但仍然无法工作。这在早期被确定为一个错误,但是(故事是这样的)它没有被修复,因为已经有 10 个用户。

(This was copied from a wiki post I wrote for physics graduate students.)

(这是从我为物理研究生写的维基帖子中复制的。)

回答by Brendan Long

I've always thought this was easier to learn with a detailed example, so here's how I think of makefiles. For each section you have one line that's not indented and it shows the name of the section followed by dependencies. The dependencies can be either other sections (which will be run before the current section) or files (which if updated will cause the current section to be run again next time you run make).

我一直认为通过详细示例更容易学习,所以这就是我对 makefile 的看法。对于每个部分,您有一行未缩进,它显示部分名称,后跟依赖项。依赖项可以是其他部分(将在当前部分之前运行)或文件(如果更新将导致下次运行时再次运行当前部分make)。

Here's a quick example (keep in mind that I'm using 4 spaces where I should be using a tab, Stack Overflow won't let me use tabs):

这是一个简单的示例(请记住,我在应该使用制表符的地方使用了 4 个空格,Stack Overflow 不允许我使用制表符):

a3driver: a3driver.o
    g++ -o a3driver a3driver.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

When you type make, it will choose the first section (a3driver). a3driver depends on a3driver.o, so it will go to that section. a3driver.o depends on a3driver.cpp, so it will only run if a3driver.cpp has changed since it was last run. Assuming it has (or has never been run), it will compile a3driver.cpp to a .o file, then go back to a3driver and compile the final executable.

当你输入时make,它会选择第一部分(a3driver)。a3driver 依赖于 a3driver.o,所以它会转到那个部分。a3driver.o 依赖于 a3driver.cpp,所以它只会在 a3driver.cpp 自上次运行后发生变化时运行。假设它已经(或从未运行过),它会将 a3driver.cpp 编译为 .o 文件,然后返回到 a3driver 并编译最终的可执行文件。

Since there's only one file, it could even be reduced to:

由于只有一个文件,它甚至可以简化为:

a3driver: a3driver.cpp
    g++ -o a3driver a3driver.cpp

The reason I showed the first example is that it shows the power of makefiles. If you need to compile another file, you can just add another section. Here's an example with a secondFile.cpp (which loads in a header named secondFile.h):

我展示第一个例子的原因是它展示了 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

This way if you change something in secondFile.cpp or secondFile.h and recompile, it will only recompile secondFile.cpp (not a3driver.cpp). Or alternately, if you change something in a3driver.cpp, it won't recompile secondFile.cpp.

这样,如果您更改 secondFile.cpp 或 secondFile.h 中的某些内容并重新编译,它只会重新编译 secondFile.cpp(而不是 a3driver.cpp)。或者,如果您更改 a3driver.cpp 中的某些内容,它不会重新编译 secondFile.cpp。

Let me know if you have any questions about it.

如果您对此有任何疑问,请告诉我。

It's also traditional to include a section named "all" and a section named "clean". "all" will usually build all of the executables, and "clean" will remove "build artifacts" like .o files and the executables:

包括一个名为“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

EDIT: I didn't notice you're on Windows. I think the only difference is changing the -o a3driverto -o a3driver.exe.

编辑:我没有注意到你在 Windows 上。我认为,唯一的区别是改变-o a3driver-o a3driver.exe

回答by friedmud

Why does everyone like to list out source files? A simple find command can take care of that easily.

为什么大家都喜欢列出源文件?一个简单的 find 命令可以轻松解决这个问题。

Here's an example of a dirt simple C++ Makefile. Just drop it in a directory containing .Cfiles and then type make...

这是一个简单的 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

回答by No one

You had two options.

你有两个选择。

Option 1: simplest makefile = NO MAKEFILE.

选项 1:最简单的 makefile = NO MAKEFILE。

Rename "a3driver.cpp" to "a3a.cpp", and then on the command line write:

将“a3driver.cpp”重命名为“a3a.cpp”,然后在命令行上写:

nmake a3a.exe

And that's it. If you're using GNU Make, use "make" or "gmake" or whatever.

就是这样。如果您使用 GNU Make,请使用“make”或“gmake”或其他任何名称。

Option 2: a 2-line makefile.

选项 2:一个 2 行的 makefile。

a3a.exe: a3driver.obj
    link /out:a3a.exe a3driver.obj

回答by John Knoeller

Your Make file will have one or two dependency rules depending on whether you compile and link with a single command, or with one command for the compile and one for the link.

您的 Make 文件将有一个或两个依赖项规则,具体取决于您是使用单个命令编译和链接,还是使用一个命令进行编译和一个命令进行链接。

Dependency are a tree of rules that look like this (note that the indent mustbe a TAB):

依赖是一棵看起来像这样的规则树(注意缩进必须是制表符):

main_target : source1 source2 etc
    command to build main_target from sources

source1 : dependents for source1
    command to build source1

There mustbe a blank line after the commands for a target, and there must notbe a blank line before the commands. The first target in the makefile is the overall goal, and other targets are built only if the first target depends on them.

必须是一个目标的命令后一个空行,而且必须不能是命令之前,一个空行。makefile 中的第一个目标是总体目标,其他目标只有在第一个目标依赖于它们时才会构建。

So your makefile will look something like this.

所以你的makefile看起来像这样。

a3a.exe : a3driver.obj 
    link /out:a3a.exe a3driver.obj

a3driver.obj : a3driver.cpp
    cc a3driver.cpp

回答by Jér?me Pouiller

I suggest (note that the indent is a TAB):

我建议(注意缩进是制表符):

tool: tool.o file1.o file2.o
    $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

or

或者

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o

The latter suggestion is slightly better since it reuses GNU Make implicit rules. However, in order to work, a source file must have the same name as the final executable (i.e.: tool.cand tool).

后一个建议稍微好一些,因为它重用了 GNU Make 隐式规则。但是,为了工作,源文件必须与最终可执行文件具有相同的名称(即:tool.ctool)。

Notice, it is not necessary to declare sources. Intermediate object files are generated using implicit rule. Consequently, this Makefilework for C and C++ (and also for Fortran, etc...).

请注意,没有必要声明来源。中间目标文件是使用隐式规则生成的。因此,这Makefile适用于 C 和 C++(也适用于 Fortran 等)。

Also notice, by default, Makefile use $(CC)as the linker. $(CC)does not work for linking C++ object files. We modify LINK.oonly because of that. If you want to compile C code, you don't have to force the LINK.ovalue.

另请注意,默认情况下,Makefile$(CC)用作链接器。$(CC)不适用于链接 C++ 对象文件。我们LINK.o只是因为这个而修改。如果要编译 C 代码,则不必强制该LINK.o值。

Sure, you can also add your compilation flags with variable CFLAGSand add your libraries in LDLIBS. For example:

当然,您还可以使用变量CFLAGS添加编译标志并将库添加到LDLIBS. 例如:

CFLAGS = -Wall
LDLIBS = -lm

One side note: if you have to use external libraries, I suggest to use pkg-configin order to correctly set CFLAGSand LDLIBS:

一方面说明:如果您必须使用外部库,我建议使用 pkg-config以便正确设置CFLAGSLDLIBS

CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)

The attentive reader will notice that this Makefiledoes not rebuild properly if one header is changed. Add these lines to fix the problem:

细心的读者会注意到,Makefile如果更改了一个标题,这将无法正确重建。添加这些行以解决问题:

override CPPFLAGS += -MMD
include $(wildcard *.d)

-MMDallows to build .d files that contains Makefile fragments about headers dependencies. The second line just uses them.

-MMD允许构建包含关于头文件依赖的 Makefile 片段的 .d 文件。第二行只是使用它们。

For sure, a well written Makefile should also include cleanand distcleanrules:

当然,一个写得很好的 Makefile 还应该包括cleandistclean规则:

clean:
    $(RM) *.o *.d

distclean: clean
    $(RM) tool

Notice, $(RM)is the equivalent of rm -f, but it is a good practice to not call rmdirectly.

请注意,$(RM)相当于rm -f,但最好不要rm直接调用。

The allrule is also appreciated. In order to work, it should be the first rule of your file:

all规则也受到赞赏。为了工作,它应该是你的文件的第一条规则:

all: tool

You may also add an installrule:

您还可以添加install规则:

PREFIX = /usr/local
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin

DESTDIRis empty by default. The user can set it to install your program at an alternative system (mandatory for cross-compilation process). Package maintainers for multiple distribution may also change PREFIXin order to install your package in /usr.

DESTDIR默认为空。用户可以将其设置为在替代系统上安装您的程序(对于交叉编译过程是必需的)。多个发行版的包维护者也可能会更改PREFIX,以便将您的包安装到/usr.

One final word: Do not place source files in sub-directories. If you really want to do that, keep this Makefilein the root directory and use full paths to identify your files (i.e. subdir/file.o).

最后一句话:不要将源文件放在子目录中。如果您真的想这样做,请将其保存Makefile在根目录中并使用完整路径来标识您的文件(即subdir/file.o)。

So to summarise, your full Makefile should look like:

总而言之,您的完整 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

回答by VectorVortec

I used friedmud's answer. I looked into this for a while, and it seems to be a good way to get started. This solution also has a well defined method of adding compiler flags. I answered again, because I made changes to make it work in my environment, Ubuntu and g++. More working examples are the best teacher, sometimes.

我使用了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

Makefiles seem to be very complex. I was using one, but it was generating an error related to not linking in g++ libraries. This configuration solved that problem.

Makefile 似乎非常复杂。我正在使用一个,但它产生了一个与未链接 g++ 库相关的错误。这个配置解决了这个问题。