Makefile详细教程_makefile教程 csdn(1)

blah: blah.o
cc blah.o -o blah # Runs third

blah.o: blah.c
cc -c blah.c -o blah.o # Runs second

Typically blah.c would already exist, but I want to limit any additional required files

blah.c:
echo “int main() { return 0; }” > blah.c # Runs first


如果删除 `blah.c`,所有三个目标都将重新运行。 如果您编辑它(并因此将时间戳更改为比 `blah.o` 更新),前两个目标将运行。 如果您运行 `touch blah.o`(并因此将时间戳更改为比 blah 更新),则只有第一个目标会运行。 如果您不做任何更改,则所有目标都不会运行。 试试看!


下一个示例没有做任何新的事情,但仍然是一个很好的附加示例。 它将始终运行两个目标,因为 `some_file` 依赖于永远不会创建的 `other_file`。



some_file: other_file
echo “This will always run, and runs second”
touch some_file

other_file:
echo “This will always run, and runs first”


### clean


clean 经常被用作去除其他目标输出的目标,但它不是 Make 中的特殊词。 您可以在此运行 `make` 和 `make clean` 来创建和删除 `some_file`。


请注意,`clean` 在这里做了两件新事情:


* 它不是第一个目标(默认),也不是先决条件。 这意味着它永远不会运行,除非您明确调用 `make clean`
* 它不是一个文件名。 如果你碰巧有一个名为 clean 的文件,这个目标将不会运行,这不是我们想要的。 有关如何解决此问题的信息,请参阅本教程后面的 `.PHONY`



some_file:
touch some_file

clean:
rm -f some_file


### 变量


变量只能是字符串。 您通常希望使用 `:=`,但 `=` 也可以。


下面是一个使用变量的例子:



files := file1 file2
some_file: $(files)
echo "Look at this variable: " $(files)
touch some_file

file1:
touch file1
file2:
touch file2

clean:
rm -f file1 file2 some_file


单引号或双引号对 Make 没有意义。 它们只是分配给变量的字符。 不过,引号对 `shell/bash` 很有用,您在 `printf` 等命令中需要它们。 在此示例中,这两个命令的行为相同:



a := one two # a is assigned to the string “one two”
b := ‘one two’ # Not recommended. b is assigned to the string “‘one two’”
all:
printf ‘$a’
printf $b


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



x := dude

all:
echo $(x)
echo ${x}

# Bad practice, but works
echo $x 

### Targets


#### all Targets


制定多个目标并希望所有目标都运行? 制定一个全部目标。 由于这是列出的第一条规则,如果在未指定目标的情况下调用 make,它将默认运行。



all: one two three

one:
touch one
two:
touch two
three:
touch three

clean:
rm -f one two three


#### 多个目标


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



all: f1.o f2.o

f1.o f2.o:
echo $@

Equivalent to:

f1.o:

echo f1.o

f2.o:

echo f2.o


### 自动变量和通配符


#### `*` 通配符


`*` 和 `%` 在 Make 中都被称为通配符,但它们的含义完全不同。 \* 在您的文件系统中搜索匹配的文件名。 我建议您始终将它包装在通配符函数中,否则您可能会陷入下面描述的常见陷阱。



Print out file information about every .c file

print: $(wildcard *.c)
ls -la $?


`*` 可以在目标、先决条件或通配符函数中使用。


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


**危险**:当`*`没有匹配到任何文件时,保持原样(除非在通配符函数中运行)



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)


#### `%` 通配符


`%` 确实很有用,但由于它可用于多种情况,所以有点令人困惑。


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


#### 自动变量


有很多自动变量,但通常只有少数几个会出现:



hey: one two
# Outputs “hey”, since this is the target name
echo $@

# Outputs all prerequisites newer than the target
echo $?

# Outputs all prerequisites
echo $^

touch hey

one:
touch one

two:
touch two

clean:
rm -f hey one two


### 花式规则


#### 隐式规则


每次让Makefile它表达爱意时,事情都会变得扑朔迷离。 也许 Make 最令人困惑的部分是制定的花式/自动规则。 调用这些“隐式”规则。 我个人不同意这个设计决定,也不推荐使用它们,但它们经常被使用,因此了解它们很有用。 这是隐式规则的列表:


* 编译 C 程序:`n.o` 是通过 $`(CC) -c $(CPPFLAGS) $(CFLAGS) $^ -o $@` 形式的命令从 `n.c` 自动生成的
* 编译 C++ 程序:`n.o` 是通过 `$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $^ -o $@` 形式的命令从 `n.cc` 或 `n.cpp` 自动生成的
* 链接单个目标文件:通过运行命令 `$(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@` 自动从 `n.o` 生成 `n`


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


* CC:用于编译C程序的程序; 默认抄送
* CXX:编译C++程序的程序; 默认 g++
* CFLAGS:提供给 C 编译器的额外标志
* CXXFLAGS:提供给 C++ 编译器的额外标志
* CPPFLAGS:提供给 C 预处理器的额外标志
* LDFLAGS:当编译器应该调用链接器时提供给编译器的额外标志


让我们看看我们现在如何构建一个 C 程序,而无需明确告诉 Make 如何进行编译:



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.o

blah.c:
echo “int main() { return 0; }” > blah.c

clean:
rm -f blah*


#### 静态模式规则


静态模式规则是另一种在 Makefile 中编写更少的方法,但我认为它更有用并且不那么“神奇”。 这是它们的语法:



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


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


一个典型的用例是将 .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.c

all.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: %.c

all.c:
echo “int main() { return 0; }” > all.c

%.c:
touch $@

clean:
rm -f *.c *.o all


#### 静态模式规则和过滤器


当我稍后介绍函数时,我将预示您可以使用它们做什么。 过滤器功能可用于静态模式规则以匹配正确的文件。 在这个例子中,我制作了 .raw 和 .result 扩展名。



obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.c

all: $(obj_files)

Note: PHONY is important here. Without it, implicit rules will try to build the executable “all”, since the prereqs are “.o” files.

.PHONY: all

Ex 1: .o files depend on .c files. Though we don’t actually make the .o file.

( f i l t e r (filter %.o, (filter(obj_files)): %.o: %.c
echo “target: $@ prereq: $<”

Ex 2: .result files depend on .raw files. Though we don’t actually make the .result file.

( f i l t e r (filter %.result, (filter(obj_files)): %.result: %.raw
echo “target: $@ prereq: $<”

%.c %.raw:
touch $@

clean:
rm -f $(src_files)


#### 模式规则


模式规则经常被使用但是很混乱。 您可以通过两种方式查看它们:


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


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



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: blah

blah::
echo “hello”

blah::
echo “hello again”


### 命令和执行


#### 命令回显/静音


在命令前添加 `@` 以阻止它被打印


您还可以使用 `-s` 运行 `make` 以在每行之前添加一个 `@`



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 shell
echo pwd

# This cd command affects the next because they are on the same line
cd ..;echo `pwd`

# Same as above
cd ..; \
echo `pwd`

#### 默认shell


默认 shell 是 `/bin/sh`。 您可以通过更改变量 `SHELL` 来更改此设置:



SHELL=/bin/bash

cool:
echo “Hello from bash”


#### 双美元符号


如果你想让一个字符串有一个美元符号,你可以使用 `$$`。 这是在 `bash` 或 `sh` 中使用 `shell` 变量的方法。


请注意下一个示例中 `Makefile` 变量和 `Shell` 变量之间的区别。



make_var = I am a make variable
all:
# Same as running “sh_var=‘I am a shell variable’; echo $sh_var” in the shell
sh_var=‘I am a shell variable’; echo $$sh_var

# Same as running "echo I am a amke variable" in the shell
echo $(make_var)

#### 使用 -k、-i 和 - 进行错误处理


在运行 make 时添加 `-k` 以在遇到错误时继续运行。 如果您想立即查看 Make 的所有错误,这将很有帮助。


在命令前添加 `-` 以抑制错误


添加 `-i` 以使每个命令都发生这种情况。



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


#### 中断或杀死 make


仅注意:如果您按 ctrl+c make,它将删除刚刚创建的较新目标。


#### make的递归使用


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



new_contents = “hello:\n\ttouch inside_file”
all:
mkdir -p subdir
printf $(new_contents) | sed -e ‘s/^ //’ > subdir/makefile
cd subdir && $(MAKE)

clean:
rm -rf subdir


#### 导出、环境和递归 make


当 Make 启动时,它会根据执行时设置的所有环境变量自动创建 Make 变量



Run this with “export shell_env_var=‘I am an environment variable’; make”

all:
# Print out the Shell variable
echo $$shell_env_var

# Print out the Make variable
echo $(shell_env_var)

导出指令采用一个变量并将其设置为所有配方中所有 `shell` 命令的环境:



shell_env_var=Shell env var, created inside of Make
export shell_env_var
all:
echo $(shell_env_var)
echo $$shell_env_var


因此,当您在 `make` 内部运行 `make` 命令时,您可以使用 `export` 指令使其可供子 `make` 命令访问。 在此示例中,导出了 `cooly`,以便 `subdir` 中的 `makefile` 可以使用它。



new_contents = “hello:\n\techo $$(cooly)”

all:
mkdir -p subdir
printf $(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 cooly

clean:
rm -rf subdir


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



one=this will only work locally
export two=we can run subcommands with this

all:
@echo $(one)
@echo KaTeX parse error: Can't use function '$' in math mode at position 12: 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 cooly

all:
mkdir -p subdir
printf $(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参数


有一个很好的[选项列表](https://bbs.csdn.net/topics/618545628)可以从 make 运行。 查看 `--dry-run`、`--touch`、`--old-file`。


您可以制定多个目标,即 make clean run test 运行 clean 目标,然后运行,然后测试。


### 变量2


#### 风格和修改


有两种类型的变量:


* 递归(使用 =) - 仅在使用命令时查找变量,而不是在定义时查找。
* 简单地扩展(使用 :=)——就像普通的命令式编程一样——只有到目前为止定义的那些才会被扩展



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 = later

all:
echo $(one)
echo $(two)


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



one = hello

one gets defined as a simply expanded variable (:=) and thus can handle appending

one := ${one} there

all:
echo $(one)


`?=` 仅在尚未设置变量时设置变量



one = hello
one ?= will not be set
two ?= will be set

all:
echo $(one)
echo $(two)


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



with_spaces = hello # with_spaces has many spaces after “hello”
after = $(with_spaces)there

nullstring =
space = $(nullstring) # Make a variable with a single space.

all:
echo " ( a f t e r ) " e c h o s t a r t " (after)" echo start" (after)"echostart"(space)"end


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



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


使用 `+=` 追加



foo := start
foo += more

all:
echo $(foo)


字符串替换也是一种非常常见且有用的修改变量的方法。


#### 命令行参数和覆盖


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



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)


#### 命令列表和定义


`define` 指令不是一个函数,尽管它可能看起来是这样。 我看到它很少使用,所以我不会详细介绍。


`define/endef` 只是创建一个分配给命令列表的变量。 请注意,这与在命令之间使用分号有点不同,因为正如预期的那样,每个命令都在单独的 `shell` 中运行。



one = export blah=“I was set!”; echo $$blah

define two
export blah=“I was set!”
echo $$blah
endef

all:
@echo “This prints ‘I was set’”
@ ( o n e ) @ e c h o " T h i s d o e s n o t p r i n t ′ I w a s s e t ′ b e c a u s e e a c h c o m m a n d r u n s i n a s e p a r a t e s h e l l " @ (one) @echo "This does not print 'I was set' because each command runs in a separate shell" @ (one)@echo"ThisdoesnotprintIwassetbecauseeachcommandrunsinaseparateshell"@(two)


#### 特定于目标的变量


可以为特定目标分配变量



all: one = cool

all:
echo one is defined: $(one)

other:
echo one is nothing: $(one)


#### 特定于模式的变量


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



%.c: one = cool

blah.c:
echo one is defined: $(one)

other:
echo one is nothing: $(one)


### Makefile 的条件部分


#### 条件if/else



foo = ok

all:
ifeq ($(foo), ok)
echo “foo equals ok”
else
echo “nope”
endif


#### 检查变量是否为空



nullstring =
foo = $(nullstring) # end of line; there is a space here

all:
ifeq ($(strip ( f o o ) ) , ) e c h o " f o o i s e m p t y a f t e r b e i n g s t r i p p e d " e n d i f i f e q ( (foo)),) echo "foo is empty after being stripped" endif ifeq ( (foo)),)echo"fooisemptyafterbeingstripped"endififeq((nullstring),)
echo “nullstring doesn’t even have spaces”
endif


#### 检查变量是否定义


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



bar =
foo = $(bar)

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

defined: $(one)

other:
echo one is nothing: $(one)


### Makefile 的条件部分


#### 条件if/else



foo = ok

all:
ifeq ($(foo), ok)
echo “foo equals ok”
else
echo “nope”
endif


#### 检查变量是否为空



nullstring =
foo = $(nullstring) # end of line; there is a space here

all:
ifeq ($(strip ( f o o ) ) , ) e c h o " f o o i s e m p t y a f t e r b e i n g s t r i p p e d " e n d i f i f e q ( (foo)),) echo "foo is empty after being stripped" endif ifeq ( (foo)),)echo"fooisemptyafterbeingstripped"endififeq((nullstring),)
echo “nullstring doesn’t even have spaces”
endif


#### 检查变量是否定义


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



bar =
foo = $(bar)

[外链图片转存中…(img-Tcp2fSMo-1714239143080)]
[外链图片转存中…(img-bBCYdidS-1714239143081)]
[外链图片转存中…(img-tkDk4vWx-1714239143081)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值