项目通用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文件分析)的主要内容,如果未能解决你的问题,请参考以下文章

编写一个通用的Makefile文件

Makefile工具使用

通用protoc Makefile

第4课.编写通用的Makefile

如何在 linux 中使用 Makefile 编译 tensorflow c_api

makefile.in与makefile.am