1. 介紹
開篇先介紹、先甩資料給你們看,以後再本身演示一下基本使用。Ninja 是Google的一名程序員推出的注重速度的構建工具,通常在Unix/Linux上的程序經過make/makefile來構建編譯,而Ninja經過將編譯任務並行組織,大大提升了構建速度。html
官網:ninja-build.orgpython
Github:github.com/ninja-build/ninjaandroid
2. 參考資料
《The Performance Of Open Source Application》第三章git
零壹軒Ninja相關文章(推薦!)github
Ninja編譯過程分析shell
3. 使用
3.1. cmake生成
通常是經過cmake來生成ninja的配置,進而進行編譯。先從cmake-examples入門:github.com/ttroy50/cmake-examples瀏覽器
好比01-basic\B-hello-headers項目,運行指令:cmake -Bbuild -GNinja 便可生成ninja工程。緩存
運行ninja編譯:
3.2. 手動寫ninja配置文件
本文重點演示一下手寫ninja配置文件方法,Demo工程結構:
./build.ninja
./src/jfz.cpp
其中jfz.cpp:
#include "Hello.h" int main(int argc, char *argv[]) { printf("sandeepin poi!"); return 0; }
build.ninja(注意結尾要有空行):
# 指定ninja最小須要版本 ninja_required_version = 1.5 # 變量 GCC = D:\Library\MinGW\bin\g++.exe cflags = -Wall # 編譯規則,指定depfile,能夠用於生成ninja_deps文件 rule compile_jfz command = $GCC -c $cflags -MD -MF $out.d $in -o $out description = 編譯 $in 成爲 $out depfile = $out.d deps = gcc build jfz.o : compile_jfz src/jfz.c # 連接規則 rule link_jfz command = $GCC $DEFINES $INCLUDES $cflags $in -o $out description = 連接 $in 成爲 $out build jfz.exe : link_jfz jfz.o # 編譯all,就是作任務build jfz.exe build all: phony jfz.exe # 默認編譯什麼(單獨運行ninja) default all
運行效果:
第一次運行按任務先編譯,再連接,最終產生了可執行文件,第二次運行因爲沒改文件,ninja不處理。ninja支持以下參數:
--version # 打印版本信息 -v # 顯示構建中的全部命令行(這個對實際構建的命令覈對很是有用) -C DIR # 在執行操做以前,切換到`DIR`目錄 -f FILE # 制定`FILE`爲構建輸入文件。默認文件爲當前目錄下的`build.ninja`。如 ./ninja -f demo.ninja -j N # 並行執行 N 個做業。默認N=3(須要對應的CPU支持)。如 ./ninja -j 2 all -k N # 持續構建直到N個做業失敗爲止。默認N=1 -l N # 若是平均負載大於N,不啓動新的做業 -n # 排練(dry run)(不執行命令,視其成功執行。如 ./ninja -n -t clean) -d MODE # 開啓調試模式 (用 -d list 羅列全部的模式) -t TOOL # 執行一個子工具(用 -t list 羅列全部子命令工具)。如 ./ninja -t query all -w FLAG # 控制告警級別
ninja -d list相關:
debugging modes: stats print operation counts/timing info 打印統計信息 explain explain what caused a command to execute 解釋致使命令執行的緣由 keepdepfile don't delete depfiles after they're read by ninja 讀取depfile後,不刪除它 keeprsp don't delete @response files on success 讀取@response後,不刪除它 nostatcache don't batch stat() calls per directory and cache them 不對每一個目錄批量處理stat()調用和緩存它們 multiple modes can be enabled via -d FOO -d BAR 多模式調用能夠接着幾個-d
ninja -w list相關,主要指定幾種狀況下告警級別是多少:
warning flags: dupbuild={err,warn} multiple build lines for one target phonycycle={err,warn} phony build statement references itself depfilemulti={err,warn} depfile has multiple output paths on separate lines
ninja -t list相關,主要集成了graphviz等一些對開發很是有用的工具。
ninja subtools: browse # 在瀏覽器中瀏覽依賴關係圖。(默認會在8080端口啓動一個基於python的http服務) clean # 清除構建生成的文件 commands # 羅列從新構建制定目標所需的全部命令 deps # 顯示存儲在deps日誌中的依賴關係 graph # 爲指定目標生成 graphviz dot 文件。如 ninja -t graph all |dot -Tpng -o graph.png query # 顯示一個路徑的inputs/outputs targets # 經過DAG中rule或depth羅列target compdb # dump JSON兼容的數據庫到標準輸出 recompact # 從新緊湊化ninja內部數據結構
這裏主要列舉幾種參數執行效果:
-n是假執行,實際未產生文件,因爲假執行,keepdepfile沒起到效果,這個受限於編譯器分析依賴,下面的統計信息就是stats效果,explain解釋了爲何執行這些任務。
這裏-v打印每一個任務執行了哪些指令,可見到keepdepfile生效了,保存了依賴.d文件。
ninja工具舉例:
一、顯示依賴
二、顯示執行指令
三、顯示目標
四、繪依賴圖(要安裝graphviz,直接打印出dot文本)
轉圖片(支持png、svg等,大圖推薦svg渲染,相關dot參數見graphviz文檔):
ninja -t graph | dot -Tpng -o jfz.png
這個demo比較簡單,實際上依賴分析功能須要編譯器提供,或者任務本身輸出依賴文件,ninja只作一個任務編排和執行功能。
4. 信息補充
4.1. 環境變量
經過環境變量NINJA_STATUS能夠控制ninja打印進度狀態的樣式,有幾個佔位符:
%s 起始edges的數量。 %t 完成構建必須運行的edges總數。 %p 起始edges的百分比。 %r 當前運行的edges數。 %u 要開始的剩餘edges數。 %f 完成的edges數。 %o 每秒完成edges的總速率 %c 當前每秒完成edges的速率(由-j或其默認值指定的構建的平均值) %e 通過的時間(以秒爲單位)。(自Ninja 1.2起可用。) %% 一個普通的%字符。 默認進度狀態爲"[%f/%t] "(請注意尾隨空格以與構建規則分開)。可能的進度狀態的另外一個示例多是"[%u/%r/%f] "。
嘗試改成export NINJA_STATUS="[%p/%f/%t %e] "(Windows下set NINJA_STATUS="[%p/%f/%t %e] ")的效果以下:
4.2. ninja_log每項含義
依次爲:開始時間、結束時間、mtime、output文件路徑名、命令行hash。
其中mtime是輸入文件們的最後修改時間的時間戳算出來的值,經測試,開始時間、結束時間、命令行hash均不會影響增量的斷定。
4.3. mtime檢查文件測試
假設如今時間是2019-12-31 15:35:55,將輸入.c文件修改時間改成以前的,不會觸發從新編譯;將輸入.c文件修改時間改成將來的,每次都觸發編譯。
4.4. frontend_file參數
特別的,AOSP定製版的ninja有frontend_file參數,能夠將控制檯輸出信息轉爲流存儲,經過其它的工具如tail -f xxx查看信息。soong源碼中就是這樣讀取ninja日誌的,效率更高,以前應該是靠cat捕獲日誌的吧。見這個提交。
4.5. ninja檢測的是任務名的文件是否生成
若是我讓輸出文件和任務名不同,ninja每次都會從新編譯:
這點要注意,爲了利用ninja的增量特性,除非無可奈何,不要讓輸出文件和任務名不一樣。
源碼編譯
本文僅嘗試官方版的編譯,AOSP版本能夠依賴不一樣,GCC版本要求不一樣,須要注意。