在我前一篇写的【 linux驱动学习(一)Makefile基础】中,Makefile写的中规中矩,其实Makefile写法很灵活,可以写得很简洁,而且减少出错的可能,现在就把之前写的Makefile改进一下。

 




[plain]​​


  1. main: main.o hello.o word.o  
  2.     gcc main.o hello.o word.o -o main  
  3.   
  4. main.o:main.h hello.h word.h  
  5. hello.o:hello.h  
  6. word.o:word.h  
  7.   
  8. clean:  
  9.     echo "cleanning project"  
  10.     -rm main *.o  
  11.     echo "clean completed"  
  12.   
  13. .PHONY:clean  
main: main.o hello.o word.o
gcc main.o hello.o word.o -o main

main.o:main.h hello.h word.h
hello.o:hello.h
word.o:word.h

clean:
echo "cleanning project"
-rm main *.o
echo "clean completed"

.PHONY:clean


这是不是比以前简单多了,但是main.o hello.o word.o这三个目标的编译命令都没有,怎么会编译呢,执行make试试看

 

 




[plain] ​ ​​​

  1. <localhost.localdomain:/data/ghostyu/linuxc> make  
  2. cc    -c -o main.o main.c  
  3. cc    -c -o hello.o hello.c  
  4. cc    -c -o word.o word.c  
  5. gcc main.o hello.o word.o -o main  
  6. <localhost.localdomain:/data/ghostyu/linuxc>  
<localhost.localdomain:/data/ghostyu/linuxc> make
cc -c -o main.o main.c
cc -c -o hello.o hello.c
cc -c -o word.o word.c
gcc main.o hello.o word.o -o main
<localhost.localdomain:/data/ghostyu/linuxc>


 

cc是什么呢,执行下which cc



[plain] ​ ​​ ​

  1.   




[plain]​​


  1. <localhost.localdomain:/data/ghostyu/linuxc> which cc  
  2. /usr/bin/cc  
<localhost.localdomain:/data/ghostyu/linuxc> which cc
/usr/bin/cc

事实上cc指向的也是gcc


其实,这是Makefile的内建隐含规则,然后make时,调用这些隐含规则。

 




[plain] ​ ​​ ​

  1. # default  
  2. OUTPUT_OPTION = -o $@  
  3.   
  4. # default  
  5. CC = cc  
  6.   
  7. # default  
  8. COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c  
  9.   
  10. %.o: %.c  
  11. #  commands to execute (built-in):  
  12.         $(COMPILE.c) $(OUTPUT_OPTION) $<  
# default
OUTPUT_OPTION = -o $@

# default
CC = cc

# default
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

%.o: %.c
# commands to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<

以上是内建规则中关于隐含规则的部分

 

‘#’为注释符,跟‘//’一样

‘CC’为Makefile变量

'$@'与‘$<’为特殊变量,'$@'的取值为规则的目标,‘$<’取值为规则的第一个条件。

​%.o: %.c​​是一种特殊的规则,称为模式规则(Pattern Rule)。

CFLAG CPPFLAG TARGET_ARCH未定义,展开为空,

现在来分析一下,隐含规则是怎样解析Makefile的。

首先,OUTPUT_OPTION是一个变量,

 




[plain] ​ ​​ ​

  1. OUTPUT_OPTION = -o $@  
OUTPUT_OPTION = -o $@

这边变量展开为:“-o main.o”

 

其次,展开COMPILE变量

 




[plain] ​ ​​ ​

  1. # default  
  2. COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c  
# default
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

为:“cc    -c”。中间有四个空格。

 

然后

 




[plain] ​ ​​ ​

  1. %.o: %.c  
%.o: %.c

这就相当于 main.o:main.c

 

最后

 




[plain] ​ ​​​

  1. $(COMPILE.c) $(OUTPUT_OPTION) $<  
$(COMPILE.c) $(OUTPUT_OPTION) $<

注意开头的空白为tab键,8个字符,这是Makefile规定的,gcc命令等必须tab开头识别

 

展开为:

 




[plain]​ ​


  1. cc    -c -o main.o main.c  
cc    -c -o main.o main.c

完整的:

 

 




[java]​ ​


  1. main.o:main.h hello.h word.h  
  2. main.o:main.c  
  3.         cc    -c -o main.o main.c  
main.o:main.h hello.h word.h
main.o:main.c
cc -c -o main.o main.c


 

这就隐含的包含了各个条件的编译

注意:上面之所以可以写成两行,是应为条件并不是一定要写在一行,可以分开写,但只能存在一条命令:

比如下列:

 




[plain]​ ​


  1. main.o: main.c main.h hello.h word.h  
  2.     gcc -c main.c  
main.o: main.c main.h hello.h word.h
gcc -c main.c

可以写成:

 

 




[java]​ ​


  1. main.o:main.h hello.h word.h  
  2. main.o:main.c  
  3.     gcc -c main.c  
main.o:main.h hello.h word.h
main.o:main.c
gcc -c main.c


写规则的目的是让make建立依赖关系图,不管怎么写,只要把所有的依赖关系都描述清楚了就行。



****************************Makefile 变量**************************





[plain] ​ ​​ ​

  1. var = $(gho)  
  2. gho = yu  
  3.   
  4. all:  
  5.         @echo $(var)  
var = $(gho)
gho = yu

all:
@echo $(var)

make all时,输出 yu



[plain] ​ ​​​

  1. <localhost.localdomain:/data/ghostyu/linuxc/test> make all  
  2. yu  
<localhost.localdomain:/data/ghostyu/linuxc/test> make all
yu

这就是Makefile中的变量,与TCL脚本的变量很类似

之所以输出yu 而非 gho,是因为‘=’不用立即展开,若果在第一等号前加‘:’,试试。





[plain]​ ​


  1. <PRE style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class=plain name="code">var := $(gho)  
  2. gho = yu  
  3.   
  4. all:  
  5.         @echo $(var)</PRE>  
  6. <PRE></PRE>  
  7. <PRE></PRE>  
  8. <PRE></PRE>  




[plain]​​


  1. var := $(gho)  
  2. gho = yu  
  3.   
  4. all:  
  5.         @echo $(var)  
var := $(gho)
gho = yu

all:
@echo $(var)


这样make all 后输出为空,这是因为var:=$(gho),遇到‘:’将立即展开,gho此时又为定义,因此输出空,若gho=yu放在前面,则依然输出yu



还有一个比较有用的赋值运算符是​ ​?=​​,例如​​var ?= $(gho)​​的意思是:如果​​var​​没有定义过,那么​​?=​​相当于​​=​​,定义​​var​​的值是​​$(gho)​​,但不立即展开;如果先前已经定义了​​var​​,则什么也不做,不会给var重新赋值。



​+=​​运算符可以给变量追加值





[plain] ​ ​​ ​

  1. var = main.o  
  2. var += $(gho)  
  3. gho = hello.o word.o  
var = main.o
var += $(gho)
gho = hello.o word.o

这是var的值为 main.o hello.o word.o


常用的特殊变量有四个,出去之前用的$@与$<,还有$? 和$^



  • $@,表示规则中的目标。
  • $<,表示规则中的第一个条件。
  • $?,表示规则中所有比目标新的条件,组成一个列表,以空格分隔。
  • $^,表示规则中的所有条件,组成一个列表,以空格分隔。

因此




[plain] ​ ​​ ​

  1. main: main.o hello.o word.o  
  2.     gcc main.o hello.o word.o -o main  
main: main.o hello.o word.o
gcc main.o hello.o word.o -o main

可以改写为:




[plain]​ ​


  1. main: main.o hello.o word.o  
  2.     gcc $^ -o $@  
main: main.o hello.o word.o
gcc $^ -o $@

这样的好处是,即使以后又往条件里加了新的目标,编译命令也不需要修改,既省事,又减少出错。


$?也很有用,有时候希望只对更新过的条件进行操作。




之前我们看到​​make​​的隐含规则数据库中用到了很多变量,有些变量没有定义(例如​​CFLAGS​​),有些变量定义了缺省值(例如​​CC​​),我们写Makefile时可以重新定义这些变量的值,也可以在缺省值的基础上追加。以下列举一些常用的变量。


​AR​

静态库打包命令的名字,缺省值是​​ar​​。

​ARFLAGS​

静态库打包命令的选项,缺省值是​​rv​​。

​AS​

汇编器的名字,缺省值是​​as​​。

​ASFLAGS​

汇编器的选项,没有定义。

CC

C编译器的名字,缺省值是​​cc​​。

CFLAGS

C编译器的选项,没有定义。

CXX

C++编译器的名字,缺省值是​​g++​​。

CXXFLAGS

C++编译器的选项,没有定义。

CPP

C预处理器的名字,缺省值是​​$(CC) -E​​。

CPPFLAGS

C预处理器的选项,没有定义。

LD

链接器的名字,缺省值是​​ld​​。

LDFLAGS

链接器的选项,没有定义。

TARGET_ARCH

和目标平台相关的命令行选项,没有定义。

OUTPUT_OPTION

输出的命令行选项,缺省值是​​-o $@​​。

LINK.o

把​​.o​​文件链接在一起的命令行,缺省值是​​$(CC) $(LDFLAGS) $(TARGET_ARCH)​​。

LINK.c

把​​.c​​文件链接在一起的命令行,缺省值是​​$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)​​。

LINK.cc

把​​.cc​​文件(C++源文件)链接在一起的命令行,缺省值是​​$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)​​。

COMPILE.c

编译​​.c​​文件的命令行,缺省值是​​$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c​​。

COMPILE.cc

编译​​.cc​​文件的命令行,缺省值是​​$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c​​。

RM

删除命令的名字,缺省值是​​rm -f​​。

***************************自动处理头文件的依赖关系*************************





[plain] ​ ​​ ​

  1. all:main  
  2. main: main.o hello.o word.o  
  3.     gcc main.o hello.o word.o -o main  
  4.   
  5. main.o:main.h hello.h word.h  
  6. hello.o:hello.h  
  7. word.o:word.h  
  8.   
  9. clean:  
  10.     echo "cleanning project"  
  11.     -rm main *.o  
  12.     echo "clean completed"  
  13.   
  14. .PHONY:clean  
all:main
main: main.o hello.o word.o
gcc main.o hello.o word.o -o main

main.o:main.h hello.h word.h
hello.o:hello.h
word.o:word.h

clean:
echo "cleanning project"
-rm main *.o
echo "clean completed"

.PHONY:clean


现在Makefile写成上面的形式,默认make all,这样有个确定,写Makefile时要查个每个源文件,确定其包含的头文件,很容易出错,为了解决这个问题,可用用gcc -M查看源文件的依赖关系,-M选项会把系统头文件也找出来,如果不需要,可以用-MM选项。





[plain] ​ ​​ ​

  1. <localhost.localdomain:/data/ghostyu/linuxc> gcc -MM *.c  
  2. hello.o: hello.c hello.h  
  3. main.o: main.c main.h hello.h word.h  
  4. word.o: word.c word.h  
<localhost.localdomain:/data/ghostyu/linuxc> gcc -MM *.c
hello.o: hello.c hello.h
main.o: main.c main.h hello.h word.h
word.o: word.c word.h


现在的问题是怎样将上述依赖包含在Makefile中。

GNUlinux建议这样:





[plain] ​ ​​ ​

  1. all:main  
  2. main: main.o hello.o word.o  
  3.     gcc main.o hello.o word.o -o main  
  4.   
  5. sources = main.c hello.c word.c  
  6.   
  7. include $(sources:.c=.d)  
  8.   
  9. %.d: %.c  
  10.     set -e; rm -f $@; \  
  11.     $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \  
  12.     sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \  
  13.     rm -f $@.$$$$