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 头文件路径-优快云博客