原文请点击Ninja 编译系统
介绍
Ninja 是一种构建系统。它将文件的相互依赖性(通常是源代码和输出可执行文件)作为输入,并快速编排它们。
Ninja加入了其他构建系统的海洋。其有所区别的的目标是快速。它诞生于我在Chromium浏览器项目上的工作,该项目拥有超过30,000个源文件,其他构建系统(包括一个由自定义非递归Makefile构建的系统)在更改一个文件后需要数十秒钟才能开始构建。Ninja 只需不到一秒钟。
哲学概述
如果其他构建系统是高级语言,Ninja的目标是成为汇编语言。
构建系统在需要做出决策时变得缓慢。当您处于编辑 – 编译周期时,您希望它尽可能快 – 您希望构建系统执行必要的最少工作以确定需要立即构建的内容。
Ninja包含描述任意依赖图所需的最基本功能。它缺乏语法使得无法表达复杂的决策。
相反,Ninja旨在与生成其输入文件的单独程序一起使用。生成器程序(如autotools项目中的./configure)可以分析系统依赖关系并尽可能多地做出决策,以便增量构建保持快速。.ninja文件生成器超越autotools,甚至构建时决定,比如“我应该使用哪个编译器标志?”或“我应该构建调试或发布模式二进制文件?”。
设计目标
以下是Ninja的设计目标:
- 即使对于非常大的项目,也可以非常快速(即即时)增量构建。
- 关于如何构建代码的是策略很少。不同的项目和更高级别的构建系统对如何构建代码有不同的看法; 例如,应该在源代码旁边构建对象还是应该将所有构建输出放到一个单独的目录中?是否有一个“包”规则来构建项目的可分发包?通过尝试允许实施这些决策而不是选择来实现这些决定,即使这会导致更多的冗长。
- 获取依正确的赖关系,特别是使用Makefile难以正确处理的情况(例如,输出需要对用于生成它们的命令行的隐式依赖;要构建C源代码,您需要使用gcc的-M标志来实现标头依赖性)。
- 当方便和速度发生冲突时,请选择速度。
一些明确的非目标:
- 简约的语法, 更易于手动编写构建文件。你应该使用另一个程序生成你的Ninja文件。这就是我们如何回避许多政策决定。
- 内置规则。 Ninja没有开箱即用的规则。如编译C代码。
- 构建时自定义。生成Ninja文件的程序有自己的主张。
- 构建时条件或路径搜索的决策能力。做决策很耗时。
重申一下,Ninja比其他构建系统更快,因为它非常简单。在创建项目的.ninja文件时,您必须告诉Ninja到底要做什么。
与Make比较
Ninja在精神和功能上与Make最接近,依赖于文件时间戳之间的简单依赖关系。
但从根本上说,make有很多功能:后缀规则,函数,内置规则,例如在构建源时搜索RCS文件。 Make的语言旨在由人类编写。许多项目发现单独使用Make足以解决构建问题。
相比之下,Ninja几乎没有任何特性; 只有在做复杂的决策生成Ninja输入文件时保证构建正确这一条原则, Ninja不太可能适用于大多数项目。
以下是Ninja为Make添加的一些功能。 (这些类型的功能通常可以使用更复杂的Makefile实现,但它们不是make本身的一部分。)
* Ninja特别支持在构建时发现额外的依赖项,从而可以轻松地使C / C ++代码的头依赖性正确。
* 构建边可以具有多个输出。
* 隐式输出取决于用于生成它们的命令行,这意味着更改(例如编译标志)将导致输出重建。
* 在运行依赖它们的命令之前,始终隐式创建输出目录。
* 规则可以提供正在运行的命令的简短描述,因此您可以打印例如构建时"CC foo.o"而不是长命令行。
* 构建始终并行运行,默认情况下基于系统具有的CPU数量。未指定的构建依赖项将导致不正确的构建。
* 命令输出始终是缓存的。这意味着并行运行的命令不会交错输出,当命令失败时,我们可以在产生故障的完整命令行旁边打印其故障输出。
使用Ninja进行项目
Ninja目前在类Unix系统和Windows上工作。它在Linux上的测试次数最多(并且具有最佳性能),但它在Mac OS X和FreeBSD上依旧要以运行良好。
如果你的项目很小,Ninja的速度影响可能不明显。 (但是,即使对于小型项目,有时也会发现Ninja的有限语法会强制更简单的构建规则,从而导致更快的构建。)另一种说法是,如果您对项目的编辑 – 编译周期时间感到满意,那么Ninja不会对你有用。
还有许多其他构建系统比Ninja本身更加用户友好或功能强大。对于一些建议:Ninja作者发现tup构建系统对Ninja的设计有影响,并认为redo的设计非常聪明。
Ninja的好处来自于将它与更智能的元构建系统结合使用。
元构建系统用于为Google Chrome和相关项目(v8,node.js)以及Google Fuschia生成构建文件。 gn可以为Chrome支持的所有平台生成Ninja文件。
一种广泛使用的元构建系统,可以在CMake版本2.8.8上生成Linux上的Ninja文件。较新版本的CMake也支持在Windows和Mac OS X上生成Ninja文件。
Ninja应该完美地融入其他元构建软件,如 [premake](http://industriousone.com/premake) .如果您这样做,请告诉我们!
运行Ninja
运行Ninja。默认情况下,它在当前目录中查找名为build.ninja的文件,并构建所有过时的目标。您可以指定要构建的目标(文件)作为命令行参数。
还有一个特殊的语法目标^,用于将目标指定为某个规则的第一个输出,该规则包含您放入命令行的源(如果存在)。例如,如果您将target指定为foo.c^,那么将构建foo.o(假设您在构建文件中有这些目标)。
ninja -h
打印帮助输出。 Ninja的许多flags 故意与Make的flags 相匹配;例如ninja -C build -j 20
更改到构建目录并并行运行20个构建命令。 (请注意,Ninja无论如何都默认并行运行命令,所以通常你不需要传递-j。)
环境变量
Ninja支持一个环境变量来控制其行为:NINJA_STATUS
,即在运行规则之前打印的进度状态。
有几个可用的占位符:
%s 起始边缘数。
%t 完成构建必须运行的边的总数。
%p 起始边缘的百分比。
%r 当前运行边缘的数量。
%u 要启动的剩余边数。
%F 完成边缘的数量。
%o 每秒成品边缘的总速率
%c 当前每秒完成边缘的速率(由-j指定的平均值或其默认值)
%e 经过的时间以秒为单位。 (自Ninja 1.2起可用。)
%% 一个普通的%字符。
默认进度状态为[%f/%t]
(请注意与构建规则分离的尾随空格)。另一个显示处理状态的例子可能是[%u/%r/%f]
。
额外的工具
Ninja命令行上的 -t 标志运行了一些我们在Ninja开发过程中发现的有用的工具。目前这些工具是:
query 转储给定目标的输入和输出。
browse 在Web浏览器中浏览依赖关系图。单击文件会将视图聚焦在该文件上,显示输入和输出。
此功能需要安装Python。默认情况下,使用端口8000并打开Web浏览器。这可以更改如下:
ninja -t browse --port = 8000 --no-browser mytarget
graph 以graphviz(一种自动图形布局工具)使用的语法输出文件。使用它像:
ninja -t graph mytarget | dot -Tpng -ograph.png
在Ninja源代码树中,ninja graph.png为Ninja本身生成一个图像。如果没有给出目标,则为所有根目标生成图表。
target 按规则或深度输出目标列表。如果使用像ninja -t目标规则名称那么它将使用要构建的给定规则打印目标列表。
如果没有给出规则,则打印源文件(图形的叶子)。
如果像ninja -t一样使用目标深度数字,它将以深度优先的方式打印目标列表,从根目标(没有输出的目标)开始。
缩进用于标记依赖项。如果深度为零,则打印所有目标。如果没有提供参数,则假定ninja -t目标深度为1。
在此模式下,可能会多次列出目标。
如果像这样使用ninja -t target all它会打印所有可用的目标而不会缩进,并且它比深度模式更快。
command 给定一个目标列表,打印一个命令列表,如果按顺序执行,可以用来重建这些目标,假设所有输出文件都已过期。
clean 删除构建的文件。默认情况下,它会删除除生成器创建的文件之外的所有内置文件。
添加-g标志还会删除生成器创建的构建文件(请参阅generator属性的规则参考)。
其他参数是目标,它删除给定目标并递归地为它们构建所有文件。
如果像ninja -t clean -r规则一样使用它会删除使用给定规则构建的所有文件。
未删除在图表中创建但未引用的文件。此工具考虑了-v和-n选项(请注意-n表示-v)。
compdb 给定一个规则列表,每个规则都应该是一个C系列语言编译器规则,
其第一个输入是源文件的名称,在标准输出上打印一个Clang工具接口所期望的JSON格式的编译数据库。自Ninja 1.2起可用。
deps 显示存储在.ninja_deps文件中的所有依赖项。给定目标时,只显示目标的依赖关系。自Ninja 1.4起可用。
recompact 重新压缩.ninja_deps文件。自Ninja 1.4起可用。
编写自己的Ninja文件
本手册的其余部分仅在您自己构建Ninja文件时才有用:例如,如果您正在编写元构建系统或支持新语言。
概念概述
Ninja会评估文件之间依赖关系的图表,并根据文件修改时间确定运行构建目标所需的任何命令。如果你熟悉Make,Ninja非常相似。
构建文件(默认名称:build.ninja)提供了一系列规则 – 较长命令的短名称,如如何运行编译器 – 以及构建语句列表,说明如何使用规则构建文件 – 应用哪个规则产生哪些产出的投入。
从概念上讲,构建语句描述项目的依赖关系图,而规则语句描述如何沿图的给定边生成文件。
语法示例
这是一个基本的.ninja文件,演示了大部分语法。它将用作以下部分的示例。
cflags = -Wall
rule cc
command = gcc $ cflags -c $ in -o $ out
build foo.o:cc foo.c
变量
尽管为了保持构建文件的可读性(可调试性)不便于手写,但Ninja支持为字符串声明更短的可重用名称。如下声明
cflags = -g
可以在等号的右侧使用,用美元符号取消引用它,如下所示:
规则cc
command = gcc $cflags -c $in -o $out
也可以使用像${in}这样的花括号来引用变量。
变量最好被称为“绑定”,因为给定的变量不能被改变,只能被遮蔽。关于阴影如何在本文档后面工作的内容还有很多。
规则
规则声明命令行的短名称。它们以包含rule关键字和规则名称的行开头。然后是一组缩进的variable = value行。
上面的基本示例声明了一个名为cc的新规则以及要运行的命令。在规则的上下文中,命令变量定义要运行的命令,$ in扩展到输入文件列表(foo.c),$ out定义命令的输出文件(foo.o)。参考中提供了完整的特殊变量列表。
构建语句
构建语句声明输入和输出文件之间的关系。它们以build关键字开头,并具有格式构建输出:rulename输入。这样的声明说所有输出文件都是从输入文件派生的。当输出文件丢失或输入发生变化时,Ninja将运行规则以重新生成输出。
上面的基本示例描述了如何使用cc规则构建foo.o。
在构建块的范围内(包括评估其关联规则),变量$ in是输入列表,变量$ out是输出列表。
构建语句后面可能跟一组缩进的键=值对,就像规则一样。在评估命令中的变量时,这些变量将影响任何变量。例如:
cflags = -Wall -Werror
rule cc
command = gcc $cflags -c $in -o $out
#If left unspcified, builds get the other $cflags.
build foo.o:cc foo.c
#But you can shadow variables like cflags for a particular build.
build special.o:cc special.c
cflags = -Wall
#The variable was only shadowed for the scope of special.o
#Subsequent build lines get the outer (original) cflags.
build bar.o:cc bar.c
有关范围界定的更多讨论,请参阅参考资料。
如果您需要从构建语句传递给规则的更复杂信息(例如,如果规则需要“第一个输入的文件扩展名”),请将其作为额外变量传递,如上面传递cflags的方式。
如果顶级Ninja文件被指定为任何构建语句的输出并且它已过期,Ninja将在构建用户请求的目标之前重建并重新加载它。
从代码生成Ninja文件
Ninja发行版中的misc / ninja_syntax.py是一个很小的Python模块,可以帮助生成Ninja文件。它允许你像ninja.rule(name =’foo’,command =’bar’,depfile =’$ out.d’)进行Python调用,它会生成相应的语法。如果它有用,请随意将其内联到项目的构建系统中。
更多细节
虚假的规则
特殊规则名称phony可用于为其他目标创建别名。例如:
build foo:phony some / file / in / a / faraway / subdir / foo
这使得ninja foo构建了更长的路径。从语义上讲,虚假规则相当于一个简单的规则,其中命令什么也不做,但虚假规则是专门处理的,因为它们在运行,记录时没有打印(见下文),也没有对作为部分打印的命令计数做出贡献构建过程。
phony还可用于为在构建时可能不存在的文件创建虚拟目标。如果编写的假构建语句没有任何依赖关系,则如果目标不存在,则该目标将被视为过期。
默认目标语句
默认情况下,如果在命令行中未指定目标,Ninja将构建未在其他位置命名为输入的每个输出。您可以使用默认目标语句覆盖此行为。如果在命令行中未指定任何输出文件,则默认目标语句会使Ninja仅构建给定的输出文件子集。
默认目标语句以default关键字开头,格式为默认目标。默认目标语句必须出现在将目标声明为输出文件的构建语句之后。它们是累积的,因此可以使用多个语句来扩展默认目标列表。例如:
默认foo吧
默认巴兹
这会导致Ninja默认构建foo,bar和baz目标。
Ninja 日志
对于每个构建的文件,Ninja会记录用于构建它的命令。使用此日志Ninja可以知道何时使用与构建文件指定的命令行不同的命令行构建现有输出(即,命令行已更改)并且知道重建文件。
日志文件保存在名为.ninja_log的文件的构建根目录中。如果在最外层范围内提供名为builddir的变量,则.ninja_log将保留在该目录中。
版本兼容性
自Ninja 1.2起可用。
Ninja版本标签遵循标准major.minor.patch格式,其中主要版本在向后不兼容的语法/行为更改上增加,而次要版本在新行为上增加。您的build.ninja可能会声明一个名为ninja_required_version的变量,该变量断言使用生成的文件所需的最小Ninja版本。例如,
ninja_required_version = 1.1
声明构建文件依赖于Ninja 1.1中引入的某些功能(可能是池语法),并且必须使用Ninja 1.1或更高版本来构建。与其他Ninja变量不同,在解析中遇到变量时会立即检查此版本要求,因此最好将其置于构建文件的顶部。
Ninja总是警告Ninja和ninja_required_version的主要版本是否不匹配;尚未出现主要版本更改,因此很难预测可能需要的行为。
C / C ++头依赖项
要获得C / C ++标头依赖项(或以类似方式工作的任何其他构建依赖项),正确的Ninja具有一些额外的功能。
标头的问题是,给定源文件所依赖的文件的完整列表只能由编译器发现:不同的预处理器定义和包含路径会导致使用不同的文件。一些编译器可以在构建时发出这些信息,Ninja可以使用它来完善其依赖关系。
考虑一下:如果文件从未被编译过,那么它必须构建,生成头部依赖关系作为副作用。如果稍后修改了任何文件(即使以更改它所依赖的标头的方式),修改也将导致重建,使依赖关系保持最新。
在加载这些特殊依赖项时,Ninja会隐式添加额外的构建边,以便在缺少列出的依赖项时不会出错。这允许您删除头文件并重建,而不会因缺少输入而导致构建中止。
depfile
gcc(以及像clang这样的其他编译器)支持在Makefile的语法中发出依赖性信息。 (可以使用任何可以在此表单中编写依赖项的命令,而不仅仅是gcc。)
将这些信息带入忍者需要合作。在Ninja端,构建中的depfile属性必须指向写入此数据的路径。 (Ninja只支持编译器发出的Makefile语法的有限子集。)然后命令必须知道将依赖项写入depfile路径。像下例中一样使用它:
规则cc
depfile = $ out.d
command = gcc -MMD -MF $ out.d [其他gcc标志]
gcc的-MMD标志告诉它输出标头依赖项,-MF标志告诉它写入它们的位置。
DEPS
(自Ninja 1.3起可用。)
事实证明,对于大型项目(特别是在Windows上,文件系统很慢),在启动时加载这些依赖项文件的速度很慢。
Ninja 1.3可以在生成后立即处理依赖关系,并在Ninja内部数据库中保存相同信息的压缩形式。
忍者以两种形式支持这种处理。
deps = gcc指定该工具以Makefile的形式输出gcc样式的依赖项。将此添加到上面的示例将导致Ninja在编译完成后立即处理depfile,然后删除.d文件(仅用作临时文件)。
deps = msvc指定该工具以Visual Studio的编译器/ showIncludes标志生成的形式输出标头依赖项。简而言之,这意味着该工具将特殊格式的行输出到其标准输出。 Ninja然后从显示的输出中过滤这些行。不需要depfile属性,但是前面的本地化字符串
池
自Ninja 1.1起可用。
池允许您为有限数量的并发作业分配一个或多个规则或边缘,这些作业比默认并行性受到更严格的限制。
例如,这可以用于限制特定昂贵的规则(如巨大可执行文件的链接步骤),或限制特定的构建语句,这些语句在并发运行时表现不佳。
每个池都有一个深度变量,该变量在构建文件中指定。然后,在规则或构建语句中使用池变量引用池。
无论您指定哪个池,ninja都不会运行比默认并行运行更多的并发作业,也不会运行命令行上指定的作业数(使用-j)。
#一次不超过4个链接。
pool link_pool
深度= 4
#一次不超过1个重物。
pool heavy_object_pool
深度= 1
规则链接
…
pool = link_pool
规则cc
…
#这里使用了link_pool。只有4个链接可以同时运行。
build foo.exe:link input.obj
#通过设置一个构建语句可以免除其规则池
#空池。这有效地将构建语句放回默认值
pool,它具有无限深度。
build other.exe:link input.obj
pool =
build语句可以直接指定池。
#这些构建中只有一个会一次运行。
build heavy_object1.obj:cc heavy_obj1.cc
pool = heavy_object_pool
build heavy_object2.obj:cc heavy_obj2.cc
pool = heavy_object_pool
控制台池
自Ninja 1.5起可用。
存在一个名为console的预定义池,深度为1.它具有特殊属性,池中的任何任务都可以直接访问提供给Ninja的标准输入,输出和错误流,通常连接到用户的控制台(因此得名)但可以重定向。这对于在控制台上生成状态更新的交互式任务或长时间运行的任务(例如测试套件)非常有用。
当控制台池中的任务正在运行时,Ninja的常规输出(例如进度状态和并发任务的输出)将被缓冲,直到完成为止。
忍者文件参考
文件是一系列声明。声明可以是以下之一:
规则声明,以规则rulename开头,然后有一系列定义变量的缩进行。
构建边,看起来像构建output1 output2:rulename input1 input2。隐式依赖关系最终可以用|来加强dependency1 dependency2。最终可以使用||添加仅订单依赖项dependency1 dependency2。 (请参阅有关依赖类型的参考。)
可以在:with |之前添加隐式输出(自Ninja 1.7以来可用) output1 output2并不出现在$ out中。 (参见输出类型的参考。)
变量声明,看起来像variable = value。
默认目标语句,看起来像默认target1 target2。
引用更多文件,看起来像subninja路径或包含路径。下面在关于范围界定的讨论中解释了这些之间的差异。
池声明,看起来像池poolname。池中有关池的解释。
词法语法
Ninja主要编码不可知,只要Ninja关心的字节(如路径中的斜线)是ASCII。这意味着例如UTF-8或ISO-8859-1输入文件应该可以工作。
注释以#开头并延伸到行尾。
换行很重要。像build foo bar这样的语句是一组以换行符结尾的空格分隔标记。必须转义令牌中的换行符和空格。
只有一个转义字符$,它具有以下行为:
$后跟换行符
转义换行符(在换行符中继续当前行)。
$后跟文字
变量引用。
$ {VARNAME}
$ varname的替代语法。
$后跟空格
空间。 (这仅在路径列表中是必需的,否则空格将分隔文件名。请参阅下文。)
$:
一个冒号。 (这只在构建行中是必需的,否则冒号会终止输出列表。)
$$
文字$。
首先将构建或默认语句解析为以空格分隔的文件名列表,然后展开每个名称。这意味着变量中的空格将在扩展文件名中产生空格。
spacing = foo bar
build $ spaced / baz其他$ file:…
#上面的构建行有两个输出:“foo bar / baz”和“other file”。
在name = value语句中,始终剥离值开头的空格。在行连续之后的行开头处的空格也被剥离。
two_words_with_one_space = foo $
酒吧
one_word_with_no_space = foo $
酒吧
其他空格只有在一行的开头才有意义。如果一条线的缩进比前一条线多,则它被视为其父级范围的一部分;如果缩进小于前一个,则关闭前一个范围。
顶级变量
在最外层文件范围中声明时,两个变量很重要。
builddir
一些Ninja输出文件的目录。请参阅构建日志的讨论。 (您还可以在此目录中存储其他构建输出。)
ninja_required_version
正确处理构建所需的最小忍者版本。请参阅版本控制的讨论。
规则变量
规则块包含影响规则处理的key = value声明列表。以下是特殊键的完整列表。
命令(必填)
要运行的命令行。每个规则可能只有一个命令声明。有关引用和执行多个命令的更多详细信息,请参阅下一节。
depfile
包含额外隐式依赖项的可选Makefile的路径(请参阅有关依赖项类型的参考)。这显然是为了支持C / C ++头依赖关系;看到完整的讨论。
DEPS
(自Ninja 1.3以来可用。)如果存在,必须是gcc或msvc中的一个来指定特殊的依赖处理。请参阅完整的讨论。生成的数据库在builddir中存储为.ninja_deps,请参阅builddir的讨论。
msvc_deps_prefix
(从Ninja 1.5开始可用。)定义应从msvc的/ showIncludes输出中删除的字符串。仅在deps = msvc且未使用英语Visual Studio版本时才需要。
描述
命令的简短描述,用于在命令运行时对其进行漂亮打印。 -v标志控制是否打印完整命令或其描述;如果命令失败,将在命令输出之前始终打印完整的命令行。
发电机
如果存在,则指定此规则用于重新调用生成器程序。使用生成器规则构建的文件有两种特殊处理方式:首先,如果命令行发生变化,则不会重建它们。其次,默认情况下不会清理它们。
在
以空格分隔的文件列表,作为引用此规则的构建行的输入提供,如果它出现在命令中,则引用shell引用。 ($ in仅为方便起见而提供;如果您需要此文件列表的某个子集或变体,只需使用该列表构造一个新变量并使用该变量。)
in_newline
与$ in相同,除了多个输入由换行而不是空格分隔。 (与$ rspfile_content一起使用;这适用于MSVC链接器中的一个错误,它使用固定大小的缓冲区来处理输入。)
出
以空格分隔的文件列表作为输出提供给引用此规则的构建行,如果它出现在命令中,则引用shell引用。
restat
如果存在,则使Ninja在执行命令后重新统计命令的输出。命令未更改的修改时间的每个输出都将被视为从未需要构建。这可能会导致输出的反向依赖性从挂起的构建操作列表中删除。
rspfile,rspfile_content
如果存在(两者),Ninja将使用给定命令的响应文件,即在调用命令之前将所选字符串(rspfile_content)写入给定文件(rspfile)并在成功执行命令后删除文件。
这在Windows操作系统上特别有用,其中命令行的最大长度是有限的,必须使用响应文件。
像下例中一样使用它:
规则链接
command = link.exe / OUT $ out [这里通常的链接标志] @ $ out.rsp
rspfile = $ out.rsp
rspfile_content = $ in
构建myapp.exe:链接a.obj b.obj [可能还有很多其他.obj文件]
解释命令变量
从根本上说,命令行在Unix和Windows上的表现不同。
在Unix上,命令是参数数组。 Ninja命令变量直接传递给sh -c,后者负责将该字符串解释为argv数组。因此引用规则是shell的引用规则,您可以使用所有正常的shell运算符,例如&&来链接多个命令,或者使用VAR = value cmd来设置环境变量。
在Windows上,命令是字符串,因此Ninja将命令字符串直接传递给CreateProcess。 (在简单执行编译器的常见情况下,这意味着开销较少。)因此,引用规则由被调用程序决定,在Windows上通常由C库提供。如果需要命令的shell解释(例如使用&&链接多个命令),请通过在命令前添加cmd / c来使命令执行Windows shell。 Ninja可能会出现“无效参数”错误,通常表示已超出命令行长度。
建立产出
有两种类型的构建输出略有不同。
显式输出,如构建行中所列。这些可用作规则中的$ out变量。
这是用于例如输出的标准输出形式。编译命令的目标文件。
隐含输出,在构造行中列出,语法为|在构建线之前的out1 out2 +(从Ninja 1.7开始可用)。语义与显式输出相同,唯一的区别是隐式输出不会出现在$ out变量中。
这用于表示未在命令的命令行上显示的输出。
构建依赖项
有三种类型的构建依赖项略有不同。
显式依赖项,如构建行中所列。这些可用作规则中的$ in变量。这些文件中的更改会导致重建输出;如果这些文件丢失且Ninja不知道如何构建它们,则构建将中止。
这是要使用的标准依赖形式,例如用于编译命令的源文件。
隐式依赖关系,可以从规则的depfile属性中获取,也可以从语法中获取dep1 dep2位于构建行的末尾。语义与显式依赖项相同,唯一的区别是隐式依赖项不会显示在$ in变量中。
这用于表示未在命令的命令行上显示的依赖项;例如,对于运行脚本的规则,脚本本身应该是隐式依赖项,因为对脚本的更改应该导致输出重建。
请注意,通过depfiles加载的依赖关系具有稍微不同的语义,如规则参考中所述。
仅使用顺序的依赖关系,用语法||表示dep1 dep2位于构建行的末尾。当它们过时时,输出在构建之前不会重建,但仅依赖于顺序的依赖项的更改不会导致重建输出。
仅订单依赖项对于仅在构建期间发现的引导依赖项非常有用:例如,在开始后续编译步骤之前生成头文件。 (在编译中使用标头后,生成的依赖文件将表示隐式依赖项。)
文件路径按原样进行比较,这意味着Ninja认为指向同一文件的绝对路径和相对路径是不同的。
可变扩展
变量在路径(在构建或默认语句中)和name = value语句的右侧进行扩展。
当评估name = value语句时,它的右侧会立即展开(根据下面的作用域规则),从那时起$ name会扩展为静态字符串作为扩展的结果。从来没有这种情况你需要“双重转义”一个值来防止它被扩展两次。
所有变量在解析时都会立即展开,但有一个重要的例外:规则块中的变量在使用规则时会扩展,而不是在声明规则时。在以下示例中,演示规则打印“这是条形图的演示”。
规则演示
command = echo“这是$ foo的演示”
构建:演示
foo =吧
评估和范围
顶级变量声明的范围限定在它们出现的文件中。
规则声明的范围也限定在它们出现的文件中。(从Ninja 1.6开始可用)
subninja关键字用于包含另一个.ninja文件,它引入了一个新的范围。包含的subninja文件可以使用父文件中的变量和规则,并为文件的作用域隐藏它们的值,但不会影响父文件中变量的值。
要在当前作用域中包含另一个.ninja文件,就像C #include语句一样,请使用include而不是subninja。
构建块中缩进的变量声明的范围限定为构建块。在构建块(或规则是使用)中展开的变量的完整查找顺序是:
特殊的内置变量($ in,$ out)。
构建块中的构建级变量。
规则块中的规则级变量(即$ command)。 (请注意上面关于扩展的讨论,这些讨论是“延迟”扩展的,并且可以使用像in in这样的范围内绑定。)
来自构建行所在文件的文件级变量。
使用subninja关键字包含该文件的文件中的变量。