Makefile嵌套编译多文件项目

    xiaoxiao2022-07-04  135

    在多文件的项目中,一个工程中的源文件比较多,其按类型、功能、模块分别放在若干个目录中,为了项目更加规整,我们常常要将源文件头文件执行文件等分开,所以在编译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  

    最新回复(0)