Bootstrap

makefile笔记

makefile

Makefile 中,预定义的变量(也称为内置变量)提供了对构建过程中的默认值和特殊值的访问。这些变量通常由 Make 自动设置,并且可以覆盖它们以改变 Make 的行为。下面是 Make 环境中常见的几个内置变量及其用途:

  1. $@ 目标文件名(依赖关系中的最后一个文件)。在规则的命令中,用 $@ 表示当前目标的名字。

  2. $^ 所有的依赖文件名列表,没有重复,并且排除了隐含目标。

  3. $< 第一个依赖文件的名字(主依赖项)。

  4. $* 用于匹配模式(Pattern)中的通配符部分。通常用于从目标文件名中提取出特定的部分,如不包含扩展名的文件名部分。

  5. $? 所有比目标更新的依赖文件名列表,只包括那些比目标新的依赖文件。

  6. $> 所有比依赖文件新的目标文件名列表。

除了这些特殊符号之外,还有一些预定义的变量代表了不同的路径和环境信息:

  • $AR - 归档命令(用于创建和修改静态库文件)。
  • $AS - 汇编程序命令。
  • $CC - C 编译器命令。
  • $CXX - C++ 编译器命令。
  • $CPP - C 预处理器命令。
  • $RANLIB - 更新库索引文件的命令。
  • $LD - 链接程序命令。
  • $LDFLAGS - 传递给链接器的默认选项。
  • $CFLAGS - 默认的 C 编译选项。
  • $CXXFLAGS - 默认的 C++ 编译选项。
  • $CPPFLAGS - 默认的预处理选项。

此外,还有一些变量提供了有关当前 Makefile 文件的信息:

  • $MAKEFILE_LIST - 包含当前所有被包含的 Makefile 名称的列表。
  • $MAKEFILE - 当前正在执行的 Makefile 的名字。
  • $MAKEFILES - 用空格分隔的所有 Makefile 名字列表。

最后,有一些变量涉及到文件名路径:

  • $CURDIR - 当前目录。
  • $VPATH - 虚拟路径,它告诉 Make 到哪里去找源文件。

了解并正确使用这些内置变量可以使 Makefile 更加灵活和强大。当然,用户也可以定义自己的变量,并且可以覆盖这些预定义的变量来改变默认的行为。

MAKE 表示命令是什么

MAKECMDGOALS表示参数是什么

.PHONY:all

all:
        @echo "MAKE = $(MAKE)"
        @echo "MAKECMDGOALS = $(MAKECMDGOALS)"

输出结果如下:

# make all
MAKE = make
MAKECMDGOALS = all


.PHONY 是一个特殊的伪目标,在 Makefile 中使用,主要用于指定一个或多个“伪目标”(phony targets)。作用是明确告诉 make,其后列出的目标名不是文件名,即使文件系统中存在同名的文件,make 也不应该认为它们已经“存在”或“最新”。这样,当你执行 make 时,make 总是会执行该伪目标对应的命令,而不会因为它在文件系统中找到了一个同名的文件就跳过执行。

@echo中的@可以使make不打印当前执行的命令。


在 Makefile 中 $$ 具有特殊的意思,因此,如果想采用 echo 输出 $ ,则必需用两个连着的 $$ 。还有就是,$@ 对于 Shell 也有特殊的意思,我们需要在 $$@ 之前再加一个脱字符 \

.PHONY:all

all:first second third
        @echo "\$$@=$@"
        @echo "$$^=$^"
        @echo "$$<=$<"

first second third:

执行结果如下:

# make
$@=all
$^=first second third
$<=first

变量的类别

=符号定义的变量,称之为递归扩展变量,会进行递归扫描

上面这种不能对变量自己赋值,会陷入死循环。如a=$(a) b这种

:=符号定义的变量,称之为简单扩展变量,只会进行一次扫描和替换

?=称之为条件赋值,当变量以前没有定义时,就定义它并将右边的值赋给它;如果一定定义了就不再改变它的值

看下面的例子

.PHONY:all

x=foo
y=$(x) b
x=later

xx:=foo
yy:=$(xx) b
xx:=later
foo=x
foo?=y

bar?=y

all:
        @echo "x = $(y), xx = $(yy)"
        @echo "foo = $(foo), bar = $(bar)"

输出结果如下:

# make
x = later b, xx = foo b
foo = x, bar = y


#也可以在命令行上对变量赋值
# make bar=x
x = later b, xx = foo b
foo = x, bar = x

#变量也可以来自系统的环境变量
# export bar=aa
# make
x = later b, xx = foo b
foo = x, bar = aa

高级变量引用功能

下面示例了Makefile的变量引用的一种高级功能

在赋值的同时完成后缀替换操作

.PHONY:all

foo = a.o b.o c.o
bar := $(foo:.o=.c)

all:
        @echo "bar = $(bar)"

输出结果如下:

# make
bar = a.c b.c c.c

overrider指令

前面的例子看到参数可以通过命令行和环境变量赋值,如果不想Makefile中定义的变量被命令行或者环境变量覆盖掉,就可以使用override指令

.PHONY:all

override foo := x

all:
        @echo "foo = $(foo)"

输出结果如下

# make foo=b
foo = x
# export foo=c
# make
foo = x

模式

Makefile中经常存在多个规则用于构建目标文件。如果对于每一个目标文件都要写一个不同的规则来描述就太繁琐了。Makefile中的模式就是用来解决这种问题的。

看下面的例子

main.c

extern void foo();

int main(){
    foo();
    return 0;
}

foo.c

#include <stdio.h>

void foo(){
    printf("This is foo (). \n");

}

Makefile

.PHONY:clean

CC = gcc
RM = rm

EXE = simple
OBJS = main.o foo.o

$(EXE): $(OBJS)
        $(CC) -o $@ $^

%.o:%.c
        $(CC)   -o $@ -c $^

clean:
        $(RM) $(EXE) $(OBJS)

上面的例子我们%.o:%.c,就是一个模式规则。它匹配所有.c文件并指定如何构建对应的.o文件。会编译出main.ofoo.o。这里的%相当于通配符。注意,这里的通配符是%,而不是*

函数

函数常常用来简化项目的Makefile

看下面的例子

.PHONY:clean

CC = gcc
RM = rm

EXE = simple

SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))

$(EXE):$(OBJS)
        $(CC) -o $@  $^

%.o:%.c
        $(CC)  -o $@ -c $^

clean:
        $(RM) $(EXE) $(OBJS)

wildcard是通配符函数, 通过它可以过滤出我们需要的文件,这里的通配符是*,其形式是$(wildcard pattern)

查找当前目录下所有.c文件,并将这些文件的名称赋值给变量SRCS

patsubst函数是用来进行字符串替换,其形式是$(patsubst pattern, replacement, text),这里的通配符是%。上面的例子中将SRCS变量中所有.c文件的后缀替换为.o,并将结果赋值给变量OBJS


addprefix函数是用来在给字符串中的每个子串前加上一个前缀,其形式是

$(addprefix prefix, names...)

看下面的例子

.PHONY:all

without_dir=fooc bar.c main.o
with_dir:=$(addprefix objs/,$(without_dir))

all:
        @echo $(with_dir)

执行结果如下:

# make
objs/fooc objs/bar.c objs/main.o


filter函数用于从一个字符串中,根据模式得到满足模式的字符串,其形式是:

$(filter pattern..., text)

看下面的例子

.PHONY:all

sources=foo.c bar.c baz.s ugh.h

s:=$(filter %.c %.s, $(sources))

all:
        @echo $(s)

执行结果如下:

# make
foo.c bar.c baz.s

filter-out函数用于从一个字符串中根据模式过滤掉一部分字符串,其形式是

$(filter-out pattern..., text)

看下面例子

.PHONY:all

objects=main1.o foo.o main2.o bar.o
result=$(filter-out main%.o,$(objects))

all:
        @echo $(result)

执行结果如下:

# make
foo.o bar.o

strip函数用于去除变量中多余的空格,其形式是:

$(strip string)

看下面例子

.PHONY:all

original=foo.c    bar.c
stripped:=$(strip $(original))

all:
        @echo "original = $(original)"
        @echo "stripped = $(stripped)"

执行结果如下:

# make
original = foo.c    bar.c
stripped = foo.c bar.c
;