轉自:http://guiquanz.me/2014/07/28/a_intro_to_Ninja/
Ninja - chromium核心構建工具Jul 28, 2014
緣由
經過上次對chromium核心代碼的初步了解之后,我轉頭去研究了一番ninja,並對其進行了一些改造(愛折騰的,都是小NB)。今天就來簡單介紹一下ninja及其使用。(BTW: 細節的內容,大家閱讀ninja 的手冊就好了,我這里不會關注。)
ninja是一個專注於速度的小型構建系統
(Ninja is a small build system with a focus on speed)。ninja是其作者為了解決chromium代碼編譯慢
這個問題(具體一點,就是發生在將Chrome移植到非Windows平台過程中的事情。欲知詳情,請閱讀Ninja, a new build system)而誕生的。其設計受到the tup build system和redo的啟發。ninja核心是由C/C++編寫的,同時有一部分輔助功能由python
和shell
實現。
ninja可以很好的組合gyp和CMake一起使用,后者為其生成.ninja文件。
ninja項目的最終編譯產出物是一個可執行文件ninja。
下載代碼 並 編譯
mkdir -p ~/ninja && cd ~/ninja
git clone https://github.com/martine/ninja
cd ninja
python ./bootstrap.py
(BTW:以上過程編譯生成可執行文件ninja。需要預先安裝 graphviz及其開發庫,gtest,git、re2c和python)
測試
由於在編譯ninja的過程中bootstrap.py
腳本通過調用configure.py
和platform_helper.py
生成了ninja項目的構建文件build.ninja
,所以我們只需要執行./ninja ninja_test
就可以通過ninja構建生成測試文件ninja_test
。這樣就可以執行測試了。
./ninja ninja_test
./ninja all
ninja 工具介紹
在介紹ninja的文法之前,還是先了解一下ninja的使用吧。執行./ninja -h
顯示幫助信息。具體參數說明,如下:
usage: ninja [options] [targets...]
if targets are unspecified, builds the 'default' target (see manual).
options:
--version # 打印版本信息(如當前版本是1.5.1)
-C DIR # 在執行操作之前,切換到`DIR`目錄
-f FILE # 制定`FILE`為構建輸入文件。默認文件為當前目錄下的`build.ninja`。如 ./ninja -f demo.ninja
-j N # 並行執行 N 個作業。默認N=3(需要對應的CPU支持)。如 ./ninja -j 2 all
-l N # 如果平均負載大於N,不啟動新的作業
-k N # 持續構建直到N個作業失敗為止。默認N=1
-n # 排練(dry run)(不執行命令,視其成功執行。如 ./ninja -n -t clean)
-v # 顯示構建中的所有命令行(這個對實際構建的命令核對非常有用)
-d MODE # 開啟調試模式 (用 -d list 羅列所有的模式)
-t TOOL # 執行一個子工具(用 -t list 羅列所有子命令工具)。如 ./ninja -t query all
ninja還集成了graphviz等一些對開發非常有用的工具。具體如下:(也就是執行 ./ninja -t list
的結果)
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內部數據結構
ninja文件示例
聊了半天,ninja的構建文件長什么模樣呢?以下的demo就是一個執行echo
,打印一行文字的ninja構建文件,和make的Makefile很類似。
rule demo
command = echo "this is a demo of $foo"
build out: demo
foo = bar
編寫你自己的ninja文件
Ninja和Make
非常相似。他執行一個文件之間的依賴圖,通過檢測文件修改時間,運行必要的命令來更新你的構建目標。
一個構建文件(默認文件名為:build.ninja)提供一個rule(規則)表
——長命令的簡短名稱,和運行編譯器的方式一下。同時,附帶提供build
(構建)語句列表,表明通過rule如何構建文件——哪條規則應用於哪個輸入產生哪一個輸出。
從概念上講,build
語句描述項目的依賴圖;而rule
語句描述當給定一個圖的一條邊時,如何生成文件。
語法示例
這是一個用於驗證絕大部分語法的.ninja文件,將作為后續描述相關的示例。具體內容,如下:
cflags = -Wall
rule cc
command = gcc $cflags -c $in -o $out
build foo.o: cc foo.c
變量
ninja支持為字符串
聲明簡短可讀的名字。一個聲明的語法,如下:
cflags = -g
可以在=
右邊使用,並通過$
進行引用(類似shell
和perl
的語法)。具體形式,如下:
rule cc
command = gcc $cflags -c $in -o $out
變量還可以用${in}
($和成對的大括號)來引用。
當給定變量的值不能被修改,只能覆蓋(shadowed)時,變量更恰當的叫法是綁定
("bindings")。
rule 規則
規則為命令行聲明一個簡短的名稱。他們由關鍵字rule
和一個規則名稱
打頭的行開始,然后緊跟着一組帶縮進格式的
variable = value行組成。
以上示例中聲明了一個名為cc
的rule,連同一個待運行的命令。在rule
(規則)上下文中,command
變量用於定義待執行的命令,$in
展開(expands)為輸入文件列表(foo.c),而$out
為命令的輸出文件列表(foo.o)。參考手冊中羅列了所有特殊的變量。
buid 構建語句
build
語句聲明輸入和輸出文件之間的一個關系。構建語句由關鍵字build
開頭,格式為build outputs: rulename inputs
。這樣的一個聲明,所有的輸出文件來源於
(derived from)輸入文件。當缺輸出文件或輸入文件變更時,Ninja將會運行此規則來重新生成輸出。
以上的簡單示例,描述了使用cc規則如何構建foo.o文件。
在build block
范圍內(包括相關規則的執行),變量$in
表示輸入列表,$out
表示輸出列表。
一個構建語句,可以和rule一樣,緊跟一組帶縮進格式的key = value對
。當在命令中變量執行時,這些變量將覆蓋
(shadow)任何變量。比如:
cflags = -Wall -Werror
rule cc
command = gcc $cflags -c $in -o $out
# 如果沒有制定,build的輸出將是$cflags
build foo.o: cc foo.c
# 但是,你可以在特殊的build中覆蓋cflags這樣的變量
build special.o: cc special.c
cflags = -Wall
# cflags變量僅僅覆蓋了special.o的范圍
# 以下的子序列build行得到的是外部的(原始的)cflags
build bar.o: cc bar.c
從代碼中生成Ninja文件
Ninja發行包中的misc/ninja_syntax.py
是一個很小的python模塊,用於生成Ninja文件。你可以使用python,執行如ninja.rule(name='foo', command='bar', depfile='$out.d')
的調用,生成合適的語法。如果這樣還不錯,可以將其整合到你的項目中。
更多細節
phony 規則
可以使用特殊的規則phony
,創建其他target
(編譯構建目標)的別名
。比如:
build foo: phony some/file/in/a/faraway/subdir/foo
這樣使得ninja foo
構建更長的路徑。從語義上講,phony
規則等同於一個沒有做任何操作的普通規則,但是phony規則通過特殊的方式進行處理,這樣當其運行時不會被打印,記日志,也不作為構建過程中打印出來的命令計數。
還可以用phony
為構建時可能還不存在的文件創建dummy
目標。
default 目標語句
默認情況下,如果沒有在命令行中指定target
,那么Ninja將構建任何地方沒有作為輸入命名的每一個輸出。可以通過default
目標語句來重寫這個行為。一個default
語句,讓Ninja構建一個給定的輸出文件子集,如果命令行中沒有指定構建目標
。
默認目標語句,由關鍵字default
打頭,並且采用default targets
的格式。一個default目標語句必須出現在,聲明這個目標作為一個輸出文件的構建語句之后。他們是累積的(cumulative),所以可以使用多個default語句來擴展默認目標列表。比如:
default foo bar
default baz
Ninja構建日志
Ninja構建日志保存在構建過程的跟目錄或.ninja文件中builddir變量對應的目錄的.ninja_log
文件中。
C/C++頭文件依賴
Ninja目前支持depfile
和deps
模式的C/C++頭文件依賴生成。 如
rule cc
depfile = $out.d
command = gcc -MMD -MF $out.d [other gcc flags here]
-MMD
標識告訴gcc要生成頭文件依賴,-MF
則說明要寫到哪里。
deps按照編譯器的名詞來管理。具體如下:(針對微軟的VC:msvc)
rule cc
deps = msvc
command = cl /showIncludes -c $in /Fo$out
Pools
為了支持並發作業,Ninja還支持pool
的機制(和用-j
並行模式一樣)。此處不詳細描述了。具體示例,如下:
# No more than 4 links at a time.
pool link_pool
depth = 4
# No more than 1 heavy object at a time.
pool heavy_object_pool
depth = 1
rule link
...
pool = link_pool
rule cc
...
# The link_pool is used here. Only 4 links will run concurrently.
build foo.exe: link input.obj
# A build statement can be exempted from its rule's pool by setting an
# empty pool. This effectively puts the build statement back into the default
# pool, which has infinite depth.
build other.exe: link input.obj
pool =
# A build statement can specify a pool directly.
# Only one of these builds will run at a time.
build heavy_object1.obj: cc heavy_obj1.cc
pool = heavy_object_pool
build heavy_object2.obj: cc heavy_obj2.cc
pool = heavy_object_pool
The console pool
更加詳細的語法
請閱讀參考手冊,此處只做概要說明。
一個ninja構建文件,由一系列的聲明構成。一個聲明可以是一個:
-
rule聲明,由
rule rulename
開頭,然后緊跟一系列帶縮進的變量定義行
; -
一個build邊,其格式為
build output1 output2: rulename input1 input2
。隱士依賴用| dependency1 dependency2
表達;Order-only依賴用行末的|| dependency1 dependency2
表達。 -
變量聲明,形如
variable = value
; -
默認目標語句,形如
default target1 target2
; -
引入更多的文件,形如
subninja path
或include path
-
一個pool聲明,形如
pool poolname
。
詞法
Ninja僅支持ASCII字符集。
注釋
以為#
開始一直到行末。
新行是很重要的。像build foo bar
的語句,是一堆空格分割分詞(token),到換行結束。一個分詞中的新行
和空格
必須進行轉譯。
目前只有一個轉譯字符,$
,其具有以下行為:
$ followed by a newline
轉譯換行
,讓當前行一直擴展到下一行。
$ followed by text
這是, 變量引用。
${varname}
這是,另$varname
的另一種語法。
$ followed by space
這表示一個空格
。(僅在path列表中,需要用空格分割文件名)
$:
這表示一個冒號。(僅在build行中需要。此時冒號終止輸出列表)
$$
這個表示,字面值的$
。
一個build或default語句,最先被解析,作為一個空格分割的文件名列表,然后每一個name都被展開。也就是說,變量中的一個空格將作為被展開后文件名中的一個空格。
spaced = foo bar
build $spaced/baz other$ file: ...
# The above build line has two outputs: "foo bar/baz" and "other file".
在一個name = value
語句中,value前的空白都會被去掉。出現跨行時,后續行起始的空白也會被去掉。
two_words_with_one_space = foo $
bar
one_word_with_no_space = foo$
bar
其他的空白,僅位於行開始
處的很重要。如果一行的縮進比前一行多,那么被人為是其父邊界的一部分。如果縮進比前一行少,那他就關閉前一個邊界。
頂層變量
Ninja支持的頂層變量有builddir
和ninja_required_version
。具體說明,如下:
- builddir: 構建的一些輸出文件的存放目錄。
- ninja_required_version:指定滿足構建需求的最小Ninja版本。
rule變量
一個rule塊包含一個key = value
的列表聲明,這直接影響規則的處理。以下是一些特殊的key:
-
command (required): 待執行的命令。這個字符串($variables被展開之后),被直接傳遞給
sh -c
,不經過Ninja翻譯。每一個規則只能包含一條command聲明。如果有多條命令,需要使用&&
符號進行鏈接。 -
depfile: 指向一個可選的Makefile,其中包含額外的
隱式依賴
。這個明確的為了支持C/C++的頭文件依賴。 -
deps: (1.3版本開始支持)如果存在,必須是gcc或msvc,來指定特殊的依賴。產生的數據庫保存在
builddir
指定目錄.ninja_deps
文件中。 -
msvc_deps_prefix: (1.5版本開始支持)定義必須從msvc的
/showIncludes
輸出中去掉的字符串。僅在deps = msvc
而且使用非英語的Visual Studio版本時使用。 -
description: 命令的簡短描述,作為命令運行時更好的打印輸出。打印整行還是對應的描述,由
-v
標記控制。如果一個命令執行失敗,整個命令行總是在命令輸出之前打印。 -
generator: 如果存在,指明這條規則是用來
重復調用
生成器程序。通過兩種特殊的方式,處理使用生成器規則構建文件:首先,如果命令行修改了,他們不會重新構建;其次,默認不會被清除。 -
in: 空格分割的文件列表被作為一個
輸入
傳遞給引用此rule的構建行,如果出現在命令中需要使用${in}
(shell-quoted)。(提供$in
僅僅為了圖個方便,如果你需要文件列表的子集或變種,請構建一個新的變量,然后傳遞新的變量。) -
in_newline: 和
$in
一樣,只是分割符為換行
而不是空格
。(僅為了和$rspfile_content一起使用,解決MSVC linker
使用固定大小的緩沖區處理輸入,而造成的一個bug。) -
out: 空格分割的文件列表被作為一個
輸出
傳遞給引用此rule的構建行,如果出現在命令中需要使用${out}
; -
restat: 如果存在,引發Ninja在命令行執行完之后,重新統計命令的輸出。
-
rspfile, rspfile_content: 如果存在(兩個同時),Ninja將為給定命令提供一個響應文件,比如,在調用命令之前將選定的字符串(rspfile_content)寫到給定的文件(rspfile),命令執行成功之后闡述文件。
這個在Windows
系統非常有用,因為此時命令行的最大長度非常受限,必須使用響應文件替代。具體使用方式,如下:
rule link
command = link.exe /OUT$out [usual link flags here] @$out.rsp
rspfile = $out.rsp
rspfile_content = $in
build myapp.exe: link a.obj b.obj [possibly many other .obj files]
構建依賴
Ninja目前支持3種類型的構建依賴。分別是:
-
羅列在build行中的
顯式的依賴
。他們可以作為規則中的$in
變量。這是標准依賴格式。 -
從
depfile
屬性或構建語句末尾的| dep1 dep2
語法獲得的隱式依賴
。這個和顯式依賴
一樣,但是不能在$in
中使用(不可見)。 -
通過構建行末
|| dep1 dep2
語法表示的次序唯一(Order-only)依賴
。他們過期的時候,輸出不會被重新構建,直到他們被重建,但僅
修改這種依賴不會引發輸出重建。
變量展開
變量在路徑
(在build或default語句)和name = value
右邊被展開。
當name = value
語句被執行,右手邊的被立即展開(根據以下的規則),從此$name
擴展為被展開結果的靜態字符串。永遠也不會存在,你將需要使用雙轉譯("double-escape")來保護一個值被第二次展開。
所有變量在解析過程,遇到的時候立即被展開,除了一個非常重要的例外:rule塊中的變量
僅在規則被使用的時候才被展開,而不是聲明的時候。在以下的示例中,demo打印出"this is a demo of bar"而不是"this is a demo of $foo"。
rule demo
command = echo "this is a demo of $foo"
build out: demo
foo = bar
評估和邊界
頂層(Top-level)變量聲明的邊界,是相關的文件。
subninja
關鍵自,用於包含另一個.ninja
文件,其表示新的邊界。被包含的subninja文件可以使用父文件中的變量,在文件邊界中覆蓋他們的值,但是這不影響父文件中變量的值。
同時,可以用#include
語句在當前邊界內,引入另一個.ninja
文件。這個有點像C
中的#include
語句。
構建塊中聲明的變量的邊界,就是其所屬的塊。一個構建塊中展開的變量的所有查詢次序為:
- 特殊內建變量($in, $out);
- build/rule塊中構建層的變量;
- 構建行所在文件中的文件層變量(File-level);
- 使用subninja關鍵字引入那個文件的(父)文件中的變量。
最后再看一下編譯ninja的構建文件
# This file is used to build ninja itself.
# It is generated by configure.py.
ninja_required_version = 1.3
# The arguments passed to configure.py, for rerunning it.
configure_args = --platform=linux
builddir = build
cxx = g++
ar = ar
cflags = -g -Wall -Wextra -Wno-deprecated -Wno-unused-parameter -fno-rtti $
-fno-exceptions -fvisibility=hidden -pipe $
-Wno-missing-field-initializers '-DNINJA_PYTHON="python"' -O2 -DNDEBUG $
-DUSE_PPOLL
ldflags = -L$builddir
rule cxx
command = $cxx -MMD -MT $out -MF $out.d $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
rule ar
command = rm -f $out && $ar crs $out $in
description = AR $out
rule link
command = $cxx $ldflags -o $out $in $libs
description = LINK $out
# browse_py.h is used to inline browse.py.
rule inline
command = src/inline.sh $varname < $in > $out
description = INLINE $out
build $builddir/browse_py.h: inline src/browse.py | src/inline.sh
varname = kBrowsePy
build $builddir/browse.o: cxx src/browse.cc || $builddir/browse_py.h
# the depfile parser and ninja lexers are generated using re2c.
rule re2c
command = re2c -b -i --no-generation-date -o $out $in
description = RE2C $out
build src/depfile_parser.cc: re2c src/depfile_parser.in.cc
build src/lexer.cc: re2c src/lexer.in.cc
# Core source files all build into ninja library.
build $builddir/build.o: cxx src/build.cc
build $builddir/build_log.o: cxx src/build_log.cc
build $builddir/clean.o: cxx src/clean.cc
build $builddir/debug_flags.o: cxx src/debug_flags.cc
build $builddir/depfile_parser.o: cxx src/depfile_parser.cc
build $builddir/deps_log.o: cxx src/deps_log.cc
build $builddir/disk_interface.o: cxx src/disk_interface.cc
build $builddir/edit_distance.o: cxx src/edit_distance.cc
build $builddir/eval_env.o: cxx src/eval_env.cc
build $builddir/graph.o: cxx src/graph.cc
build $builddir/graphviz.o: cxx src/graphviz.cc
build $builddir/lexer.o: cxx src/lexer.cc
build $builddir/line_printer.o: cxx src/line_printer.cc
build $builddir/manifest_parser.o: cxx src/manifest_parser.cc
build $builddir/metrics.o: cxx src/metrics.cc
build $builddir/state.o: cxx src/state.cc
build $builddir/util.o: cxx src/util.cc
build $builddir/version.o: cxx src/version.cc
build $builddir/subprocess-posix.o: cxx src/subprocess-posix.cc
build $builddir/libninja.a: ar $builddir/browse.o $builddir/build.o $
$builddir/build_log.o $builddir/clean.o $builddir/debug_flags.o $
$builddir/depfile_parser.o $builddir/deps_log.o $
$builddir/disk_interface.o $builddir/edit_distance.o $
$builddir/eval_env.o $builddir/graph.o $builddir/graphviz.o $
$builddir/lexer.o $builddir/line_printer.o $builddir/manifest_parser.o $
$builddir/metrics.o $builddir/state.o $builddir/util.o $
$builddir/version.o $builddir/subprocess-posix.o
# Main executable is library plus main() function.
build $builddir/ninja.o: cxx src/ninja.cc
build ninja: link $builddir/ninja.o | $builddir/libninja.a
libs = -lninja
# Tests all build into ninja_test executable.
test_cflags = -g -Wall -Wextra -Wno-deprecated -Wno-unused-parameter $
-fno-rtti -fno-exceptions -fvisibility=hidden -pipe $
-Wno-missing-field-initializers -DNINJA_PYTHON="python" -O2 -DNDEBUG $
-DUSE_PPOLL -DGTEST_HAS_RTTI=0
build $builddir/build_log_test.o: cxx src/build_log_test.cc
cflags = $test_cflags
build $builddir/build_test.o: cxx src/build_test.cc
cflags = $test_cflags
build $builddir/clean_test.o: cxx src/clean_test.cc
cflags = $test_cflags
build $builddir/depfile_parser_test.o: cxx src/depfile_parser_test.cc
cflags = $test_cflags
build $builddir/deps_log_test.o: cxx src/deps_log_test.cc
cflags = $test_cflags
build $builddir/disk_interface_test.o: cxx src/disk_interface_test.cc
cflags = $test_cflags
build $builddir/edit_distance_test.o: cxx src/edit_distance_test.cc
cflags = $test_cflags
build $builddir/graph_test.o: cxx src/graph_test.cc
cflags = $test_cflags
build $builddir/lexer_test.o: cxx src/lexer_test.cc
cflags = $test_cflags
build $builddir/manifest_parser_test.o: cxx src/manifest_parser_test.cc
cflags = $test_cflags
build $builddir/ninja_test.o: cxx src/ninja_test.cc
cflags = $test_cflags
build $builddir/state_test.o: cxx src/state_test.cc
cflags = $test_cflags
build $builddir/subprocess_test.o: cxx src/subprocess_test.cc
cflags = $test_cflags
build $builddir/test.o: cxx src/test.cc
cflags = $test_cflags
build $builddir/util_test.o: cxx src/util_test.cc
cflags = $test_cflags
build ninja_test: link $builddir/build_log_test.o $builddir/build_test.o $
$builddir/clean_test.o $builddir/depfile_parser_test.o $
$builddir/deps_log_test.o $builddir/disk_interface_test.o $
$builddir/edit_distance_test.o $builddir/graph_test.o $
$builddir/lexer_test.o $builddir/manifest_parser_test.o $
$builddir/ninja_test.o $builddir/state_test.o $
$builddir/subprocess_test.o $builddir/test.o $builddir/util_test.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
# Ancillary executables.
build $builddir/build_log_perftest.o: cxx src/build_log_perftest.cc
build build_log_perftest: link $builddir/build_log_perftest.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/canon_perftest.o: cxx src/canon_perftest.cc
build canon_perftest: link $builddir/canon_perftest.o | $builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/depfile_parser_perftest.o: cxx src/depfile_parser_perftest.cc
build depfile_parser_perftest: link $builddir/depfile_parser_perftest.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/hash_collision_bench.o: cxx src/hash_collision_bench.cc
build hash_collision_bench: link $builddir/hash_collision_bench.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/manifest_parser_perftest.o: cxx $
src/manifest_parser_perftest.cc
build manifest_parser_perftest: link $builddir/manifest_parser_perftest.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
# Generate a graph using the "graph" tool.
rule gendot
command = ./ninja -t graph all > $out
rule gengraph
command = dot -Tpng $in > $out
build $builddir/graph.dot: gendot ninja build.ninja
build graph.png: gengraph $builddir/graph.dot
# Generate the manual using asciidoc.
rule asciidoc
command = asciidoc -b docbook -d book -o $out $in
description = ASCIIDOC $out
rule xsltproc
command = xsltproc --nonet doc/docbook.xsl $in > $out
description = XSLTPROC $out
build $builddir/manual.xml: asciidoc doc/manual.asciidoc
build doc/manual.html: xsltproc $builddir/manual.xml | doc/style.css
build manual: phony || doc/manual.html
# Generate Doxygen.
rule doxygen
command = doxygen $in
description = DOXYGEN $in
doxygen_mainpage_generator = src/gen_doxygen_mainpage.sh
rule doxygen_mainpage
command = $doxygen_mainpage_generator $in > $out
description = DOXYGEN_MAINPAGE $out
build $builddir/doxygen_mainpage: doxygen_mainpage README COPYING | $
$doxygen_mainpage_generator
build doxygen: doxygen doc/doxygen.config | $builddir/doxygen_mainpage
# Regenerate build files if build script changes.
rule configure
command = ${configure_env}python configure.py $configure_args
generator = 1
build build.ninja: configure | configure.py misc/ninja_syntax.py
default ninja
# Packaging
rule rpmbuild
command = misc/packaging/rpmbuild.sh
description = Building rpms..
build rpm: rpmbuild
build all: phony ninja ninja_test build_log_perftest canon_perftest $
depfile_parser_perftest hash_collision_bench manifest_parser_perftest
針對ninja的優化
Ninja是一塊非常好的構建工具,其實也是一個特殊的編譯器,其中有很多值得學習和借鑒的地方。比如,使用re2c
將正則表達式編譯為c代碼(PHP也是用了這個工具,干了類似的事情),使用graphviz
生成dot格式的依賴文件等等。當然,我不太喜歡其對python
的依賴。安裝了其他依賴工具和庫之后,還需要安裝python
,否則沒法編譯ninja。經過分析之后,我為ninja定制了一個Makefile編譯方案,同時修改了部分python文件,這樣在沒有python的情況下依然可以編譯和使用ninja。如果需要使用ninja -t browse
和構建ninja_test等測試目標,那還是需要安裝python。當然,要去掉這些依賴也不是很難的事情,如果需要哪天有空我可能就將其修改了。修改后的版本一貫的放在github上,需要的自取。僅將此文作為學習ninja的一個階段性總結。歡迎交流和反饋。