AA.Makefile编译规则一

Makefile概述

go 复制代码
Makefile make 是以目标为靶向执行的脚本,变量类型也只有字符,目标 依赖 需要按照目标执行,
而写在顶格的则全局执行,或者说默认先执行。
targets:目标,可以是 Object File,也可以是可执行文件,还可以是一个标签;
prerequisites:依赖文件,要生成 targets 需要的文件或者是目标。可以是多个,也可以是没有;
command:make 需要执行的命令(任意的 shell 命令)。可以有多条命令,每一条命令占一行。
bash 复制代码
targets : prerequisites
    command
bash 复制代码
#简单举个例子:
#Makefile的简写命令
test:test.c
	gcc -o	test test.c
#作用生成一个可执行test文件,依赖于test.c,规则是gcc -o test test.c

(01)Makefile的自动推导

bash 复制代码
#通过依赖关系推导编译前后关系和依赖
main:main.o test1.o test2.o
	gcc main.o test1.o test2.o -o main
main.o:main.c test.h
	gcc -c main.c -o main.o
test1.o:test1.c test.h
	gcc -c test1.c -o test1.o
test2.o:test2.c test.h
	gcc -c test2.c -o test2.o

(02)Makefile的隐含规则

bash 复制代码
#Makefile可以简写
#总结:.h后缀文件可以成为依赖但是不能进入规则
test:test.o test.h
	gcc  test.o -o test
#等于:
test:test.o
	gcc test.o -o test
test.o:test.c test.h
  	gcc -c test.c  -o test.o

(03)Makefile的PHONY:伪指令

bash 复制代码
.PHONY告诉make这个目标是一个伪目标,即使存在一个名为该目标名的文件,也不要尝试更新它。
这对于避免文件名与目标名冲突非常重要。例如:
.PHONY: pack debug
类似如上定义pack 和 debug两个伪目标

‌Makefile中的默认伪目标主要有以下几种‌:
‌all‌:这是最常见的默认伪目标,用于执行所有的编译和构建任务
‌clean‌:用于清理编译生成的文件
‌install‌:用于安装编译好的程序
‌test‌:用于运行测试
其中all是默认的伪目标,make 不带参数时会以all为目标运行

.PHNOY:clean #这一句加不加都无所谓(前提是当前目录没有名为clean的文件)
clean:
	rm -rf *o
	rm -rf test
注意:实际上是没有clean这个指令的,这是弄的伪指令
 使用make的时候这句不会执行(前提是没有其他的目标文件或者其他的目标文件依赖于clean)
 使用make clean 才会执行我清除指令
 我们可以仿照上面定义更多的位指令也没问题,执行伪指令的方式是make 位指令
 就会根据相应的规则执行相关命令。
 -rf的作用就是不管能不能执行,接着往下走不报错,执行完这条位指令

(04)Makefile常见的参数

export定义shell环境变量

bash 复制代码
#可以在 Makefile 顶部使用 export 将变量导出为环境变量,对所有规则生效:
export SHELL_VAR = Hello, Global
target:
    @echo $$SHELL_VAR
bash 复制代码
#如果希望变量在多个命令中共享,可以使用 export:
target:
    @export var="Hello, World"; \
    echo $$var; \
    echo "Again: $$var"

Makefile变量和Makefile里shell变

ruby 复制代码
1.在shell脚本里,变量使用的格式为$name ${name},而$(pwd) 'ls' 为调用shell命令。
1.在Makefile的全局位置,Makefile变量使用为$name $(name) ${name}。
3.在Makefile的shell位置,Makefile变量使用为$name $(name) ${name}。
4.在Makefile的shell位置,shell变量使用为$$name和$${name}, 而$$(date)为Makefile里shell命令。
  其实就算为了区分shell和Makefile变量,增加了一个转义$。
5.@ 取消回显,一行命令只需要开始地方使用即可。
bash 复制代码
VAR := Hello, Makefile
target:
    @echo "$(VAR)"  # 直接使用 Makefile 变量
    @var="$(VAR)"; echo $$var  # 传递给 shell 变量

make -f 执行 make 命令时,它会去当前目录下查找名为Makefile的文件,可以使用-f 选项指定文件,不再使用名为Makefile的文件

make -C 可以使用-C 选项指定目录,切换到其他目录里去

'@' 符号的使用 通常makefile会将其执行的命令行在执行前输出到屏幕上。如果将'@'添加到命令行前,这个命令将不被make回显出来。

' - ' 符号的使用 通常删除,创建文件如果碰到文件不存在或者已经创建,那么希望忽略掉这个错误,继续执行,就可以在命令前面添加 '-'

bash 复制代码
target:
	-rm dir
	-mkdir aaadir

在Makefile 中 -n 的含义 在命令行中输入 make -n,所有命令会被打印但不会执行,-n 可帮助开发者理解命令执行的先后顺序,避免逻辑错误。

在Makefile 中'# ','\'的含义 #相当于注释,转意字符是,\可以用来换行,Makefile对缩进有要求

(05)Makefile变量的定义和使用

变量的基本赋值: 简单赋值(:=),需要和$()一起用才能明显看出效果,什么是简单赋值, 从什么时候开始引用,就从什么时候替换,替换变量即使在将来改变, 也不会改变已经被替换了的变量改变. 举个例子:

bash 复制代码
x:=tes
y:=$(x)t
x:=new
test:
	@echo "y=$(y)"
	@echo "x=$(x)"
#输入:
make test
#打印结果:
y=test
x=new
#总结:简单理解为不是赋值变量,是赋值变量所代表的东西

递归赋值(=),需要$()才能明显看出效果,不截断,持续替代 举个例子:

bash 复制代码
x=tes
y:=$(x)t
x=new
test:
	@echo "y=$(y)"
	@echo "x=$(x)"
#输入:
make test
#打印结果:
y=newt
x=new
#总结:简单理解就是赋值变量,而不是赋值变量所代表的东西

条件赋值(?=),在前面是否已经赋值,如果已经赋值,那么?=就不赋值了, 否则将会赋值 举个例子:

bash 复制代码
x=test
x?=new
y?=no
test:
	@echo "x=$(x)"
	@echo "y=$(y)"
#输入:
make test
#打印结果:
x=test
y=no
#总结:前面赋值了,后面就不赋值了,如果前面没赋值,就从这里开始赋值

追加赋值(+=),就是当前的加上要追加的内容赋值给当前的变量 举个例子:

bash 复制代码
x=test
y:=$(x)
x+=$(y)
test:
	@echo "x=$(x)"
#输入:
make test
#打印结果:
#x=test test
#总结:就相当于加法其他语言的+=运算规则

(06)Makefile常见的变量

bash 复制代码
$@	表示规则的目标文件名。如果目标是一个文档文件(Linux 中,一般成 .a 文件为文档文件,
	也成为静态的库文件),那么它代表这个文档的文件名。在多目标模式规则中,
	它代表的是触发规则被执行的文件名。

$^ 代表的是所有依赖文件列表,使用空格分隔。如果目标是静态库文件,
	它所代表的只能是所有的库成员(.o 文	件)名。
	一个文件可重复的出现在目标的依赖中,变量"$^"只记录它的第一次引用的情况。
	就是说变量"$^"会去掉重复的依赖文件。

$<	规则的第一个依赖的文件名。如果是一个目标文件使用隐含的规则来重建,
	则它代表由隐含规则加入的第一个依赖文件。
	
$%	当目标文件是一个静态库文件时,代表静态库的一个成员名。
	
$?	所有比目标文件更新的依赖文件列表,空格分隔。如果目标文件时静态库文件,代表的是库文件(.o 文件)。

$+	类似"$^",但是它保留了依赖文件中重复出现的文件。主要用在程序链接时库的交叉引用场合。

$*	在模式规则和静态模式规则中,代表"茎"。"茎"是目标模式中"%"所代表的部分(当文件名中存在目录时,"茎"也包含目录部分)。

总结:
上面说得比较复杂,实际上D,F表示文类型,用D,和F是目录还是普通带后缀的文件
$<实际上就是依赖文件列表中的第一个文件
$^实际上就是依赖文件列表中的所有文件
$*和$@在shell中一个表示整体,一个表示独立的,在Makefile中一般用$@就够了,表示生成目标文件
$%就是用来代表一个静态库成员文件。注意一个文件,一个静态库文件.a文件
$?用来更新的,只要比生成目标更晚生成的文件都算在内
$+和$^作用差不多都是依赖列表的全部文件,但是区别是,$+可以记录多次引用,而$^只会记录第一次引用的情况。
入门就先掌握$^,$@,$<就好了

(07)Makefile通配符(* [] ?)

bash 复制代码
# []的用法:
gcc -c test1.c test2.c test3.c -o test1.o test2.o test3.o
#实际可以写做:gcc -c test[1-3].c -o test[1-3].o
bash 复制代码
#*的用法(%的用法和*差不多的):
gcc test1.c test2.c test3.c -o test
#实际可以写做:gcc *.c -o test
bash 复制代码
# ?的用法:
gcc test1.c test2.c test3.c -o test
#实际上可以写做:gcc test?.c -o test
bash 复制代码
拓展:
	wildcarb:展开通配符,不能用来展开还不存在的东西,不同于依赖
	$^:在当前整个一段有效语句中对应的依赖文件
	$@:在当前整个一段有效语句中对应的目标文件
实例:
** 1 假设当前目录中只存在test.c 文件和Makefile文件
	obj=$(wildcard *.o)
	test:$(obj)
		gcc $^ -o $@
	上面会直接报错,为什么,因为会先执行wildcard *.o但是此时文件.o是不存在的。
	
假如这样就可以:
	obj=$(wildcard *.c)
	test:$(obj)
		gcc $^ -o $@
		这样就不会报错,因为.c是存在的;这里我用了一下隐式规则(gcc -o 和gcc -c -o)。

看到这里应该发现了规律:
$^和$@到底是指什么?
	好吧上面挨着的例子实际上等于:
	obj=(wildcard *.c)
	test:$(obj)
		gcc  $(obj) -o test
$@:一条完整语句中的的目标文件
$^: 一条完整语句中的依赖文件

(08)Makefile中if for while语句使用

Makefile中ifeq和ifneq的使用 ifneq就是判断是否字符串不相等 注意:ifeq (,)之间有空格

bash 复制代码
obj=main
obj1=main.c
obj2=test.c
obj3=test1.c
ifeq ($(obj),main)	#判断$(obj)和main是否一样,实际上就是比较字符串
	objt=$(obj2)
else
	objt=$(obj3)
endif		#每次使用ifeq或则ifneq结尾都需要添加endif
test:$(obj1) $(objt)
	gcc -o test $(obj1) $(objt)

Makefile的ifdef和ifndef的使用说明 同理:ifndef就是判断是否没赋值

bash 复制代码
test=
all:
ifdef	test	#判断是否定义了test,实际上就是看对其是否赋值
	@echo yes
else
	@echo no
endif	
#同理无论ifdef还是ifndef结尾需要添加endif表示结束标准
#这里的打印结果是 yes,这里对test进行了定义也就是赋值,尽管是空值但是仍然算赋值

Makefile的if使用说明

bash 复制代码
target:	
	if [ condition ]; then \
		echo "Processing..."; \
		# 其他命令; \
	fi
target:
	@if [ $(test) = cc ]; then \
		echo "if test:$(test)"; \
	fi

Makefile的for使用说明

bash 复制代码
@for dir in $(subdirs); do \
	@echo -------compiling $$dir-----------; \
	$(MAKE) -C ; \
done
target:	
    for file in $(FILES); do \
		echo "Processing..."; \
		# 其他命令; \
    done
target:
	@for ii in {1..5}; do \
		echo "for test, ii:$${ii}"; \
	done

Makefile的while使用说明

bash 复制代码
target:
    while [ condition ]; do \
        echo "Processing..."; \
        # 其他命令; \
    done
target:
    i=1; while [ $$i -le 5 ]; do \
        echo "Iteration $$i"; \
        i=$$((i+1)); \
    done

(10)Makefile 常用函数表

一、字符串处理函数

vbnet 复制代码
1.$(subst FROM,TO,TEXT)
函数名称:字符串替换函数---subst。
函数功能:把字串"TEXT"中的"FROM"字符替换为"TO"。
返回值:替换后的新字符串。
$(subst FROM,TO,TEXT)
result := $(subst ee,EE,feet on the street)
# 输出: fEEt on the strEEt
makefile 复制代码
2.$(patsubst PATTERN,REPLACEMENT,TEXT)
函数名称:模式替换函数---patsubst。
函数功能:搜索"TEXT"中以空格分开的单词,将否符合模式"TATTERN"替换为"REPLACEMENT"。参数"PATTERN"中可以使用模
式通配符"%"来代表一个单词中的若干字符。如果参数"REPLACEMENT"中也包含一个"%",那么"REPLACEMENT"中的"%"将是
"TATTERN"中的那个"%"所代表的字符串。在"TATTERN"和"REPLACEMENT"中,只有第一个"%"被作为模式字符来处理,后续的作为字符本上来处理。在两个参数中当使用第一个"%"本是字符本身时,可使用反斜杠"\"对它进行转义处理。
返回值:替换后的新字符串。
函数说明:参数"TEXT"单词之间的多个空格在处理时被合并为一个空格,但前导和结尾空格忽略。
$(patsubst PATTERN,REPLACEMENT,TEXT)
SOURCES := main.c utils.c helper.c
OBJECTS := $(patsubst %.c,%.o,$(SOURCES))
#输出: Sources: main.c utils.c helper.c
# 输出: Objects: main.o utils.o helper.o
makefile 复制代码
3.$(strip STRINT)
函数名称:去空格函数---strip。
函数功能:去掉字串(若干单词,使用若干空字符分割)"STRINT"开头和结尾的空字符,并将其中多个连续空字符合并为一个空字符。
返回值:无前导和结尾空字符、使用单一空格分割的多单词字符串。
函数说明:空字符包括空格、[Tab]等不可显示字符。
makefile 复制代码
4.$(findstring FIND,IN)
函数名称:查找字符串函数---findstring。
函数功能:搜索字串"IN",查找"FIND"字串。
返回值:如果在"IN"之中存在"FIND",则返回"FIND",否则返回空。
函数说明:字串"IN"之中可以包含空格、[Tab]。搜索需要是严格的文本匹配。
makefile 复制代码
5.$(filter PATTERN...,TEXT)
函数名称:过滤函数---filter。
函数功能:过滤掉字串"TEXT"中所有不符合模式"PATTERN"的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符"%"。存在多个模式时,模式表达式之间使用空格分割。
返回值:空格分割的"TEXT"字串中所有符合模式"PATTERN"的字串。
函数说明:"filter"函数可以用来去除一个变量中的某些字符串,我们下边的例子中就是用到了此函数。
$(filter PATTERN...,TEXT)
FILES := main.c utils.o helper.c README.md
C_FILES := $(filter %.c,$(FILES))
#输出: C files: main.c helper.c
FILES := main.c utils.h helper.c README.md config.h
SOURCE_FILES := $(filter %.c %.h,$(FILES))
# 输出: main.c utils.h helper.c config.h
sql 复制代码
6.$(filter-out PATTERN...,TEXT)
函数名称:反过滤函数---filter-out。
函数功能:和"filter"函数实现的功能相反。过滤掉字串"TEXT"中所有符合模式"PATTERN"的单词,保留所有不符合此模式的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割。。
返回值:空格分割的"TEXT"字串中所有不符合模式"PATTERN"的字串。
函数说明:"filter-out"函数也可以用来去除一个变量中的某些字符串,(实现和"filter"函数相反)。
bash 复制代码
7.$(sort LIST)
函数名称:排序函数---sort。
函数功能:给字串"LIST"中的单词以首字母为准进行排序(升序),并取掉重复的单词。
返回值:空格分割的没有重复单词的字串。
函数说明:两个功能,排序和去字串中的重复单词。可以单独使用其中一个功能。
scss 复制代码
8.$(word N,TEXT)
函数名称:取单词函数---word。
函数功能:取字串"TEXT"中第"N"个单词("N"的值从1开始)。
返回值:返回字串"TEXT"中第"N"个单词。
函数说明:如果"N"值大于字串"TEXT"中单词的数目,返回空字符串。如果"N"为0,出错!
scss 复制代码
9.$(wordlist S,E,TEXT)
函数名称:取字串函数---wordlist。
函数功能:从字串"TEXT"中取出从"S"开始到"E"的单词串。"S"和"E"表示单词在字串中位置的数字。
返回值:字串"TEXT"中从第"S"到"E"(包括"E")的单词字串。
函数说明:"S"和"E"都是从1开始的数字。
当"S"比"TEXT"中的字数大时,返回空。如果"E"大于"TEXT"字数,返回从"S"开始,到"TEXT"结束的单词串。如果"S"大于"E",返回空。
scss 复制代码
10.$(words TEXT)
函数名称:统计单词数目函数---words。
函数功能:字算字串"TEXT"中单词的数目。
返回值:"TEXT"字串中的单词数。
makefile 复制代码
11.$(firstword NAMES...)
函数名称:取首单词函数---firstword。
函数功能:取字串"NAMES..."中的第一个单词。
返回值:字串"NAMES..."的第一个单词。
函数说明:"NAMES"被认为是使用空格分割的多个单词(名字)的序列。函数忽略"NAMES..."中除第一个单词以外的所有的单词。

二、文件名处理函数

makefile 复制代码
1.$(dir NAMES...)
函数名称:取目录函数---dir。
函数功能:从文件名序列"NAMES..."中取出各个文件名目录部分。文件名的目录部分就是包含在文件名中的最后一个斜线("/")(包括斜线)之前的部分。
返回值:空格分割的文件名序列"NAMES..."中每一个文件的目录部分。
函数说明:如果文件名中没有斜线,认为此文件为当前目录("./")下的文件。
$(dir NAMES...)
PATHS := src/main.c include/config.h /usr/local/bin/app ./README.md
DIRS := $(dir $(PATHS))
# 输出: Directories: src/ include/ /usr/local/bin/ ./
makefile 复制代码
2.$(notdir NAMES...)
函数名称:取文件名函数------notdir。
函数功能:从文件名序列"NAMES..."中取出非目录部分。目录部分是指最后一个斜线("/")(包括斜线)之前的部分。删除所有文件名中的目录部分,只保留非目录部分。
返回值:文件名序列"NAMES..."中每一个文件的非目录部分。
函数说明:如果"NAMES..."中存在不包含斜线的文件名,则不改变这个文件名。以反斜线结尾的文件名,是用空串代替,因此当"NAMES..."中存在多个这样的文件名时,返回结果中分割各个文件名的空格数目将不确定!这是此函数的一个缺陷。
$(notdir NAMES...)
PATHS := src/main.c include/config.h /usr/local/bin/app ./README.md
NODIRS := $(notdir $(PATHS))
# 输出: Directories: main.c config.h config.h README.md
makefile 复制代码
3.$(suffix NAMES...)
函数名称:取后缀函数---suffix。
函数功能:从文件名序列"NAMES..."中取出各个文件名的后缀。后缀是文件名中最后一个以点"."开始的(包含点号)部分,如果文件名中不包含一个点号,则为空。
返回值:以空格分割的文件名序列"NAMES..."中每一个文件的后缀序列。
函数说明:"NAMES..."是多个文件名时,返回值是多个以空格分割的单词序列。如果文件名没有后缀部分,则返回空。
$(suffix NAMES...)
FILES := main.c utils.o README.md archive.tar.gz /usr/lib/libm.a
SUFFIXES := $(suffix $(FILES))
# 输出: File suffixes: .c .o .md .gz .a
makefile 复制代码
4.$(basename NAMES...)
函数名称:取前缀函数---basename。
函数功能:从文件名序列"NAMES..."中取出各个文件名的前缀部分(点号之后的部分)。前缀部分指的是文件名中最后一个点号之前的部分。
返回值:空格分割的文件名序列"NAMES..."中各个文件的前缀序列。如果文件没有前缀,则返回空字串。
函数说明:如果"NAMES..."中包含没有后缀的文件名,此文件名不改变。如果一个文件名中存在多个点号,则返回值为此文件名的最后一个点号之前的文件名部分。
$(basename NAMES...)
FILES := main.c utils.o README.md archive.tar.gz /usr/lib/libm.a
BASENAMES := $(basename $(FILES))
# 输出: Basenames: main utils README archive /usr/lib/libm
makefile 复制代码
5.$(addsuffix SUFFIX,NAMES...)
函数名称:加后缀函数---addsuffix。
函数功能:为"NAMES..."中的每一个文件名添加后缀"SUFFIX"。参数"NAMES..."为空格分割的文件名序列,将"SUFFIX"追加到此序列的每一个文件名的末尾。
返回值:以单空格分割的添加了后缀"SUFFIX"的文件名序列。
$(addsuffix SUFFIX,NAMES...)
NAMES := main util helper
FILES := $(addsuffix .c,$(NAMES))
# 输出: Source files: main.c util.c helper.c
makefile 复制代码
6.$(addprefix PREFIX,NAMES...)
函数名称:加前缀函数---addprefix。
函数功能:为"NAMES..."中的每一个文件名添加前缀"PREFIX"。参数"NAMES..."是空格分割的文件名序列,将"SUFFIX"添加到此序列的每一个文件名之前。
返回值:以单空格分割的添加了前缀"PREFIX"的文件名序列。
$(addprefix PREFIX,NAMES...)
NAMES := main.c util.c helper.c
OBJS := $(addprefix obj/,$(NAMES))
# 输出: Object files: obj/main.c obj/util.c obj/helper.c
makefile 复制代码
7.$(join LIST1,LIST2)
函数名称:单词连接函数------join。
函数功能:将字串"LIST1"和字串"LIST2"各单词进行对应连接。就是将"LIST2"中的第一个单词追加"LIST1"第一个单词字后合并为一个单词;将"LIST2"中的第二个单词追加到"LIST1"的第一个单词之后并合并为一个单词,......依次列推。
返回值:单空格分割的合并后的字(文件名)序列。
函数说明:如果"LIST1"和"LIST2"中的字数目不一致时,两者中多余部分将被作为返回序列的一部分。
$(join LIST1,LIST2)
LIST1 := src- lib- util-
LIST2 := main.c helper.c
JOINED := $(join $(LIST1),$(LIST2))
# 输出: Joined list: src-main.c lib-helper.c util-
makefile 复制代码
8.$(wildcard PATTERN)
函数名称:获取匹配模式文件名函数---wildcard
函数功能:列出当前目录下所有符合模式"PATTERN"格式的文件名。
返回值:空格分割的、存在当前目录下的所有符合模式"PATTERN"的文件名。
函数说明:"PATTERN"使用shell可识别的通配符,包括"?"(单字符)、"*"(多字符)等。
$(wildcard PATTERN)
PATTERN: 文件匹配模式(支持 *, ?, [...] 等通配符
SRC_FILES := $(wildcard src/*.c lib/*.c)
# 输出可能类似: Source files: src/main.c src/helper.c lib/utils.c

三、其它函数

makefile 复制代码
1.$(foreach VAR,LIST,TEXT)
函数功能:函数"foreach"不同于其它函数。它是一个循环函数。类似于Linux的shell中的循环(for语句)。这个函数的工作过程是这样
的:如果必要(存在变量或者函数的引用),首先展开变量"VAR"和"LIST";而表达式"TEXT"中的变量引用不被展开。执行时把"LIST"中使
用空格分割的单词依次取出赋值给变量"VAR",然后执行"TEXT"表达式。重复直到"LIST"的最后一个单词(为空时结束)。"TEXT"中的变量
或者函数引用在执行时才被展开,因此如果在"TEXT"中存在对"VAR"的引用,那么"VAR"的值在每一次展开式将会到的不同的值。
返回值:空格分割的多次表达式"TEXT"的计算的结果。
$(foreach VAR,LIST,TEXT)
NAMES := main util helper
FILES := $(foreach name,$(NAMES),$(name).c)
\# 输出: Files: main.c util.c helper.c
makefile 复制代码
2.$(if CONDITION,THEN-PART[,ELSE-PART])
函数功能:函数"if"提供了一个在函数上下文中实现条件判断的功能。就像make所支持的条件语句---ifeq。第一个参数"CONDITION",在函
数执行时忽略其前导和结尾空字符并展开。"CONDITION"的展开结果非空,则条件为真,就将第二个参数"THEN_PATR"作为函数的计算表达
式,函数的返回值就是第二表达式的计算结果;"CONDITION"的展开结果为空,将第三个参数
"ELSE-PART"作为函数的表达式,返回结果为第三个表达式的计算结果。
返回值:根据条件决定函数的返回值是第一个或者第二个参数表达式的计算结果。当不存在第三个参数"ELSE-PART",并且"CONDITION"展开为空,函数返回空。
函数说明:函数的条件表达式"CONDITION"决定了,函数的返回值只能是"THEN-PART"或者"ELSE-PART"两个之一的计算结果。
$(if CONDITION,THEN-PART[,ELSE-PART])
DEBUG := 1
CFLAGS := $(if $(DEBUG),-g -O0,-O2)
# 当DEBUG=1时输出: CFLAGS: -g -O0
# 当DEBUG为空时输出: CFLAGS: -O2
COMPILER := gcc
CC_FLAGS := $(if $(filter gcc,$(COMPILER)),-Wall -Wextra,$(if $(filter clang,$(COMPILER)),-Weverything,))
# 当COMPILER=gcc时输出: Compiler flags: -Wall -Wextra
# 当COMPILER=clang时输出: Compiler flags: -Weverything
# 其他情况输出: Compiler flags:
ruby 复制代码
3.\$(call VARIABLE,PARAM,PARAM,...)
函数功能:"call"函数是唯一一个可以创建定制参数化的函数的引用函数。我们可以将一个变量定义为一个复杂的表达式,用"call"函数根据不同的参数对它进行展开来获得不同的结果。
在执行时,将它的参数"PARAM"依次赋值给临时变量"$(1)"、"$(2)"(这些临时变量定义在"VARIABLE"的值中,参考下边的例
子)......
call函数对参数的数目没有限制,也可以没有参数值,没有参数值的"call"没有任何实际存在的意义。执行时变量"VARIABLE"被展开为在函数
上下文有效的临时变量,变量定义中的"\$(1)"作为第一个参数,并将函数参数值中的第一个参数赋值给它;变量中的"\$(2)"一样被赋值为函数的第二个
参数值;依此类推(变量$(0)代表变量"VARIABLE"本身)。之后对变量"VARIABLE" 表达式的计算值。
返回值:参数值"PARAM"依次替换"\$(1)"、"\$(2)"...... 之后变量"VARIABLE"定义的表达式的计算值。
函数说明:
1.函数中"VARIBLE"是一个变量名,而不是对变量的引用。因此,通常"call"函数中的"VARIABLE"中不包含"$"(当然,除了此变量名是
一个计算的变量名)。
2.当变量"VARIBLE"是一个make内嵌的函数名时(如"if"、"foreach"、"strip"等),对"PARAM"参数的使用需要注意,因
为不合适或者不正确的参数将会导致函数的返回值难以预料。
3. 函数中多个"PARAM"之间使用逗号分割。
4.变量"VARIABLE"在定义时不能定义为直接展开式!只能定义为递归展开式。
$(call VARIABLE,PARAM,PARAM,...)
reverse = $(2) $(1)
RESULT := $(call reverse,hello,world)
# 输出: Result: world hello
typescript 复制代码
4.value函数 \$(value VARIABLE)
函数功能:不对变量"VARIBLE"进行任何展开操作,直接返回变量"VARIBALE"代表的值。这里"VARIABLE"是一个变量名,一般不包含"\$"(当然,除了计算的变量名),
返回值:变量"VARIBALE"所定义文本值(不展开其中的变量或者函数应用)。
PATH_VAR := /usr/local/$(shell uname -m)/bin
RAW_VALUE := \$(value PATH_VAR)
@echo "Original value: \$(RAW_VALUE)"

#输出: Original value: /usr/local/$(shell uname -m)/bin@echo "Expanded value: $(PATH_VAR)"
# 输出: Expanded value: /usr/local/x86_64/bin
javascript 复制代码
5.eval函数
函数功能:函数"eval"是一个比较特殊的函数。使用它我们可以在我们的Makefile中构造一个可变的规则结构关系(依赖关系链),其中可以使用其
它变量和函数。函数"eval"对它的参数进行展开,展开的结果作为Makefile的一部分,make可以对展开内容进行语法解析。展开的结果可以包含
一个新变量、目标、隐含规则或者是明确规则等。也就是说此函数的功能主要是:根据其参数的关系、结构,对它们进行替换展开。
返回值:函数"eval"的返回值时空,也可以说没有返回值。
函数说明:"eval"函数执行时会对它的参数进行两次展开。第一次展开过程发是由函数本身完成的,第二次是函数展开后的结果被作为Makefile内容
时由make解析时展开的。明确这一点对于使用"eval"函数非常重要。在理解了函数"eval"二次展开的过程后。实际使用时,当函数的展开结果中存
在引用(格式为:\$(x))时,那么在函数的参数中应该使用"\$\$"来代替"\$"。因为这一点,所以通常它的参数中会使用函数"value"来取一个变量
的文本值。
typescript 复制代码
6.origin函数\$(origin VARIABLE)
函数功能:函数"origin"查询参数"VARIABLE"(通常是一个变量名)的出处。
函数说明:"VARIABLE"是一个变量名而不是一个变量的引用。因此通常它不包含"\$"(当然,计算的变量名例外)。
返回值:返回"VARIABLE"的定义方式。用字符串表示。
. undefined
变量"VARIABLE"没有被定义。
. default
变量"VARIABLE"是一个默认定义(内嵌变量)。如"CC"、"MAKE"、"RM"等变量。如果在Makefile中重新定义这些变量,函数返回值将相应发生变化。
. environment
变量"VARIABLE"是一个系统环境变量,并且make没有使用命令行选项"-e"(Makefile中不存在同名的变量定义,此变量没有被替代)。
. environment override
变量"VARIABLE"是一个系统环境变量,并且make使用了命令行选项"-e"。Makefile中存在一个同名的变量定义,使用"make -e"时环境变量值替代了文件中的变量定义。
. file
变量"VARIABLE"在某一个makefile文件中定义。
. command line
变量"VARIABLE"在命令行中定义。
. override
变量"VARIABLE"在makefile文件中定义并使用"override"指示符声明。
. automatic
变量"VARIABLE"是自动化变量。
go 复制代码
7.shell函数 $(shell cmd)
不同于除"wildcard"函数之外的其它函数。make可以使用它来和外部通信。
函数功能:函数"shell"所实现的功能和shell中的引用(``)相同。实现了命令的扩展。意味着需要一个shell
命令作为它的参数,而返回的结果是此命令在shell中的执行结果。make仅仅对它的回返结果进行处理;make将函数的返回结果中的所有换行符
("\n")或者一对"\n\r"替换为单空格;并去掉末尾的回车符号("\n")或者"\n\r"。函数展开式时,它所调用的命令(它的参数)得到执
行。除了对它的引用出现在规则的命令行中和递归的变量定义引用以外,其它决大多数情况下,make在读取Makefile时函数shell就被扩展。
返回值:函数"shell"的参数在shell中的执行结果。
函数说明:函数本身的返回值是其参数的执行结果,没有进行任何处理。对结果的处理是由make进行的。当对函数的引用出现在规则的命令行中,命令行在执行
时函数引用才被展开。展开过程函数参数的执行时在另外一个shell进程中完成的,因此对于出现在规则命令行的多级"shell"函数引用需要谨慎处理,
否则会影响效率(每一级的"shell"函数的参数都会有各自的shell进程)。
go 复制代码
**8.error 函数$(error TEXT...)**
函数功能:产生致命错误,并提示"TEXT..."信息给用户,之后退出make的执行。需要说明的是:"error"函数是在函数展开式(函数被调用时)才
提示信息并结束make进程。因此如果函数出现在命令中或者一个递归的变量定义中时,在读取Makefile时不会出现错误。而只有包含
"error"函数引用的命令被执行,或者定义中引用此函数的递归变量被展开时,才会提示致命信息"TEXT..."同时make退出执行。
返回值:空字符
函数说明:"error"函数一般不出现在直接展开式的变量定义中,否则在make读取Makefile时将会提示致命错误。
go 复制代码
**9. warning 函数$(warning TEXT...)**
函数功能:函数"warning"类似于函数"error",区别在于它不会导致致命错误(make不退出),而只是提示"TEXT...",make的执行过程继续。
返回值:空字符
函数说明:用法和"error"类似,展开过程相同。
makefile 复制代码
用于生产指定对象的 目标 依赖 命令 伪目标.PHONY
变量要用$()
= := ?= +=
ifdef xxx
endif
ifeq (xxx,xxx)
endif
ifneq (xxx,xxx)
endif



$(substr from,to,text) //字符串替换,text中from换成to
$(patsubstr from,to,text)//模式字符串替换,from,to可以带通配符号
$(strip text) //去掉空格,左右空格去掉,中间空格连续的只保留一个
$(sort text) //text从新拍下,按照字母排序。
$(findstring DST,TEXT)//在TEXT中找DST,找到则返回DST,失败则返回空
$(filter partition,TEXT)//过滤字符串,符合模式的则保留
$(filter-out partition,TEXT)//过滤字符串。符合模式的去除。
$(words NAMES) //NAMES的单词个数
$(firstword NAMES)//NAMES的第一个单词
$(word INDEX,NAMES)//NAMES的第INDEX个单词
$(wordlist S,E,NAMES)//NAMES的S到E之间单词

$(dir NAMES)
$(nodir NAMES)
$(suffix NAMES)
$(basename NAMES)
$(addsuffix suffix,NAMES)//添加后缀
$(addprefix prefix,NAMES)//添加前缀
$(join list1,list2)
$(wildcard partition)
相关推荐
吴声子夜歌5 天前
Shell编程实例——编写安全的shell脚本(二)
linux·运维·shell
吴声子夜歌6 天前
Shell编程实例——内务及管理任务(一)
linux·运维·shell
吴声子夜歌6 天前
Shell编程实例——高级脚本编程(一)
linux·运维·网络·shell
吴声子夜歌6 天前
Shell编程实例——bash的配置与自定义(二)
linux·运维·shell
吴声子夜歌7 天前
Shell编程实例——与解析相关的任务(二)
linux·运维·shell
吴声子夜歌7 天前
Shell编程实例——脚本编程的附加特性
linux·运维·shell
云计算练习生7 天前
什么是内核?操作系统内核到底管哪些事
linux·windows·操作系统·内核·shell
吴声子夜歌10 天前
Shell编程实例——bash入门
linux·运维·shell
Swizard10 天前
Linux 日志神器 journalctl 完全指南:彻底替代传统日志查看方式
shell
吴声子夜歌11 天前
Shell编程——数组
linux·运维·shell