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版本要求不一样,须要注意。