一.本程序的Makefile分为3类:

  1. 顶层目录的Makefile
  2. 顶层目录的Makefile.build
  3. 各级子目录的Makefile

1.顶层目录的Makefile

  它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,
   主要是定义工具链前缀CROSS_COMPILE,
   定义编译参数CFLAGS,
   定义链接参数LDFLAGS,
   这些参数就是文件中用export导出的各变量。

CROSS_COMPILE = # 交叉编译工具头,如:arm-linux-gnueabihf-
AS      = $(CROSS_COMPILE)as # 把汇编文件生成目标文件
LD      = $(CROSS_COMPILE)ld # 链接器,为前面生成的目标代码分配地址空间,将多个目标文件链接成一个库或者一个可执行文件
CC      = $(CROSS_COMPILE)gcc # 编译器,对 C 源文件进行编译处理,生成汇编文件
CPP    = $(CC) -E
AR      = $(CROSS_COMPILE)ar # 打包器,用于库操作,可以通过该工具从一个库中删除或则增加目标代码模块
NM     = $(CROSS_COMPILE)nm # 查看静态库文件中的符号表

STRIP       = $(CROSS_COMPILE)strip # 以最终生成的可执行文件或者库文件作为输入,然后消除掉其中的源码
OBJCOPY  = $(CROSS_COMPILE)objcopy # 复制一个目标文件的内容到另一个文件中,可用于不同源文件之间的格式转换
OBJDUMP = $(CROSS_COMPILE)objdump # 查看静态库或则动态库的签名方法

# 共享到sub-Makefile
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP

# -Wall : 允许发出 GCC 提供的所有有用的报警信息
# -O2 : “-On”优化等级
# -g : 在可执行程序中包含标准调试信息
# -I : 指定头文件路径(可多个)
CFLAGS := -Wall -O2 -g 
CFLAGS += -I $(shell pwd)/include

# LDFLAGS是告诉链接器从哪里寻找库文件,这在本Makefile是链接最后应用程序时的链接选项。
LDFLAGS := 

# 共享到sub-Makefile
export CFLAGS LDFLAGS

# 顶层路径
TOPDIR := $(shell pwd)
export TOPDIR

# 最终目标
TARGET := test

# 本次整个编译需要源 文件 和 目录
# 这里的“obj-y”是自己定义的一个格式,和“STRIP”这些一样,*但是 一般内核会搜集 ”obj-”的变量*
obj-y += main.o # 需要把当前目录下的 main.c 编进程序里
obj-y += sub.o # 需要把当前目录下的 sub.c 编进程序里
obj-y += subdir/ # 需要进入 subdir 这个子目录去寻找文件来编进程序里,具体是哪些文件,由 subdir 目录下的 Makefile 决定。
#obj-y += $(patsubst %.c,%.o,$(shell ls *.c))

# 第一个目标
all : start_recursive_build $(TARGET)
    @echo $(TARGET) has been built !
    
# 处理第一个依赖,**转到 Makefile.build 执行**
start_recursive_build:
    make -C ./ -f $(TOPDIR)/Makefile.build
    
# 处理最终目标,把前期处理得出的 built-in.o 用上
$(TARGET) : built-in.o
    $(CC) -o $(TARGET) built-in.o $(LDFLAGS)
    
# 清理
clean:
    rm -f $(shell find -name "*.o")
    rm -f $(TARGET)
    
# 彻底清理
distclean:
    rm -f $(shell find -name "*.o")
    rm -f $(shell find -name "*.d")
    rm -f $(TARGET)

2.顶层目录的Makefile.build

   这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o

# 伪目标
PHONY := __build
__build:

# 清空需要的变量
obj-y :=
subdir-y :=
EXTRA_CFLAGS :=

# 包含同级目录Makefile
# 这里要注意,相对路径为 执行本 Makefile.build 的路径
include Makefile

# 获取当前 Makefile 需要编译的子目录的目录名
# obj-y := a.o b.o c/ d/
# $(filter %/, $(obj-y))   : c/ d/
# __subdir-y  : c d
# subdir-y    : c d
__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y	+= $(__subdir-y)

# 把子目录的目标定为以下注释
# built-in.o d/built-in.o
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)

# 获取当前目录需要编进程序的文件名作为,并写为目标
# a.o b.o
cur_objs := $(filter-out %/, $(obj-y))
# 使修改头文件 .h 后,重新make后可以重新编译(重要)
dep_files := $(foreach f,$(cur_objs),.$(f).d)
# 列出存在的文件
dep_files := $(wildcard $(dep_files))

ifneq ($(dep_files),)
  include $(dep_files)
endif


PHONY += $(subdir-y)

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

# 优先编译 子目录的内容
$(subdir-y):
	make -C $@ -f $(TOPDIR)/Makefile.build

# 链接成 目标
built-in.o : $(cur_objs) $(subdir_objs)
	$(LD) -r -o $@ $^

dep_file = .$@.d

# 生成 cur_objs 目标
%.o : %.c
	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
	
.PHONY : $(PHONY)

3.各级子目录的Makefile

 "obj-y += file.o"  表示把当前目录下的file.c编进程序里,
   "obj-y += subdir/" 表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。
   "EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置
   "CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置

注意: 
1. "subdir/"中的斜杠"/"不可省略
2. 顶层Makefile中的CFLAGS在编译任意一个.c文件时都会使用
3. CFLAGS  EXTRA_CFLAGS  CFLAGS_xxx.o 三者组成xxx.c的编译选项

# 可以不添加下面两个变量
EXTRA_CFLAGS  := 
CFLAGS_test.o := 

obj-y += test.o
obj-y += subdir/

二、怎么使用这套Makefile:


1.把顶层Makefile, Makefile.build放入程序的顶层目录
   在各自子目录创建一个空白的Makefile

2.确定编译哪些源文件
   修改顶层目录和各自子目录Makefile的obj-y : 
    obj-y += xxx.o
    obj-y += yyy/
    这表示要编译当前目录下的xxx.c, 要编译当前目录下的yyy子目录    

3. 确定编译选项、链接选项
   修改顶层目录Makefile的CFLAGS,这是编译所有.c文件时都要用的编译选项;
   修改顶层目录Makefile的LDFLAGS,这是链接最后的应用程序时的链接选项;
   
   修改各自子目录下的Makefile:
   "EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置
   "CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置
   
4. 使用哪个编译器?
   修改顶层目录Makefile的CROSS_COMPILE, 用来指定工具链的前缀(比如arm-linux-)
   
5. 确定应用程序的名字:
   修改顶层目录Makefile的TARGET, 这是用来指定编译出来的程序的名字

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

  • 以上为学习和整理韦东山老师Makefile资料而来的
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐