目录

入门

为什么存在 Makefile?

Make 有哪些替代方案?

运行示例

生成文件语法

初学者示例

变量

目标

全部目标

多个目标

自动变量和通配符

* 通配符

% 通配符

自动变量

花式规则

静态模式规则

静态模式规则和过滤器

隐含规则

模式规则

双冒号规则

命令和执行

命令回显/静音

命令执行

默认外壳

误差处理-k,-i以及-

中断或杀死 make

递归使用make

使用导出进行递归生成

要提出的论点

变量

风味和修饰

命令行参数和覆盖

命令列表和定义

特定于目标的变量

特定于模式的变量

Makefile 的条件部分

有条件的 if/else

检查变量是否为空

检查变量是否已定义

$(makeflags)

职能

第一功能

字符串替换

foreach 函数

如果函数

调用函数

外壳函数

其他特性

包含 Makefile

vpath 指令

多线

.PHONY

.delete_on_error

Makefile 手册


我编写本指南是因为我永远无法完全围绕 Makefile 进行思考。他们似乎充斥着潜规则和深奥的符号,问简单的问题也得不到简单的答案。为了解决这个问题,我坐了几个周末,阅读了所有关于 Makefile 的内容。我已将最关键的知识浓缩到本指南中。每个主题都有一个简短的描述和一个可以自己运行的自包含示例。

如果您主要了解 Make,请考虑查看 Makefile Cookbook,它有一个适用于中型项目的模板,其中对 Makefile 的每个部分正在做什么有充分的评论。

祝你好运,我希望你能够杀死令人困惑的 Makefile 世界!

入门

为什么存在 Makefile?

Makefile 用于帮助决定大型程序的哪些部分需要重新编译。在绝大多数情况下,编译 C 或 C++ 文件。其他语言通常有自己的工具,其用途与 Make 类似。当您需要根据已更改的文件执行一系列指令时,它也可以在程序之外使用。本教程将重点介绍 C/C++ 编译用例。

这是您可以使用 Make 构建的示例依赖关系图。如果任何文件的依赖项发生更改,则该文件将被重新编译:

Make 有哪些替代方案?

流行的 C/C++ 替代构建系统是SCons、CMake、Bazel和Ninja。一些代码编辑器(如Microsoft Visual Studio)有自己的内置构建工具。对于 Java,有Ant、Maven和Gradle。Go 和 Rust 等其他语言有自己的构建工具。

Python、Ruby 和 Javascript 等解释性语言不需要与 Makefile 类似的语言。Makefiles 的目标是根据已更改的文件来编译需要编译的任何文件。但是当解释语言中的文件发生变化时,不需要重新编译。当程序运行时,将使用文件的最新版本。

运行示例

要运行这些示例,您需要一个终端并安装“make”。对于每个示例,将内容放在名为 的文件中Makefile,然后在该目录中运行命令make。让我们从最简单的 Makefile 开始:

hello:echo "hello world"

以下是运行上述示例的输出:

$ make
echo "hello world"
hello world

就是这样!如果您有点困惑,这里有一个介绍这些步骤的视频,同时描述了 Makefile 的基本结构。

生成文件语法

Makefile 由一组规则组成。规则通常如下所示:

targets: prerequisitescommandcommandcommand
  • 目标是文件名,用空格隔开。通常,每个规则只有一个。
  • 命令是一系列通常用于使目标(多个)步骤。这些需要以制表符开头,而不是空格。
  • 先决条件也文件名,用空格隔开。在运行目标命令之前,这些文件需要存在。这些也称为依赖项

初学者示例

以下 Makefile 具有三个独立的规则。当您make blah在终端中运行时,它将构建一个blah以一系列步骤调用的程序:

  • Makeblah作为目标给出,所以它首先搜索这个目标
  • blah需要blah.o,所以搜索blah.o目标
  • blah.o需要blah.c,所以搜索blah.c目标
  • blah.c没有依赖关系,所以echo命令运行
  • cc -c然后运行该命令,因为所有blah.o依赖项都已完成
  • topcc命令运行,因为所有的blah依赖都完成了
  • 就是这样:blah是一个编译好的c程序
blah: blah.occ blah.o -o blah # Runs thirdblah.o: blah.ccc -c blah.c -o blah.o # Runs secondblah.c:echo "int main() { return 0; }" > blah.c # Runs first

这个 makefile 有一个单一的目标,叫做some_file. 默认目标是第一个目标,因此在这种情况下some_file将运行。

some_file:echo "This line will always print"

这个文件some_file第一次会make ,第二次会提示已经制作完成,导致make: 'some_file' is up to date.

some_file:echo "This line will only print once"touch some_file

在这里,目标some_file“取决于” other_file。当我们运行时make,默认目标(some_file,因为它是第一个)将被调用。它将首先查看其依赖项列表,如果其中任何一个较旧,它将首先运行这些依赖项的目标,然后自行运行。第二次运行时,两个目标都不会运行,因为两个目标都存在。

some_file: other_fileecho "This will run second, because it depends on other_file"touch some_fileother_file:echo "This will run first"touch other_file

这将始终运行两个目标,因为some_file取决于从未创建的 other_file。

some_file: other_filetouch some_fileother_file:echo "nothing"

clean经常用作去除其他目标输出的目标,但在make.

some_file: touch some_fileclean:rm -f some_file

变量

变量只能是字符串。这是使用它们的示例:

files = file1 file2
some_file: $(files)echo "Look at this variable: " $(files)touch some_filefile1:touch file1
file2:touch file2clean:rm -f file1 file2 some_file

使用 ${} 或 $() 引用变量

x = dudeall:echo $(x)echo ${x}# Bad practice, but worksecho $x

目标

全部目标

制作多个目标并且您希望所有目标都运行?做一个all目标。

all: one two threeone:touch one
two:touch two
three:touch threeclean:rm -f one two three

多个目标

当规则有多个目标时,将为每个目标运行的命令
$@是一个包含目标名称的自动变量。

all: f1.o f2.of1.o f2.o:echo $@
# Equivalent to:
# f1.o
#     echo $@
# f2.o
#     echo $@

自动变量和通配符

* 通配符

无论*%被称为在制作通配符,但是他们的意思是完全不同的事情。*在您的文件系统中搜索匹配的文件名。我建议你总是把它包装在wildcard函数中,否则你可能会陷入下面描述的常见陷阱。这奇怪地没有帮助,我发现它比有用更令人困惑。

# Print out file information about every .c file
print: $(wildcard *.c)ls -la  $?

*可以在目标、先决条件或wildcard函数中使用。

危险:*不能直接用在一个变量定义中

危险:*不匹配任何文件时,保持原样(除非在wildcard函数中运行)

thing_wrong := *.o # Don't do this! '*' will not get expanded
thing_right := $(wildcard *.o)all: one two three four# Fails, because $(thing_wrong) is the string "*.o"
one: $(thing_wrong)# Stays as *.o if there are no files that match this pattern :(
two: *.o # Works as you would expect! In this case, it does nothing.
three: $(thing_right)# Same as rule three
four: $(wildcard *.o)

% 通配符

% 确实很有用,但由于可以使用的情况多种多样,因此有些令人困惑。

  • 在“匹配”模式下使用时,它匹配字符串中的一个或多个字符。这种匹配称为词干。
  • 在“替换”模式下使用时,它采用匹配的词干并将其替换为字符串。
  • % 最常用于规则定义和某些特定功能中。

有关使用它的示例,请参阅以下部分:

  • 静态模式规则
  • 模式规则
  • 字符串替换
  • vpath 指令

自动变量

有很多自动变量,但通常只显示几个:

hey: one two# Outputs "hey", since this is the first targetecho $@# Outputs all prerequisites newer than the targetecho $?# Outputs all prerequisitesecho $^touch heyone:touch onetwo:touch twoclean:rm -f hey one two

花式规则

静态模式规则

make 喜欢 c 编译。每次它表达爱意时,事情都会变得混乱。这是一种称为静态模式的新型规则的语法:

targets ...: target-pattern: prereq-patterns ...commands

本质是给定的目标与目标模式匹配(通过%通配符)。匹配的任何内容都称为词干。然后将词干替换到 prereq-pattern 中,以生成目标的 prereq。

一个典型的用例是将.c文件编译成.o文件。这是手动方式

objects = foo.o bar.o all.o
all: $(objects)# These files compile via implicit rules
foo.o: foo.c
bar.o: bar.c
all.o: all.call.c:echo "int main() { return 0; }" > all.c%.c:touch $@clean:rm -f *.c *.o all

这是使用静态模式规则的更有效的方法

objects = foo.o bar.o all.o
all: $(objects)# These files compile via implicit rules
# Syntax - targets ...: target-pattern: prereq-patterns ...
# In the case of the first target, foo.o, the target-pattern matches foo.o and sets the "stem" to be "foo".
# It then replaces the '%' in prereq-patterns with that stem
$(objects): %.o: %.call.c:echo "int main() { return 0; }" > all.c%.c:touch $@clean:rm -f *.c *.o all

静态模式规则和过滤器

虽然我稍后会介绍函数,但我会预测你可以用它们做什么。该filter函数可用于静态模式规则以匹配正确的文件。在这个例子中,我组成了.raw.result扩展。

obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.call: $(obj_files)$(filter %.o,$(obj_files)): %.o: %.cecho "target: $@ prereq: $<"
$(filter %.result,$(obj_files)): %.result: %.rawecho "target: $@ prereq: $<" %.c %.raw:touch $@clean:rm -f $(src_files)

隐含规则

make 中最令人困惑的部分可能是创建的魔法规则和变量。以下是隐含规则的列表:

  • 编译 C 程序:使用以下形式的命令n.o自动n.c生成$(CC) -c $(CPPFLAGS) $(CFLAGS)
  • 编译 C++ 程序:n.o由以下形式的命令自动生成n.ccn.cpp使用$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)
  • 链接单个目标文件:通过运行命令n自动生成n.o$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)

因此,隐式规则使用的重要变量是:

  • CC:编译C程序的程序;默认抄送
  • CXX:编译C++程序的程序;默认 G++
  • CFLAGS: 提供给 C 编译器的额外标志
  • CXXFLAGS: 提供给 C++ 编译器的额外标志
  • CPPFLAGS: 提供给 C 预处理器的额外标志
  • LDFLAGS: 当编译器应该调用链接器时提供给编译器的额外标志
CC = gcc # Flag for implicit rules
CFLAGS = -g # Flag for implicit rules. Turn on debug info# Implicit rule #1: blah is built via the C linker implicit rule
# Implicit rule #2: blah.o is built via the C compilation implicit rule, because blah.c exists
blah: blah.oblah.c:echo "int main() { return 0; }" > blah.cclean:rm -f blah*

模式规则

经常使用模式规则,但很容易混淆。您可以从以下两种方式看待它们:

  • 一种定义自己的隐式规则的方法
  • 一种更简单的静态模式规则形式

让我们先从一个例子开始:

# Define a pattern rule that compiles every .c file into a .o file
%.o : %.c$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

模式规则在目标中包含一个“%”。这个 '%' 匹配任何非空字符串,其他字符匹配它们自己。模式规则的先决条件中的“%”代表与目标中的“%”匹配的相同词干。

这是另一个例子:

# Define a pattern rule that has no pattern in the prerequisites.
# This just creates empty .c files when needed.
%.c:touch $@

双冒号规则

双冒号规则很少使用,但允许为同一目标定义多个规则。如果这些是单冒号,则会打印警告,并且只会运行第二组命令。

all: blahblah::echo "hello"blah::echo "hello again"

命令和执行

命令回显/静音

@在命令之前添加一个命令以阻止它被打印
您还可以运行 make with在每行之前 -s添加一个@

all: @echo "This make line will not be printed"echo "But this will"

命令执行

每个命令都在一个新的 shell 中运行(或者至少效果是这样)

all: cd ..# The cd above does not affect this line, because each command is effectively run in a new shellecho `pwd`# This cd command affects the next because they are on the same linecd ..;echo `pwd`# Same as abovecd ..; \echo `pwd`

默认外壳

默认外壳是/bin/sh. 您可以通过更改变量 SHELL 来更改此设置:

SHELL=/bin/bashcool:echo "Hello from bash"

误差处理-k-i以及-

-k在运行 make 时添加以在出现错误时继续运行。如果您想一次查看 Make 的所有错误,这将很有帮助。在命令之前
添加一个-以抑制错误
添加-i使每个命令都发生这种情况。

one:# This error will be printed but ignored, and make will continue to run-falsetouch one

中断或杀死 make

仅注意:如果您ctrl+c制作,它将删除它刚刚制作的较新的目标。

递归使用make

要递归调用 makefile,请使用特殊的$(MAKE)而不是make因为它会为您传递 make 标志并且本身不会受到它们的影响。

new_contents = "hello:\n\ttouch inside_file"
all:mkdir -p subdirprintf $(new_contents) | sed -e 's/^ //' > subdir/makefilecd subdir && $(MAKE)clean:rm -rf subdir

使用导出进行递归生成

export 指令接受一个变量并使其可被 sub-make 命令访问。在这个例子中,cooly被导出以便 subdir 中的 makefile 可以使用它。

注意:export 与 sh 的语法相同,但它们不相关(尽管功能相似)

new_contents = "hello:\n\\techo \$$(cooly)"all:mkdir -p subdirecho $(new_contents) | sed -e 's/^ //' > subdir/makefile@echo "---MAKEFILE CONTENTS---"@cd subdir && cat makefile@echo "---END MAKEFILE CONTENTS---"cd subdir && $(MAKE)# Note that variables and exports. They are set/affected globally.
cooly = "The subdirectory can see me!"
export cooly
# This would nullify the line above: unexport coolyclean:rm -rf subdir

您需要导出变量才能让它们在 shell 中运行。

one=this will only work locally
export two=we can run subcommands with thisall: @echo $(one)@echo $$one@echo $(two)@echo $$two

.EXPORT_ALL_VARIABLES 为您导出所有变量。

.EXPORT_ALL_VARIABLES:
new_contents = "hello:\n\techo \$$(cooly)"cooly = "The subdirectory can see me!"
# This would nullify the line above: unexport coolyall:mkdir -p subdirecho $(new_contents) | sed -e 's/^ //' > subdir/makefile@echo "---MAKEFILE CONTENTS---"@cd subdir && cat makefile@echo "---END MAKEFILE CONTENTS---"cd subdir && $(MAKE)clean:rm -rf subdir

要提出的论点

有一个很好的选项列表可以从 make 运行。退房--dry-run--touch--old-file

您可以创建多个目标,即make clean run test运行clean目标,然后run,然后test

变量

风味和修饰

有两种类型的变量:

  • 递归(使用=) -仅查找时,该命令的变量使用,而不是当它的定义
  • 简单扩展(使用:=) - 就像普通的命令式编程一样 - 只有到目前为止定义的那些才会被扩展
# Recursive variable. This will print "later" below
one = one ${later_variable}
# Simply expanded variable. This will not print "later" below
two := two ${later_variable}later_variable = laterall: echo $(one)echo $(two)

简单地扩展(使用:=)允许您附加到变量。递归定义会产生无限循环错误。

one = hello
# one gets defined as a simply expanded variable (:=) and thus can handle appending
one := ${one} thereall: echo $(one)

?= 仅设置尚未设置的变量

one = hello
one ?= will not be set
two ?= will be setall: echo $(one)echo $(two)

行尾的空格不会被删除,但开头的空格会被删除。要使用单个空格创建变量,请使用$(nullstring)

with_spaces = hello   # with_spaces has many spaces after "hello"
after = $(with_spaces)therenullstring =
space = $(nullstring) # Make a variable with a single space.all: echo "$(after)"echo start"$(space)"end

一个未定义的变量实际上是一个空字符串!

all: # Undefined variables are just empty strings!echo $(nowhere)

使用+=附加

foo := start
foo += moreall: echo $(foo)

字符串替换也是一种非常常见且有用的修改变量的方法。另请查看文本函数和文件名函数。

命令行参数和覆盖

您可以使用 覆盖来自命令行的变量override。在这里,我们运行 makemake option_one=hi

# Overrides command line arguments
override option_one = did_override
# Does not override command line arguments
option_two = not_override
all: echo $(option_one)echo $(option_two)

命令列表和定义

“定义”实际上只是一个命令列表。它与成为一个函数无关。请注意,这与在命令之间使用分号有点不同,因为正如预期的那样,每个命令都在单独的 shell 中运行。

one = export blah="I was set!"; echo $$blahdefine two
export blah=set
echo $$blah
endef# One and two are different.all: @echo "This prints 'I was set'"@$(one)@echo "This does not print 'I was set' because each command runs in a separate shell"@$(two)

特定于目标的变量

可以为特定目标分配变量

all: one = coolall: echo one is defined: $(one)other:echo one is nothing: $(one)

特定于模式的变量

您可以为特定目标模式分配变量

%.c: one = coolblah.c: echo one is defined: $(one)other:echo one is nothing: $(one)

Makefile 的条件部分

有条件的 if/else

foo = okall:
ifeq ($(foo), ok)echo "foo equals ok"
elseecho "nope"
endif

检查变量是否为空

nullstring =
foo = $(nullstring) # end of line; there is a space hereall:
ifeq ($(strip $(foo)),)echo "foo is empty after being stripped"
endif
ifeq ($(nullstring),)echo "nullstring doesn't even have spaces"
endif

检查变量是否已定义

ifdef 不扩展变量引用;它只是查看是否定义了某些内容

bar =
foo = $(bar)all:
ifdef fooecho "foo is defined"
endif
ifdef barecho "but bar is not"
endif

$(makeflags)

此示例向您展示如何使用findstring和测试 make 标志MAKEFLAGS。运行此示例make -i以查看它打印出 echo 语句。

bar =
foo = $(bar)all:
# Search for the "-i" flag. MAKEFLAGS is just a list of single characters, one per flag. So look for "i" in this case.
ifneq (,$(findstring i, $(MAKEFLAGS)))echo "i was passed to MAKEFLAGS"
endif

职能

第一功能

函数主要用于文本处理。使用$(fn, arguments)或调用函数${fn, arguments}。您可以使用call内置函数创建自己的。Make 有相当数量的内置函数。

bar := ${subst not, totally, "I am not superman"}
all: @echo $(bar)

如果要替换空格或逗号,请使用变量

comma := ,
empty:=
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space),$(comma),$(foo))all: @echo $(bar)

不要在第一个之后的参数中包含空格。这将被视为字符串的一部分。

comma := ,
empty:=
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space), $(comma) , $(foo))all: # Output is ", a , b , c". Notice the spaces introduced@echo $(bar)

字符串替换

$(patsubst pattern,replacement,text) 执行以下操作:

“在匹配模式的文本中查找空格分隔的单词并用替换替换它们。这里模式可能包含一个作为通配符的 '%',匹配单词中任意数量的任何字符。如果替换也包含一个 '%', '%' 被与模式中的 '%' 匹配的文本替换。只有模式和替换中的第一个 '%' 被这样处理;任何后续的 '%' 不变。” ( GNU 文档)

替换引用$(text:pattern=replacement)是对此的简写。

还有另一种仅替换后缀的简写:$(text:suffix=replacement)%这里没有使用通配符。

注意:不要为此速记添加额外的空格。它将被视为搜索或替换术语。

foo := a.o b.o l.a c.o
one := $(patsubst %.o,%.c,$(foo))
# This is a shorthand for the above
two := $(foo:%.o=%.c)
# This is the suffix-only shorthand, and is also equivalent to the above.
three := $(foo:.o=.c)all:echo $(one)echo $(two)echo $(three)

foreach 函数

在foreach函数如下:$(foreach var,list,text)。它将一个单词列表(用空格分隔)转换为另一个。var设置为列表中的每个单词,并text为每个单词展开。
这在每个单词后附加一个感叹号:

foo := who are you
# For each "word" in foo, output that same word with an exclamation after
bar := $(foreach wrd,$(foo),$(wrd)!)all:# Output is "who! are! you!"@echo $(bar)

如果函数

if检查第一个参数是否为非空。如果是,则运行第二个参数,否则运行第三个参数。

foo := $(if this-is-not-empty,then!,else!)
empty :=
bar := $(if $(empty),then!,else!)all:@echo $(foo)@echo $(bar)

调用函数

Make 支持创建基本函数。您只需通过创建一个变量“定义”的功能,但使用的参数$(0)$(1)等你再调用与特殊功能call的功能。语法是$(call variable,param,param)$(0)是可变的,而$(1)$(2)等都是PARAMS。

sweet_new_fn = Variable Name: $(0) First: $(1) Second: $(2) Empty Variable: $(3)all:# Outputs "Variable Name: sweet_new_fn First: go Second: tigers Empty Variable:"@echo $(call sweet_new_fn, go, tigers)

外壳函数

shell - 这会调用 shell,但它会用空格替换换行符!

all: @echo $(shell ls -la) # Very ugly because the newlines are gone!

其他特性

包含 Makefile

include 指令告诉 make 读取一个或多个其他 makefile。它是 makefile makefile 中的一行,如下所示:

include filenames...

当您使用诸如-M基于源创建 Makefile 之类的编译器标志时,这特别有用。例如,如果某些 c 文件包含一个头文件,那么该头文件将被添加到由 gcc 编写的 Makefile 中。我在Makefile Cookbook 中对此进行了更多讨论

vpath 指令

使用 vpath 指定某些先决条件存在的位置。格式vpath <pattern> <directories, space/colon separated>
<pattern>可以有一个%,它匹配任何零个或多个字符。
您也可以使用变量 VPATH 全局执行此操作

vpath %.h ../headers ../other-directorysome_binary: ../headers blah.htouch some_binary../headers:mkdir ../headersblah.h:touch ../headers/blah.hclean:rm -rf ../headersrm -f some_binary

多线

当命令太长时,反斜杠(“\”)字符使我们能够使用多行

some_file: echo This line is too long, so \it is broken up into multiple lines

.PHONY

添加.PHONY到目标将防止 make 将虚假目标与文件名混淆。在这个例子中,如果文件clean被创建,make clean 仍然会运行。.PHONY很好用,但为了简单起见,我将在其余示例中跳过它。

some_file:touch some_filetouch clean.PHONY: clean
clean:rm -f some_filerm -f clean

.delete_on_error

如果命令返回非零退出状态,make 工具将停止运行规则(并将传播回先决条件)。
DELETE_ON_ERROR如果规则以这种方式失败,将删除规则的目标。所有目标都会发生这种情况,而不仅仅是像 PHONY 这样的之前的目标。始终使用它是个好主意,即使 make 不是出于历史原因。

.DELETE_ON_ERROR:
all: one twoone:touch onefalsetwo:touch twofalse

Makefile 手册

让我们来看一个非常有趣的 Make 示例,它适用于中型项目。

这个 makefile 的巧妙之处在于它会自动为您确定依赖项。您所要做的就是将 C/C++ 文件放在src/文件夹中。

# Thanks to Job Vranish (https://spin.atomicobject.com/2016/08/26/makefile-c-projects/)
TARGET_EXEC := final_programBUILD_DIR := ./build
SRC_DIRS := ./src# Find all the C and C++ files we want to compile
# Note the single quotes around the * expressions. Make will incorrectly expand these otherwise.
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')# String substitution for every C/C++ file.
# As an example, hello.cpp turns into ./build/hello.cpp.o
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)# String substitution (suffix version without %).
# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
DEPS := $(OBJS:.o=.d)# Every folder in ./src will need to be passed to GCC so that it can find header files
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))# The -MMD and -MP flags together generate Makefiles for us!
# These files will have .d instead of .o as the output.
CPPFLAGS := $(INC_FLAGS) -MMD -MP# The final build step.
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)$(CC) $(OBJS) -o $@ $(LDFLAGS)# Build step for C source
$(BUILD_DIR)/%.c.o: %.cmkdir -p $(dir $@)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@# Build step for C++ source
$(BUILD_DIR)/%.cpp.o: %.cppmkdir -p $(dir $@)$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@.PHONY: clean
clean:rm -r $(BUILD_DIR)# Include the .d makefiles. The - at the front suppresses the errors of missing
# Makefiles. Initially, all the .d files will be missing, and we don't want those
# errors to show up.
-include $(DEPS)

参考链接: 
Makefile Tutorial By Example

https://github.com/theicfire/makefiletutorial

Makefile入门: 用最美味的例子相关推荐

  1. MakeFile入门详解

          makefile很重要 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和profession ...

  2. 网页制作表单代码java_JSP动态网页入门:表单输入例子

    我们将创建一个web页面,它有一个输入表单,用户可以输入一个股票代号以获得出当前股票价格(有20分钟延迟).如果输入有误,则显示错误提示页面. quote.jsp 首先,用以下代码创建quote.js ...

  3. Linux加减程序编写,Makefile 入门(加减乘除实现)

    Makefile 入门(加减乘除实现) 准备 使用任意Linux发行版即可,本文使用WSL Ubuntu. 开始之前,需要安装必要的工具: sudo apt install make g++ 开始 1 ...

  4. Makefile入门一、helloworld

    Makefile入门一.helloworld 1.了解gcc从源码到可执行文件的步骤 2.Makefile的helloworld 3.提到Makefile不得不了解gcc命令 1.了解gcc从源码到可 ...

  5. 浅显易懂 Makefile 入门 (10)— 嵌套执行 make、export 的使用

    1. 嵌套执行 make 在一个大的工程文件中,不同的文件按照功能被划分到不同的模块中,每个模块可能都会有自己的编译顺序和规则,如果在一个 Makefile 文件中描述所有模块的编译规则,就会很乱,执 ...

  6. 浅显易懂 Makefile 入门 (06)— 文件名操作函数(dir、notdir、suffix、basename、addsuffix、addperfix、join、wildcard)

    编写 Makefile 的时候,很多情况下需要对文件名进行操作.例如获取文件的路径,去除文件的路径,取出文件前缀或后缀等等. 注意:下面的每个函数的参数字符串都会被当作或是一个系列的文件名来看待. 1 ...

  7. 浅显易懂 Makefile 入门 (02)— 普通变量和自动变量定义、使用($@、$^、$< 作用)、变量覆盖 override、变量的来源 origin

    1. 变量的定义 Makefile 文件中定义变量的基本语法如下: 变量的名称=值列表 变量的名称可以由大小写字母.阿拉伯数字和下划线构成.等号左右的空白符没有明确的要求,因为在执行 make 的时候 ...

  8. 浅显易懂 Makefile 入门 (01)— 什么是Makefile、为什么要用Makefile、Makefile规则、Makefile流程如何实现增量编译

    1. 什么是 Makefile Makefile 文件描述了 Linux 系统下 C/C++ 工程的编译规则,它用来自动化编译 C/C++ 项目.一旦写编写好 Makefile 文件,只需要一个 ma ...

  9. Linux 驱动开发之内核模块开发 (二)—— 内核模块编译 Makefile 入门

    一.模块的编译        我们在前面内核编译中驱动移植那块,讲到驱动编译分为静态编译和动态编译:静态编译即为将驱动直接编译进内核,动态编译即为将驱动编译成模块. 而动态编译又分为两种: a -- ...

最新文章

  1. Py中re.sub学习【转载】
  2. 时间自适应卷积:比自注意力更快的特征提取器
  3. 微积分31--微分学在几何上的应用
  4. SVM基本思想及入门学习(转载+自己解释为什么minL(w)变成minmaxL(a,w))
  5. 从Java连接到Cassandra
  6. android emulator培训,android emulator(未完)
  7. 深圳爱思拓大数据 网站_建议收藏!13个大数据学习网站很少人知道!附大数据自学资料分享...
  8. mysql中timestamp字段
  9. 4.4 数值分析: 局部收敛性
  10. Performance Test Framework (PTF)是压力测试框架(轩)
  11. 区块链重要基础知识2——哈希函数的原理以及应用于区块头部
  12. 如何保存在线文档html代码,html代码怎样保存和使用
  13. 无人驾驶5: 贝叶斯公式
  14. layui 改写 table 排序,填加中文按照拼音排序
  15. 使用Bind提供域名解析服务
  16. 有人对你说辛苦了要怎么回复
  17. 从Python爬虫到Spark预处理数据的真实需求[三]
  18. charles入门使用
  19. 对接淘宝天猫平台的第一篇
  20. 数据挖掘的R包和函数的集合

热门文章

  1. 1522 N 叉树的直径
  2. killall出现-bash: killall: command not found及使用方法
  3. 借贷宝人脸识别无需绑卡,需合作者共同冲刺
  4. Linux防火墙基本命令(干货)
  5. Spring cloud的分布式事务解决方案(txlcn)
  6. 毕业设计 STM32的智能WIFI视频人脸追踪监控系统
  7. C# 将字符串转换成二维码
  8. python千位分隔符_js为数字添加千位分隔符
  9. “甜心教主”回归 王心凌新专辑全是书名?
  10. spoof_call的分析与改进