makefile 的核心其实只有3句话.
甲. 宏变量. 乙.依赖关系. 丙. 构建命令
不妨理解为宏语句,依赖语句,命令语句. 还有一个条件语句.
========================================
甲: makefile 的 变量赋值, 包含等号的语句为宏语句.
========================================
1、“=” 最后计算展开后赋值
make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:
x = foo
y = $(x) bar
x = xyz
在上例中,y的值将会是 xyz bar ,而不是 foo bar 。
2、“:=” 立即赋值
“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。
x := foo
y := $(x) bar
x := xyz
在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。
?= 是如果没有被赋值过就赋予等号后面的值
TEMP ?= var
ifeq($(TEMP),undefined)
TEMP = var
endif
+= 是添加等号后面的值
========================================
乙: makefile 依赖关系, 包含冒号":"的语句为依赖语句.
========================================
依赖关系是形如x:y, 则说y依赖与x, 当y比较新时,要执行构建命令.
依赖关系要构成一个完整的链.
----------------------------------------
有问题的最简单的Makefile
----------------------------------------
all:
gcc -o main main.o
main.o: main.c
gcc -c -o main.o main.c
clean:
rm *.o main
运行make, 提示如下错误:
gcc -o main main.o
gcc: main.o: No such file or directory
gcc: no input files
make: *** [all] Error 1
我很奇怪,不是告诉它main.o 依赖main.c 吗,按定义的规则去生成。
而且我查看了make -d 的输出,发现其并没有继续检查main.o 的依赖关系。
后来我对比了正确的makefile 写法, 发现要把main.o 写在all 的右边才能作为依赖
才能使makefile继续检查它的生成关系。
如果你使用隐含规则,甚至不用写.c 生成.o 的规则。
----------------------------------------
下面是正确的写法:all 右边有依赖
----------------------------------------
all:main.o
gcc -o main main.o
clean:
rm *.o main
运行make 的结果:
[root@hjj /home/samba/testapp]# make
cc -c -o main.o main.c
gcc -o main main.o
========================================
丙: make 命令语句.(以tab开始的bash命令)
========================================
命令就是bash 命令,所以你可以使用bash 语言的一些技巧而不仅仅是执行一条命令.
========================================
丁: 除了上边3类语句,makefile 还有条件语句,其功能类似于c语言的条件编译.
========================================
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
条件语句有ifeq, ifneq, ifdef, ifndef
条件是一个变量是否定义及值(字符串值)是否相等.
句法书写举例:
ifeq(argv1,argv2) # 字符串不用加单引号或双引号
ifdef(variable-name) # 不能加$, 是变量名字
举例:
如果 strip 函数返回值为空
ifeq($(strip $(foo)),)
xxx
endif
给个例子:
[~/test]$ cat Makefile
foo=abc
ifdef foo
$(warning foo defined)
endif
========================================
makefile 执行的两个阶段
========================================
第一阶段:读取所有的makefile文件(包括 include 项,命令行 选项)
内建所有的变量、明确规则和隐含规则,
并建立所有目标和依赖之间的依赖关系结构链表。
第二阶段:根据第一阶段已经建立的依赖关系结构链表决定哪些目标需要更新,并使用对应的规则来重建这些目标
========================================
makefile 调试
========================================
调试就是试图监视到make的执行过程, 保证它能够按我们预定的执行过程来运行.
$(warning)函数
它可以放在makefile 中的任何地方:
开始的位置、工作目标或必要条件列表中以及命令脚本中。
这让你能够在最方便的地方查看变量的输出值
实际上就两个地方, 宏语句中和命令语句中.
========================================
对make 的控制, make 命令的参数.
========================================
-n 或“--just-print”,那么其只是显示命令,但不会执行命令,
这个功能很有利于我们调试我们的Makefile,
-p 或(--print-data-base) 是另一个你常会用到的选项。显示make 所运行的命令, 然后输出它的内部数据库。
数据库里的数据将会依种类划分
成以下几个部分:
variables: 有4种, 环境变量(enviroment),自动变量(automatic),默认变量(default),makefile 中定义的变量.
implicit rules. 默认的类型依赖关系及处理法则
target . .default_goal 是默认的维护目标
-d 是另一个有用的选项。 大量的信息可能让人眼花。
“-s”或“--slient”则是全面禁止命令的显示。 控制命令回显。
makefile 以tab开头表示命令,
当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来. 为了向屏幕少输出信息。
令makefile 不包含内部默认规则 make -r 即可.
********************************************************************************
linux makefile 的 V=1 起了什么作用?
makefile 可以在命令行为宏变量赋值, 这实际上是从命令行为宏变量V设置了值V=1,以后就可以用$V来引用了.
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
********************************************************************************
--------------------------------
如何调试makefile变量
--------------------------------
网上看到的一段代码。编写如下一段show_vars.mak
%:
$(warning '$*=$($*)')
d-%:
$(warning '$*=$($*)')
$(warning origin = $(origin $*))
$(warning value = $(value $*))
$(warning flavor = $(flavor $*))
这里% 就是来替换你的目标,就是命令行上你指定的变量,$* 为茎,对于不带.后缀的目标,$*就等于%, 就是变量, $($*) 就是变量引用,这样就显示了变量及变量定义。
d-% 将对变量有更详细的显示。 用户可搜索“如何调试makefile变量” 找到实验例子,这里就不赘述了。
用法: make -f <你的makefile> -f show_vars.mak <预显示的变量>
用remake 可以调试Makefile, 类似于gdb 操作,但需要慢慢熟悉起来.