Makefile 是一种用于自动化构建的文件,它描述了一个软件项目的编译规则和依赖关系,并提供了一些工具来自动执行这些规则。
Makefile 的主要作用如下:
make
命令,就可以自动地重新编译修改过的源文件,并生成最终的可执行文件或库文件。综上所述,Makefile 是一个用于自动化构建的强大工具,它可以让编译、构建、打包等操作变得更加高效和可靠。
首先我们知道一个项目文件它包含许多.c文件,当程序运行时,它首先需要经过编译和链接生成可执行文件。
其中程序链接时,如果手动编译,需要敲很多命令,尤其文件大的的时候,这个时候我们可以在项目文件目录内执行make命令,makemingl可以自动的帮我们完成链接步骤。
但是make 在执行时,需要一个命名为 Makefile 的文件。这个文件告诉 make 以何种方式编译源代码和链接程序。执行make命令时,它会自动在当前目录下寻找一个叫“makefile”的文件然后去执行它,这个makefile文件就是我们要写的内容,当我们把makefile写好后,其内容就是告诉程序怎么执行。
基本语法:
target: dependencies
[tab] command
其中,“target”表示要生成的目标文件的名称,“dependencies”表示目标文件所依赖的其他文件,“command”是用于生成目标文件的命令。
注意:这里的[tab]
指的是缩进用tab键,而不能用空格,makefile里面格式要求严格。
规则:
在 Makefile 中,伪目标是一种特殊的目标,它不对应于实际的文件或命令,而是用于定义一些特殊的行为或控制流程。
通常,伪目标在 Makefile 中以一个 .PHONY 指示符声明。这样一来,当使用 make 命令时,make 将不会检查是否存在同名的文件,而仅仅按照规则执行相应的命令。
例如,以下是一个包含伪目标的 Makefile:
.PHONY: clean
clean:
rm -rf *.o
在上面的例子中,clean 是一个伪目标,当我们运行 make clean
命令时,Makefile 会执行 rm -rf *.o
命令,清除当前目录下所有的 .o
文件。由于 clean 不是一个真正的文件,因此需要使用 .PHONY 指示符显式地声明它是一个伪目标。
伪目标在 Makefile 中的应用非常广泛,例如用于清理临时文件、打包发布代码、运行测试等等操作,都可以通过建立相应的伪目标来简化命令行操作。
Makefile 变量是 Makefile 中用来保存值的标识符。它们可以包含任何文本,如文件名、目录名、命令行选项或其他任何字符串。
Makefile 变量使用 $ 符号进行引用,例如 $(VAR),其中 VAR 是变量的名称。在 Makefile 中,变量的值可以通过以下方式定义:
VAR = value
VAR := value
VAR += value
VAR ?= value
其中,直接赋值和条件赋值的区别在于,如果变量已经被定义,条件赋值将不会对其进行覆盖,而直接赋值则会对其进行覆盖。
延迟赋值和追加赋值的区别在于,延迟赋值会立即展开变量,而追加赋值会在变量后面添加新的内容。例如:
FOO = bar
BAZ := $(FOO) qux
QUX += xyzzy
在这个例子中,BAZ
的值为 bar qux
,QUX
的值为 xyzzy
。
这四种赋值方式的区别如下:
VAR = value
直接赋值将变量 VAR
的值设置为 value
。如果变量 VAR
已经存在,那么它将被覆盖。
VAR := value
延迟赋值允许在定义变量时立即展开所有变量引用。也就是说,在变量定义之后的任何地方引用 VAR
都会得到其值 value
,或者说永远是第一次定义的值,即使后面修改也不行,这个赋值方式通常用于保存命令输出等需要立即展开的值。
VAR += value
追加赋值将 value
添加到变量 VAR
的末尾。如果变量 VAR
不存在,则等同于直接赋值。如果变量 VAR
已经存在,则将 value
添加到其末尾,并且每次使用 +=
将会添加一个新的 value
。
4. 条件赋值:VAR ?= value
条件赋值只有在变量未被定义时才会设置变量的值。如果 VAR
已经定义,则不会进行任何操作。这种赋值方式通常用于默认值。
在 Makefile 中,函数的格式为:
$(function arguments)
其中,function
是函数名,而 arguments
则是被传递给函数的参数。多个参数之间用逗号 ,
分隔。
语法:$(shell command)
作用:执行 shell 命令,并返回其输出结果。
语法:$(subst from,to,text)
作用:将文本 text
中所有的 from
替换为 to
。
语法:$(patsubst pattern,replacement,text)
作用:按照模式 pattern
匹配文本 text
中的内容,并替换为 replacement
。
假设我们有一个文件列表,其中包含了一些以 .txt
结尾的文件名。现在我们想把这些文件名中的 .txt
去掉,可以使用 patsubst
函数来实现:
假设我们的文件列表为:
file1.txt file2.txt file3.doc file4.txt
我们可以使用以下命令将所有的 .txt
后缀去掉:
$(patsubst %.txt,%,$(FILES))
其中,%.txt
是模式,表示匹配任意以 .txt
结尾的字符串;%
是替换文本,表示将模式匹配到的部分替换为空字符串;$(FILES)
是需要进行替换的文本。
运行上述命令后,得到的输出为:
file1 file2 file3.doc file4
可以看到,所有的 .txt
后缀都被成功地去掉了。
基本格式:
$(foreach var,list,text)`
作用::将变量 var
依次设置为 list
中的每个元素,并将结果拼接成一个字符串 text
例如:
clean :
@echo "hello word"
.PHONY : clean
all :
@echo $b
@$(foreach var,$(c),echo $(var))
a = $(shell find . -name '*.c')
c := h e l l o w o r d
b = $(subst main,max,$(a))
在Makefile中,dir函数可以用于获取一个文件路径的目录部分。其语法如下:
$(dir names...)
其中,names是要处理的文件名列表,可以是一个或多个文件名,使用空格分隔。如果names中某个文件名带有路径部分,则dir函数将返回该文件名的目录部分;否则,dir函数将返回当前目录。
下面是一个示例 Makefile 文件,演示了如何使用 dir 函数:
SRC := src/main.c src/foo/bar.c
OBJS := $(patsubst %.c,%.o,$(SRC))
all: $(OBJS)
%.o: %.c
@echo "Compiling $<"
gcc -c $< -o $@
clean:
rm -f $(OBJS)
dirs:
@echo $(dir $(SRC))
在上述 Makefile 中,我们定义了一个变量 SRC,它包含了两个源代码文件:src/main.c 和 src/foo/bar.c。然后,我们使用 patsubst 函数将 SRC 中的每个 .c 文件名称替换成相应的 .o 文件名称,并将结果存储到变量 OBJS 中。
接着,我们为每个 .o 文件生成一条编译规则,并将其作为 all 目标的依赖项。在编译规则中,我们使用 $< 自动化变量指代当前目标的第一个依赖项(即对应的 .c 文件),并使用 dir 函数获取该 .c 文件所在的目录。
最后,我们定义了一个名为 dirs 的伪目标,用于输出 SRC 中每个文件的目录部分。在规则中,我们使用 $(dir $(SRC)) 获取 SRC 中所有文件的目录部分,并将其打印到终端上。
这里没有介绍notdir命令,顾名思义就是于获取一个文件路径的文件名部分,即最后一个/后面的内容。
在Makefile中,filter函数可以用于从一个列表中筛选出满足某些条件的元素。其语法如下:
$(filter pattern...,text)
其中,pattern是要匹配的模式,可以使用通配符%,即表示任意长度的字符串;text是要处理的列表,可以是一个或多个元素,使用空格分隔。
filter函数将返回text列表中所有匹配成功的元素。如果pattern以%号开头,则表示以该模式结尾的字符串均可匹配成功。
在 Makefile 中,basename 函数用于获取路径中的文件名部分。
语法如下:
$(basename names...)
其中 names
参数是一个或多个路径名称。该函数会返回 names
参数中每个路径名称的文件名(此时没有后缀名)部分。
在Makefile中,$<是一个自动化变量,用于表示当前规则的第一个依赖文件名。举个例子:
main.o: main.c foo.h bar.h
gcc -c $< -o $@
在这个规则中,我们指定了目标文件main.o依赖于源文件main.c和头文件foo.h、bar.h。当执行该规则时,Make会自动将$<替换成当前规则的第一个依赖文件(即main.c),并将其作为编译器的源文件参数。
需要注意的是,<只能在规则的命令行中使用,而不能在规则的其他地方,比如依赖关系或者目标文件名中使用。如果需要使用其他依赖文件,可以使用<只能在规则的命令行中使用,而不能在规则的其他地方,比如依赖关系或者目标文件名中使用。如果需要使用其他依赖文件,可以使用^(表示所有依赖文件)或者$+(表示所有依赖文件,但不去除重复项)自动化变量来代替。
$^
表示所有依赖项,以空格分隔。例如,在下面的规则中:
my_program: main.o utils.o
$(CC) $(LDFLAGS) $^ -o $@
$^
表示main.o utils.o
。
注意,如果有多个依赖项,使用$^
将会把它们一起传递给编译器或链接器。而如果只需要传递其中的某些依赖项,则可以使用$<
或手动指定依赖项的名称。
$@
是 Makefile 的一个自动变量,表示规则中的目标文件名。在一个规则中,可以使用 $@
来引用该规则的目标文件。
例如,考虑以下 Makefile 规则:
my_program: main.o utils.o
$(CC) $^ -o $@
这个规则将 main.o
和 utils.o
编译成一个名为 my_program
的可执行文件。$^
展开为所有依赖文件,而 $@
表示规则的目标文件即 my_program
。在这里,$@
被用于指定生成文件的名称,即告诉编译器要把编译后的文件输出到 my_program
文件中。
理解了这么多,我们直接来一点实际操作,这里以静态链接库为例:
all : main
main : main.o libmymath.a
gcc -L /usr/lib $^ -o $@
main.o : main.c
gcc -c main.c -o main.o
libmymath.a : mymath.o
ar rcs $@ $^
mymath.o : mymath.c
gcc -c $^ -o $@
clean :
rm -rf main
.PHONY: clean
这是本人基于上篇博客Linux下链接库编写的makefile,初次接触,个人理解就行。
现在我们对main.c
文件就行修改,修改后只需要执行make命令就行了,不需要又重新按照静态链接的步骤一步步去弄,makefile本身简化操作。
我们把main.c
的a=8
改成了a=15
,接下来看演示。
makefile是一个比较基础的知识点,当然它也可以深化,我们这里仅仅学习使用,网上也有相关教程,几乎都是几个小时就讲完了,相当于补充知识点。同时这也是Linux运维中make命令的原理介绍。
新星计划:Linux运维@刘晨阳导师创作打卡6!