一個簡單的例子:
ab.out : a.o b.o
g++ -o ab.out o/a.o o/b.o
a.o : a.cpp
g++ -c a.cpp -o o/a.o
b.o : b.cpp
g++ -c b.cpp -o o/b.o
clean:
rm -f o/a.o o/b.o
再看下面一個:
simple_server_objects = socket/ServerSocket.o socket/Socket.o server.o
simple_client_objects = socket/ClientSocket.o socket/Socket.o client.o
all : server client
server: $(simple_server_objects)
g++ -o server $(simple_server_objects) -lmysqlpp -lmemcached -lmysqlclient -L/usr/local/lib
client: $(simple_client_objects)
g++ -o client $(simple_client_objects)
Socket.o: socket/Socket.cpp
g++ -c socket/Socket.cpp -o socket/Socket.o
ServerSocket.o: socket/ServerSocket.cpp
g++ -c socket/ServerSocket.cpp -o socket/ServerSocket.o
ClientSocket.o: socket/ClientSocket.cpp
g++ -c socket/ClientSocket.cpp -o socket/ClientSocket.o
server.o: server.cpp
g++ -c server.cpp -o server.o -I/usr/local/mysql/include
simple_client_main.o: simple_client_main.cpp
g++ -c client.cpp -o client.o
clean:
rm -f *.o socket/*.o server client
1 #定義編譯選項 2 CC = g++ 3 LINK = g++ 4 CFLAGS = -Wall 5 6 #定義頭文件目錄,鏈接庫目錄,鏈接文件 7 INCLUDE_PATH = -Iinclude 8 LIB_PATH = -Llib -L/usr/local/lib -Lsrc/lib 9 LIBS = -lboost_thread -lboost_system -Llib #-ljsoncpp 10 11 #定義項目代碼根目錄及所有文件夾目錄 12 SRC_DIR = src 13 VPATH = $(SRC_DIR) 14 VPATH += $(SRC_DIR)/base 15 VPATH += $(SRC_DIR)/data 16 VPATH += $(SRC_DIR)/include 17 VPATH += $(SRC_DIR)/operate 18 VPATH += $(SRC_DIR)/operate/detail 19 20 #找出所有 .cpp 文件和相應的 .o 文件(帶目錄) 21 SRC_FILES = $(foreach n, $(VPATH),$(wildcard $(n)/*.cpp)) 22 OBJ_FILES = $(SRC_FILES:.cpp=.o) 23 24 #把所有的 .o 文件放到定義好的輸出文件夾中統一管理 25 OUTPUT_DIR := debug 26 OUTPUT_OBJS = $(addprefix $(OUTPUT_DIR)/,$(subst $(SRC_DIR)/, ,$(OBJ_FILES))) 27 28 #創建存放 .o 文件的目錄結構 29 $(shell mkdir -p "$(OUTPUT_DIR)") 30 $(shell mkdir -p "$(OUTPUT_DIR)/base") 31 $(shell mkdir -p "$(OUTPUT_DIR)/data") 32 $(shell mkdir -p "$(OUTPUT_DIR)/operate") 33 $(shell mkdir -p "$(OUTPUT_DIR)/operate/detail") 34 35 #更新 ctags 36 #$(shell ctags -R --c++-kinds=+p --fields+iaS --extra=+q .) 37 38 #定義輸出目標名 39 TARGET = server 40 41 #鏈接 42 $(TARGET) : $(OUTPUT_OBJS) 43 $(LINK) $(OUTPUT_OBJS) -o $@ $(LIB_PATH) $(LIBS) 44 45 #編譯 46 $(OUTPUT_DIR)/%.o : %.cpp 47 $(CC) -c $< -o $@ 48 49 #清除 50 .PHONY:clean 51 clean: 52 -rm -rf $(OUTPUT_DIR)/* 53 -rm -rf $(TARGET)
備注:大體的步驟如上,不過需要提前生成存放 .o 文件的目錄結構,可以通過 $(shell mkdir -p "$(OUTPUT_DIR)") 等一系列命令來生成,另外需要事先定義好一些變量,如頭文件目錄,鏈接庫目錄,鏈接庫名稱等。該通用步驟可以使MAKEFILE寫起來很簡單,不過感覺效率不高,太多依賴自動查找,另外頭文件改動后,也要先 clean 再 make,這一點才是最致命的。待續。
GCC頭文件的環境變量是 export CPLUS_INCLUDE_PATH=/root/new_s3/src/include
加入到GCC環境變量相比使用 -I 來包含頭文件的區別是,在使用 -MM 生成依賴關系時,前者會忽略生成指定路徑的頭文件的依賴關系。
二、自動依賴
#定義編譯選項 CC = g++ LINK = g++ CFLAGS = -g -Wall -O0 #定義頭文件目錄,鏈接庫目錄,鏈接文件 #INCLUDE_PATH = -Isrc/include -Isrc/include/mysql LIB_PATH = -Lsrc/lib LIBS = -lboost_thread -lboost_system -ljsoncpp -lmysqlpp -lmysqlclient #定義項目代碼根目錄及所有文件夾目錄 SRC_DIR = src/Server VPATH = $(SRC_DIR) VPATH += $(SRC_DIR)/base VPATH += $(SRC_DIR)/data VPATH += $(SRC_DIR)/include VPATH += $(SRC_DIR)/operate #設置GCC編譯時的查找的頭文件目錄。如果在 -MM 時用-I來指定,那么會生成依賴關系,如BOOST庫等,但一般情況下BOOST庫是不會修改的。 export CPLUS_INCLUDE_PATH=src/include:src/include/mysql #找出所有 .cpp 文件和相應的 .o 文件(帶目錄) SRC_FILES = $(foreach n, $(VPATH),$(wildcard $(n)/*.cpp)) ALL_FILES = $(foreach n, $(VPATH),$(wildcard $(n)/*.h $(n)/*.cpp)) OBJ_FILES = $(notdir $(SRC_FILES:.cpp=.o)) .PHONY:all all : depend server #生成依賴關系文件,這里不用偽目標,這樣只有當代碼被改動時,依賴關系文件才會被重新生成 #.PHONY:depend depend:$(ALL_FILES) #@export CPLUS_INCLUDE_PATH=src/include:src/include/mysql && $(CC) $(CFLAGS) -MM $(SRC_FILES) > depend -include depend #鏈接 server : $(OBJ_FILES) $(LINK) $(OBJ_FILES) -o $@ $(LIB_PATH) $(LIBS) #清除 .PHONY:clean clean: -rm -rf $(TARGET) -rm -rf *.o
該方法缺點:每次都需要生成所有源文件的依賴關系文件,即使是改動一個源文件的情況下。
time make -j4 使用4線程來編譯程序
通配符可以使用在變量中,如 objects = *.o ,不過 *.o 並不會展開,Makefile 中的變量,就是C/C++中的宏,如果需要展開的話,即 objects 的值是所有 .o 文件的集合,可以:
objects = $(wildcard *.o)
Makefile 默認只在當前目錄下尋找依賴文件和目標文件,如果定義了VPATH變量,則會到變量中所指示的目錄中去尋找,VPATH變量的值,應該用空格或冒號分開,(在WINDOWS下是用空格或分號),與之類似的還有一個vpath關鍵字,它靈活,可以指定在不同的目錄里搜索不同類型的文件,使用方法有三種:
vpath <pattern> <dir> #為模式指定目錄
vpath <pattern> #清除某種模式的搜索目錄
vpath #清除所有模式的搜索目錄
<pattern>中使用 % ,來匹配0或若干個字符。如:vpath %.h src/include
偽目標也可以做為依賴:
.PHONY : cleanall cleanobj cleandiff cleanall : cleanobj cleandiff rm program cleanobj : rm *.o cleandiff : rm *.diff
files = a.o b.o c.i
$(filter %.o,$(files)) : %.o : %.cpp
感覺 $(filter %.o,$(files)) : %.cpp 與 $(file) : %.o : %.cpp 效果一樣。
$(files: .o = .d) #把變量$(files) 中的所有以 .o 結尾(后面是空格或結束符)字符串換成 .d
gcc 的 -M 和 -MM 選項,可以生成依賴關系,前者會包括標准庫的頭文件,后者不帶。
命令行前如果使用 @ 符號,則這個命令不會被 make 顯示出來,如 @echo 正在編譯……,會輸出“正在編譯……”
可以通過 make -n 或 make --just-print 來查看而不執行命令。 make -s 或 make --slient 是全面禁止顯示命令。$(subst <from>,<to>,<text>) #字符串替換,返回處理后的字符串 $(patsubst <from>,<to><text>) #模式字符串替換,相當於 $(var:%.c=%.o),返回處理后的字符串 $(strip <string>) #去首尾空格,返回處理后的字符串 $(findstring <find>,<text>) #查找字符串,找到則返回<find>,否則返回空字符串 $(filter <pattern>,<text>) #過濾出text中符合模式的字符串,如:$(filter %.c %.cpp,$(source)) $(filter-out <pattern>,<text>) #和上面相反,返回不符合模式的字串,同樣可以有多個模式 $(sort <text>) #給字符串中的單詞排序,注意它會去掉重復的單詞 $(word <n>,<text>) #取出第n個單詞 $(wordlist <s>,<e>,<text>) #取出第s到e個單詞,包括第s和第e的單詞 $(words <text>) #統計單詞個數 $(firstword <text>) #返回第一個單詞,相當於 $(word 1,<text>)
文件函數:
$(dir <names>) #返回文件名序列的目錄部分 $(nodir <names>) #返回文件名序列的文件部分 $(suffix <names>) #返回文件名序列的后綴部分 $(basename <names>) #返回文件名序列的前綴部分(返回最后一個小數點前的字串) $(addsuffix <suffix>,<names>) #為文件名序列中每個單詞加后綴 $(addprefix <prefix>,<names>) #為文件名序列中每個單詞加前綴 $(join <list1>,<list2>) #連接單詞,如 $(join aaa bbb,111 222 333) 返回 aaa111 bbb222 333
$(foreach <var>,<list>,<cmd>) 把<list>中的單詞逐一取出來,放到<var>中,然后執行<cmd>,如:
names := a b c dfiles := $(foreach n,$(names),$(n).o)
$@ 目標文件集 $< 第一個依賴項 $? 所有比目標新的依賴項 $^ 所有依賴集合 $+ 同上,只是不去重復 另外,加上D或F,分別表示目錄部分或文件部分,如 $(@F) 當一個模式匹配包含有斜杠(實際也不經常包含)的文件時,那么在進行模式匹配時,目 錄部分會首先被移開,然后進行匹配,成功后,再把目錄加回去。在進行"莖"的傳遞時,我 們需要知道這個步驟。例如有一個模式"e%t",文件"src/eat"匹配於該模式
生成動態鏈接庫與靜態鏈接庫的方法(先生成 *.o 文件,直接通過源文件生成亦可):
靜態鏈接庫其實就可以看成是 *.o 文件的簡單打包:
ar -crv libtest.a test.o 或: ar -r libtest.a test.cpp -r選項是必須的,表示插入到備份文件,並且有則替換 -c表示創建備份文件 -v表示顯示詳情
動態鏈接庫優於靜態鏈接庫,無論是在內存使用和磁盤使用上(多進程時),既可以使用源碼文件直接生成,也可以使用 *.o 文件生成(*.o 文件的生成必須使用 -fPIC 參數,而從*.o 文件生成動態鏈接庫則不用再重復此參數):
gcc -c -fPIC test.cpp && gcc test.o -o libtest.so -shared
或: gcc test.cpp -o libtest.so -shared -fPIC
-shared 該選項指定生成動態連接庫(讓連接器生成T類型的導出符號表,有時候也生成弱連接W類型的導出符號),不用該標志外部程序無法連接。相當於一個可執行文件
-fPIC 表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。
g++ -x c++-header -c stdc++.h -o stdc++.h.gch
使用-x c++-header來說明這個文件作為C++的預編譯頭文件。注意:C和C++的處理方式不一樣,C要使用-x c-header選項。
