快速编写“专家级”makefile(4.打造更专业的编译环境)

    前面的 simple 和 complicated 项目都是采用了单一的目录结构,但大型的项目往往用多个目录来存放不同的模块。下面我们通过 huge 项目来模拟一个更加专业的编译环境。
    下图说明了 huge 项目将采用的目录结构
    从图中:
  1. huge 最上层有两个目录: build 和 code
  2. build 目录用于存放个 Makefile 文件间的共享文件 make.rule ,以及编译整个项目的 Makefile,在 build 还会自动生成 libs 和 exes 两个子目录                                                                                                                            (1)libs : 用于存放编译出来的目标文件                                                                                                            (2)exes:用于存放编译出来的可执行文件
  3. code 目录用于存放项目的源程序文件,在 code 中还会创建 foo 库和 huge 主程序两个子目录
  4. 对于每个软件模块子目录,分为用于存放 .c 文件的 src 子目录和用于存放 .h 文件的 inc 子目录。当进行项目编译时,我们希望 make 在 src 目录下面创建 deps 和 objs 目录。
  5. 在每一个 src 目录中都会有一个 Makefile ,用于构建所在目录中的源程序文件,可以推测,在 build 目录下的 Makefile ,将调用每一个软件模块中 src 子目录内的 Makefile 。
    我们采用以下命令来完成这些目录的创建工作:
$mkdir -p build code/foo/src  code/foo/inc  code/huge/src

    huge / code / foo / src / Makefile
   
   
  1. .PHONY : all clean
  2. MKDIR = mkdir
  3. RM = rm
  4. RMFLAG = -rf
  5. CC = gcc
  6. AR = ar
  7. ARFLAG = crs
  8. DIR_OBJS = objs
  9. DIR_EXES = ../../../build/exes
  10. DIR_DEPS = deps
  11. DIR_LIBS = ../../../build/libs
  12. DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) $(DIR_LIBS)
  13. RMS = $(DIR_OBJS) $(DIR_DEPS)
  14. EXE =
  15. ifneq("$(EXE)", "")
  16. EXE := $(addprefix $(DIR_EXES)/, $(EXE))
  17. RMS += $(EXE)
  18. endif
  19. LIB = libfoo.a
  20. ifneq("$(LIB)", "")
  21. LIB := $(addprefix $(DIR_LIBS)/, $(LIB))
  22. RMS += $(LIB)
  23. endif
  24. SRCS = $(wildcard *.c)
  25. OBJS = $(SRCS :.c = .o)
  26. OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
  27. DEPS = $(SRCS :.c = .dep)
  28. DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
  29. ifeq("$(wildcard $(DIR_OBJS))", "")
  30. DEP_DIR_OBJS := $(DIR_OBJS)
  31. endif
  32. ifeq("$(wildcard $(DIR_EXES))", "")
  33. DEP_DIR_EXES := $(DIR_EXES)
  34. endif
  35. ifeq("$(wildcard $(DIR_DEPS))", "")
  36. DEP_DIR_DEPS := $(DIR_DEPS)
  37. endif
  38. ifeq("$(wildcard $(DIR_LIBS))", "")
  39. DEP_DIR_LIBS := $(DIR_LIBS)
  40. endif
  41. all : $(EXE) $(LIB)
  42. ifneq($(MAKECMDGOALS), clean)
  43. include $(DEPS)
  44. endif
  45. $(DIRS) :
  46. $(MKDIR) $@
  47. $(EXE) : $(DEP_DIR_EXES) $(OBJS)
  48. $(CC) -o $@ $(filter %.o, $^)
  49. $(LIB) : $(DEP_DIR_LIBS) $(OBJS)
  50. $(AR) $(ARFLAG) $@ $(filter %.o, $^)
  51. $(DIR_OBJS) / %.o : $(DEP_DIR_OBJS) %.c
  52. $(CC) -o $@ -c $(filter %.c, $^)
  53. $(DIR_DEPS) / %.dep : $(DEP_DIR_DEPS) %.c
  54. @echo "Creating $@ ..."
  55. @set -e;\
  56. $(RM) $(RMFLAG) $@.tmp;\
  57. $(CC) -E -MM $(filter %.c, $^) > $@.tmp;\
  58. sed 's,\(.*\)\.o[ :]*,objs/\1.o $@: ,g' < $@.tmp > $@;\
  59. $(RM) $(RMFLAG) $@.tmp
  60. clean :
  61. $(RM) $(RMFLAG) $(RMS)
    其中更改如下:
  1. 增加了 AR 和 ARFLAG 两个变量,用于创建静态库
  2. 将 exes 目录的实际位置以相对路径的形式赋值给 DIR_EXES 变量
  3. 增加了 DIR_LIBS 变量以记录 libs 目录的实际位置,同样采用相对路径的形式
  4. 在 DIRS 变量中增加了 DIR_LIBS 变量的值,以便创建 build / libs 目录
  5. 新增了 RMS 变量用于表示需要删除的目录和(或)文件。由于这个 Makefile 只是针对构建 libfoo.a 库的,所以当运行 “make clean” 时,不应将位于 build 目录下的 exes 和 libs 目录全部删除。
  6. 清除了对 EXE 变量所赋值的 complicated,同时增加了 ifneq 条件语句用于判断 EXE 变量的值是否为空。只有当 EXE 不为空时才需要为 EXE 变量的值增加目录前缀并将 $(EXE) 加入到 RMS 变量中,以便在调用 “make clean” 时清除它
  7. 新增了 LIB 变量,用于存放最终生成库的名字,同样使用条件语法来决定是否需要为 LIB 变量中的值增加目录前缀
  8. 为 all 目标增加 $(LIB) 先决条件
  9. 增加了一条用于生成库的规则,使用 ar 工具来生成库
  10. 在 clean 目标命令中,采用删除 RMS 变量中的内容而不是 DIRS 变量中的内容

       运行结果:
/Makefile / huge / code / foo / src
$ touch foo.c
$ make
mkdir deps
Creating deps / foo.dep ...
mkdir objs
gcc -o objs / foo.o -c foo.c
ar crs ../../../build/libs/libfoo.a objs/foo.o
$ ls
Makefile deps/   foo.c  objs/
$ ls  ../../../build/libs/
libfoo.a
$ make clean
rm -rf objs deps ../../../build/libs/libfoo.a
$ ls ../../../build/libs/
               
    从运行结果:
         确实在 build / libs 目录下面生成了 libfoo.a 库文件,运行了 “make clean” ,并没有将 build / libs 目录删除,只是删除了 libfoo.a 文件
         下面要做的是将这个 Makefile 运用到 code / huge / src 目录。
          参考文献:《专业嵌入式软件开发》李云·著                                                             2016年7月5日,星期二
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值