面试被问到关于Makefile的问题,除了读u-boot和内核的Makefile等机会偶尔接触,根本就不熟悉,说读出个大概意思吧?很多细节不知道又不容易理解,没办法,还是从头动手练一下的好,这个行当,任何时候,不动手都是不行的。
可是话说回来,没什么项目什么的怎么找机会练Makefile,干脆就把最简单的程序拆开来,C语言基础知识就不说了,大概意思就是写个小程序,弄几个简单函数比如area()和sum()、print(),分别存在单独的C文件,再让他们都“不辞劳苦”的引用几个基础头文件或者参数设置和宏。
head.h就只直接引用
#include<stdio.h>
parameter.h就是一个
#define PI 3.14
sum是
return a+b;
area是返回圆面积(引用parameter.h)
return PI*D*D;
不怕麻烦的可以自行设计和延展功能,不再赘述。
root@v:/usr/local/shellProgram/Project1# ls
area.c head.h main main.c Makefile parameter.h sum.c
#Makefile版本一 #定义依赖关系,最终成品main需要依赖如下三个对象 main: main.o sum.o area.o #上边那个结果的实现过程 cc -o main main.o sum.o area.o #同上,main.o对象需要依赖main.c和他引用的head.h,后边跟上实现命令 main.o:main.c head.h cc -c main.c sum.o:sum.c head.h cc -c sum.c area.o:area.c head.h parameter.h cc -c area.c clean: rm main main.o sum.o area.o #注意clean需要单独使用makeclean执行,会删除所有.o文件盒最终的结果main文件 #所有命令都要用tab缩进,注释用#,依赖关系用冒号。依赖多个需要空格分开。
上边的东西比较简单直观,适合初学者,但是问题也很多,当你打算自己加个print功能进去,打算再加个计算体积函数,在Makefile版本一中要改三处,这在更大的工程中是无法想象的,所以需要进行改进,出现了版本二
#Makefile版本二 #定义objects参数(这个参数名自拟,不过为了方便理解,最好是相关单词和简称) objects = main.o sum.o area.o #引用objects参数,形式为$(objects),把他理解为C语言的宏,文本替换 #将所有用到main.o sum.o area.o的地方直接替换为$(objects)即可 main:$(objects) cc -o main $(objects) main.o:main.c head.h cc -c main.c sum.o:sum.c head.h cc -c sum.c area.o:area.c head.h parameter.h cc -c area.c clean: rm main $(objects)
和编程语言类似,所有功能都以人为本,很自然的,这里引入了参数概念,一次定义,多次使用,修改时修改定义即可,是不是很方便呢?
好了,初级Makefile已经完成了,不过如果读过其他的Makefile,会觉得略显冗余,别人的怎么那么简洁呢,这就要提到脚本的潜规则了——引入Makefile版本三
#Makefile版本三 objects = main.o sum.o area.o main:$(objects) cc -o main $(objects) #除了要你指定最终产出“main”的指令外,其他的CC全都删了 main.o:main.c head.h sum.o:sum.c head.h area.o:area.c head.h parameter.h #伪指令标识.Phony .Phony:clean clean: -rm main $(objects)
潜规则就是那些东西make会自己去寻找自己去编译,不用自己指定命令。
另外就是伪指令了,像clean这种命令你给不依赖其他对象,也不产出任何目标文件,纯粹就是为了执行rm命令而设计。这时候你最好指定clean为.Phony。
最后,两个很重要的需要保持的习惯:
减号“-”,回想u-boot等编译过程,是不是看到一大堆错误出现而脚本依然“风雨无阻”,这就是减号的作用了,他使得即使某处出现错误脚本也能继续运行,对于大型脚本是非常重要的特性,而且加上也没有害处,所以要养成习惯。
如果有一点经验,会发现如果有多个标签,并且不像本例是顺次依赖的,是互相独立的,类似于switch和case的结构,那么脚本默认执行第一个“分支”,为了防止各种意外发生,另外一个好习惯就是永远把clean写最后。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
当然,除了一个目标依赖一群对象以外,也可以一群目标依赖一个对象,比如:
main.o sum.o area.o:head.h
但是回想一下,你又要一对多,又要多对一,眼花缭乱,到时候怎么分辨,可读性太差,就跟你写个C语句不带括号弄了一大堆加减号和星号似的,除非是参加编程大赛,不然害人害己,没有太多现实价值。