Easymake 是一個在linux系統中 C/C++ 開發的通用 makefile。在一個簡單的 C/C++ 程序中使用 easymake,你甚至能夠不寫一行 makefile 代碼來生成目標文件。linux
Easymake 包含如下功能:ios
VPATH
變量。我將在後面的例子中一步步地向你展現如何使用 easymake 來構建你的應用程序。別看文檔這麼長,下面一節的內容中大部分是在講一個簡單的 C/C++ 程序的開發。就像 easymake 的名字同樣,easymake 是很是易學易用的。git
在這一節中將展現如何在一個簡單的程序中使用 easymake。接下來讓咱們一個加法程序,用戶輸入兩個數字,而後程序輸出這兩個數字相加的結果。這個程序的源代碼能夠在 samples/basics
目錄中找到。github
這個程序很簡單,因此這裏跳過程序設計環節。這裏直接展現程序的 C/C++ 代碼,而後再轉入咱們的正題。shell
File: main.cppvim
#include <iostream> #include "math/add.h" using namespace std; int main(){ cout<<"please enter two integer:"<<endl; int a,b; cin>>a>>b; cout<<"add("<<a<<","<<b<<") returns "<<add(a,b)<<endl; }
File: math/add.hcentos
#ifndef ADD_H #define ADD_H int add(int,int); #endif
File: math/add.cpp模塊化
#include "math/add.h" int add(int a,int b){ return a+b; }
代碼很簡單,能夠直接使用命令行來構建程序。若是你對 makefile 的語法熟悉,你也能夠很快地寫出一個 makefile 來作完成這個事情。那麼如何使用 easymake 來構建這個程序呢?先別急,接下來將使用剛纔提到的三種方法來構建程序,但願你能清晰地瞭解和比較這三種方式。函數
g++ -c -o main.o main.cpp g++ -c -o add.o math/add.cpp -I. g++ -o target main.o add.o
或者也能夠只用一條命令 g++ -o target main.cpp math/add.cpp -I.
來構建程序。單元測試
而後輸入 ls
和 ./target
,就能夠觀察到程序的執行結果了:
[root@VM_6_207_centos basics]# ls add.o bin main.cpp main.o makefile math target [root@VM_6_207_centos basics]# ./target please enter two integer: 5 3 add(5,3) returns 8
建立一個新的 Makefile 文件,代碼以下:
target: main.o add.o g++ -o target main.o add.o main.o: main.cpp g++ -c -o main.o main.cpp -I. add.o: math/add.cpp g++ -c -o add.o math/add.cpp -I.
結果基本是同樣的:
[root@VM_6_207_centos basics]# make g++ -c -o main.o main.cpp -I. g++ -c -o add.o math/add.cpp -I. g++ -o target main.o add.o [root@VM_6_207_centos basics]# ls add.o main.cpp main.o makefile math target [root@VM_6_207_centos basics]# ./target please enter two integer: 8 9 add(8,9) returns 17
使用 makefile 的好處就是,若是能很好地肯定依賴關係,那麼就不須要在每次構建時把全部的源文件都從新編譯一次。可是隨着項目的代碼的增加,即便在一個良好的模塊化設計中,手工維護依賴關係也是一件很繁瑣並且很容易出錯的工做。例如,假設咱們須要增長一個 multiply.cpp
和 multiply.h
文件,讓程序支持乘法計算的功能,那麼我必須修改咱們的 makefile 才能構建新的程序。另外,若是頭文件 add.h
被修改了,multiply.cpp
就不須要從新編譯,因此咱們應該在 makefile 中增長 .cpp 文件和 .h 文件之間的依賴關係的代碼。到這裏,我想你也會以爲咱們應該有一個通用的 makefile 來幫助咱們自動維護依賴關係了吧。
在這個例子中,包含 easymake.mk
文件就足夠了。把咱們的 Makefile 修改爲下面的代碼:
include ../../easymake.mk
在命令行中輸入 make
構建咱們的程序。接下來咱們給你展現一些細節來幫助你理解 makefile 是如何構建程序的。
[root@VM_6_207_centos basics]# ls main.cpp makefile math [root@VM_6_207_centos basics]# make g++ -c -o bin/main.o main.cpp -I. entry detected g++ -c -o bin/math/add.o math/add.cpp -I. g++ -o bin/target bin/main.o bin/math/add.o BUILD_ROOT/TARGET: bin/target ENTRY: main.cpp [root@VM_6_207_centos basics]# ./bin/target please enter two integer: 3 5 add(3,5) returns 8
你也許也已經注意到,和以前的方式相比,主要的不一樣就是輸出中的 entry detected
,BUILD_ROOT/TARGET: bin/target
和 ENTRY: main.cpp
。bin/target
就是咱們的程序。至於這裏的entry,會在後面講到。
如今能夠看一下當前的目錄結構:
[root@VM_6_207_centos basics]# tree . . ├── bin │ ├── easymake_current_entry_file │ ├── easymake_detected_entries │ ├── easymake_entries_tmp.d │ ├── main.d │ ├── main.o │ ├── math │ │ ├── add.d │ │ └── add.o │ └── target ├── main.cpp ├── makefile └── math ├── add.cpp └── add.h 3 directories, 12 files
Easymake 使用 bin
目錄做爲 BUILD_ROOT
,用來存放生成的文件,這樣一來咱們的源文件目錄也不會被污染。這裏面的 *.d
和 easy_make_*
文件都是由 easymake 額外生成用來維護依賴關係的。*.d
的文件其實也算是 makefile 的一部分,例如 main.d 文件的內容以下:
[root@VM_6_207_centos basics]# cat bin/main.d bin/main.o: main.cpp math/add.h math/add.h:
這些依賴關係是 easymake 自動生成的,因此每當 math/add.h
被修改了,main.o
就會從新生成。事實上,你不須要關注這些細節來使用 easymake,因此咱們就忽略這些額外生成的文件吧。若是你有興趣,能夠查看 easymake.mk
的源代碼,我以爲代碼的註釋得已經足夠幫助你理解了。
若是你想使用 gcc 編譯器的 -O2
優化選項和連接器的 -static
選項來構建這個程序。那麼你須要增長几行代碼來修改編譯和連接選項。下面是修改後的 makefile:
COMPILE_FLAGS += -O2 LINK_FLAGS += -static include ../../easymake.mk
而後從新構建程序:
[root@VM_6_207_centos basics]# make clean rm -f \$(find bin -name \*.o) rm -f \$(find bin -name \*.d) rm -f \$(find bin -name \*.a) rm -f \$(find bin -name \*.so) rm -f \$(find bin -name \*.out) rm -f bin/target [root@VM_6_207_centos basics]# make g++ -c -o bin/main.o main.cpp -O2 -I. entry detected g++ -c -o bin/math/add.o math/add.cpp -O2 -I. g++ -o bin/target bin/main.o bin/math/add.o -static BUILD_ROOT/TARGET: bin/target ENTRY: main.cpp
除些之外,還有更多可供設置的選項,使用 make help
命令你就能夠看到它們。注意 basic settings 和 user settings 兩部分的內容便可,其餘部分能夠忽略。
[root@VM_6_207_centos basics]# make help --------------------- basic settings: SETTINGS_ROOT : build_settings BUILD_ROOT : bin TARGET : target VPATH : CPPEXT : cpp CEXT : c GCC : gcc GXX : g++ LINKER : g++ --------------------- user settings files: build_settings/entry_list build_settings/compile_flags build_settings/compile_search_path build_settings/link_flags build_settings/link_search_path --------------------- user settings: ENTRY_LIST : ENTRY : COMPILE_FLAGS : -O2 COMPILE_SEARCH_PATH : . LINK_FLAGS : -static LINK_SEARCH_PATH : CPPSOURCES : main.cpp math/add.cpp CSOURCES : --------------------- internal informations: ... ... ...
如今咱們須要給程序增長一個乘法運算功能,首先寫一個 C++ 函數來作乘法運算,而後,在咱們修改 main.cpp
的代碼以前,咱們應該測試一下這個這個 C++ 函數的功能,確保新增長的乘法模塊的邏輯是正確的。下面的例子會告訴你若是使用 easymake 來完成這項工做,你能夠在 samples/entries
文件夾中找到這個例子的代碼。
File math/multiply.h
:
#ifndef MULTIPLY_H #define MULTIPLY_H #include "stdint.h" int64_t multiply(int32_t,int32_t); #endif
File math/multiply.cpp
:
#include "math/multiply.h" int64_t multiply(int32_t a,int32_t b){ return (int64_t)a*(int64_t)b; }
在命令行中輸入 mkdir test
和 vim test/multiply.cpp
而後編寫咱們的代碼。爲了簡單起見,這裏僅僅是在 main
函數中打印了 8 乘 8 的結果。
#include "math/multiply.h" #include <iostream> using namespace std; int main(){ cout<<"multiply(8,8)="<<multiply(8,8)<<endl; }
如今直接輸入命令 make
和 ./bin/target
就能夠看到測試程序的輸出了。
[root@VM_6_207_centos entries]# make g++ -c -o bin/main.o main.cpp -O2 -I. entry detected g++ -c -o bin/math/add.o math/add.cpp -O2 -I. g++ -c -o bin/math/multiply.o math/multiply.cpp -O2 -I. g++ -c -o bin/test/multiply.o test/multiply.cpp -O2 -I. entry detected g++ -o bin/target bin/math/add.o bin/math/multiply.o bin/test/multiply.o -static BUILD_ROOT/TARGET: bin/target ENTRY: test/multiply.cpp [root@VM_6_207_centos entries]# ./bin/target multiply(8,8)=64 [root@VM_6_207_centos entries]#
注意到 main.cpp
和 test/multiply.cpp
都有被成功編譯,可是隻有 test/multiply.cpp
被連接到目標文件中,並且輸出中 ENTRY
對應的值也變成了 test/multiply.cpp
。在 easymake,全體一個包含 main
函數定義的源文件都會被自動檢測到,而且被看成程序入口文件(ENTRY
)。在衆多入口文件當中,只有一個會被選中,其餘文件不會被連接到目標文件中。另外注意這裏的 ENTRY
所表示的文件名對應的文件也能夠不存在,在某些場景中,例如生成動態庫 so 文件,就須要選擇這個 ENTRY
來阻止其餘入口文件被連接到目標文件中。
如今你確定是在納悶,easymake 是如何知道要選擇 test/multiply.cpp
而不是 main.cpp
的?是否是很神奇?其實這裏使用的是入口文件的最後修改時間。若是有多個入口文件,並且用戶沒有顯式地聲明使用哪一個入口,那麼 easymake 就會自動選擇最新的那個計算器文件。
若是你須要顯式地聲明 ENTRY
,以選擇 main.cpp
爲例,能夠輸入命令 make ENTRY=main.cpp
或者 make ENTRY=m
:
[root@VM_6_207_centos entries]# make ENTRY=main.cpp g++ -o bin/target bin/main.o bin/math/add.o bin/math/multiply.o -static BUILD_ROOT/TARGET: bin/target ENTRY: main.cpp
到這裏已經完成了乘法模塊的測試,接下來能夠修改 main.cpp
的代碼來整合咱們的新模塊了。爲了簡潔,接下來的步驟就不在這裏贅述了,若是有須要,能夠查看 samples/entries
目錄中的代碼。
最新的代碼和文檔請前往此處下載 https://github.com/roxma/easymake 。有任何BUG或者改進建議請聯繫我 roxma@qq.com