zoukankan      html  css  js  c++  java
  • 4通用Makefile编写

    a.c

    #include<stdio.h>
    #include "a.h"
    
    int main()
    {
        
        printf("hello world
    ");
        printf("A= %d
    ",A);
        test_fun();
        return 0;
    }

    a.h

    #define A 1

    b.c

    #include <stdio.h>
    
    int test_fun()
    { 
        printf("it is B
    ");
        return 0;
    }

    编译test_Makefile的方法:
    a. gcc -o test a.c b.c
    对于a.c: 预处理、编译、汇编
    对于b.c:预处理、编译、汇编
    最后链接
    优点:命令简单
    缺点:如果文件很多,即使你只修改了一个文件,但是所有的文件文件都要重新"预处理、编译、汇编"
          效率低

    b. 写Makefile
    核心:规则

    目标:依赖1 依赖2
        命令

    命令执行的条件:
    i. "依赖"文件 比 "目标"文件 新
    ii.没有"目标"这个文件
        这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于dependencies中的文件,其生成规则定义在command中。dependencies 中如果有一个以上的文件时间要比target文件要新的话,command所定义的命令就会被执行。这就是 Makefile的规则。也就是Makefile中最核心的内容。在Makefile中的命令,必须要以[Tab]键开始。

    2.2 变量定义

        Makefile中变量的定义一般有两种: =和:=。 =符号定义的变量叫延时变量,只有在使用的时候才扩展开来; :=符号定义的变量为立即变量,一旦定义就扩展。 使用=定义的变量不能追加新值,使用:=定义的变量可以使用+=追加新值

    2.3 文件指示

        在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:include <filename> filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

    2.4 伪目标

        伪目标并不是一个文件,只是一个标签,由于伪目标不是文件,所以make无法生成它的依赖关系和决定 它是否要执行。我们只有通过显示地指明这个目标才能让其生效。当然,伪目标的取名不能和文件名重名,不然其就失去了伪目标的意义了。当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记.PHONY来显示地指明一个目标是伪目标,向make说明,不管是否有这个文件,这个目标就是伪目标。

    2.5 自动化变量

        $<    第一个依赖文件的名称

        $?    所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚

        $@    目标的完整名称

        $^    所有的依赖文件,以空格分开,不包含重复的依赖文件

    Makefile_1 

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

    只要有任一文件变化,就执行gcc -o test a.c b.c效率低

    Makefile_2

    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

    文件不会随着某个文件变化而全部编译

    Makefile_3

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

    加通通配符,通配符%,$@表示目标值,$<表示第一个依赖,$^表示所有依赖

    不能生成依赖文件

    顶层Makefile 顶层Makefile.build 子目录Makefile

    编译过程:

        从顶层开始递归进入子目录,当进入到一个目录的最底层时,开始使用GCC编译,再将该层的所有.o文件打包成build-in.o,返回它的上一层目录再递归进入子目录,当编译完所有的子目录后,就开始编译顶层的.c文件,最后将顶层的.o文件和顶层每个子目录的build-in.o链接成我们的目标文件

    电子书Makefile

    CROSS_COMPILE = arm-linux-
    AS        = $(CROSS_COMPILE)as
    LD        = $(CROSS_COMPILE)ld
    CC        = $(CROSS_COMPILE)gcc
    CPP        = $(CC) -E
    AR        = $(CROSS_COMPILE)ar
    NM        = $(CROSS_COMPILE)nm
    
    STRIP        = $(CROSS_COMPILE)strip
    OBJCOPY        = $(CROSS_COMPILE)objcopy
    OBJDUMP        = $(CROSS_COMPILE)objdump
    
    export AS LD CC CPP AR NM 
    export STRIP OBJCOPY OBJDUMP
    编译选项

    CFLAGS := -Wall -O2 -g
    CFLAGS += -I $(shell pwd)/include
    链接选项
    LDFLAGS := -lm -lfreetype
    
    export CFLAGS LDFLAGS

    假目标

    PHONY := __build
    __build:

    obj-y :=
    subdir-y :=
    包含当前目录下的Makefile
    include Makefile

    工程必备:

    顶层Makefile 顶层Makefile.build 子目录Makefile

    编译过程:

        从顶层开始递归进入子目录,当进入到一个目录的最底层时,开始使用GCC编译,再将该层的所有.o文件打包成build-in.o,返回它的上一层目录再递归进入子目录,当编译完所有的子目录后,就开始编译顶层的.c文件,最后将顶层的.o文件和顶层每个子目录的build-in.o链接成我们的目标文件

    顶层Makefile解析(随工程而变):

    #----------------------------------------------指定编译工具链---------------------------------------------------

    CROSS_COMPILE =                             #指定编译器种类

    AS        = $(CROSS_COMPILE)as         #

    LD        = $(CROSS_COMPILE)ld          #链接工具

    CC        = $(CROSS_COMPILE)gcc       #编译工具

    CPP        = $(CC) -E                             #

    AR        = $(CROSS_COMPILE)ar         #打包工具

    NM        = $(CROSS_COMPILE)nm       #

    STRIP        = $(CROSS_COMPILE)strip              #优化工具

    OBJCOPY        = $(CROSS_COMPILE)objcopy   #

    OBJDUMP        = $(CROSS_COMPILE)objdump  #

    export AS LD CC CPP AR NM                           #将定义的变量导出,方便其他makefile使用

    export STRIP OBJCOPY OBJDUMP                   #将定义的变量导出,方便其他makefile使用

    CFLAGS := -Wall -O2 -g                                    #编译器参数

    CFLAGS += -I $(shell pwd)/include                     #指定编译器头文件(根据实际项目手动修改)

    LDFLAGS := -lm -lfreetype -lvga                         #指定编译器链接库(根据实际项目手动修改)

    export CFLAGS LDFLAGS                                #将定义的变量导出,方便其他makefile使用

    TOPDIR := $(shell pwd)                                     #获得当前程序的顶层目录

    export TOPDIR                                                 #输出顶层目录

    TARGET := show_file                                      #编译后的程序名(根据实际项目手动修改)

    #-------------------------顶层要生成的.o文件以及顶层文件夹(根据实际项目手动修改)------------------

    obj-y += main.o

    obj-y += display/

    obj-y += draw/

    obj-y += encoding/

    obj-y += fonts/

    #--------------------------------------------顶层的第一个规则(默认规则)-----------------------------------------

    all :

        make -C ./ -f $(TOPDIR)/Makefile.build           #进入当前目录,使用顶层的makefile.build进行编译

        $(CC) $(LDFLAGS) -o $(TARGET) built-in.o    #将编译好的built-in.o文件链接生成我们的目标文件

    #------------------------------------------------顶层的清除规则-------------------------------------------------------

    clean:

        rm -f $(shell find -name "*.o")                        #删除所有的.o文件

        rm -f $(shell find -name "*.d")                        #删除所有的.d文件

        rm -f $(TARGET)                                         #删除目标文件

    .PHONY:all clean

    顶层Makefile.build解析(无需改动):

    PHONY := __build                                        #定义一个PHONY变量

    __build:                                                       #开头说明__build伪目标,使其成为Makefile.build的第一个目标

    obj-y :=                                                        #定义当前目录的目标变量,初始值为空

    subdir-y :=                                                   #定义当前目录的子目录变量,初始值为空

    include Makefile                                          #将当前目录的Makefile包含进来,初始化obj-y

                                                                       #obj-y:=a.o b.o c/ d/

    __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))   #筛选出当前目录的目标变量中的子目录,并且去掉/

                                                                                       #$(filter %/, $(obj-y)):c/ d/

                                                                                       #__subdir-y:c d

    subdir-y += $(__subdir-y)                                           #将开始定义的subdir-y赋值为__subdir-y

                                                                                       #subdir-y:c d

    subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)  #对于subdir-y里面的每一个值(目录),增加一个相应的目录/built-in.o的变量值

                                                                                       #subdir_objs:c/built-in.o d/built-in.o

    cur_objs := $(filter-out %/, $(obj-y))                            #得到obj-y中的.o文件

                                                                                       #cur_objs:a.o b.o

    dep_files := $(foreach f,$(cur_objs),.$(f).d)                #对于所有的.o文件,定义它的依赖文件名

                                                                                       #dep_files: .a.d .b.d

    dep_files := $(wildcard $(dep_files))

    ifneq ($(dep_files),)                                                    #根据依赖文件名,判断依赖文件是否存在,存在就包含就来

        include $(dep_files)

    endif

    PHONY += $(subdir-y) #将$(subdir-y)也加入到变量PHONY中

    --------------------------------------------Makefile. build的第一个规则--------------------------------------------------------------

    __build : $(subdir-y) built-in.o                                    #第一个规则

    $(subdir-y):                                                                #第一个规则的第一个依赖规则

        make -C $@ -f $(TOPDIR)/Makefile.build              #依次进入该子目录变量里面存储的值,使用的Makefile.build进行编译

    built-in.o : $(cur_objs) $(subdir_objs)                       #第一个规则的第二个依赖规则

          $(LD) -r -o $@ $^                                                 #该规则的命令:将该目录下的.o和$(subdir_obj)打包成built-in.o文件

    dep_file = .$@.d                                              #

    %.o : %.c                                                                   #第一个规则的第二个依赖规则的依赖规则 

    $(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<#用于将目录下所有的.c文件编译成.o文件

    .PHONY : $(PHONY)                                                  #将PHONY声明为伪目标

    本程序的Makefile分为3类:
    1. 顶层目录的Makefile
    2. 顶层目录的Makefile.build
    3. 各级子目录的Makefile

    一、各级子目录的Makefile:
       它最简单,形式如下:
    obj-y += file.o
    obj-y += subdir/
      
       "obj-y += file.o"表示把当前目录下的file.c编进程序里,
       "obj-y += subdir/"表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。

    注意: "subdir/"中的斜杠"/"不可省略

    二、顶层目录的Makefile:
       它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,主要是定义工具链、编译参数、链接参数──就是文件中用export导出的各变量。

    三、顶层目录的Makefile.build:
       这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o
       详细的讲解请看视频。

    四、怎么使用这套Makefile:
    1.把顶层Makefile, Makefile.build放入程序的顶层目录
    2.修改顶层Makefile
    2.1 修改工具链
    2.2 修改编译选项、链接选项
    2.3 修改obj-y决定顶层目录下哪些文件、哪些子目录被编进程序
    2.4 修改TARGET,这是用来指定编译出来的程序的名字

    3. 在各一个子目录下都建一个Makefile,形式为:
    obj-y += file1.o
    obj-y += file2.o
    obj-y += subdir1/
    obj-y += subdir2/

    4. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除

  • 相关阅读:
    broncho a1 hack指南-准备硬件
    嵌入式GUI ftk0.1发布
    ASP.net页面防刷新
    C#反射入门教程(转)
    万物生萨顶顶
    [转载内容]C# win程序中主窗体菜单的权限控制
    VB.net技巧更新(一)
    XML与dataset里相互读取操作
    操作EXCEL代码(c#完全版)
    [转载内容]动态创建菜单,menustrip,根据权限显示菜单,控制菜单可用,反射,给窗体传值,反射对象传值,public static Object CreateInstance ( Type type, params Object[] args )
  • 原文地址:https://www.cnblogs.com/CZM-/p/5346528.html
Copyright © 2011-2022 走看看