• 项目通用Makefile的编写(包含Makefile.build文件分析)


    1、前言;

    下面分析的工程和Makefile是图片解码播放器项目的,具体可参考博客:《从零开始用C语言实现图片解码播放器(有源码)》

    2、工程文件夹目录结构

    .
    ├── bin			——存放生成的可执行程序
    ├── build		——存放Makefile、Makefile.build文件
    ├── doc			——项目说明文档
    ├── image		——存放要播放的图片
    │   ├── bmp
    │   ├── jpg
    │   └── png	
    ├── include		——相关头文件
    │   ├── common	——公共的配置头文件
    │   ├── decode	——图片解码相关的头文件
    │   │   ├── bmp
    │   │   ├── jpg
    │   │   ├── png
    │   │   │   └── libpng16
    │   │   └── zlib
    │   ├── fb				——屏幕显示相关头文件
    │   ├── imageManager	——图片管理头文件
    │   └── touchScreen		——触摸屏管理头文件
    ├── lib					——依赖的动态库
    └── src					——源码目录
        ├── decode			——图片解码相关源码
        ├── fb				——屏幕显示相关源码
        ├── imageManager	——图片管理相关源码
        └── touchScreen		——触摸屏相关源码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3、主Makefile文件

    # 默认编译链
    CROSS_COMPILE ?= /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
    
    
    # 交叉编译工具铿
    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
    
    # 顶层目录的路径
    TOPDIR = $(shell pwd)/..
    export TOPDIR
    
    # export导出的变量是给Makefile.build使用
    export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP
    
    # 编译器在编译时的参数设置
    CFLAGS := -Wall -O2 -g -DDEBUG
    
    # 添加头文件路径
    CFLAGS += -I $(TOPDIR)/include 
    CFLAGS += -I $(TOPDIR)/include/common
    CFLAGS += -I $(TOPDIR)/include/fb
    CFLAGS += -I $(TOPDIR)/include/touchScreen
    CFLAGS += -I $(TOPDIR)/include/decode/bmp
    CFLAGS += -I $(TOPDIR)/include/decode/png
    CFLAGS += -I $(TOPDIR)/include/decode/jpg
    CFLAGS += -I $(TOPDIR)/include/decode/zlib
    CFLAGS += -I $(TOPDIR)/include/imageManager
    
    # 添加库的查找路径
    LDFLAGS := -L  $(TOPDIR)/lib
    
    # 添加库
    LDFLAGS += -ljpeg -lz -lpng
    
    # 导出给Makefile.build使用
    export CFLAGS LDFLAGS
    
    # 定义将来编译生成的可执行程序的名称
    TARGET :=  $(TOPDIR)/bin/imagePlayer
    
    # 添加项目中所有用到的源文件,有顶层目录下的,c文件,和子文件夹
    
    # 添加源文件所在路径(注意目录名后面加/),也是子Makefile的路径
    obj-y += $(TOPDIR)/src/
    obj-y += $(TOPDIR)/src/decode/
    obj-y += $(TOPDIR)/src/touchScreen/
    obj-y += $(TOPDIR)/src/fb/
    obj-y += $(TOPDIR)/src/imageManager/
    
    all: 
    	# 当前目录下执行Makefile.build
    	make -C ./ -f $(TOPDIR)/build/Makefile.build
    	
    	# 将当前目录的built-in.o和库链接成可执行程序
    	$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
    
    cp:
    	cp /mnt/hgfs/share_file_Ubuntu1404/picturePlayer/myPlayer/* /root/rootfs/picturePlayer/myPlayer/ -rf
    
    clean:
    	cd ..
    	rm -f $(shell find -name "*.o")
    	rm -f $(TARGET)
    	cd -
    
    distclean:
    	cd ..
    	rm -f $(shell find -name "*.o")
    	rm -f $(shell find -name "*.d")
    	rm -f $(TARGET)
    	cd -
    	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    4、Makefile.build文件分析

    
    # 将__build定义为伪目标
    PHONY := __build
    __build:
    
    # 这里初值为空,下面引入Makefile文件后会被覆盖
    obj-y :=
    subdir-y :=
    
    # 包含同级目录的Makefile
    include Makefile
    
    # 从obj-y变量中,将"/"结尾的字符串提取出来,也就是包含的子文件夹目录
    __subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
    subdir-y	+= $(__subdir-y)
    
    # 将subdir-y变量中的字符串依次赋值给f变量,形成新的$(f)/built-in.o字符串
    subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
    
    # 筛选出obj-y中不以"/"结尾的字符串,也就是普通文件,一般是.o结尾
    cur_objs := $(filter-out %/, $(obj-y))
    
    # 为每个.o文件生成.d文件
    # 注意.$(f).d是隐藏文件,需要ls -a查看
    dep_files := $(foreach f,$(cur_objs),.$(f).d)
    dep_files := $(wildcard $(dep_files))
    
    # 如果.d文件不是空,则将.d文件都包含进来
    ifneq ($(dep_files),)
      include $(dep_files)
    endif
    
    
    PHONY += $(subdir-y)
    
    # __build是Makefile的目标
    
    __build : $(subdir-y) built-in.o
    
    # 依次跳转到子目录中,执行Makefile.build文件
    $(subdir-y):
    	make -C $@ -f $(TOPDIR)/build/Makefile.build
    
    # 生成当前目录的built-in.o,依赖当前目录的.o文件和子目录下的built-in.o文件
    built-in.o : $(cur_objs) $(subdir_objs)
    	$(LD) -r -o $@ $^
    
    # dep_file变量是用来生成.d文件的
    dep_file = .$@.d
    
    # Makefile中的规则,把.c文件编译成.o文件
    %.o : %.c
    	$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<
    
    # 重新定义 .PHONY的依赖
    .PHONY : $(PHONY)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    5、整个编译流程

    图片:编译流程

    (1)首先执行./build/目录下的Makefile文件(主Makefile),然后会跳转到各个子目录下面去编译源码,将子目录下的源码都编译成build-in.o文件;
    (2)将各个子目录下的build-in.o文件和主Makefile同级目标下的源文件一起编译成build-in.o文件;
    (3)再将./build/build-in.o编译链接成可执行程序;

    6、新增源文件如何修改Makefile

    (1)每个存放源码的目录下都有一个Makefile文件,里面的内容很简单,只是申明有哪些源文件,一般都是"obj-y += xxx.o";
    (2)在已有目录下新增源文件:增加xxx.c文件,则在同级目录的Makefile中增加"obj-y += xxx.o";
    (3)增加子目录存放源文件:在主Makefile中追加子目录的路径,然后在子目录中增加Makefile,Makefile中的内容是"obj-y += xxx.o",把子目录中全部源文件都添加到Makefile中;

    7、Makefile分析容易陷入的误区

    (1)指导整个工程编译链接过程的是Makefile文件中的内容,和Makefile文件这个名字没有必然联系,你可以给这个文件取任何名字;
    (2)make是linux中的命令,执行make命令时如果没有特别指定文件路径,则默认读取当前路径下的Makefile文件;
    (3)因为make命令默认是读取名字叫Makefile的文件,所以我们一般命名Makefile,如果我们想取别的名字也是可以的,比如我们取名叫Makefile_test,执行make命令时用-f选项来显示的指明文件名字(“make -f Makefile_test”),效果是一样的;
    (4)总结:make命令是用来执行Makefile文件里的内容的,重要的是Makefile文件里的内容而不是Makefile这个名字;

    8、通用Makefile框架的设计思路分析

    8.1、工程中各个Makefile文件的区别

    (1)整个工程中,和Makefile.build文件同级目录下有个Makefile文件,各个源码目录下也有Makefile文件;
    (2)和Makefile.build文件同级目录下的Makefile文件可以叫做主Makefile,其他源码目录下的Makefile文件叫子Makefile;
    (3)主Makefile文件中会指定子Makefile所在的路径,并且会跳转到各个子目录下面去执行make命令;
    (4)主Makefile的内容比较多;子Makefile的内容很单一,基本就是"obj-y += xxx.o",指明当前路径下有哪些源文件,更像是一个配置文件;

    8.2、主Makefile和Makefile.build文件的关联

    (1)主Makefile文件和Makefile.build文件都是指导工程编译链接的,可以看做是同等地位,但是为了区分才取了不同的名字,两者会互相调用;
    (2)主Makefile文件会"make -C ./ -f $(TOPDIR)/build/Makefile.build"调用Makefile.build文件;
    (3)Makefile.build文件也会通过"include Makefile"引用当前目录的Makefile文件;
    (4)主Makefile负责整个工程的编译流程控制,Makefile.build文件负责去编译各个子目录;
    (5)在"make -C"跳转到子目录执行make命令时,用"-f"指定了本次编译是读取Makefile.build文件,而子目录的Makefile是被Makefile.build用include包含进去;

    8.3、整体流程分析

    (1)整个编译流程采用了递归的思想:先跳转到各个子目录,利用Makefile.build文件将子目录下的源文件编译成build-in.o;
    (2)将各个子目录下的build-in.o文件链接成总的build-in.o文件;
    (3)最后用总的build-in.o文件链接成可执行文件;

  • 相关阅读:
    CISAW信息安全保障人员--风险管理
    vue组件库发布到npm
    vue+springboot实现登录或注册滑动验证码( AJ-Captcha)
    83 # 静态服务中间件 koa-static 的使用以及实现
    C语言-位运算
    CSS外观属性总结
    Vue-依赖注入(provide-inject)
    蓝桥杯每日一题203.11.7
    【Golang】gin客户端动态加载Html格式文件
    等精度频率计verilog,quartus仿真视频,原理图,代码
  • 原文地址:https://blog.csdn.net/weixin_42031299/article/details/126562725