大数据最全Makefile详细教程_makefile教程 csdn(1),大数据开发开发手册

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

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

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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)

all:
ifdef foo
echo “foo is defined”
endif
ifndef bar
echo “but bar is not”
endif


#### `$(makeflags)`


此示例向您展示如何使用 `findstring` 和 `MAKEFLAGS` 测试 make 标志。 使用 `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 ( s p a c e ) , (space), (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 := ( p a t s u b s t (patsubst %.o,%.c, (patsubst(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` 设置为列表中的每个单词,并为每个单词扩展文本。  
 这会在每个单词后附加一个感叹号:



foo := who are you

For each “word” in foo, output that same word with an exclamation after

bar := ( f o r e a c h w r d , (foreach wrd, (foreachwrd,(foo),$(wrd)!)

all:
# Output is “who! are! you!”
@echo $(bar)


#### if函数


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



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

all:
@echo $(foo)
@echo $(bar)


#### 调用函数


Make 支持创建基本函数。 您仅通过创建一个变量来“定义”该函数,但使用参数 `$(0)`、`$(1)` 等。然后您使用特殊调用函数调用该函数。 语法是 `$(call variable,param,param)`。 `$(0)` 是变量,而 `$(1)、$(2)` 等是参数。



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 - 这会调用 shell,但它会用空格替换换行符!



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


### 其他特性


#### include Makefile


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



include filenames…


当您使用编译器标志(如 `-M`)基于源代码创建 `Makefile` 时,这尤其有用。 例如,如果某些 `c` 文件包含头文件,则该头文件将添加到由 `gcc` 编写的 `Makefile` 中。


#### vpath 指令


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



vpath %.h …/headers …/other-directory

some_binary: …/headers blah.h
touch some_binary

…/headers:
mkdir …/headers

blah.h:
touch …/headers/blah.h

clean:
rm -rf …/headers
rm -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` 仍将运行。 从技术上讲,我应该在每个带有 `all` 或 `clean` 的示例中都使用它,但我没有保持示例干净。 此外,“phony”目标的名称通常很少是文件名,实际上许多人会跳过这一点。



some_file:
touch some_file
touch clean

.PHONY: clean
clean:
rm -f some_file
rm -f clean


#### .delete\_on\_error


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



.DELETE_ON_ERROR:
all: one two

one:
touch one
false

two:
touch two
false


### 生成文件


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


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



TARGET_EXEC := final_program

BUILD_DIR := ./build
SRC_DIRS := ./src

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

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

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

h some_file
touch clean

.PHONY: clean
clean:
rm -f some_file
rm -f clean


#### .delete\_on\_error


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



.DELETE_ON_ERROR:
all: one two

one:
touch one
false

two:
touch two
false


### 生成文件


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


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



TARGET_EXEC := final_program

BUILD_DIR := ./build
SRC_DIRS := ./src

[外链图片转存中…(img-ABEy1KR6-1715750334548)]
[外链图片转存中…(img-MhMB3mOQ-1715750334548)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

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

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值