Ninja文件的基本语法
2017-08-16 17:16:27 +08 字数:2222 标签: NinjaNinja是一种类似GNU make的编译系统。 就像make有Makefile,它也有自己的编译配置文件。 相对来说,Ninja文件没有分支、循环的流程控制,本质上就是纯粹的配置文件,所以要比Makefile简单得多。
本文介绍Ninja文件的独特语法。
基本 ¶
概念 ¶
概念 | (非官方)译名 | 解释 |
---|---|---|
edge | 边 | 即build语句,指定目标(输出)、规则与输入,是编译过程拓扑图中的一条边(edge)。 |
target | 目标 | 编译过程需要产生的目标,由build语句指定。 |
output | 输出 | build语句的前半段,是target的另一种称呼。 |
input | 输入 | build语句的后半段,用来产生output的文件或目标,另一种称呼是依赖。 |
rule | 规则 | 通过指定command与一些内置变量,决定如何从输入产生输出。 |
pool | 池 | 一组rule或edge,通过指定其depth,可以控制并行上限。 |
scope | 作用域 | 变量的作用范围,有rule与build语句的块级,也有文件级别。rule也有scope。 |
关键字 ¶
关键字 | 作用 |
---|---|
build | 定义一个edge。 |
rule | 定义一个rule。 |
pool | 定义一个pool。 |
default | 指定默认的一个或多个target。 |
include | 添加一个ninja文件到当前scope。 |
subninja | 添加一个ninja文件,其scope与当前文件不同。 |
phony | 一个内置的特殊规则,指定非文件的target。 |
变量 ¶
变量有两种,内置变量与自定义变量。
二者都可以通过var = str
的方式定义,通过$var
或${var}
的方式引用。
变量类型只有一种,那就是字符串。
顶层的内置变量有两个:
内置变量 | 作用 |
---|---|
builddir | 指定一些文件的输出目录,如.ninja_log 、.ninja_deps 等。 |
ninja_required_version | 指定ninja 命令的最低版本。 |
通过以下代码,可以在build.ninja中指定两个变量,影响编译过程。
builddir = /tmp/
ninja_required_version = 1.7
rule ¶
rule name
command = echo ${in} > ${out}
var = str
形如以上的,就是rule代码块。
通常,一个rule就是通过${in}
输入的目标列表,生成${out}
的输出目标列表。
目标一般都是文件。
例外是,有一个内置的特殊规则phony
,可以指定非文件目标。
除了可以自定义变量以外,包括command
在内,rule还有以下内置变量:
内置变量 | 作用 |
---|---|
command | 定义一个规则所必备的变量,指定实际执行的命令。 |
description | command的说明,会替代command在无-v 时打印。 |
generator | 指定后,这条rule生成的文件不会被默认清理。 |
in | 空格分割的input列表。 |
in_newline | 换行符分割的input列表。 |
out | 空格分割的output列表。 |
depfile | 指定一个Makefile文件作为额外的显式依赖。 |
deps | 指定gcc或msvc方式的依赖处理。 |
msvc_deps_prefix | 在deps=msvc 情况下,指定需要去除的msvc输出前缀。 |
restat | 在command执行结束后,如果output时间戳不变,则当作未执行。 |
rspfile, rspfile_content | 同时指定,在执行command前,把rspfile_content写入rspfile文件,执行成功后删除。 |
build edge ¶
build foo: phony bar
var = str
形如以上的,就是build代码块,也是编译过程中的一个edge。
其中,foo
就是output,bar
就是input,:
后面第一个位置的phony
就是rule,var
就是自定义变量。
在build块中,也可以对rule块的变量进行扩展(复写)。
最复杂的build代码块形式,莫过于:
build output0 output1 | output2 output3: rule_name $
input0 input1 $
| input2 input3 $
|| input4 input5
var0 = str0
var1 = str1
其中,行末的$
是转义字符,并未真正换行;
output0
和output1
是显示(explicit)输出,会出现在${out}
列表中;
出现在|
后面的output2
和output3
是隐式(implicit)输出,不会出现在${out}
列表中;
rule_name
是规则名称;
input0
和input1
是显示依赖(输入),会出现在${in}
列表中;
出现在|
后面的input2
和input3
是隐式依赖,不会出现在${in}
列表中;
出现在||
后面的input4
和input5
是隐式order-only依赖,不会出现在${in}
列表中。
所谓order-only依赖,如字面意思,就是指仅仅是顺序需要的依赖。
在首次编译时,和其它依赖的表现相同;
再次编译时,如果input4
或input5
有缺失,Ninja会更新它们,但不会执行这条edge,更新output0
和output1
。
order-only的依赖,不是真的依赖,而是可有可无、只是需要在当前edge之前执行而已。
pool ¶
pool的意义,在于限制一些非常消耗硬件资源的edge同时执行。
pool example
depth = 2
rule echo_var
command = echo ${var} >> ${out}
pool = example
build a: echo_var
var = a
build b: echo_var
var = b
build c: echo_var
var = c
以上代码,通过pool = example
,在rule或build代码块中指定对应的edge所属的pool为example
。
由于example
的depth = 2
,所以a、b、c三个target最多只有2个可以同时生成。
目前,Ninja只有一个内置的pool,名为console。 这个pool的depth等于1,只能同时执行1个edge。 它的特点是,可以直接访问stdin、stdout、stderr三个特殊stream。
总结 ¶
相对Makefile来说,Ninja的内容非常的精简。 本文虽然简短,但绝大部分内容都已覆盖。 Ninja的设计哲学,就是用最精简的配置,执行复杂的编译过程,以此提高增量编译效率和准确度。
虽然介绍了语法,但手写build.ninja仍然是不推荐的。 可以在《List of generators producing ninja build files》中选取合适的工具,来生成Ninja文件。