Ninja构建系统入门

Stella981
• 阅读 1754

1. 介绍

开篇先介绍、先甩资料给大家看,之后再自己演示一下基本使用。Ninja 是Google的一名程序员推出的注重速度的构建工具,一般在Unix/Linux上的程序通过make/makefile来构建编译,而Ninja通过将编译任务并行组织,大大提高了构建速度。

官网:ninja-build.org

Github:github.com/ninja-build/ninja

2. 参考资料

《The Performance Of Open Source Application》第三章

使用Ninja代替make

零壹軒Ninja相关文章(推荐!)

Ninja编译过程分析

Ninja - chromium核心构建工具

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工具举例:

1、显示依赖

2、显示执行指令

3、显示目标

4、绘依赖图(要安装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版本要求不同,需要注意。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这