Linux Makefile

Makefile变量

1.自定义变量

变量的赋值:

  • "="赋值 如果变量多次赋值,变量的值为最后一次赋值结果,与在文件中的位置无关。
    x=a
    y=$(x)
    x=b
    print:
        @echo $(y)
    
    执行结果
    dhl@dhl:~$ make
    b
    
    变量y的值为b,是X变量最后一次赋值的结果,"="赋值的时机是Makefile全部展开后再给变量y进行赋值。
  • ":="赋值 在变量赋值时立即展开进行赋值。
    x=a
    y:=$(x)
    x=b
    print:
        @echo $(y)
    
    执行结果
    dhl@dhl:~$ make
    a
    
    变量y的值为a,是X变量第一次赋值的结果,":="赋值的时机是立即展开。
  • "+= 赋值 追加赋值,在保持原有值的基础上添加新值。
    x = a 
    x += b
    print:
        @echo $(x)
    
  • "?="赋值 如果变量没有定义则进行定义赋值
    x =
    x ?= b
    y ?= c
    print:
        @echo "x:"$(x)
        @echo "y:"$(y)
    
    执行结果
    dhl@dhl:~$ make
    x:
    y:c
    

变量的取值:
使用$()或者${}取变量值,如遇需要使用'$'场景,使用$$。

x =
x ?= b
y ?= c
print:
    @echo $$"x:"$(x)
    @echo $$"y:"$(y)

执行结果

dhl@dhl:~$ make
$x:
$y:c

2.自动变量

  • $* 不包含扩展名的目标文件名称
  • $+ 所有依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件
  • $< 第一个依赖文件的名称
  • $? 所有时间戳比目标文件玩的依赖文件,以空格分开
  • $@ 目标文件的完整名称
  • $^ 所有不重复的目标依赖文件,以空格分开
  • $% 如果目标是归档成员,则改变量表示目标的归档成员名称

3.隐含变量

  • AR 库文件维护程序的名称,默认值ar。ARFLAGS AR程序选项,无默认值。
  • AS 汇编程序的名称,默认值as。ASFLAGS AS汇编选项,无默认值。
  • CC C编译器的名称,默认值cc。CFLAGS C编译器选项,无默认值。
  • CPP C预编译器的名称,默认值$(CC) -E。CPPFLAGS C预编译选项,无默认值。
  • CXX C++编译器的名称,默认值g++。CXXFLAGS C++编译器选项,无默认值。
  • LD 链接器名称,默认值ld。LDFLAGS 链接器选项,无默认值。
  • RM 文件删除程序的名称,默认值rm -f。
  • MAKEFLAGS 接收make命令行传递的选项和变量,默认值为空。
MAKEFLAGS += -rR --include-dir=$(CURDIR)
  • -r表示禁止使用内置的隐含规则。
  • -R表示禁止使用内置的变量定义
  • CURDIR当前的目录

4.路径变量

  • CURDIR 当前目录路径。
  • VPATH 用于指定额外的搜索路径。当在当前目录找不到文件时,make会在VPATH指定的路径中依次搜索。例如:
    VPATH = src:include
    
    这表示make会在srcinclude目录中搜索文件。VPATH变量可以给多个路径赋值,各路径之间可以用空格或冒号隔开,搜索的顺序是按照书写时的顺序进行。

Makefile的关键字

1. .PHONY关键字

.PHONY是一个特殊的目标声明,用于告诉make工具,后面列出的目标是 “伪目标”(伪目标并不是一个真正的文件,而是一个动作或者命令的名称)。

2. .SUFFIXES关键字

.SUFFIXES: 是用于声明后缀规则的一个特殊变量,用来定义文件扩展名转换规则。它用于处理不同文件类型之间的自动转换,尤其是在没有显式规则的情况下。

.SUFFIXES:  # 清空默认的后缀规则
.SUFFIXES: .f90 .o  # 定义新的后缀规则

.f90.o:  # 定义从 .f90 到 .o 的转换规则
    $(FC) $(FCFLAGS) -c $<

3.vpath

可以指定不同的文件在不同的搜索目录中,优先级高于VPATH,也更灵活。

  • vpath 为符合模式的文件指定搜索目录,例如vpath %.c src 。
  • vpath 清除符合模式的文件的搜索目录
  • vpath 清除所有已被设置好了的文件搜索目录

Makefile的分支结构

1.ifeq和ifneq
判断变量的值是否等于指定的值

ifeq ($(ARCH),x86)
CC=GCC
else
CC=arm-linux-gnueabihf-gcc
endif

ifneq ($(ARCH),arm)
AR=ar
else
AR=arm-linux-gnueabihf-ar
endif

print:
    @echo $(CC)
    @echo $(AR)

执行结果

dhl@dhl:~$ make
arm-linux-gnueabihf-gcc
ar
dhl@dhl:~$ make ARCH=x86
GCC
ar
dhl@dhl:~$ make ARCH=arm
arm-linux-gnueabihf-gcc
arm-linux-gnueabihf-ar

2.ifdef和ifndef
判断变量是否定义

SET=

ifdef SET
NAME=dhl
endif

print:
    @echo $(origin SET)
    @echo $(SET)
    @echo $(origin NAME)
    @echo $(NAME)

执行结果

dhl@dhl:~$ make
file

undefined

dhl@dhl:~$ make SET=y
command line
y
file
dhl

值得注意的是ifdef和ifndef判断的标准是变量定义并赋值。和函数origin的标准不同


Makefile常用函数

1.origin

函数语法:$(origin <variable>)
返回值变量的来源,可能是以下的值。

undefined:表示变量variable未被定义。
command line:表示变量variable是在命令行中被定义的。
environment:表示变量variable是作为环境变量被定义的。
file:表示变量variable是在 Makefile 中被定义的。
default:表示变量variable是默认定义的。
override:表示变量variable被override指示符重新定义。
automatic:表示变量variable是一个命令运行中的自动化变量。

示例:

ifeq ($(origin VAR), undefined)
	VAR = default
endif

如果变量VAR没有定义,就定义变量VAR赋值为default

2.shell

函数语法:$(shell <command> <arguments>)
shell函数把执行操作系统命令后的输出作为函数返回。
示例:

SRC = $(shell find src -name "*.c")

在src目录下查找.c文件,并将文件名列表赋值给SRC变量。

3.subst

函数语法:$(subst <from>,<to>,<text>)
字符串替换函数,把text的from替换为to。
示例

$(subst ov,OV,love)

将love中的ov替换为OV,最终输出lOVe,注意,和内容之间没有空格。

4.patsubst

函数语法:$(patsubst <pattern>,<replacement>,<text>)
模式字符串替换函数,使用通配符匹配格式进行内容替换。
示例:

OBJS = $(patsubst %.c,%.o,$(SRC))

将SRC变量中以.c结尾的字段替换成以.o结尾,赋值给OBJS。以上实际场景是根据.c文件列表生成.o文件列表。

5.foreach

函数语法:$(foreach <var>,<list>,<text>)
循环函数,把字串中的元素逐一取出来,执行包含的表达式

CFLAGS += $(foreach item,$(INC),-I $(item))

将INC变量中每个字段前添加-I 追加赋值给CFLAGS。以上实际场景是头文件目录前添加-I后赋值给CFLAGS,也就是在编译时指定都文件目录。

6.dir

函数语法:$(dir <names...>)
取文件路径中的目录部分

CPATH = $(dir $(SRC))

获取SRC变量各个字段中文件路径的目录部分赋值给CPATH变量。以上实际场景是获取源文件所在的各个目录。

7.notdir

函数语法:$(notdir <names...>)
获取文件路径中的文件名部分

CFILES = $(notdir $(SRC))

获取SRC变量各个字段中文件路径的文件名部分赋值给CFILES变量。以上实际场景是获取各个源文件名称。

8.filter

函数语法:$(filter <pattern...>,<text>)
过滤器函数,以模式过滤字符串中的单词,保留符合模式的单词,可以有多个模式,空格隔开。

SOS := $(filter %.so, $(libs))

从变量libs中匹配.so的字段。以上实际场景是在存储库文件路径信息列表的变量中匹配出动态库列表。

9.filter-out

函数语法:$(filter-out <pattern...>,<text>)
过滤器去除函数,以模式过滤字符串中的单词,去除符合模式的单词,可以有多个模式,空格隔开。

CFILES := $(filter-out %.o, $(files))
从变量files中过滤出.o的字段。以上实际场景是在存储文件路径信息列表的变量中去除.o文件。

10. basename

函数语法:$(basename <names...>)
去后缀函数,去掉指定文件列表的后缀

LIBNAME = $(subst lib,,$(basename $(LIBS)))

去除变量LIBS中字段的后缀,并删除字段中lib,以上实际场景是获取LIBS中存储库列表的库名。

11.wildcard

函数语法:$(wildcard <name...>)
通配符函数,通配符函数将符合匹配模式的文件名用空格分隔扩展为列表

SRC += $(wildcard extend/*.c)

匹配extend目录下的所有.c文件。

12.findstring

函数语法:$(findstring <FIND>,<IN>)
在IN中查找FIND,查找到FIND,返回FIND,失败返回空

$(findstring this, this is test)

在this is test中查找this,成功返回this

13.sort

函数语法:$(sort <list>)
排序函数,给变量list中字段以首字母升序排列

$(sort foo bar lose)

foo bar lose后以首字母升序排列

14.word

函数语法:$(word <n>,<text>)
取词函数,在text中取低n个字段

$(word 2, foo bar baz)

结果返回bar

15.wordlist

函数语法:$(wordlist <s>,<e>,<text>)
在text中取第s到第e之间的字段

$(wordlist 2,3,t1 t2 t3 t4)

返回t2 t3

16.words

函数语法:$(words <text>)
统计text字段的个数

$(words t1 t2 t3 t4)

返回4

17.firstword

函数语法:$(firstword <words>)
获取words中的第一个字段

$(firstword t1 t2 t3 t4)

返回t1

18.lastword

函数语法:$(lastword <words>)
获取words中的最后一个字段

$(lastword t1 t2 t3 t4)

返回t4

19.suffix

函数语法:$(suffix <names…>)
文件名序列中取出各个文件名的后缀。如果文件没有后缀,则返回空字串

$(suffix src/test.c)

返回.c

20.addsuffix

函数语法:$(addsuffix <suffix,<names...> )
把后缀加到中的每个单词后面

$(addsuffix .c,foo bar)

返回foo.c bar.c

21.addprefix

函数语法:$(addprefix <prefix>,<names...> )
把前缀加到中的每个单词前面

$(addprefix src/,foo)

返回src/foo

22.join

函数语法:$(join <list1>,<list2> )
把list1和list2按照顺序合并

suffix=.c .o .h
file=a b c
name = $(join $(file),$(suffix))

name为a.c b.o c.h

23.info

函数语法:$(info <text>)
向标准输出打印文本 ,用于输出调试信息。

24.warning

函数语法:$(warning <info>)
向标准输出打印文本 ,用于输出警告信息。make继续执行。

25.error

函数语法:$(error <text>)
向标准错误输出打印文本 ,用于输出指明错误信息。make停止执行。

26.call

函数语法:$(call <expression>,<parm1>,`,...)
call函数可以调用新建的参数化函数,$(0)是函数名称、$(1)是第一个参数...。expression的返回值就是call函数的返回值。

myfun = $(patsubst %.c,%.o,$(1))
files=t1.c t2.c t3.c
print:
    @echo $(call myfun,$(files))

执行结果

dhl@dhl:~$ make
t1.o t2.o t3.o

另一种定义方式:

define myfun
$(patsubst %.c,%.o,$(1))
endef

files=t1.c t2.c t3.c

print:
    @echo $(call myfun,$(files))

27.eval

函数语法:$(eval <text>)
将 作为 makefile 的一部分而被 make 解析执行。注意,这里不再将text作为字符串,而当作make命令处理。

define MA
ALL:
        @echo hello world
endef

$(info $(MA))
$(info ----------------------)
$(eval $(MA))

输出

ALL:
	@echo hello world
----------------------
hello world

28.if

函数语法:
$(if CONDITION,THEN-PART)
$(if CONDITION,THEN-PART,ELSE-PART)
如果CONDITION为真,则返回表达式THEN-PART,否则返回ELSE-PART

install_path = $(if $(install__path), $(install__path), /usr/local)

install__path非空则返回install__path,否则返回/usr/local


Makefile执行

1. 导出环境变量

export ENVNAME=ENVVALUE

2. 命令行传递变量值

make VAR2=value

3.变量值的优先级

命令行传递的变量值优先级 > 文件内定义的变量值优先级 > 环境变量值的优先级

4.override

在默认情况下命令行传递的变量值的优先级最高,在一些场景下想让文件内定义的变量值不被命令行传参覆盖,可以使用override指示符修饰。

override SET=n

print:
    @echo $(SET)

执行结果

dhl@dhl:~$ make SET=y
n

makefile常用实现方式

1. 获取主机硬件架构

HOSTARCH := $(shell uname -m | \
	sed -e s/i.86/x86/ \
		-e s/sun4u/sparc64/ \
		-e s/arm.*/arm/ \
		-e s/sa110/arm/ \
		-e s/ppc64/powerpc/ \
		-e s/ppc/powerpc/ \
		-e s/macppc/powerpc/\
		-e s/sh.*/sh/)

2. 获取主机系统

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]'| \
	sed -e 's/\(cygwin\).*/cygwin/')

tr '[:upper:]' '[:lower:]'大写转小写

linux内核源码的Makefile语句分析

1. t1=$(if $(t:1=),@)

展开

t1 = $(if $(patsubst %1, %, $(t)), @)
$(patsubst %1, %, $(t)) t为1时为空
$(if $(patsubst %1, %, $(t)), @)实际为
$(if $(patsubst %1, %, $(t)), @, )

t为1时t1为空,t为非1时为@
$(VAR:x=y) 这种语法相当于 $(patsubst %x,%y,$(VAR)) 的缩写

2. 目标匹配

%config: scripts_basic outputmakefile FORCE
	$(Q)$(MAKE) $(build)=scripts/kconfig $@

可以匹配make xxx_defconfig

posted @ 2025-08-11 17:53  宏浪  阅读(12)  评论(0)    收藏  举报