使用 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