项目通用Makefile的编写(包含Makefile.build文件分析)
Posted 正在起飞的蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目通用Makefile的编写(包含Makefile.build文件分析)相关的知识,希望对你有一定的参考价值。
1、前言;
下面分析的工程和Makefile是图片解码播放器项目的,具体可参考博客:;
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 ——触摸屏相关源码
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 -
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)
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文件链接成可执行文件;
以上是关于项目通用Makefile的编写(包含Makefile.build文件分析)的主要内容,如果未能解决你的问题,请参考以下文章