makefile 快速回忆

Makefile

关键
makefile 中的变量本质上类似于C的宏
工作原理是查找依赖
include<othrer makefile> 行为和C的include行为相同
每条规则下的每一行command,命令间没联系。可以通过分号,在一行写多个。或者使用续行符/
基础结构(结构)
TARGET:OBJS
	COMMAND
	
makefile工作原理:
判断OBJS(依赖文件)是否比TARGET(目标)新,是则执行COMMAND(命令)
OBJS和COMMAND都可以为空
只使用make,后面不接目标,命令执行Makefile的第一条规则
使用make [target],则执行target对应的规则


target : obj1 obj2
	shell命令
obj1:file1
	shell命令
obj2:file2
	shell命令
注意点:
1. shell命令必须以 tab 开头,不能用4个空格代替
2. 当obj1 或 obj2 的更新时间比target新的时候,就会重新执行下方的shell命令来更新target
进阶(符号,规则)
TATGET=test.so
OBJS=arc.o
CC=gcc
CFLAGS=-fPIC -c
LDFLAGS=-shared
LIBS=-lpthread -L../libs
INCLUDE=-I../include
$(TARGET):$(OBJS)
	$(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $(INCLUDE) $^ -o $@
arc.o : arc.c
	$(CC) $^ -o arc.o

clean:
	-rm $(OBJS) $(TARGET)
.PHONY: clean

注意:
1.makefile中有许多内部变量,遇到时再学习,$^ 表示不重复的依赖文件,$@ 表示不带后缀名的目标文件
2.当命令失败时,make会停止,如果允许命令失败后继续执行,在命令前加-
3.默认情况下,makefile会把命令逐行打印出来,在命令前加@可以让命令不打印
4.makefile注释和shell相同
5.变量定义是使用 a = b,改变a的值时,用 a := b

自动推导:
a.o : xx.h
会自动推到出
a.o : a.c xx.h
	gcc -o a.o a.c xx.h
谨慎使用自动推导


引入文件:
将其他Makefile文件添加(引用)到当前位置
1.`include<filename> # filename可以是当前操作系统Shell的文件模式(可以包含路径和通配符),可以通过空格,引入多个文件`
2.如果 make 执行时,有 -I 或 --include-dir 参数,那么 make 就会在这个参数所指定的目录下去寻找。
3.如果环境变量定义了MAKEFILES,make时会将该值当成是include对象



GNU 的 make 工作时的执行步骤如下:
1.读入所有的 Makefile
2.读入被 include 的其它 Makefile
3.初始化文件中的变量
4.推导隐晦规则,并分析所有规则
5.为所有的目标文件创建依赖关系链
6.根据依赖关系,决定哪些目标要重新生成
7.执行生成命令

文件搜索:
查找依赖文件时,默认是在当前目录下,可以使用VPATH和vpath添加查找路径
VPATH=path1:bbb/path2,定义指定两个目录,path1 和 bbb/path2,目录由冒号分隔,当前目录永远最高优先搜索。
vpath <pattern> <directories> 	# 为符合模式< pattern>的文件指定搜索目录<directories>
vpath <pattern>                 # 清除符合模式< pattern>的文件的搜索目录
vpath                           # 清除所有已被设置好了的文件搜索目录
比如: vapth %.h ../headers 这样.h文件都会在../heards下查找


嵌套执行:
在makefile中,通过规则下的command,执行其他makefile
ubsystem:
	cd subdir && $(MAKE)
关于变量传递:
1.变量可以传递到下级的 Makefile 中(如果你显示的声明),但是不会覆盖下层的 Makefile 中所定义的变量,除非指定了 -e 参数。
2.传递变量到下级 Makefile 中,export<variable ...>
3.不想让某些变量传递到下级 Makefile 中,unexport<variable ...>
4.如果你要传递所有的变量,只要一个 export 就行了。后面什么也不用跟
5.系统级的环境变量,一个是 SHELL ,一个是 MAKEFLAGS ,这两个变量不管你是否 export ,其总是要传递到下层 Makefile, MAKEFILES 变量,其中包含了 make 的参数信息。如果不想传递,可以在嵌套执行前赋空值

特殊符号:
$(xxx) : 使用makefile定义的变量
$$(xxx):使用shell定义的变量

$@ 表示目标文件
$^ 表示所有的依赖文件
$< 表示第一个依赖文件
$? 表示比目标还要新的依赖文件列表
高级(函数)
makefile 函数
字符串处理:
$(<function> <arguments>),参数通过逗号分隔

1. 字符串替换函数
$(subst <from>,<to>,<text>),将text字符串中,和<from>匹配的部分,替换成<to>

2.模式串替换函数
$(patsubst <pattern>,<replacement>,<text>)
$(patsubst %.c,%.o,a.c b.c c.c) ,结果为 a.o b.o c.o
模式中%表示任意

3.去除收尾空字符
$(strip <string>)

4.正向过滤
$(filter <pattern...>,<text>),保留符合pattern的字符串
$(filter %.c %.h,a.c a.h a.o a.so) ,结果为a.c a.h

5.反向过滤
$(filter-out <pattern...>,<text>) ,删除text中符合pattern的字符串
$(filter-out %.c %.h,a.c a.h a.o a.so) ,结果为a.o a.so

6.截取字符串
$(wordlist <s>,<e>,<text>),使用位置参数,截取字符串,从1开始
$(wordlist 1,2,abc efg hij mkl) 结果为abc efg


文件或目录处理:
1.去除后缀(配合filter使用,当筛选出某种文件之后,去除后缀)
$(basename <names...>)
$(basename ./src/main.c add.c),结果为./src/main add

2.截取后缀
$(suffix <names...>)

3.添加前缀(常用于指定生成文件的目录)
$(addprefix <prefix>,<names...>)

4.添加后缀(常用于指定生成文件的格式)
$(addsuffix <suffix>,<names...>)

5.通配符,变量的定义和函数引用,统配符会失效,此时需要使用wildcard
$(wildcard ./module/*.c)

6.使用shell命令的结果
$(SHELL <command>)

7.截取目录
$(dir <names...>)
$(dir ./src/main.c add.c),结果为./src/ ./

8.截取文件名
$(notdir <names...>)
$(nodir ./src/main.c add.c),结果为main.c add.c

9.字符串连接
$(join <list1>,<list2>)
将7. 和 8. 的结果当成list1,list2, 调用之后结果为./src/main.c ./add.c

静态模式

<targets...>: <target-pattern>: <prereq-patterns ...>
	<commands>
targets 定义了一系列的目标文件,可以有通配符,是目标的一个集合
target-parrtern 是指明了 targets 的模式,也就是的目标集模式
prereq-parrterns 是目标的依赖模式,它对 target-parrtern 形成的模式再进行一次依赖目标的定义

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
	$(CC) -c $(CFLAGS) $< -o $@
等价于:
foo.o : foo.c
	$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
	$(CC) -c $(CFLAGS) bar.c -o bar.o
	
	
$(filter%.o,$(files)) 表示调用 Makefile 的 filter 函数,过滤 $filter 集,只要其中模式为 %.o 的内容。

技巧

变量替换
foo := a.o b.o c.o
bar := $(foo:.o=.c)
这个示例中,我们先定义了一个 $(foo) 变量,而第二行的意思是把 $(foo) 中所有以 .o 字串结尾的内容全部替换成 .c ,所以我们的 $(bar) 的值就是 a.c b.c c.c ;另外一种变量替换的技术是以静态模式定义的,如:
foo := a.o b.o c.o
bar := $(foo:%.c=%.o)

环境变量传递规则
1.make 运行时的系统环境变量可以在 make 开始运行时被载入到 Makefile 文件中
2.如果 Makefile 中已定义了这个变量,或是这个变量由 make 命令行带入,那么系统的环境变量的值将被覆盖
3.make 指定了 -e 参数,那么,系统环境变量将覆盖 Makefile 中定义的变量。
4.Makefile 中定义的变量,如果使用了export,则会以系统环境变量的方式传递到下层的 Makefile 中,否则不传递
自动变量
$@ # 表示目标文件的名称,包含扩展名 
$^ # 表示所有的依赖文件,以空格隔开,不重复
$< # 表示第一个依赖文件的名称
$+ # 表示所有的依赖文件,空格隔开,可以重复
$* # 表示目标文件的名称,不包含扩展名
$? # 依赖项中,所有比目标文件新的依赖文件
目标变量,模式变量
prog : CFLAGS = -g

prog : prog.o foo.o bar.o
	$(CC) $(CFLAGS) prog.o foo.o bar.o
	
在这个示例中,不管全局的 $(CFLAGS) 的值是什么,在 prog 目标,以及其所引发的所有规则中 $(CFLAGS) 的值都是 -g

这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的规则中去。

模式变量:
用法和目标变量基本一样,只不过把规则换成模式指定的了
<pattern ...> : <variable-assignment>
<pattern ...> : override <variable-assignment>

%.o : CFLAGS = -O
这样,所有.o的目标,CFLAGS值都是 -o

条件判断
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
origin函数
origin 函数会以其返回值来告诉你这个变量的“出生情况”:

如果 variable 从来没有定义过,返回 undefined
如果 variable 是一个默认的定义,返回 default
如果 variable 是一个环境变量,并且当 Makefile 被执行时, -e 参数没有被打开,返回 environment
如果 variable 这个变量被定义在 Makefile 中,返回 file
如果 variable 这个变量是被命令行定义的,返回 command line
如果 variable 是被 override 指示符重新定义的,返回 override
如果 variable 是一个命令运行中的自动化变量,返回 automatic
函数功能速查
取目录函数 dir
取文件函数 notdir
匹配文件函数 wildcard
取后缀函数 suffix
取前缀函数 basename
加后缀函数 addsuffix
加前缀函数 addprefix
连接函数 join
调试技巧
1. make -n target
只显示要执行的命令,不会真正执行命令
2. make -j target
将执行的命令都打印出来,同时也会真正执行命令
3.$(warning $(var))
打印信息,和行号
4.$(info $(var))
打印信息

总结

知识点列表
自动推导
引入文件, 路径, MAKEFILES
依赖文件查找路径 VPATH, vpath
makefile工作流程
静态模式 
嵌套执行,系统变量,export(单,全,否)
命令包(多行变量)
赋值 =, :=, ?=, +=, 切记使用:=直接赋值
变量替换
环境变量传递规则
自动变量
目标变量,模式变量
条件判断
场景1
目录树下有多个目录,每个目录下有若干个c文件,最后的目标文件由所有的c编译后组成。
一:获取源文件列表,并设置查找路径
1.遍历取得所有c文件名
2.去除文件名路径,获得.c列表
3.转换文件名,为中间文件.o列表
4.配置vpath查找路径
二:构建(使用静态模式)
target : $(objlist)
	$(CC) -o $@ $(FLAG) $^
$(objlist) : %.o : %.c
	$(CC) -o $@ $(FLAG) $^
常用技巧
1.批量替换
foo := a.o b.o c.o
bar := $(foo:.c=.o)

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

2. 条件判断
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif

3.依赖搜索路径
VPATH=path1:bbb/path2
vapth %.h ../headers

4.获取目录树下所有.h .c文件
$(shell find -type f -name *.c)


5.制定make命令的搜索路径
make -I <path>


易疏漏点
1.变量定义后不要写注释,否则可能多余空格会被当成变量值
2.变量赋值时使用:= 而不是 =

调试

命令行参数:

posted @ 2022-05-28 20:00  木瓜粉  阅读(27)  评论(0编辑  收藏  举报