舒適提示:閱讀本文的同窗最好能瞭解makefile和python的編寫規則。python
不懂的同窗可以先保存在收藏夾。以便往後查看。mysql
事實上以前我一直很是懶,我不想了解makefile規則。因爲在linux下開發我一直使用Qt creator。(很是多時候正是一些「懶人」的創造力。解放了咱們的雙手,顯然現在咱們還需要用雙手寫makefile)。Qt creator是一個很是好的IDE。而且可以跨平臺開發。但是相比VS,顯然仍是不夠優秀。linux
所以很是多開發人員都會選擇在Windows下開發C/C++程序,而後部署在Linux下運行。固然我也不例外。因此近期花了幾個晚上了解makefile的編寫規則。sql
開始的時候。我參照網上一些makefile的樣例,寫了一個第一版的makefile。然而這個makefile在編譯個人project的時候報錯。shell
主要出錯體現在:數組
%.o: %.cppapp
$(CXX) -fpic -c $(INCPATH) $< -o $@函數
固然可以寫成學習
$(objdir)/%.o:$(srcdir)/%.cppui
$(CXX) -fpic -c $(INCPATH) $< -o $@
緣由在於:
一、.o文件與.cpp文件處於不一樣的文件夾下。
二、不一樣的.o文件或不一樣的.cpp文件處於不一樣的文件夾下。
這時我找到兩種解決方法:
一、就是用VPATH這個特殊變量,但是我不可能將所有要包括的文件夾都一一手動包括進來,因而我放棄。
二、就是把所有的編譯規則列舉出來。
我終於選擇另一種解決方法。
因爲以前遇到這個困難時,我特地去了解Qt生成的makefile(事實上這個makefile是根據.proproject文件生成的)。而這個makefile正是將所有的編譯規則都列舉出來。
因而就有如下這個python腳本。事實上開始的時候我想用shell來作這一步工做的,但是我看到sed和awk。我頭都暈,以前還一直抵觸學習sed和awk。所以最後選擇了python。
#encoding: utf-8 import os import os.path import 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++ # @FLAGS gcc/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.endswith(SUFFIX) > 0: 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函數。
經過遍歷以前設定的project工做文件夾。調用SearchFiles遍歷該文件夾下所有的源文件(.cpp),及設定目標文件(.o)的絕對路徑,終因而tuple的形式保存到OBJ2SRC數組中。還有的是將所有源文件保存在SOURCES中。固然這個過程當中會遞歸遍歷所有子文件夾,查找到所有的源文件。終於在for val in OBJ2SRC:遍歷所有的數據;列出所有的源文件(.cpp)生成所相應的目標文件(.o)。將編譯規則寫進makefile。
這是我要編譯的project。固然截圖僅僅是其中一部分。
這個project需要包括的文件除了在DatabaseServer下,還要包括在../common其中(腳本中代碼WD.append(ROOTPATH + "/common");)。
我截圖是爲了證實。這個腳本是可行的。有人可能會說爲何不寫一個測試樣例。
事實上我想說。很是多時候要弄懂一些技術。動手去作或許是最好的方法。假設你有什麼問題,歡迎與我討論!