• Makefile分析基础


    http://blog.csdn.net/ghostyu/article/details/6866863

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

     

    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试试看

     

     

    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
    1.   
    
    
    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时,调用这些隐含规则。

     

    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是一个变量,

     

    1. OUTPUT_OPTION = -o $@  
    OUTPUT_OPTION = -o $@
    这边变量展开为:“-o main.o”

     


    其次,展开COMPILE变量

     

    1. # default  
    2. COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c  
    # default
    COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
    为:“cc    -c”。中间有四个空格。

     


    然后

     

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

     


    最后

     

    1. $(COMPILE.c) $(OUTPUT_OPTION) $<  
            $(COMPILE.c) $(OUTPUT_OPTION) $<
    注意开头的空白为tab键,8个字符,这是Makefile规定的,gcc命令等必须tab开头识别

     

    展开为:

     

    1. cc    -c -o main.o main.c  
            cc    -c -o main.o main.c
    完整的:

     

     

    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

     

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

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

    比如下列:

     

    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
    可以写成:

     

    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 变量**************************
    1. var = $(gho)  
    2. gho = yu  
    3.   
    4. all:  
    5.         @echo $(var)  
    var = $(gho)
    gho = yu
    
    all:
            @echo $(var)
    make all时,输出 yu
    1. <localhost.localdomain:/data/ghostyu/linuxc/test> make all  
    2. yu  
    <localhost.localdomain:/data/ghostyu/linuxc/test> make all
    yu
    
    这就是Makefile中的变量,与TCL脚本的变量很类似
    之所以输出yu 而非 gho,是因为‘=’不用立即展开,若果在第一等号前加‘:’,试试。
    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>  
    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重新赋值。

    +=运算符可以给变量追加值
    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

    常用的特殊变量有四个,出去之前用的$@与$<,还有$? 和$^
    • $@,表示规则中的目标。

    • $<,表示规则中的第一个条件。

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

    • $^,表示规则中的所有条件,组成一个列表,以空格分隔。

    因此
    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
    可以改写为:
    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


    ***************************自动处理头文件的依赖关系*************************
    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选项。
    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建议这样:
    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 $@.$$$$  
  • 相关阅读:
    mysql事务隔离级别回顾
    单链表倒数第K个节点的查找和显示
    mysql 行转列,对列的分组求和,对行求和
    获取分组后统计数量最多的纪录;limit用法;sql执行顺序
    You can't specify target table 'e' for update in FROM clause
    mysql 行转列 (结果集以坐标显示)
    springmvc执行流程 源码分析
    jdk动态代理 案例
    项目日志log管理
    apache和tomcat的区别
  • 原文地址:https://www.cnblogs.com/riskyer/p/3297498.html
Copyright © 2020-2023  润新知