Makefile规则日积月累

没有检索到摘要

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1."="和":="的区别

"=" makefile会将整个makefile展开后,再决定变量的值,也就是说,变量的值将会是整个makefile中最后被指定的值.

x = foo

y=$(x)bar

x=xyz

y的值是xyzbar,而不是foobar

":="表示的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

x:=foo

y:=$(x)bar

x:=xyz

以上定义只对变量的赋值会有影响,但命令中的定义不会受影响,而是全局展开的。

查看makefile的隐藏规则:

make -p -f /dev/null |grep MAKE

获取绝对地址:

makefile的realpath返回文件的绝对地址,可以使用 $(shell pwd)来替代。

 INCLUDE的优先级:

如果任何一个通过include引入文件的被规则更新,那么makefile会接着清除它的内部数据库并重新读取整个Makefile,在完成了读取,更新,重新读取的过程后,如果仍有文件不存在而导致的include 指令执行失败,那么makefile就会报告错误并终止执行。

Makefile的静态模式%.o : %.c

Makefile的静态模式%.o : %.c_猪哥-嵌入式的博客-优快云博客_%.o

      静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活  
我们还是先来看一下语法: 

<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 $@ 

上面的例子中,指明了我们的目标从$object 中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object 集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.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

关于$符号:

Makefile 里面 $ 是元字符(meta-chara...),也就是它是有特殊意义的。那在 Makefile 里面表示"字符" $ 就得用 $$. 看第一次求值的结果就知道了,不用多说。

有的时候$$可以代替括号,用来对多字母标识符进行引用:

解读:

第一遍,Makefile读取时候,直接对$展开,遇到 $ABC,由于 A表示符没有定义,所以展开为"  BC",输出 BC。而$(ABC),由于()的作用,ABC被识别为整体标识符,由于ABC有定义,所以展开为 "i  love you", 第三和第四种情况只查一个""符,没有本质区别,以echo $$ABC为例, $$ABC第一遍展开为$ABC,当给echo 输出的时候,再次展开的时候,变为$ABC,有定义,直接输出"i love you."

至于为何第一次 直接在makefile中echo $ABC不行,而经过$$两级展开后,变得可以,只能怀疑第三行的第一次展开是makfile系统下展开的,没有括号的情况下,$只和第一个字母配对,多字母标识符必须要加括号。而第6行则不同,它第二次是给echo看的,是echo解析的,echo 不需要加括号就可以识别整个多字母标识符号。这个过程有点像直接在bash shell中这样调用:

makefile强制更新目标:

第一步,建立空目标

PHONY += FORCE
FORCE:

第二步,将空目标作为想要强制更新的目标的依赖:

$(target): FORCE
    xxx

Makefile中获取git环境的版本ID

commid = $(shell git rev-parse HEAD)
all:
    echo $(commid)

Makefile中更新版本信息的方式:

Makefile中执行shell脚本,不在命令中的直接忽略,在命令中的执行,要写成一行,或者 \隔开。

源码路径搜索

在Makefile中增加源代码搜索路径可以通过vpath指令来实现。vpath指令允许你指定一个或多个目录,当查找文件时,如果在当前目录找不到文件,就会在这些指定的目录中查找。

例如,如果你想要在src目录下搜索源文件,你可以在Makefile中添加以下内容:

LINUX内核中在进行out of source tree编译的时候:

$ make menuconfig O=/home/czl/Workspace/linux-kernel/output
$ make O=/home/czl/Workspace/linux-kernel/output

会利用到这个特性查找源码,原理是这样的,Kbuild定义了两个变量,src和obj,其中,src指向源码目录,obj目录指向编译目标目录,按道理执行 make O=/path/to/output/object这种编译方式时,obj和src应该是不同的,但是经过实际测试,在任何编译目录下,这两个目录都是相同的:

并且Makefile.build中,也能看出无论何种情况下,src和obj都是相同的。

并且即便在cc_o_c实际编译源码的命令实现中,src和obj也是相同的:

这是为何呢? 要知道此时的obj和src实际上都是相对于/path/to/output/object下的相对路径,如果这样的话,是如何找到编译的源码的呢?我们看一下编译的依赖源文件:

可以看到,依赖源文件竟然是在真正的源码路径下,但是我们编译的时候,不是已经切换到OUTPUT下了吗,怎么找的到呢?

"$<"是自动变量,会根据环境自动调整,既然src/obj都是一个相对目录保持不变,无法和output组合找到源码位置,那么一定是环境那里的设置,导致自动变量自动找到了源码,这个设置的地方就是顶层Makefile 的VPATH,VPATH是Makefile的内建变量,设置了将源码添加到VPATH,则MAKEFILE将可以自动找到源码所在的位置。

srctree指向了源码的绝对目录,添加到VPATH后,不管MAKEFIL工作在那个目录,都可以通过VPATH找到srctree,得到真正的源码位置.参考dumpstack项目的pcie目录构建方式。

所以,即便src符号没有携带源码位置的绝对路径信息,仍然可以通过VPATH找到源码。

可以看到,只有make[0]指向源码树,其余的子阶make是真正的编译流,他们全部都在O=指定的目录下,但是即便如此,有了VPATH的牵线搭桥,仍然可以在低阶的MAKE执行流程中获取源码真正的位置。

源码位置=${VPATH}/${src}/%.c

编译过程中,make[x]中的x表示的是第几级make,在MAKEFILE中每次执行一次MAKE就会fork一个新的make进程执行对应的目标命令,每个新进程执行级X都会加一,所以make[x-1]是make[x]的父进程。

参考文档

Makefile基础教程(路径搜索)_makefile 头文件路径-优快云博客


结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值