本文給出萬能Makefile的具體實現,以及對其中的關鍵點進行解析。所謂C++萬能Makefile,即可編譯鏈接所有的C++程序,而只需作很少的修改。
號稱萬能Makefile,一統江湖。我對原版的Makefile做了些修改。首先揭開它的廬山真面目:
#################################################### # Generic makefile - 萬能Makefile # for compiling and linking C++ projects on Linux # Author: George Foot Modified:Jackie Lee #################################################### ### Customising # # Adjust the following if necessary; EXECUTABLE is the target # executable's filename, and LIBS is a list of libraries to link in # (e.g. alleg, stdcx, iostr, etc). You can override these on make's # command line of course, if you prefer to do it that way. # # EXECUTABLE := main # 可執行文件名 LIBDIR:= # 靜態庫目錄 LIBS := # 靜態庫文件名 INCLUDES:=. # 頭文件目錄 SRCDIR:= # 除了當前目錄外,其他的源代碼文件目錄 # # # Now alter any implicit rules' variables if you like, e.g.: CC:=g++ CFLAGS := -g -Wall -O3 CPPFLAGS := $(CFLAGS) CPPFLAGS += $(addprefix -I,$(INCLUDES)) CPPFLAGS += -MMD # # # The next bit checks to see whether rm is in your djgpp bin # # directory; if not it uses del instead, but this can cause (harmless) # # `File not found' error messages. If you are not using DOS at all, # # set the variable to something which will unquestioningly remove # # files. # RM-F := rm -f # # You shouldn't need to change anything below this point. # SRCS := $(wildcard *.cpp) $(wildcard $(addsuffix /*.cpp, $(SRCDIR))) OBJS := $(patsubst %.cpp,%.o,$(SRCS)) DEPS := $(patsubst %.o,%.d,$(OBJS)) MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS)) MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.cpp,$(MISSING_DEPS))) .PHONY : all deps objs clean veryclean rebuild info all: $(EXECUTABLE) deps : $(DEPS) objs : $(OBJS) clean : @$(RM-F) *.o @$(RM-F) *.d veryclean: clean @$(RM-F) $(EXECUTABLE) rebuild: veryclean all ifneq ($(MISSING_DEPS),) $(MISSING_DEPS) : @$(RM-F) $(patsubst %.d,%.o,$@) endif -include $(DEPS) $(EXECUTABLE) : $(OBJS) $(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -L,$(LIBDIR)) $(addprefix -l,$(LIBS)) info: @echo $(SRCS) @echo $(OBJS) @echo $(DEPS) @echo $(MISSING_DEPS) @echo $(MISSING_DEPS_SOURCES)
注:1)命令行前的空白符必須爲一個製表符(Tab);如,@$(RM-F) *.o前不是空格,而是一個製表符;
內容解析
1.Makefile基本語法
target爲要生成的目標文件;dependency爲target的依賴文件;command爲用於生成target的命令行;
<target> : <dependency> <dependency> ... (tab)<command> (tab)<command> . . .
2.賦值符號 := 與 =
:=與=的區別在於,符號:=表示立即展開變量值。例如:
A:=foo
B:=$(A)
A:=bar
這時,B的值仍爲foo,因爲它已被展開,不會再隨A的值改變而改變。
3.符號#是Makefile的註釋符號
4.wildcard函數
SRCS:=$(wildcard *.cpp) 表示列舉當前目錄中擴展名爲.cpp的所有文件,然後賦值給變量SRCS。詳細請google之。
5.patsubst函數
OBJS := $(patsubst %.cpp,%.o,$(SRCS))表示,將$(SRCS)中所有滿足模式%.cpp的字符串替換爲%.o。
6.filter-out函數
$(filter-out $(A),$(B))表示從B中過濾掉A中的內容,返回剩餘內容;
7. “.PHONY”
用.PHONY修飾的target是“僞目標”,不需要生成真實的文件;make假定phony target是已經生成的,然後更新它後邊的依賴文件和執行它下邊的命令(command);
8.all deps objs clean veryclean rebuild info
這些都是“僞目標”。
all是第一個目標,所以輸入make時它被默認執行;all生成或更新所有*.cpp文件對應的*.d文件和*.o文件,並鏈接所有*.o文件生成可執行文件$(EXECUTABLE)。
deps僅僅生成*.d文件;.d文件是什麼文件?它包含了代碼文件的依賴信息。
objs僅僅生成*.o文件;.o文件是C++代碼編譯後的中間結果文件,廢話!
clean用於刪除*.d文件和*.o文件。
veryclean刪除*.d文件、*.o文件,還有名爲$(EXECUTABLE)的可執行文件。
rebuild先調用veryclean清除結果文件,再調用all重新編譯和鏈接。
info查看某些信息。
使用方法:
make deps即可執行deps;
9.ifneq...else...endif
條件語句,ifneq表示如果不想等,則...;
10.include <files>語句
include表示把<files>的內容包含進來;
$(DEPS)是包含依賴信息的文件,每個源文件對應一個.d文件;-include $(DEPS)表示把這些依賴信息包含進來;
11.鏈接*.o文件,生成可執行文件
主菜來了!
$(EXECUTABLE) : $(OBJS) $(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))
$(EXECUTABLE)爲可執行文件名;$(OBJS)爲所有.o文件名;$(CC)在這裏是g++;$(addprefix -l,$(LIBS)添加引用庫;
前面說好的*.d文件和*.o文件是怎麼生成的呢?貌似沒有命令指出要生成它們呀!請看隱含規則!
12. 隱含規則(Implicit rules)
$(EXECUTABLE)依賴於$(OBJS),但makefile中沒有指明$(OBJS)依賴於誰,也沒指明命令生成它們;
這時,make的隱含規則開始起作用;針對$(OBJS)中的每個目標,make自動調用:
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
依次生成.o文件和.d文件;
$<表示依賴文件列表的第一個文件名;
$@表示目標文件名;
之所以會生成.d文件,是由於“-MMD”這一編譯選項。爲g++加上這一選項後,編譯器會生成文件依賴信息,並存放至.d文件中。
每一個.cpp文件相應地生成一個.d文件和一個.o文件。
13.@符號
命令行前的@符號表示不回顯命令行;
14.CFLAGS和CPPFLAGS
這兩者包含編譯選項,更詳細內容請Google之。
-g 添加gdb調試信息;
-Wall 提示warning信息;
-O3 表示第3級優化;