老牛知点所以然-也说Makefile自动变量/后缀依赖/伪指令

想学习一个短小精悍的C项目,被导流到了cJSON这个C语言的项目.
代码clone下来有一年多了,每每望洋兴叹,感慨这方面的知识匮乏.也曾经盲人摸象式的点滴积累摸索Makefile中的一些细节,终于终于,历经艰深晦涩的一段懵懂,慢慢抚触到了一些精美的珠玑.

引入

学习这类脚本型的语言,或者说工具,最大的拦路虎是各种奇怪的符号和陌生的语法结构,比如,当我第一次注意到Makefile中有$@$<这类的符号,满心都是懵逼的.即使各种百度,面对各种专业的说法,我的内心是更加崩溃的,这到底是个什么玩意???

自动变量

上述两个符号,其实叫做自动变量,他们专业的解释是:$@代表目标文件,$<代表第一个依赖文件.对于一个颤颤巍巍想窥探Makefile奥秘的新手来说,这是毁灭性的说法.以cJSON中Makefile中出现这两个符号的代码片段为例:

$(CJSON_STATIC): $(CJSON_OBJ)
	$(AR) rcs $@ $<
#cJSON_Utils
$(UTILS_STATIC): $(UTILS_OBJ)
	$(AR) rcs $@ $<

Makefile的书写规则,顶格写的xxx:yyy代表的是依赖关系,xxx代表目标文件,yyy代表生成xxx文件所依赖的文件.首行有tab符号的,是根据上一行的依赖关系,要执行的命令.那么第二行,$(AR) rcs代表调用Linux系统的ar命令,以rcs为命令参数,将$<代表的文件,归档到$@代表的文件中去.于是就有了如下图示:
相比原Makefile,我在ar命令参数中增加了v
这玩意通俗的讲,就是从上面依赖关系里取实际参数的,理解成占位符?或者SQL中防止注入的通配符?$@代表目标文件,那么取的就是CJSON_STATIC变量所代表的真实值libJson.a,$<代表的第一个依赖文件,那就是CJSON_OBJ变量所代表的cJSON.o了.
当然,Makefile中还有其他两种类型的自动变量:$^ $? $% $+ $*各自有不同的意义.单就$@ $<两个来说,按照上述理解应该是没错了.

后缀依赖

百度半天才感觉这个称呼靠谱点,就是Makefile中一行.c.o的解释,原文:

.c.o:
	$(CC) -c $(R_CFLAGS) $<

先不管.c.o是干嘛的,直接看第二行,其实就是调用gcc,传入R_CFLAGS代表的编译参数,编译某个C文件.
细读cJson中的Makefile,发现在执行make static过程中,CJSON_STATIC依赖CJSON_OBJ,CJSON_OBJ依赖cJSON.ccJSON.h两个文件.
但是死活没有找到在那里候执行的gcc编译指令.
然后只能推测.c.o下面的指令,是gcc编译语句,于是推断,.c.o的作用,是将目录下所有c文件,执行其后面的指令,编译成目标文件. 结论对吗?不能说错,只不过不是我原来想象的样子.
为了验证我的推断,我新建了目录,写了两个简单的c文件和一个Makefile文件:
在这里插入图片描述
其中,a.c和b.c里各有个一简单的函数,Makefile内容如下所示:

a.o:a.c
b.o:b.c

.c.o:
	gcc -c $< -o $@

我天真的以为由于有.c.o的后缀匹配,且表明了各自两个目标文件和源文件的依赖关系,make程序,会自动将我目录下c源文件编译成目标文件.但是每次它只编译了第一行的依赖关系,一度让我很困惑.果然是新手,竟然忘记了Makefile把各种文件和依赖,组织成了依赖树,需要有牵一发的那个点,才能动全身.下图来自知乎话题"Makefile概念⼊⻔"中配图:
在这里插入图片描述
显然,最后生成app.executable这个文件时所触发的依赖关系,才是提纲挈领,牵一发而动全身的那个点.
于是,在我的Makefile最上面,增加一行,变成这样,才触发了后缀匹配的编译,形成了依赖树,最终,虽然表明依赖关系的a.o:a.cb.o:b.c后面虽然都没有指明任何指令,但是由于依赖关系满足.c.o,于是各自执行了gcc -c $< -o $@指令:

#make后不加任何参数,会默认触发all
all: a.o b.o
a.o:a.c
b.o:b.c

.c.o:
	gcc -c $< -o $@

另外,.c.o是可以被%o:%c替代的.

伪指令

.PHONY:就是这个货,像一个泛着黑光的刺,突兀的钉在Makefile中;我知道它很重要,但是众里寻他千百度,我就是理解不了,它是干什么的.山重水复疑无路,柳暗花明又一村,有些东西的理解,对我这种资质平常的人来说,只能靠持续不断的盲人摸象,慢慢理解.
Makefile的书写规则,顶格写的带冒号的,表明依赖关系,其实冒号前后都是文件,意思是,生成冒号前面的目标文件,需要由冒号后面的依赖文件,其下一行的指令操作,才能生成.
.PHONY:这个指令,是声明,出现在其后面的单词,在以后的依赖中不代表一个文件,其目的,是让其所声明的依赖关系,永远不成立.那么被.PHONY:声明的文件后面的指令,会永远执行.
场景:我需要一个clean指令,每次执行make clean,就将目录下所有目标文件删除:
改写我目录中的Makefile:
在这里插入图片描述
执行make clean,则目录下所有的目标文件被删除了;然后在目录下新建一个clean文件,空文件就可以,然后执行make,重新生成目标文件,然后再次执行make clean,神奇的一幕出现了,clean不执行了!

在这里插入图片描述
如上图所示,由于clean被当成了一个目标文件,make程序发现clean文件已经存在了,且不需要更新,所以clean后面的指令就不执行了,而是报了clean文件已经是最新的错误.
这种情况下,就需要用.PHONY:声明clean不是一个文件了,使clean的依赖条件永远不成立,clean后面的指令,永远能执行:
在这里插入图片描述
执行make clean:
在这里插入图片描述
clean后面的指令,被执行了.当然,为了演示,我这个Makefile文件透着怪怪的丑陋,但是就说明问题而言,这种丑陋,正是可贵的/笑哭
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值