Makefile嵌套编译多文件项目

在多文件的项目中,一个工程中的源文件比较多,其按类型、功能、模块分别放在若干个目录中,为了项目更加规整,我们常常要将源文件头文件执行文件等分开,所以在编译Makefile时就要做好整个项目的编译准备工作,Makefile定义了一系列的规则来指定,哪些文件需要编译,需要生成什么目标,需要生成库等等。
下面就用实例来列出这些规则,这也是一个基本框架,后边如果需要添加更多的源文件和目录就可以如法炮制,这就可以作为一个较为通用的规则适配到更多的工程中,我也是在这里做一下记录,已被今后需要。

 

有几种思路:
1.在每层都去编译生成需要的.o文件,然后从顶层Makefile文件去链接编译目标文件
2.编译所有.o文件输出到obj下,在obj下创建Makefile编译obj下的.o生成目标文件
3.在每层加载当前目录的依赖文件,在顶层根据依赖文件生成目标文件
下边是这个测试工程的文件树
.
├── bin
├── Makefile
├── obj
└── src
    ├── api
    │?? ├── api_a.c
    │?? ├── api.c
    │?? └── Makefile
    ├── main
    │?? ├── hello.c
    │?? ├── main.c
    │?? └── Makefile
    └── Makefile

 

一、用第一种方法

 

在每层都去编译生成需要的.o文件,然后从顶层Makefile文件去链接编译目标文件

 

 

 

1.首先编写顶层Makefile

 

#TOP ./Makefile
CC = gcc        #编译工具
FLAGS =         #编译规则
TAG = test      #目标文件

 

TOPDIR = $(PWD)    #顶层目录

 

OBJDIR = $(TOPDIR)/obj #输出文件路径
BINDIR = $(TOPDIR)/bin #输出目标文件路径
SRCDIR = $(TOPDIR)/src #源文件路径

 

INC = -I./inc           #指定头文件检索的路径

 

export CC TAG TOPDIR SUBDIR OBJDIR BINDIR INC   #导出全局变量

 

all:CHECK $(SRCDIR) $(TAG)    #make 目标

 

CHECK:      #检测并创建需要的目录
    mkdir -p $(OBJDIR) $(BINDIR)

 

$(SRCDIR):ECHO #去执行对应目录中的Makefile文件
    make -C $@

 

$(TAG):        #生成目标文件,查找源文件目录下的所有.o文件编译生成目标到指定路径下
    $(CC) -o $(addprefix $(BINDIR)/,$(TAG)) $$(find ./${SRCDIR} -name '*.o')

 

ECHO: 
    @echo $@   #打印一下,这里还有个作用后边说

 

CLEANDIR:ECHO
    make -C $(SRCDIR) clean

 

.PHONY : clean
clean :CLEANDIR
    -rm $(BINDIR)/$(TAG)1234567891011121314151617181920212223242526272829303132333435

 

 

 

2.然后执行第二层Makefile

 

#./src/Makefile
SUBDIR = main api   #这里就添加源文件的目录结构

 

all:$(SUBDIR)

 

$(SUBDIR):ECHO
    make -C $@     #进入底层目录的Makefile

 

#这里这个echo比较神奇,因为SUBDIR变量定义就是两个目录名,也不是绝对路径,如果被展开执行make -C main api 这是一个未知的路径
#但是加入echo后,就会变成 make -C main 和 make -C api 就能遍历执行 make -C
#这个可以使用CLEANDIR:的执行规则遍历执行各目录

 

ECHO: 
    @echo $@

 


CLEANDIR:
    @for dir in $$(echo $(SUBDIR)); \
 do make -C $$dir clean ;\
    done

 

.PHONY : clean
clean :CLEANDIR1234567891011121314151617181920212223

 

这里存在的问题就是这个ECHO这个作用,为什么会这样执行,可能就要更深入探究Makefile的隐含规则

 

 

 

3.执行底层各目录的Makefile

 

 

 

#./src/main/Makefile
OBJ = main.o
OBJ += hello.o  #添加需要的文件就行,类似内核编译那种

 

$(OBJ):$(OBJ:.o=.c)
    $(CC) -c $^

 

clean:
    -rm $(OBJ)123456789

 

这里最后执行的结果文件树如下

 

.
├── bin
│?? └── test
├── Makefile
├── obj
└── src
    ├── api
    │?? ├── api_a.c
    │?? ├── api_a.o
    │?? ├── api.c
    │?? ├── api.o
    │?? └── Makefile
    ├── main
    │?? ├── hello.c
    │?? ├── hello.o
    │?? ├── main.c
    │?? ├── main.o
    │?? └── Makefile
    └── Makefile

 

这种情况的好处就是.o文件和.c文件在同一路径下,在编译时如果对应的.c文件没有被修改则在总的编译不会被再次编译
内核中往往在这里生成一个lib从而供顶层Makefile来调用,更加简洁

 

 

 

二、第二种思路

 

编译所有.o文件输出到obj下,在obj下创建Makefile编译obj下的.o生成目标文件
这种相对于第一种方法变化不大,只是把.o文件做了统一路径输出,其实也没什么用,只是方便整理

 

 

 

1.首先编写顶层Makefile

 

和上边的基本类似

 

 

 

CC=gcc
TAG = test

 

TOPDIR = $(PWD)

 

OBJDIR = $(TOPDIR)/obj
BINDIR = $(TOPDIR)/bin
SRCDIR = $(TOPDIR)/src

 

INC = -I./inc

 

export CC TAG TOPDIR SUBDIR OBJDIR BINDIR INC

 

all:CHECK $(SRCDIR) $(TAG)

 

CHECK:
    mkdir -p $(OBJDIR) $(BINDIR)

 

$(SRCDIR):ECHO
    make -C $@

 

$(TAG):ECHO        #这里只需要把obj路径下的.o文件编译加载就行
    $(CC) -o $(BINDIR)/$(TAG) $(wildcard $(OBJDIR)/*.o)
#   $(CC) -o $(addprefix $(BINDIR)/,$(TAG)) $(wildcard $(OBJDIR)/*.o)

 

ECHO:
    @echo $@

 


.PHONY : clean
clean :
    -rm $(BINDIR)/$(TAG)
    -rm $(OBJDIR)/*.o  #这里的clean就不需要去调用各层的Makefile来执行,更方便清理123456789101112131415161718192021222324252627282930313233

 

 

 

2.执行第二层Makefile

 

和第一种方法中的基本一样,去掉清理

 

 

 

SUBDIR = main\
            api

 

all:$(SUBDIR)

 


$(SUBDIR):ECHO
    make -C $@

 


ECHO: 
    @echo $@123456789101112

 

3.执行底层各目录的Makefile
在输出目标时添加obj路径

 

 

 

OBJ = main.o
OBJ += hello.o

 

all:$(OBJ)
$(OBJ):%.o:%.c #添加生成依赖规则,这个很重要
    $(CC) -c $^ -o $(OBJDIR)/$@    #添加obj路径

 

ECHO:
    @echo $(OBJS)
clean:
    -rm $(OBJ)1234567891011

 

从这里的依赖规则来看,第一种方法它不需要自定义规则,因为输出的路径就在当前路径,所以可以使用自动规则来生成目标
但是这里如果要指定路径,那就不能使用自动的规则来做,添加这个%.o:%.c
“%”表示长度任意的非空字符串
“%.o” 表明要所有以”.o” 结尾的目标
依赖模式”%.c”则取模式”%.o”中的”%”,替代为所有以”.c”结尾的目标

 

.
├── bin
│?? └── test
├── Makefile
├── obj
│?? ├── api_a.o
│?? ├── api.o
│?? ├── hello.o
│?? └── main.o
└── src
    ├── api
    │?? ├── api_a.c
    │?? ├── api.c
    │?? └── Makefile
    ├── main
    │?? ├── hello.c
    │?? ├── main.c
    │?? └── Makefile
    └── Makefile

这种情况下生成的.o文件和.c文件不在同一路径下,在编译时如果对应的.c文件没有被修改则在总的编译会被再次编译,如果文件很多且比较大,这样就会影响编译时间
---------------------

转自:https://blog.csdn.net/leumber/article/details/79842778
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值