Makefile学习笔记,简单版。

学习资料:
makefile官方文档https://www.gnu.org/software/make/manual/make.html
陈皓大佬的《跟我一起写Makefile》

学习笔记:
Makefile 由一组规则组成。规则通常如下所示:

	command
	command
	command
  • 目标是文件名,用空格分隔。通常,每个规则只有一个。
  • 这些命令是一系列步骤,通常用于创建目标。这些需要以制表符开头,而不是空格。
  • 先决条件也是文件名,用空格分隔。在运行目标的命令之前,这些文件需要存在。这些也称为依赖关系
all: target1 target2 target3
target1:
# 编译规则1
target2:
# 编译规则2
target3:
# 编译规则3

all被设置为第一个目标,并且target1、target2和target3被列为all的依赖。当你在命令行中运行make时,make命令会寻找并执行all目标规则,这将依次执行target1、target2和target3的编译规则。
因此,通过在Makefile中设置all作为默认目标规则,你可以简化构建过程,只需运行make命令即可执行整个编译过程,无需显式指定目标

变量

$/符号表示取变量的值,当变量名多于一个字符时,使用"( )"

$符的其他用法

$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 代表第一个依赖文件
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))

ALL: hello.out

hello.out: $(OBJ)
        gcc $< -o $@
%.o: %.c
        gcc -c $< -o $@
  1. 定义变量SRC,用于存储所有的.c源文件;
  2. 定义变量OBJ,用于存储所有的.o目标文件;
  3. 定义默认目标ALL,依赖于hello.out;
  4. 定义目标hello.out,依赖于$(OBJ),即所有的.o目标文件;
  5. 定义模式规则%.o: %.c,表示将.c源文件编译成对应的.o目标文件。

wildcard *.c 是 Makefile 中的一个通配符,用于匹配当前目录下所有扩展名为 .c 的文件。它的作用是将匹配到的文件名列表赋值给变量 SRC
patsubst 是 Makefile 中的一个模式替换函数,用于将匹配到的模式进行替换。具体来说,$(patsubst %.c, %.o, $(SRC)) 这个表达式的作用是将变量 SRC 中所有扩展名为 .c 的文件名替换为相应的 .o 文件名。换句话说,它会将所有的源文件(.c 文件)转换为对应的目标文件(.o 文件)。

Makefile 中支持的通配符:

  • *:匹配任意长度的任意字符,可以出现在文件名的任何位置。例如,*.c 会匹配所有以 .c 结尾的文件。
  • ?:匹配任意一个字符。这在指定依赖关系时特别有用,$? 表示检查目标依赖列表中已更新的文件。
  • [...]:用于匹配方括号内指定的任意一个字符。例如,[abc] 会匹配文件名中包含 abc 的任意文件。

Makefile函数:

Makefile中的函数用于处理字符串、文件路径、以及执行系统命令:

  1. 文本处理函数
  • subst:替换字符串中的一部分。
  • patsubst:模式替换,用于替换符合特定模式的字符串。
  • findstring:查找子串在字符串中的位置。
  • filter:过滤列表中符合或不符合模式的元素。
  • filter-out:反向过滤,即过滤出不符合模式的元素。
  • sort:对字符串进行排序。
  1. 文件名处理函数
  • wildcard:获取匹配特定模式的文件列表。
  • dir:获取文件的目录部分。
  • notdir:获取不带路径的文件名。
  • basename:获取路径的基本名称,即最后的文件名或目录名。
  • abspath:获取文件的绝对路径。
  • dirname:获取文件的目录路径。
  1. 调用系统命令的函数
  • shell:执行shell命令并返回结果。
  • call:执行一个make命令。
  1. 控制结构函数
  • ifelseendif:条件判断。
  • foreachendforeach:循环遍历。

变量赋值

1、"="是最普通的等号,在Makefile中容易搞错赋值等号,使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。

VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA

经过上面的赋值后,最后VIR_B的值是AA B,而不是A B,在make时,会把整个Makefile展开,来决定变量的值
  2、“:=” 表示直接赋值,赋予当前位置的值。

VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA

最后BIR_B的值是A B,即根据当前位置进行赋值。因此相当于“=”,“:=”才是真正意义上的直接赋值
3.“?=” 表示如果该变量没有被赋值,赋值予等号后面的值。

VIR ?= new_value

​ 如果VIR在之前没有被赋值,那么VIR的值就为new_value。

VIR := old_value
VIR ?= new_value

​ 这种情况下,VIR的值就是old_value

​ 4、**“+=”**和平时写代码的理解是一样的,表示将符号后面的值添加到前面的变量上

Makefile预定义变量

是一些在执行make命令时自动设置的变量,它们提供了有关当前构建环境的信息。以下是一些常见的Makefile预定义变量:

  • $@:表示规则中的目标文件名。
  • $<:表示规则中的第一个依赖文件名。
  • $^:表示规则中的所有依赖文件名,以空格分隔。
  • $?:表示规则中所有比目标文件更新的依赖文件名,以空格分隔。
  • $*:表示规则中的目标文件名,但不包含扩展名。
  • $(@D):表示目标文件所在的目录路径。
  • $(@F):表示目标文件的文件名,不包含目录路径。
  • $(<D):表示第一个依赖文件所在的目录路径。
  • $(<F):表示第一个依赖文件的文件名,不包含目录路径。
  • $(^D):表示所有依赖文件所在的目录路径,以空格分隔。
  • $(^F):表示所有依赖文件的文件名,不包含目录路径,以空格分隔。
  • $(?D):表示所有比目标文件更新的依赖文件所在的目录路径,以空格分隔。
  • $(?F):表示所有比目标文件更新的依赖文件的文件名,不包含目录路径,以空格分隔。

嵌套执行Makefile

在一些大工程中,会把不同模块或不同功能的源文件放在不同的目录中,我们可以在每个目录中都写一个该目录的Makefile这有利于让我们的Makefile变的更加简洁,不至于把所有东西全部写在一个Makefile中。

列如在子目录subdir目录下有个Makefile文件,来指明这个目录下文件的编译规则。外部总Makefile可以这样写

subsystem:
            cd subdir && $(MAKE)
其等价于:
subsystem:
            $(MAKE) -C subdir

定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较有利于维护。两个例子意思都是先进入"subdir"目录,然后执行make命令
  我们把这个Makefile叫做总控Makefile,总控Makefile的变量可以传递到下级的Makefile中,但是不会覆盖下层Makefile中所定义的变量,除非指定了 "-e"参数。
  如果传递变量到下级Makefile中,那么可以使用这样的声明
  export
  如果不想让某些变量传递到下级Makefile,可以使用
  unexport

export variable = value
等价于
variable = value
export variable
等价于
export variable := value
等价于
variable := value
export variable

如果需要传递所有变量,那么只要一个export就行了。后面什么也不用跟,表示传递所有变量

Makefile宏

Makefile中的宏类似于变量,用于给一组值命名,以便在Makefile中重复使用。

定义宏: 在Makefile中定义宏通常使用:=?=操作符。:=是直接赋值,而?=是在变量未被赋值时使用默认值。例如:

CFLAGS := -O2 -Wall

这里定义了一个名为CFLAGS的宏,它代表了编译选项。

使用宏: 在Makefile的规则中,可以通过$(MACRO_NAME)的形式来使用宏。例如,如果有一个名为CFLAGS的宏,可以在编译命令中使用它:

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

在这个例子中,$(CFLAGS)会被替换为之前定义的编译选项。

宏的作用域: 宏的作用域通常是全局的,除非它们在目标的上下文中被重新定义。这意味着一旦宏被定义,它可以在整个Makefile文件中使用,直到被重新定义或者Makefile文件结束。

总的来说,Makefile中的宏是一种强大的工具,可以帮助简化构建规则,提高代码的可读性和可维护性。通过合理地使用宏,可以使得Makefile更加简洁和灵活。

指定头文件路径

一般都是通过"-I"(大写i)来指定,假设头文件在:

/home/develop/include

则可以通过-I指定:

-I/home/develop/include

将该目录添加到头文件搜索路径中
在Makefile中则可以这样写:

CFLAGS=-I/home/develop/include

然后在编译的时候,引用CFLAGS即可,如下

your app:*.c
gcc $(CFLAGS) -o yourapp

指定库文件路径

与上面指定头文件类似只不过使用的是"-L"来指定

LDFLAGS=-L/usr/lib -L/path/to/your/lib
1

告诉链接器要链接哪些库文件,使用"-l"(小写L)如下:

LIBS = -lpthread -liconv

困惑讲解

在Makefile中,$@是一个特殊的变量,它代表当前规则的目标文件名。当执行make命令时,Makefile会根据目标文件名来查找对应的规则,并执行该规则的命令。

下面是一个示例的Makefile代码:

# 定义一个目标文件名为output.txt的规则
output.txt: input.txt
	# 使用echo命令将input.txt的内容输出到output.txt
	echo "This is the content of input.txt" > $@

# 定义一个目标文件名为clean的规则
.PHONY: clean
	# 使用rm命令删除output.txt文件
	clean:
		rm output.txt

    

在这个例子中,我们定义了两个规则:output.txtclean

  • output.txt规则的目标是生成一个名为output.txt的文件。它的依赖文件是input.txt。在执行这个规则时,会执行echo "This is the content of input.txt" > $@命令,其中$@表示当前规则的目标文件名,即output.txt。这条命令的作用是将字符串"This is the content of input.txt"输出到output.txt文件中。
  • clean规则是一个伪目标(.PHONY),它没有实际的文件作为目标。执行这个规则时,会执行rm output.txt命令,用于删除output.txt文件。

通过使用$@变量,我们可以方便地引用当前规则的目标文件名,从而实现对不同目标文件的操作。

echo:会在shell中显示echo这条命令和这条命令的输出结果

@echo:不会在shell中显示echo这条命令,但是会显示命令的输出结果

例如:

echo_test:
	echo "hello world"

echo_test_with_address:
	@echo "hello world"

输出

echo test的输出结果为:
echo "hello world"
hello world

echo test with address的输出结果为:
hello world
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值