快速編寫“專家級”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日,星期二
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章