一种可自动实现增量编译的Makefile模板

    科技2022-07-14  130

    Makefile介绍

    程序的生成可分为预处理 编译 汇编 链接这几个阶段,各个阶段的功能不再赘述。对于平时写的各种小程序,并不需要很精细的管理。如果某一处发生了改动,直接把所有文件把的所有阶段全都执行一遍。当工程比较小的时候怎么做没有问题,但一旦工程比较大,就需要选择性的进行编译、链接等操作,否则会消耗很多时间。当源码有改动时,只对改动所能影响到的文件进行重构,这就是增量编译的概念。 Makefile 可以简单的认为是一个工程文件的编译规则,描述了整个工程的编译和链接等规则。其中包含了那些文件需要编译,那些文件不需要编译,那些文件需要先编译,那些文件需要后编译,那些文件需要重建等等。编译整个工程需要涉及到的,在 Makefile 中都可以进行描述。换句话说,Makefile 可以使得我们的项目工程的编译变得自动化,不需要每次都手动输入一堆源文件和参数。 Makefile是Linux下进行多文件编程的必备技能。本篇博客给出了一种比较方便的Makefile方案,由于本人也是初学者,如果方案有任何不合理或者可改进之处,可进行指正,请大家多多支持。

    设计要点

    在编写程序的时候,所做的改动无非就是源文件和头文件的改动。如果改动了源文件,则对应的目标文件和可执行程序就需要重新生成。比较麻烦的是头文件的改动,一旦头文件改动,那么任何包含它的源文件亦需要重新生成。 那么如何知道头文件会影响哪些源文件,或者说源文件依赖于哪些头文件呢?很幸运的是,gcc编译器有-MM选项,可以自动的得出源文件的依赖文件。 在本博客的Makefile里,就是利用了该功能,把依赖关系写进文件中,在通过读取文件的方式读入Makefile里。 本案例的Makefile包含以下相关知识:

    Makefile的运行原理Makefile变量和函数的使用Makefile通配符 * %Makefile自动化变量 $@ $<Makefile搜索路径 vpathMakefile文件包含 -includeMakefile伪目标 .PHONYGCC编译器的 -c -MM功能

    使用方法

    创建文件夹,分别用于存放头文件、源文件、目标文件等。分别把头文件和源文件放入指定的目录中创建Makefile文件,把代码复制进去根据自己创建的目录,修改Makefile中的内容指定编译器以及语言标准等编译选项执行make命令修改源码,再次执行make命令

    以源码分享中的Makefile为例,其文件目录结构为 Makefile 就是含有源码的Makefile文件 obj文件夹 用于存放 目标文件.o 和 依赖文件 .d src文件夹 用于存放C语言源文件.c inc文件夹 用于存放C语言头文件.h bin文件夹 用于存放生成的可执行文件

    当然,文件目录 可执行文件名 编译器 等等都可以进行修改,把所有文件存放在一个目录 或者 用多个文件夹存放源文件等也可以,只要在Makefile文件里修改对应参数即可。

    源码分享

    #当前文件夹 CUR_DIR = .#$(shell pwd) #源文件夹 可包含多个 SRC_DIR = $(CUR_DIR)/src #头文件夹 可包含多个 INC_DIR = $(CUR_DIR)/inc #可执行文件夹 BIN_DIR = $(CUR_DIR)/bin #中间文件夹 OBJ_DIR = $(CUR_DIR)/obj #依赖文件夹 DEP_DIR = $(CUR_DIR)/obj #可执行文件名 EXCU = main.out #编译器: gcc g++ CC = gcc #源文件后缀名: c cpp * SUFFIX = c #编译选项: #C常用标准: c89 c99 gnu90 gnu99 gnu11 #C++常用标准: c++98 c++11 gnu++98 gnu++11 CFLAGS = -g -Wall -std=gnu99 CPATH = $(addprefix -I,$(INC_DIR)) #-------------------------------------------------------------------- #文件集合 SRCS = $(wildcard $(addsuffix /*.$(SUFFIX),$(SRC_DIR))) OBJS = $(addprefix $(OBJ_DIR)/,$(addsuffix .o,$(basename $(notdir $(SRCS))))) DEPS = $(addprefix $(DEP_DIR)/,$(addsuffix .d,$(basename $(notdir $(SRCS))))) BINS = $(BIN_DIR)/$(EXCU) #Makefile路径设置 vpath vpath %.c $(SRC_DIR) vpath %.h $(INC_DIR) vpath %.o $(OBJ_DIR)#重要 用于寻找目标%.o vpath %.d $(DEP_DIR)#重要 用于寻找目标%.d #第一个目标 all:$(BINS) #包含依赖文件 没有则不包含 继续执行 -include $(DEPS)#文件名为%.d 内部目标名为 %.o 没有路径 #链接 生成可执行文件 利用 %.o 的依赖信息 $(BINS):$(notdir $(OBJS)) $(CC) $(OBJS) $(CFLAGS) $(CPATH) -o $(BINS) #生成.o和.d文件 %.o:%.c $(CC) $< $(CFLAGS) $(CPATH) -c -o $(OBJ_DIR)/$@ @$(CC) $< $(CFLAGS) $(CPATH) -MM -o $(DEP_DIR)/$(basename $(@F)).d #清理文件 .PHONY:clean clean: rm -f $(BINS) $(OBJS) $(DEPS)

    改进方向

    由于本人的学识、时间和精力所限,对于静态库和动态库的支持还有待完善。在后续的改善中,可着手于对库文件使用和生成进行优化。

    Processed: 0.010, SQL: 8