• 一个适用于层级目录结构的makefile模版


    今天写了个层次化的Makefile模版,用来自动化编译项目,这个模版应当包含以下功能:

    • 适用于层次化结构,Makefile主要内容都放在顶层目录下的Makefile.env中,子层Makefile包含这个Makefile.env,只要增加一些变量就可以编译,特别方便添加新的功能模块
    • 自动解析头文件依赖

    我的程序的目录结构是这样的:

    1. 源文件目录src,模块xxx放在src/xxx下,主程序在src/main下面

    2.公共头文件放在include目录下,模块xxx的头文件放在include/xxx目录下

    3.模块输出的链接库放在lib目录下

    4.可执行文件放在bin目录下

    先来看一下Makefile.env,这个类似于c的头文件,包含了所有Makefile的公共部分,

    复制代码
    ###########  MakeFile.env  ##########
    # Top level pattern, include by Makefile of child directory
    # in which variable like TOPDIR, TARGET or LIB may be needed
    
    CC=gcc
    MAKE=make
    
    AR=ar cr
    RM = -rm -rf
    
    CFLAGS+=-Wall
    
    dirs:=$(shell find . -maxdepth 1 -type d)
    dirs:=$(basename $(patsubst ./%,%,$(dirs)))
    dirs:=$(filter-out $(exclude_dirs),$(dirs))
    SUBDIRS := $(dirs)
    
    SRCS=$(wildcard *.c)
    OBJS=$(SRCS:%.c=%.o)
    DEPENDS=$(SRCS:%.c=%.d)
    
    
    all:$(TARGET)  $(LIB) subdirs
    
    $(LIB):$(OBJS) 
        $(AR)  $@  $^
        cp $@ $(LIBPATH) 
    
    subdirs:$(SUBDIRS)
        for dir in $(SUBDIRS);
        do $(MAKE) -C $$dir all||exit 1;
        done
    
    $(TARGET):$(OBJS)
        $(CC) -o $@ $^ $(LDFLAGS)
        cp $@ $(EXEPATH)
    
    
    $(OBJS):%.o:%.c
        $(CC) -c $< -o $@ $(CFLAGS)
    
    
    -include $(DEPENDS)
    
    $(DEPENDS):%.d:%.c
        set -e; rm -f $@; 
        $(CC) -MM $(CFLAGS) $< > $@.$$$$; 
        sed 's,($*).o[:]*,1.o $@:,g' < $@.$$$$ > $@; 
        rm $@.$$$$
    
    clean:
        for dir in $(SUBDIRS);
        do $(MAKE) -C $$dir clean||exit 1;
        done
        $(RM) $(TARGET) $(LIB)  $(OBJS) $(DEPENDS)
    复制代码

    当前目录下的子目录是通过shell命令自动得到的,subdirs:$(SUBDIRS) 这块会进入每个子目录执行make,当然有些子目录并不需要编译,可以通过exclude_dirs指定,比如顶层目录的exclude_dirs=bin lib include。

    $(DEPENDS):%.d:%.c 这块作用是自动生成头文件依赖,这部分包括5条命令,看起来很复杂,其实原理很简单,假设main.c,包含头文件depend.h, 解析过程如下:

    1. @set –e 命令设置当前Shell进程状态为:如果执行的任何一条命令的退出状态非零则立刻终止当前进程。

    2. rm -f $@ 删除原来的main.d文件

    3. gcc的-MM参数能够生成文件的依赖关系main.o:main.c depend.h,写入文件main.d. $$$$,$$是进程号

    4. sed命令作用是将main.o:main.c depend.h替换成main.o main.d:main.c depend.h, 并写入main.d文件

    5. rm -f $@.$$$$删除临时文件

    Include $(SRCS:.c=.d)将main.d包含进来后,Makefile增加了以下依赖

    main.o main.d:main.c depend.h

    不管是main.c还是depend.h的变化都会更新main.o 以及main.d,main.d的更新又反过来更新上面这条依赖关系。

    这条依赖下面并没有对应的命令,为什么会更新目标文件呢?这跟Makefile的运行步骤有关系,引用下陈浩先生的《跟我一起写Makefile》

    GNU的 make 工作时的执行步骤如下:

    1、读入所有的 Makefile。

    2、读入被 include 的其它 Makefile。

    3、初始化文件中的变量。

    4、推导隐晦规则,并分析所有规则。

    5、为所有的目标文件创建依赖关系链。

    6、根据依赖关系,决定哪些目标要重新生成。

    7、执行生成命令。

    所以1-5 步为第一个阶段,形成了所有的依赖关系链,6-7 为第二个阶段,决定了所有需要生成的目标文件后,执行对应的命令。上面的依赖关系虽然没有命令,但是确定了main.o要重新生成,就会找到以下编译模块生成目标文件

    $(OBJS):%.o:%.c
        $(CC) -c $< -o $@ $(CFLAGS)

    假设有一个模块first,源文件都放在src/first下,Makefile如下

    复制代码
    TOPDIR=./../..
    
    LIB=libfirst.a
    
    INCPATH=$(TOPDIR)/include/first
    LIBPATH=$(TOPDIR)/lib
    CFLAGS= -I$(INCPATH)
    
    include $(TOPDIR)/Makefile.env
    复制代码

    TOPDIR是相对于顶层目录的相对路径,LIB是要生成的链接库,这样只要几行命令就可以完成当前模块的编译了,而且first下面还可以添加子模块。

    假设main.c在src/main目录下,调用了first模块,Makefile如下

    复制代码
    TOPDIR=./../..
    
    TARGET=main
    
    LIBPATH=$(TOPDIR)/lib
    EXEPATH=$(TOPDIR)/bin
    
    CFLAGS= -I$(TOPDIR)/include/first 
    LDFLAGS= -lfirst
    
    include $(TOPDIR)/Makefile.env
    复制代码

    TARGET是生成的可执行文件名,在LIBPATH目录下寻找链接库,生成的可执行文件会被mv到EXEPATH目录下

    src下没有源文件,只有目录,所以Makefile非常简单

    TOPDIR=./..
    
    include $(TOPDIR)/Makefile.env

    顶层目录下的Makefile也很简单,相对增加了exclude_dirs,排除不需要编译的目录

    TOPDIR=.
    
    exclude_dirs= include  bin  lib
    
    include $(TOPDIR)/Makefile.env

    现在只需要在顶层目录下make一下,src下所有目录都会编译,生成的链接库放在lib下,可执行文件在bin目录中。如果要增加新的功能模块,只要在src/目录下新建目录,增加一个类似first下的Makefile即可,是不是很方便?

  • 相关阅读:
    Sum Root to Leaf Numbers [LeetCode]
    Symmetric Tree [LeetCode]
    Combination Sum II [LeetCode]
    Maximal Rectangle [LeetCode]
    Trapping Rain Water [LeetCode]
    Combination Sum [LeetCode]
    05 如何“响铃”
    04 八进制
    03 关键字?保留字?预留字?
    020 函数之变量的作用域
  • 原文地址:https://www.cnblogs.com/lidabo/p/4628791.html
Copyright © 2020-2023  润新知