arm裸机学习预备知识三:Makefile

注:以下学习内容学习于韦东山老师arm裸机第一期教程

一.Makefile的引入及规则

    1.1 在keil,mdk,avr等工具开发程序时点点鼠标就可以编译了,他的内部机制就是使用Makefile来组织管理程序,决定编译哪一个文件.

    1.2 如下例:

        
        a.c文件
        
        #include<stdio.h>

        int main()
        {
            func_b();
            return 0;
        }
        
        b.c文件
        
        #include <stdio.h>

        void func_b()
        {
            printf("This is func_b!\n");
        }

        将两个文件上传到Linux服务器用gcc进行编译

        gcc -o test a.c b.c

       执行生成的test程序,成功打印出了This is func_b!

        

        对于gcc -o test a.c b.c命令

            a.具体流程:  对a.c执行预处理、编译、汇编、得到a.o

                               对b.c执行预处理、编译、汇编、得到b.o

                               最后将a.o b.o链接起来得到test应用程序

            b.这条命令在每次执行时都会对a.c,b.c进行重新处理,及时没有经过修改也会进行一次处理过程(当文件很多时就会使得效率低下)

            

             目的:只对被修改的文件进行重新编译,那么如何知道哪个文件被修改了?

            方法:比较 a.o与a.c的时间,如果不同说明a.c被修改

                   比较 b.o与b.c的时间,如果不同说明b.c被修改

                   比较 a.o、b.o与test的时间,如果不同需要重新生成test                

                    
                

    1.3 Makefile的基本语法规则

    

        目标文件:依赖文件1,2.....

        (tab键)    :命令

        当依赖比目标新,或者目标不存在时执行命令

        编写Makefile如下   

            test:a.o b.o
                gcc -o test a.o b.o、b
            a.o:a.c
                gcc -c -o a.o a.c
            b.o:b.c
                gcc -c -o b.o b.c           

        编译程序时直接执行make命令即可,只会编译被修改的文件.

        直接执行make,如下,两个文件都被编译

        [email protected]:/work/arm9/hareware/5.Makefile/001.test_app$ make
        gcc -c -o a.o a.c
        gcc -c -o b.o b.c
        gcc -o test a.o b.o

        修改a.c文件之后再次执行make,如下,只对a.c进行了编译

        [email protected]:/work/arm9/hareware/5.Makefile/001.test_app$ make
        gcc -c -o a.o a.c
        gcc -o test a.o b.o        

二.Makefile语法

    2.1 通配符%.o, $@, $<, $^

        在上面的Makefile中,

        test:a.o b.o
                gcc -o test a.o b.o
        a.o:a.c
            gcc -c -o a.o a.c
        b.o:b.c
            gcc -c -o b.o b.c

        如果有很多很多.c文件,难道要一个个写吗?

        因此引入通配符

        test:a.o b.o
            gcc -o test $^
        %.o:%.c
            gcc -c -o $@ $<

        $@表示目标文件,$<表示第一个依赖文件,&^表示所有的依赖


        如果要添加一个c.c文件,直接在b.o后面添加一个c.o即可,如下:

        test:a.o b.o c.o
            gcc -o test $^
        %.o:%.c
            gcc -c -o $@ $<    

    2.2 假象目标

        

        2.2.1 假设想要清除文件,修改Makefile如下

            test:a.o b.o c.o
                gcc -o test $^
            %.o:%.c
                gcc -c -o $@ $<    
                
            clean:
                rm *.o test

            执行make clean即可清除文件

          同时可以推测出make的用法,make 后面可以直接跟上目标名称,如果不加目标名称就会默认去生成第一个目标

            

            分析流程: 当执行make clean时,发现目录下面没有名为clean的文件,因此会执行后面的条件,但是当目录下面有了名为clean的文件,就会提示clean已经是最新的了,如下:

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/phyton$ > clean

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/phyton$ make clean

                make: 'clean' is up to date.
                       

                        因此,我们需要将目标定义为假象目标,是用.PHYON关键字来定义,如下

                        test:a.o b.o c.o
                            gcc -o test $^
                        %.o:%.c
                            gcc -c -o $@ $<    
                            
                        clean:
                            rm *.o test
                            
                        .PHONY: clean

                        这时,再次执行make clean就完全没有问题了.

    

    2.3 变量

        2.3.1 即时变量(简单变量)

                A := xxx #A的值在定义即确定

                

        2.3.2 延时变量

                B = xxx  #B的值使用到时才确定

                

             如下例:    

                A := $(C)        #A即可确定,但是C还没有定义,因此A是空
                B = $(C)        #B在使用时才确定,因此B=abc
                C = abc

                all:
                    @echo A = $(A) #加上@符号,在执行的时候就不会显示命令
                    @echo B = $(B)  
                      

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/var$ make
                A =
                B = abc

    2.2.3    如果将C的定义放到最后,如下:

                A := $(C)        #A即可确定,但是C还没有定义,因此A是空
                B = $(C)        #B在使用时才确定,因此B=abc

                all:
                    @echo A = $(A) #加上@符号,在执行的时候就不会显示命令
                    @echo B = $(B)
                
                C = abc 

                执行make

               [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/var$ make

               A =

               B = abc

                从结果可以看出没有影响,因为执行make的时候程序本身会将整个Makefile读进去来分析,然后解析变量

        

        2.2.4

                A := $(C)        #A即可确定,但是C还没有定义,因此A是空
                B = $(C)        #B在使用时才确定,因此B=abc
                
                C = abc
                all:
                    @echo A = $(A) #加上@符号,在执行的时候就不会显示命令
                    @echo B = $(B)
                
                C = 123
                

                执行make

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/var$ make

                A =

                B = 123

                C被后面的值覆盖了

                

        2.2.5

                A := $(C)        #A即可确定,但是C还没有定义,因此A是空
                B = $(C)        #B在使用时才确定,因此B=abc
                
                C = abc
                all:
                    @echo A = $(A) #加上@符号,在执行的时候就不会显示命令
                    @echo B = $(B)
                
                C ?= 123  

                执行make

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/var$ make

                A =

                B = abc

                由于是?=,C并不会被后面的值覆盖了

                

        2.2.6  

                A := $(C)        #A即可确定,但是C还没有定义,因此A是空
                B = $(C)        #B在使用时才确定,因此B=abc
                
                C = abc
                all:
                    @echo A = $(A) #加上@符号,在执行的时候就不会显示命令
                    @echo B = $(B)
                
                C += 123              

                执行make

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/var$ make

                A =

                B = abc 123                

                中间会有一个空格

        

        2.2.8 变量类型小结

                := #即时变量

              =  #延时变量

              ?= #延时变量,如果该变量在前面已经定义着忽略这句

              += #附加,他是即时变量还是延时变量取决于前面的定义

              

三.Makefile函数

        3.1  $(foreach var, list, text)    

             #对于list里面的每一个变量,执行text公式

            

             如下:    

                A = a b c
                #$(foreach var, list, text)
                B = $(foreach f, $(A), $(f).o)

                all:
                    @echo B = $(B)          

                执行make

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/function$ make

                B = a.o b.o c.o

       

        3.2 filter函数

            3.2.1 $(filter pattern...,text)    

                  #在text中取出符合pattern格式的值

            

            3.2.2 $(filter-out pattern...,text)    

                  #在text中取出不符合pattern格式的值

                  如下:    

                
                    A = a b c d/
                    B = $(filter %/, $(A))
                    C = $(filter-out %/, $(A))

                    all:
                        @echo A = $(A)
                        @echo B = $(B)     
                        @echo C = $(C)

                    执行make   

                    [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/function$ make

                    A = a b c d/

                    B = d/

                    C = a b c                   

        3.3 $(wildcard pattern)

            #pattern定义了文件名的格式

            #wildcard取出其中存在的格式

            

            如下1:    

                A =  $(wildcard *.c)

                all:
                    @echo A = $(A) 
                   

                执行make,在当前目录下存放了a.c,b.c,c.c三个c文件   

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/function$ make

                A = a.c c.c b.c

            如下2:                

                file = a.c b.c c.c d.c e.c
                A =  $(wildcard $(file))

                all:
                    @echo A = $(A) 

                执行make,可以判断出变量file中真实存在的文件

                [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/function$ make

                A = a.c c.c b.c
               

        3.4 $(patsubst pattern , replacement, text)

            #从text列表中把符合格式pattern的值替换成replacement格式

            

            如下:           

            file = a.c b.c c.c d.c e.c abc

            dep_files = $(patsubst %.c, %.d, $(file))

            all:
                @echo A = $(dep_files)               

            执行make,可以判断出变量file中真实存在的文件

            [email protected]:/work/arm9/hareware/5.Makefile/002.synatx/function$ make

            A = a.d b.d c.d d.d e.d abc

            abc不符合.c的格式,因此不会进行替换.

            

四.Makefile实例

        4.1 在Makefile规则引入的例子中,我们在修改c.c函数如下

    
         #include<stdio.h>
	 #include "c.h"

	 void func_c()
	{
	    printf("This is func_c = %d\n", C);
	}

            其中C是一个宏,在c.h头文件中定义为1

            

            对a.c b.c c.c 进行编译链接,并且执行

            [email protected]:/work/arm9/hareware/5.Makefile/003.example$ make

            gcc -c -o a.o a.c

            gcc -c -o b.o b.c

            gcc -c -o c.o c.c

            gcc -o test a.o b.o c.o

            [email protected]:/work/arm9/hareware/5.Makefile/003.example$ ./test

            This is func_b!

            This is func_c = 1
           

            但是当修改c.h中的宏C为2时,再次进行编译,提示已经是最新的了,再次执行C仍然等于1

            [email protected]:/work/arm9/hareware/5.Makefile/003.example$ make

            make: 'test' is up to date.
 

            这个工程的Makefile如下

            test:a.o b.o c.o
            gcc -o test $^
            %.o:%.c
                gcc -c -o $@ $<    
                
            clean:
                rm *.o test
                
            .PHONY: clean
           

            我们修改了c.h,而c.c依赖于c.h,因此也应该被修改,但是我们的Makefile中并没有体现出来c.h依赖于c.h

            修改Makefile如下,便能够成功的修改c.c导致c.o被重新编译:

                test:a.o b.o c.o
                    gcc -o test $^

                c.o:c.c c.h

                %.o:%.c
                    gcc -c -o $@ $<    
                    
                clean:
                    rm *.o test
                    
                .PHONY: clean
               

        4.2 但是我们如何确定这些头文件的依赖关系?手工写出不是解决办法,因此我们要根据某些规则来自动生成.

            

            通过gcc 的-M命令可以得到依赖关系,例如执行gcc -M c.c,得出c.c的依赖头文件如下

            [email protected]:/work/arm9/hareware/5.Makefile/003.example$ gcc -M c.c

            c.o: c.c /usr/include/stdc-predef.h /usr/include/stdio.h \

             /usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \

             /usr/include/x86_64-linux-gnu/bits/wordsize.h \

             /usr/include/x86_64-linux-gnu/gnu/stubs.h \

             /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \

             /usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h \

             /usr/include/x86_64-linux-gnu/bits/types.h \

             /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \

             /usr/include/_G_config.h /usr/include/wchar.h \

             /usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h \

             /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \

             /usr/include/x86_64-linux-gnu/bits/sys_errlist.h c.
           

            我们需要将依赖写入文件中去,可以通过gcc -M -MF c.d c.c命令,这样就会生成一个c.d的文件

            通过 gcc -c -o c.o c.c -MD -MF c.d 即可以生成c.o,也可以生成c.d依赖文件

            

            修改Makefile如下:

                test:a.o b.o c.o
                gcc -o test $^

                c.o:c.c c.h

                %.o:%.c
                    gcc -c -o $@ $< -MD -MF [email protected]    #.表示隐藏文件,需要ls -a才可以看到
                    
                clean:
                    rm *.o test *.d
                    
                .PHONY: clean
                

            执行make后ls -a得到如下结果

            [email protected]:/work/arm9/hareware/5.Makefile/003.example$ ls -a
            .  ..  a.c  a.o  .a.o.d  b.c  b.o  .b.o.d  c.c  c.h  c.o  .c.o.d  Makefile  test        

            有.a.o.d,.b.o.d,.c.o.d这三个依赖文件

            

        4.3 我们需要在Makefile中将这些依赖文件包含进去,            

                objs = a.o b.o c.o
                dep_files := $(patsubst %, .%.d, $(objs))

                #判断依赖文件是否存在
                dep_files := $(wildcard $(dep_files))

                test:$(objs)
                    gcc -o test $^
                    
                #如果依赖文件不等于空,就将文件包含进去
                ifneq ($(dep_files),)
                include $(dep_files)
                endif
                    
                c.o:c.c c.h

                %.o:%.c
                    gcc -c -o $@ $< -MD -MF [email protected]    
                    
                clean:
                    rm *.o test

                disclean:
                    rm $(dep_files)
                .PHONY: clean
   

            再次执行make,会生成依赖文件和可执行程序,然后修改c.h文件的宏定义为4,再次make

            [email protected]:/work/arm9/hareware/5.Makefile/003.example$ make

            gcc -c -o c.o c.c -MD -MF .c.o.d

            gcc -o test a.o b.o c.o

            [email protected]:/work/arm9/hareware/5.Makefile/003.example$ ./test

            This is func_b!

            This is func_c = 

            只编译了c.c文件

            

        4.4 添加CFLAG

            修改Makefile如下:

            
            objs = a.o b.o c.o
            dep_files := $(patsubst %, .%.d, $(objs))

            dep_files := $(wildcard $(dep_files))

            #-Werror表示对所有的警告当作错误处理,
            #-I.指定了当前目录是编译器搜索的默认目录
            CFLAGS = -Werror -I.

            test:$(objs)
                gcc -o test $^
                
            ifneq ($(dep_files),)
            include $(dep_files)
            endif
                
            c.o:c.c c.h

            %.o:%.c
                gcc $(CFLAGS) -c -o $@ $< -MD -MF [email protected]    
                
            clean:
                rm *.o test

            disclean:
                rm $(dep_files)
            .PHONY: clean
            


      
                   

猜你喜欢

转载自blog.csdn.net/qq_36521904/article/details/80588009