溫馨提示:閱讀本文的同學最好能瞭解makefile和python的編寫規則。不懂的同學可以先儲存在收藏夾,以便日後查看。
轉載請以超連結標明:http://www.guimigame.com/thread-460-1-1.html
其實之前我一直很懶,我不想瞭解makefile規則,因為在linux下開發我一直使用Qt creator。(很多時候正是一些“懶人”的創造力,解放了我們的雙手,顯然現在我們還需要用雙手寫makefile)。Qt creator是一個很好的IDE,而且可以跨平台開發。但是相比VS,顯然還是不夠優秀。因此很多開發人員都會選擇在Windows下開發C/C++程式,然後部署在Linux下執行。當然我也不例外。所以最近花了幾個晚上瞭解makefile的編寫規則。
開始的時候,我參照網上一些makefile的例子,寫了一個初版的makefile。然而這個makefile在編譯我的工程的時候報錯。
主要出錯體現在:
%.o: %.cpp
$(CXX) -fpic -c $(INCPATH) $< -o $@
當然可以寫成
$(objdir)/%.o:$(srcdir)/%.cpp
$(CXX) -fpic -c $(INCPATH) $< -o $@
原因在於:
1、.o檔案與.cpp檔案處於不同的目錄下。
2、不同的.o檔案或不同的.cpp檔案處於不同的目錄下。
這時我找到兩種解決方案:
1、就是用VPATH這個特殊變數,但是我不可能將所有要包含的目錄都一一手動包含進來,於是我放棄。
2、就是把所有的編譯規則列舉出來。
我最終選擇第二種解決方案。因為之前遇到這個困難時,我特意去瞭解Qt產生的makefile(其實這個makefile是依據.pro工程檔案產生的)。而這個makefile正是將所有的編譯規則都列舉出來。於是就有下面這個python指令碼。其實開始的時候我想用shell來做這一步工作的,但是我看到sed和awk,我頭都暈,之前還一直抵觸學習sed和awk。因此最後選擇了python。
#encoding: utf-8import osimport os.pathimport sys#sys.exit(0);###########################################################################################################################本指令碼的作用是:通過配置必要的資訊,用python來產生makefile。(支援人員:www.guimigame.com)#@FILENAME 執行指令碼輸出makefile檔案名稱#@BIN 產生可執行檔名#@SUFFIX 源檔案尾碼#@ROOTPATH“根”目錄路徑(指令碼工作目錄的上一層)#@PWD當前工作目錄#@WD 工作目錄,如果程式有多個工作目錄請一一用append加上#@BINDIR可執行檔輸出目錄#@OBJDIR中間檔案輸出目錄#@INCROOTPATH標頭檔包含路徑的“根路徑”,方便INCPATH的編寫#@LIBROOTPATH 包含庫的“根”路徑,方便LIBS的編寫#@INCPATH 標頭檔包含路徑#@SYSLIBS 包含的系統庫#@LIBS編譯器需要包含的庫# @CXX一般填寫gcc/g++#@FLAGSgcc/g++的編譯標誌##########################################################################################################################FILENAME = 'makefile';BIN= "DatabaseServer";SUFFIX = ".cpp";ROOTPATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));PWD = os.getcwd();WD= [];WD.append(PWD);WD.append(ROOTPATH + "/common");BINDIR= PWD + "/Bin/";OBJDIR= BINDIR + "obj/";INCROOTPATH= "-I " + ROOTPATH;LIBROOTPATH = "-L " + ROOTPATH;INCPATH = INCROOTPATH + "/common" + " " + INCROOTPATH + "/lib/include";SYSLIBS= " -lmysqlclient -lpthread"LIBS= LIBROOTPATH + "/lib/linux " + "-lTimeManager -lServerConfig -lGameSocket -lCommon -lTinyxml" + SYSLIBS;CXX= "g++";FLAGS= '-g -Wall';###########################################################################################################################以下不需要再配置##########################################################################################################################OBJFILE= '';OBJ2SRC= [];SOURCES= "";def SearchFiles(path):global OBJFILE;global OBJ2SRC;global SOURCES;global SUFFIX;listFile = os.listdir(path);for file in listFile:if os.path.isdir(os.path.join(path, file)):SearchFiles(os.path.join(path, file));elif file.find(SUFFIX) > 0:if file.find(SUFFIX + "~") > 0:continue;OBJFILE = file;OBJFILE = OBJFILE.replace(SUFFIX,'.o');OBJ2SRC.append([OBJDIR + OBJFILE,path + "/" + file]);SOURCES += path + "/" + file + " ";for dir in WD:SearchFiles(dir);if os.path.exists(FILENAME): os.remove(FILENAME);f = open(FILENAME,'w');f.write("PWD = " + PWD + "\n");f.write("CXX = " + CXX + "\n");f.write("INCPATH= " + INCPATH + "\n");f.write("LIBS = " + LIBS + "\n");f.write("BINDIR= " + BINDIR +"\n");f.write("OBJDIR= " + OBJDIR + "\n");f.write("BIN = " + BIN + "\n");f.write("SOURCES= " + SOURCES+ "\n");f.write("SOURCEFILES= $(notdir $(SOURCES))\n");f.write("OBJECTS= $(addprefix $(OBJDIR), $(patsubst %.cpp,%.o,$(SOURCEFILES)))\n");f.write("FLAGS= " + FLAGS + "\n");f.write("\n");f.write("all:makedir $(OBJECTS)\n");f.write("$(CXX) $(FLAGS) $(INCPATH) -o $(BIN) $(OBJECTS) $(LIBS);\n");f.write("\n");f.write("makedir:\n");f.write('$(shell if [ -n "$(OBJDIR)" -a ! -e "$(OBJDIR)" ];then mkdir -p $(OBJDIR); fi)\n');f.write('$(shell if [ -n "$(BINDIR)" -a ! -e "$(BINDIR)" ];then mkdir -p $(BINDIR); fi)\n');f.write("\n");for val in OBJ2SRC:f.write(val[0] + ":" + val[1] +"\n");f.write("rm -f $@\n");f.write("$(CXX) -fpic -c $(INCPATH) $< -o $@\n");f.write("\n");f.close();os.system("make");os.system("mv " + BIN + " " + BINDIR);os.system("cd " + OBJDIR + ";rm -f *.o");
如何編寫makefile和python,這裡不作說明。因為這篇文章不是makefile和python的教程。以下要說明的是SearchFiles函數。
通過遍曆之前設定的工程工作目錄,調用SearchFiles遍曆該目錄下所有的源檔案(.cpp),及設定目標檔案(.o)的絕對路徑,最終是tuple的形式儲存到OBJ2SRC數組中;還有的是將所有源檔案儲存在SOURCES中。當然這個過程中會遞迴遍曆所有子目錄,尋找到所有的源檔案。最終在for val in OBJ2SRC:遍曆所有的資料;列出所有的源檔案(.cpp)產生所對應的目標檔案(.o),將編譯規則寫進makefile。
這是我要編譯的工程,當然只是其中一部分。這個工程需要包含的檔案除了在DatabaseServer下,還要包含在../common當中(指令碼中代碼WD.append(ROOTPATH + "/common");)。我是為了證明,這個指令碼是可行的。有人可能會說為什麼不寫一個測試例子。其實我想說,很多時候要弄懂一些技術,動手去做也許是最好的方法。如果你有什麼問題,歡迎與我討論!