使用 Ninja 代替 make
摘自:https://www.jianshu.com/p/d118615c1943html
前言
在傳統的 C/C++ 等項目構建時,一般會採用 make 系統使用 Makefile 文件來進行整個項目的編譯構建,經過 Makefile 中指定的編譯所依賴的規則使得程序的構建很是簡單,而且在複雜項目中能夠避免因爲少部分源碼修改而形成的不少沒必要要的重編譯。可是它仍然不夠好,由於其大並且複雜,有時候咱們並不須要 make 那麼強大的功能,相反咱們須要更靈活,速度更快的編譯工具。Ninja 做爲一個新型的編譯工具,小巧而又高效,它就是爲此而生。python
這篇文章介紹 Ninja 的安裝以及如何使用 Ninja 來構建項目git
首先,咱們須要安裝 Ninja,只須要去官網下載一個 release 的二進制版本,放在系統目錄(好比 /usr/bin)中就能夠了,很是的簡單。另外,如今大多數 Linux 發行版都有本身的包管理工具,直接使用包管理工具來下載也很簡單。github
下面簡單介紹下經過編譯 Ninja 源碼的方式來安裝
首先,確保已經安裝了這些依賴:g++,graphviz,gtest,git,re2c 和 python2.7+。數據庫
獲取源碼
$ git clone git://github.com/ninja-build/ninja.git && cd ninja $ git checkout release $ cat README $ ls COPYING HACKING.md README RELEASING bootstrap.py configure.py doc/ misc/ src/
咱們能夠去 HACKING.md 中查看更多信息。bootstrap
編譯
一切就緒以後,執行下列命令來編譯 ninja瀏覽器
$ ./configure.py --bootstrap
上述命令會在當前目錄下生成一個叫 ninja (Windows 下是 ninja.exe)的可執行文件,而後咱們把這個文件拷到系統目錄(好比 /usr/bin)就完成安裝了。ruby
編譯過程解析
實際上 ninja 自己也是經過 ninja 系統來編譯完成的。
具體過程就是:執行 ./configure.py --bootstrap
以後先編譯源碼(生成一個 a.out),而後在當前目錄生成一個 ninja.build(這個文件相似於 make 工具的 Makefile,語法和規則很是相似)。而後再根據這個 ninja.build 來從新編譯生成可執行文件 ninja,在 ninja 根據 ninja.build 來編譯時會自動建立一個 build 目錄用於存放編譯過程當中的臨時文件,好比 *.o 等。bash
執行./configure.py
時還能夠指定其餘選項:數據結構
--bootstrap bootstrap a ninja binary from nothing
--verbose enable verbose build
--platform choose known platforms
--host choose host known_platforms
--debug enable debugging extras
--profile enable profiling
--with-gtest
--with-python use EXE as the Python interpreter
--force-pselect ppoll() is used by default where available
能夠經過 ./configure.py -h
能夠查看更多幫助。
若是咱們想要開啓 Ninja 的其餘特性(好比:Bash completion, Emacs 和 Vim 編輯模式等),編譯完成以後,咱們須要把 /misc 目錄中的文件拷貝到合適的位置。
測試
如今,咱們能夠測試一下 ninja 是否成功安裝而且可使用。
當直接執行 ninja
命令是,它會在當前目錄下默認尋找 build.ninja 文件來進行編譯。
ninja 的語法格式是:
$ ninja [options] TARGETs
上述 options 若是沒有則能夠省略。好比,直接執行 ./ninja ninja_test
將會生成可執行文件 ninja_test,而後再執行 ninja_test 就能夠看到測試結果。
以下:
$ ./ninja ninja_test $ ./ninja_test [214/226] SubprocessTest.SetWithLotsRaise [ulimit -n] above 1025 (currently 1024) to make this test go [226/226] ElideMiddle.ElideInTheMiddle passed
或者,咱們還能夠直接執行 ./ninja all
,這樣,ninja 就會執行 ninja.build 中指定的全部目標了。
$ ./ninja all [10/10] LINK canon_perftest
上述 ninja_test 和 all 都是 ninja.build 中的 build rule,概念相似於 Makefile 中的 target recipe。
測試完成以後,咱們就把 ninja 拷貝到一個系統目錄中 /usr/bin 來完成整個的安裝。
提示: build.ninja 文件相似於 Makefile,熟悉它的語法規則以後咱們也能夠手動編寫。另外,能夠經過 ninja -f NINJA_FILE
的方式來指定 .ninja 文件
更多選項
實際上, ninja 還提供了一個 Python based generator ,它其實是一個 Python 模塊 misc/ninja_syntax.py
,經過它咱們能夠較方便的生成 build.ninja 文件。好比,在咱們的 Python 文件中引入該模塊以後,就能夠直接經過調用 ninja.rule(name='foo', command='bar', depfile='$out.d')
來生成符合 ninja 語法的內容。下面是一個簡單例子:
from ninja_syntax import Writer with open("build.ninja", "w") as buildfile: n = Writer(buildfile) if platform.is_msvc(): n.rule("link", command="$cxx $in $libs /nologo /link $ldflags /out:$out", description="LINK $out") else: n.rule("link", command="$cxx $ldflags -o $out $in $libs", description="LINK $out")
另外,咱們還能夠在執行 cmake 時經過 -G 選項指定生成器爲 ninja 來生成 build.ninja。
好比:
$ cd build
$ cmake -GNinja ../proj_src_dir
Ninja 工具集
Ninja 還集成了 graphviz 等一些對開發很是有用的工具,經過執行 ./ninja -t list
能夠查看 ninja 中集成了哪些工具。
下面是一個常見的工具集列表:
ninja subtools: browse # 在瀏覽器中瀏覽依賴關係圖。(默認會在 8080 端口啓動一個基於python的http服務) clean # 清除構建生成的文件 commands # 羅列從新構建制定目標所需的全部命令 deps # 顯示存儲在deps日誌中的依賴關係 graph # 爲指定目標生成 graphviz dot 文件。 如 ninja -t graph all |dot -Tpng -ograph.png query # 顯示一個路徑的inputs/outputs targets # 經過DAG中rule或depth羅列target compdb # dump JSON兼容的數據庫到標準輸出 recompact # 從新緊湊化ninja內部數據結構
手動編寫 .ninja 文件
.ninja 的語法規則跟 Makefile 相似,雖然有許多 generator 工具 能夠用來自動生成 .ninja 文件,可是在某些場合可能須要手動編寫或修改 .ninja 文件,下面作個簡單介紹:
# VARIABLE: (referenced like $name or alternate ${name}) cflag = -g -Wall -Werror # RULE: rule RULE_NAME command = gcc $cflags -c $in -o $out description = ${out} will be treat as "$out" # BUILD statement: build TARGET_NAME: RULE_NAME INPUTS # PHONE rule:(creating alias) build ALIAS: phony INPUTS ... # DEFAULT target statement(cumulative): default TARGET1 TARGET2 default TARGET3 $ ninja build TARGET1 TARGET2 TARGET3
例子: build.ninja
ninja_required_version = 1.3 #variable cc = g++ cflags = -Wall # rule rule cc command = gcc $cflags -c $in -o $out description = compile .cc # build build foo.o: cc foo.c
.ninja_log
能夠用來指定保存 build 時產生的 log。
子模塊 和 include 指令
subninja 指令能夠用來引入其餘 .ninja 文件,從而引入一個新的 scope。這意味着,子模塊中能夠引用父模塊中的變量。
好比:
subninja obj/content/content_resources.ninja subninja obj/extensions/extensions_strings.ninja subninja obj/third_party/expat/expat_nacl.ninja
include 指令也是用來引入其餘 .ninja 文件,可是不一樣的是,引入的其餘 .ninja 文件會被引入當前 scope,子模塊中不能夠訪問父模塊中的變量。
include obj/content/content_resource.ninja include obj/extensions/extensions_strings.ninja include obj/third_party/expat/expat_nacl.ninja
參考
https://github.com/ninja-build/ninja
https://ninja-build.org/manual.html
https://www.jianshu.com/p/d118615c1943