一、前言 Make工具最主要也是最基本的功能就是通過makefile文件來描述源程序之間的相互關(guān)系并自動維護編譯工作,。而makefile 文件需要按照某種語法進行編寫,,文件中需要說明如何編譯各個源文件并連接生成可執(zhí)行文件,,并要求定義源文件之間的依賴關(guān)系。 然而make的命令"博客精深",對于初學(xué)者來說,,真是望而生畏,,這篇文章不是make詳解,,只是講解實用makefile的編寫和使用。 在linux上,,如果用gcc一個個編譯源碼,,實在很繁瑣,尤其是隨著源代碼的增加,,這種繁瑣更是明顯,,很多人,包括我,,其實要求很簡單,,只要把頭文件和cpp或者c文件放在當前目錄或者其他目錄,直接一個命令就可以編譯了,,但是make的命令太多了,,相當打擊初學(xué)者的信心。 常用的make指令其實不多,,夠用就行了,,接下來開始講解實用makefile的編寫,看完之后大家可以直接下載make工程直接一個make命令完成編譯,。 本文相關(guān)環(huán)境:redhat 5.5 + g++ version 4.1.2 + GNU Make 3.81 g++可以支持cpp文件編譯,,gcc編譯cpp文件在鏈接時會出現(xiàn)點問題,所以這里統(tǒng)一使用g++,。
二,、Makefile的規(guī)則 target ... : prerequisites ...
command
...
...
comman如果和target不是同一行,需要在第二行鍵入\t再鍵入command.
target也就是一個目標文件,,可以是Object File,,也可以是執(zhí)行文件。還可以是一個標簽(Label) 這是一個文件的依賴關(guān)系,,也就是說,,target這一個或多個的目標文件依賴于prerequisites中的文件,其生成規(guī)則定義在command中,。 prerequisites中如果有一個以上的文件比target文件要新的話,,command所定義的命令就會被執(zhí)行。這就是Makefile的規(guī)則,。也就是Makefile中最核心的內(nèi)容,。
三、使用make一個個編譯源代碼 假設(shè)我們寫一個簡單的程序,,源碼位于/usr/make/main.cpp,代碼如下: //main.cpp #include make的最大好處是自動化編譯,于是我們新建/usr/make/makefile,makefile文件內(nèi)容如下: main: main.o g++ main.o -o main main.o: main.cpp g++ -c main.cpp -o main.o clean: rm -rf *.o main 保存之后,,直接執(zhí)行make,就可以生成main.o目標文件,main可執(zhí)行文件了。 然而我們這個程序?qū)嵲趩伪×它c,,于是我們需要加入一個App類,,控制整個程序的啟動和關(guān)閉周期,。 新建/usr/make/app.h文件,代碼如下: #ifndef APP_H #define APP_H class App{ public: static App& getInstance(); bool start(); bool shutdown(); private: App(); App(const App&); App& operator=(const App&); bool m_stopped; }; #endif
新建/usr/make/app.cpp文件,,代碼如下: #include "app.h" #include
修改/usr/make/main.cpp文件,,代碼如下: //main.cpp #include 修改/usr/make/makefile,內(nèi)容如下: main: main.o app.o g++ main.o app.o -o main main.o: main.cpp g++ -c main.cpp -o main.o app.o: app.cpp g++ -c app.cpp -o app.o clean: rm -rf *.o main
執(zhí)行make,./main,發(fā)現(xiàn)后臺會每隔5秒打印出"app run",只能ctrl+c結(jié)束. 以后我們修改了main.cpp,app.cpp,app.h文件后,我們只要輸入make,,就可以自動編譯了,,不必每次一個個g++命令去進行繁雜的編譯過程。 這種方式,,以后每添加一個源碼文件,,我們就要手工修改makefile的內(nèi)容,添加依賴和命令,,仍然顯得不自動,。
四、使用make自動推導(dǎo)編譯 GNU的make很強大,,它可以自動推導(dǎo)文件以及文件依賴關(guān)系后面的命令,,于是我們就沒必要去在每一個[.o]文件后都寫上類似的命令,因為,,我們的make會自動識別,并自己推導(dǎo)命令,。 我們的是新的makefile又出爐了: CPP_SOURCES = $(wildcard *.cpp)
CPP_OBJS = $(patsubst %.cpp, %.o, $(CPP_SOURCES))
default:compile
$(CPP_OBJS):%.o:%.cpp
g++ -c $< -o $@
compile: $(CPP_OBJS)
g++ $^ -o main
clean:
rm -f $(CPP_OBJS)
rm -f main
下面來一步步解析這個makefie的語法. 初始化: wildcard函數(shù)功能是展開成一列所有符合由其參數(shù)描述的文件名,,文件間以空格間隔,本例是產(chǎn)生一個所有以 '.cpp' 結(jié)尾的文件的列表,然后存入變量 CPP_SOURCES. patsubst函數(shù)是匹配替換的函數(shù),有三個參數(shù),,第一個是一個需要匹配的式樣,,第二個表示用什么來替換它,第三個是一個需要被處理的由空格分隔的字列,本例是把 CPP_SOURCES的后綴為cpp文件列表,,替換成后綴為o的文件列表,。 生成目標文件 $(CPP_OBJS):%.o:%.cpp g++ -c $< -o $@
上面的例子中,指明了我們的目標從$(CPP_OBJS)中獲取,,"%.o"表明要所有以".o"結(jié)尾的目標,,也就是"main.o app.o",也就是變量$(CPP_OBJS)集合的模式. 展開之后是: main.o: main.cpp g++ -c main.cpp -o main.o main.o: app.cpp g++ -c app.cpp -o app.o 鏈接 上述的default目標是是makefile的第一個目標,,依賴compile,,compile文件不存在,也不會出現(xiàn),,所以會執(zhí)行compile目標,。 default和compile只是makefile一個普通的規(guī)則而已,沒有特殊之處,。 compile: $(CPP_OBJS) g++ $^ -o main "$<"表示所有的依賴目標集,表示main.o app.o 展開之后是 compile: main.o app.o g++ main.o app.o -o main
五、實用makefile的完善 雖然我們已經(jīng)使用了makefile的自動推導(dǎo),,但是這個makefile還有有些不足: 1.只能支持cpp文件,不支持c文件 2.生成的目標文件全放在當前目錄,,造成當前目錄混亂 3.h文件和cpp文件或者c文件沒有分離,造成目錄里的源碼文件2倍爆炸,而且也不支持多目錄 4.不支持第三方的庫文件,,包括include文件和lib文件,。 于是,有一個完善的makefile如下: TARGET = main OBJ_PATH = objs CC = g++ CFLAGS = -Wall -Werror -g LINKFLAGS = #INCLUDES = -I include/myinclude -I include/otherinclude1 -I include/otherinclude2 INCLUDES = -I include #SRCDIR =src/mysrcdir src/othersrc1 src/othersrc2 SRCDIR = src #LIBS = -Llib -lcurl -Llib -lmysqlclient -Llib -llog4cpp LIBS = C_SRCDIR = $(SRCDIR) C_SOURCES = $(foreach d,$(C_SRCDIR),$(wildcard $(d)/*.c) ) C_OBJS = $(patsubst %.c, $(OBJ_PATH)/%.o, $(C_SOURCES)) CPP_SRCDIR = $(SRCDIR) CPP_SOURCES = $(foreach d,$(CPP_SRCDIR),$(wildcard $(d)/*.cpp) ) CPP_OBJS = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(CPP_SOURCES)) default:init compile $(C_OBJS):$(OBJ_PATH)/%.o:%.c $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ $(CPP_OBJS):$(OBJ_PATH)/%.o:%.cpp $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ init: $(foreach d,$(SRCDIR), mkdir -p $(OBJ_PATH)/$(d);) compile:$(C_OBJS) $(CPP_OBJS) $(CC) $^ -o $(TARGET) $(LINKFLAGS) $(LIBS) clean: rm -rf $(OBJ_PATH) rm -f $(TARGET) install: $(TARGET) cp $(TARGET) $(PREFIX_BIN) uninstall: rm -f $(PREFIX_BIN)/$(TARGET) rebuild: clean compile
這個makefile的功能是掃描src文件夾的c文件和cpp文件,,進行編譯,,編譯的命令是g++,編譯選項是-Wall -Werror -g,表示盡可能打印出報錯信息,,有錯誤和警告就停止編譯,,并且生成調(diào)試信息。如果是生產(chǎn)環(huán)境,,請把-g去掉,。最后連接所有目標文件為main。如果所有的c文件和cpp文件沒有main函數(shù)入口,,鏈接會報錯,。 使用這個makefile,需要注意的幾個變量是: TARGET 表示最后生成的可執(zhí)行文件的名字,,默認生成的main可執(zhí)行文件在當前工作目錄,。 OBJ_PATH 表示編譯過程中產(chǎn)生的目標文件放在那個目錄,默認生成放在在當前目錄的objs目錄下,,無需手工mkdir,,自動生成。 CC 默認是g++,,如果是使用gcc的,,請修改,但是gcc在編譯cpp文件后,,鏈接時會出現(xiàn)問題,,請自行解決,。 INCLUDES 配置頭文件的目錄,默認是include目錄,,如果放在當前目錄,,可以改為"INCLUDES = .",如果要配置多個include目錄,,可以按照注釋,,自行配置。 SRCDIR 配置c文件和cpp文件的目錄,,默認是src目錄,,如果喜歡放在當前目錄,可以改為"SRCDIR= .",如果要配置多個源碼目錄,,可以按照注釋,,自行配置。 LIBS 配置第三方的動態(tài)鏈接庫和靜態(tài)鏈接庫,,當程序使用了第三方庫時,,我們編譯是要配置第三方的頭文件目錄,鏈接時就要配置鏈接的庫文件,,LIBS配置可以參考注釋,,如果不清楚,請看本人博客里的“gcc常用命令”文章. 來自:http://www.cnblogs.com/ggjucheng/archive/2011/12/14/2288055.html |
|