在上一篇文章“小項目實用makefile”中,已經說明了單個makefile管理層次目錄的侷限性。本文,主要總結一下項目中的一種實用makefile樹寫法,爲10來個人協作的中小型項目makefile編寫,提供參考。
1. 需求
從實用角度,makefile樹應該達到以下需求:
1)自動加入編譯系統。新增目錄、文件後,能夠自動添加(理想),或只需少許修改,就能添加到整個項目編譯中。
2)可讀性好,易於添加。新增目錄、文件,linux新人能自己看懂添加(理想),或只需口頭10s描述就能很好完成。
3)模塊化。新增目錄、文件後的makefile,和其他目錄完全沒有關係(理想),或只需與最鄰近的前、後有關係。
2. 實現
2.1 自動加入編譯系統
使用makefile遞歸執行,能夠解決此問題。以下腳本,能夠執行SUBDIRS指定的子目錄內的makefile。
- makefile。
- SUBDIRS = modual-a modual-b modual-c
- .PHONY: subdirs $(SUBDIRS)
- subdirs: $(SUBDIRS)
- $(SUBDIRS):
- $(MAKE) -C $@
說明:
1)變量SUBDIRS,指定當前目錄下,要遞歸編譯的子目錄。
2).PHONY及subdirs目標結構,能保證遞歸到子目錄中。
2.2 可讀性好,易於添加;模塊化
使用makefile的變量定義,其位置前後不敏感特性可以做到。示例腳本如下:
- SUBDIRS = modual-a modual-b modual-c
- OBJECTS = x.o y.o z.o
- all:subdirs ${OBJECTS}
- clean:cleansubdirs
- rm ${OBJECTS}
說明:
1)變量OBJECTS,指定當前目錄的目標文件。新增一個文件z.cpp,在變量OBJECTS中,增加z.o即可。
2)新增一個目錄dir-c,在變量SUBDIRS中,增加dir-c。然後參考本目錄中的makfile,在dir-c中,建立一個類似makfile即可。
2.3 減少重複腳本
實現中,將makefile中的共用變量,轉移到文件中,使用include包含,能夠達到函數效果,減少重複腳本,容易改變。
3. 實例
示例項目目錄如下所示(點擊下載示例項目):
- project-test
- +-- makeconfig
- | +-- make.global
- +-- src
- | +-- module-a
- | | +-- test.cpp
- | | +-- Makefile
- | +-- module-b
- | | +-- test.cpp
- | | +-- Makefile
- | +-- main.cpp
- | +-- Makefile
- +-- Makefile
說明:
1)project-test/makeconfig/make.global,要包含的makefile共用變量。
2)project-test/Makefile,頂層makefile,指定可執行目標,及源碼目錄。
3)project-test/src/Makefile,子目錄的makeflile。目錄module-a、module-b的類似,每個目錄一個。
3.1 project-test/makeconfig/make.global
如下所示:
- # compile macro
- CC = g++
- CFLAGS = -O2 -Wall
- LDFLAGS = -lm
- INCLUDES = -I/usr/local/include
- # recursive make
- .PHONY: subdirs ${SUBDIRS} cleansubdirs
- subdirs: ${SUBDIRS}
- ${SUBDIRS}:
- ${MAKE} -C $@ all
- # recursive make clean
- cleansubdirs:
- @for dir in ${SUBDIRS}; do \
- ${MAKE} -C $$dir clean; \
- done
- # dependence
- %.o: %.cpp
- ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@
- %.o: %.cc
- ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@
說明:
1)包含4個區域,共用變量,遞歸make,遞歸makeclean,依賴關係。
2)遞歸makeclean,使用了不打印的shell語法。原因是,如果和遞歸一樣寫,會造成目標重載警告。
3.2 project-test/Makefile
如下所示:
- # target, subdir, objects in current dir
- TARGET = test
- SUBDIRS = src
- OBJECTS =
- all:subdirs ${OBJECTS}
- ${CC} -o ${TARGET} $$(find ./${SUBDIRS} -name '*.o') ${LDFLAGS} ${INCLUDES}
- clean:cleansubdirs
- rm -f ${TARGET} ${OBJECTS}
- # path of "make global scripts"
- # NOTE, use absolute path. export once, use in all subdirs
- export PROJECTPATH=${PWD}
- export MAKEINCLUDE=${PROJECTPATH}/makeconfig/make.global
- # include "make global scripts"
- include ${MAKEINCLUDE}
說明:
1)使用shell語法,搜索出指定目錄的所有.o,鏈接爲執行目標文件。
2)使用export,指定項目絕對路徑,指定共用變量,包含所有目錄共用的makefile變量。
3)每個目標使用一個頂層的makefile,來執行make和makeclean的遞歸入口,共用makefile。
3.3 project-test/src/Makefile
如下所示:
- # subdir and objects in current dir
- SUBDIRS = module-a module-b
- OBJECTS = main.o
- all:subdirs ${OBJECTS}
- clean:cleansubdirs
- rm -f ${OBJECTS}
- include ${MAKEINCLUDE}
說明:
1)增加目錄,只用修改子目錄變量SUBDIRS;增加文件,修改當前目標文件變量OBJECTS。
2)不同目錄下,源碼相同名字,但類、函數不相同,可以正常編譯。
4. 專業makefile樹
以上,只是一個項目最最普通的makefile樹。一個實現文件一個.o文件,不考慮庫生成,不考慮功能配置項,不考慮平臺兼容性。
一些開源項目,考慮了各種平臺兼容性,及功能特性,通常使用了autoconf和automake,自動生成特殊頭文件和宏定義,來達到效果。使用以下3條命令,向用戶提供配置項設置,編譯,庫、頭文件、或目標文件安裝路徑。在複雜兼容項目中,非常實用。
./configure
make
make install
還在繼續學習中。
參考資料:
1. GNU Make Manual:http://www.gnu.org/software/make/manual/
2. 同事J的makefile樹。