什么是Ninja
在Unix/Linux下通常使用Makefile來控制代碼的編譯,但是Makefile對於比較大的項目有時候會比較慢,看看上面那副漫畫,代碼在編譯都變成了程序員放松的借口了。所以這個Google的程序員在開發Chrome的時候因為忍受不了Makefile的速度,自己重新開發出來一套新的控制編譯的工具叫作Ninja,Ninja相對於Makefile這套工具更注重於編譯速度。除了Chrome現在還有一些其他的比較大的項目也在開始使用Ninja,比如LLVM。我試用了一下感覺還是不錯,比如編譯Cmake時間大概是原來的1/4。Ninja試用C++實現,其支持的語法非常簡單,作者在這里說明了為了控制復雜度。
代碼如何編譯
其實對於C/C++和很多其他程序的編譯都是一個道理,就是把一些源代碼文件編譯成目標文件,或者有的目標文件再編譯到一個庫里,然后再鏈接起來。所以Ninja的配置文件分為兩個部分,rule和文件依賴關系。看個簡單的例子:
cc=gcc cflags= -g -c
rule cc command = $cc $cflags $in -o $out rule link command = $cc $in -o $out rule cleanup command = rm -rf *.exe *.o
build func.o : cc func.c
build main.o : cc main.c
build app.exe : link main.o func.o
build all: phony || app.exe
build clean: cleanup
非常易懂,編譯的可執行未見叫做app.exe, 其中有三條rule: cc, link, cleanup。看看這個官方的試用手冊,還有一些附加參數可以加在rule的下面,比如description用來在編譯的時候顯示出來。Ninja還有個比較好玩的功能就是Ninja -t graph all命令,這可以用來生成編譯時候的依賴關系,可以用dot來生成圖片等。Ninja的實現也可以大概推測到,根據用戶給的依賴關系圖,並行 地編譯各個文件。
使用Ninja的一個問題就是需要生成這個build.ninja文件,對於大型項目來說這樣一條一條地寫配置文件是不可能的。幸好我們可以使用Cmake來生成這個配置文件,Cmake對應的是automake這樣的東西。在Cmake的最新版本中已經支持參數Camke -G Ninja,Cmake會根據用戶給定的CMakeLists.txt來生成build.ninja文件。而CmakeLists文件相對來說要簡單一些,只要寫清楚編譯的可執行文件的名字,和其依賴的包含main函數的源文件。把我的迷宮小項目來舉個例子,在項目文件夾下寫配置文件CMakeLists.txt:
cmake_minimum_required(VERSION 2.8) project (Maze) add_library(maze A_star.cpp Algorithm.cpp DFS_L.cpp DFS_R.cpp DisjSets.cpp Maze.cpp) add_executable(Maze.exe main.cpp) target_link_libraries(Maze.exe maze)
add_library寫明了生成一個叫做maze.a的庫文件,然后和main.cpp編譯出來的main.o生成可執行文件,寫好CmakeList.txt后運行Cmake -G Ninja, 然后運行ninja all就能編譯這個工程。具體的Cmake語法參考這里,對於不少項目來說Cmake已經足夠使用,只是我覺得Cmake還是稍微復雜了一點。
我這樣來使用
整個Ninja是使用C++寫的開源項目,如果我們想增加一些自己的feature可以hack一下,不過作者估計不會接受增加語法支持的patch。我准備做一個小的hack來自動分析我當前的源碼,自動生成build.ninja文件,不要求處理所有的復雜情況,只是分析.cc和.c,自動檢測main函數文件。最后用戶只用配置鏈接參數就可以了。我覺得這樣用起來就非常方便了,待完成中,順便看看Ninja的內部實現。
