转自:http://guiquanz.me/2014/07/28/a_intro_to_Ninja/

Ninja - chromium核心构建工具Jul 28, 2014

[在线编辑]

缘由

经过上次对chromium核心代码的初步了解之后,我转头去研究了一番ninja,并对其进行了一些改造(爱折腾的,都是小NB)。今天就来简单介绍一下ninja及其使用。(BTW: 细节的内容,大家阅读ninja 的手册就好了,我这里不会关注。)

ninja一个专注于速度的小型构建系统(Ninja is a small build system with a focus on speed)。ninja是其作者为了解决chromium代码编译慢这个问题(具体一点,就是发生在将Chrome移植到非Windows平台过程中的事情。欲知详情,请阅读Ninja, a new build system)而诞生的。其设计受到the tup build systemredo的启发。ninja核心是由C/C++编写的,同时有一部分辅助功能由pythonshell实现。

ninja可以很好的组合gypCMake一起使用,后者为其生成.ninja文件。

ninja项目的最终编译产出物是一个可执行文件ninja。

下载代码 并 编译


mkdir -p ~/ninja && cd ~/ninja
git clone https://github.com/martine/ninja
cd ninja
python ./bootstrap.py

(BTW:以上过程编译生成可执行文件ninja。需要预先安装 graphviz及其开发库,gtestgitre2cpython

测试

由于在编译ninja的过程中bootstrap.py脚本通过调用configure.pyplatform_helper.py生成了ninja项目的构建文件build.ninja,所以我们只需要执行./ninja ninja_test就可以通过ninja构建生成测试文件ninja_test。这样就可以执行测试了。


./ninja ninja_test
./ninja all

ninja 工具介绍

在介绍ninja的文法之前,还是先了解一下ninja的使用吧。执行./ninja -h显示帮助信息。具体参数说明,如下:


usage: ninja [options] [targets...] if targets are unspecified, builds the 'default' target (see manual). options:
--version # 打印版本信息(如当前版本是1.5.1) -C DIR # 在执行操作之前,切换到`DIR`目录
-f FILE # 制定`FILE`为构建输入文件。默认文件为当前目录下的`build.ninja`。如 ./ninja -f demo.ninja -j N # 并行执行 N 个作业。默认N=3(需要对应的CPU支持)。如 ./ninja -j 2 all
-l N # 如果平均负载大于N,不启动新的作业
-k N # 持续构建直到N个作业失败为止。默认N=1
-n # 排练(dry run)(不执行命令,视其成功执行。如 ./ninja -n -t clean)
-v # 显示构建中的所有命令行(这个对实际构建的命令核对非常有用) -d MODE # 开启调试模式 (用 -d list 罗列所有的模式)
-t TOOL # 执行一个子工具(用 -t list 罗列所有子命令工具)。如 ./ninja -t query all

ninja还集成了graphviz等一些对开发非常有用的工具。具体如下:(也就是执行 ./ninja -t list 的结果)


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内部数据结构

ninja文件示例

聊了半天,ninja的构建文件长什么模样呢?以下的demo就是一个执行echo,打印一行文字的ninja构建文件,和make的Makefile很类似。


rule demo
command = echo "this is a demo of $foo" build out: demo
foo = bar

编写你自己的ninja文件

Ninja和Make非常相似。他执行一个文件之间的依赖图,通过检测文件修改时间,运行必要的命令来更新你的构建目标

一个构建文件(默认文件名为:build.ninja)提供一个rule(规则)表——长命令的简短名称,和运行编译器的方式一下。同时,附带提供build(构建)语句列表,表明通过rule如何构建文件——哪条规则应用于哪个输入产生哪一个输出。

从概念上讲,build语句描述项目的依赖图;而rule语句描述当给定一个图的一条边时,如何生成文件。

语法示例

这是一个用于验证绝大部分语法的.ninja文件,将作为后续描述相关的示例。具体内容,如下:


cflags = -Wall rule cc
command = gcc $cflags -c $in -o $out build foo.o: cc foo.c

变量

ninja支持为字符串声明简短可读的名字。一个声明的语法,如下:


cflags = -g

可以在=右边使用,并通过$进行引用(类似shellperl的语法)。具体形式,如下:


rule cc
command = gcc $cflags -c $in -o $out

变量还可以用${in}($和成对的大括号)来引用。

当给定变量的值不能被修改,只能覆盖(shadowed)时,变量更恰当的叫法是绑定("bindings")。

rule 规则

规则为命令行声明一个简短的名称。他们由关键字rule一个规则名称打头的行开始,然后紧跟着一组带缩进格式的 variable = value行组成。

以上示例中声明了一个名为cc的rule,连同一个待运行的命令。在rule(规则)上下文中,command变量用于定义待执行的命令,$in展开(expands)为输入文件列表(foo.c),而$out为命令的输出文件列表(foo.o)。参考手册中罗列了所有特殊的变量。

buid 构建语句

build语句声明输入和输出文件之间的一个关系。构建语句由关键字build开头,格式为build outputs: rulename inputs。这样的一个声明,所有的输出文件来源于(derived from)输入文件。当缺输出文件或输入文件变更时,Ninja将会运行此规则来重新生成输出。

以上的简单示例,描述了使用cc规则如何构建foo.o文件。

build block范围内(包括相关规则的执行),变量$in表示输入列表,$out表示输出列表。

一个构建语句,可以和rule一样,紧跟一组带缩进格式的key = value对。当在命令中变量执行时,这些变量将覆盖(shadow)任何变量。比如:


cflags = -Wall -Werror
rule cc
command = gcc $cflags -c $in -o $out # 如果没有制定,build的输出将是$cflags
build foo.o: cc foo.c # 但是,你可以在特殊的build中覆盖cflags这样的变量
build special.o: cc special.c
cflags = -Wall # cflags变量仅仅覆盖了special.o的范围
# 以下的子序列build行得到的是外部的(原始的)cflags
build bar.o: cc bar.c

从代码中生成Ninja文件

Ninja发行包中的misc/ninja_syntax.py是一个很小的python模块,用于生成Ninja文件。你可以使用python,执行如ninja.rule(name='foo', command='bar', depfile='$out.d')的调用,生成合适的语法。如果这样还不错,可以将其整合到你的项目中。

更多细节

phony 规则

可以使用特殊的规则phony,创建其他target(编译构建目标)的别名。比如:


build foo: phony some/file/in/a/faraway/subdir/foo

这样使得ninja foo构建更长的路径。从语义上讲,phony规则等同于一个没有做任何操作的普通规则,但是phony规则通过特殊的方式进行处理,这样当其运行时不会被打印,记日志,也不作为构建过程中打印出来的命令计数。

还可以用phony为构建时可能还不存在的文件创建dummy目标。

default 目标语句

默认情况下,如果没有在命令行中指定target,那么Ninja将构建任何地方没有作为输入命名的每一个输出。可以通过default目标语句来重写这个行为。一个default语句,让Ninja构建一个给定的输出文件子集,如果命令行中没有指定构建目标

默认目标语句,由关键字default打头,并且采用default targets的格式。一个default目标语句必须出现在,声明这个目标作为一个输出文件的构建语句之后。他们是累积的(cumulative),所以可以使用多个default语句来扩展默认目标列表。比如:


default foo bar
default baz

Ninja构建日志

Ninja构建日志保存在构建过程的跟目录或.ninja文件中builddir变量对应的目录的.ninja_log文件中。

C/C++头文件依赖

Ninja目前支持depfiledeps模式的C/C++头文件依赖生成。 如


rule cc
depfile = $out.d
command = gcc -MMD -MF $out.d [other gcc flags here]

-MMD标识告诉gcc要生成头文件依赖,-MF则说明要写到哪里。

deps按照编译器的名词来管理。具体如下:(针对微软的VC:msvc)


rule cc
deps = msvc
command = cl /showIncludes -c $in /Fo$out

Pools

为了支持并发作业,Ninja还支持pool的机制(和用-j并行模式一样)。此处不详细描述了。具体示例,如下:


# No more than 4 links at a time.
pool link_pool
depth = 4 # No more than 1 heavy object at a time.
pool heavy_object_pool
depth = 1 rule link
...
pool = link_pool rule cc
... # The link_pool is used here. Only 4 links will run concurrently.
build foo.exe: link input.obj # A build statement can be exempted from its rule's pool by setting an
# empty pool. This effectively puts the build statement back into the default
# pool, which has infinite depth.
build other.exe: link input.obj
pool = # A build statement can specify a pool directly.
# Only one of these builds will run at a time.
build heavy_object1.obj: cc heavy_obj1.cc
pool = heavy_object_pool
build heavy_object2.obj: cc heavy_obj2.cc
pool = heavy_object_pool
The console pool

更加详细的语法

请阅读参考手册,此处只做概要说明。

一个ninja构建文件,由一系列的声明构成。一个声明可以是一个:

  • rule声明,由rule rulename开头,然后紧跟一系列带缩进的变量定义行

  • 一个build边,其格式为build output1 output2: rulename input1 input2。隐士依赖用| dependency1 dependency2表达;Order-only依赖用行末的|| dependency1 dependency2表达。

  • 变量声明,形如variable = value

  • 默认目标语句,形如default target1 target2

  • 引入更多的文件,形如subninja pathinclude path

  • 一个pool声明,形如pool poolname

词法

Ninja仅支持ASCII字符集。

注释以为#开始一直到行末。

新行是很重要的。像build foo bar的语句,是一堆空格分割分词(token),到换行结束。一个分词中的新行空格必须进行转译。

目前只有一个转译字符,$,其具有以下行为:


$ followed by a newline

转译换行,让当前行一直扩展到下一行。


$ followed by text

这是, 变量引用。


${varname}

这是,另$varname的另一种语法。


$ followed by space

这表示一个空格。(仅在path列表中,需要用空格分割文件名)


$:

这表示一个冒号。(仅在build行中需要。此时冒号终止输出列表)


$$

这个表示,字面值的$

一个build或default语句,最先被解析,作为一个空格分割的文件名列表,然后每一个name都被展开。也就是说,变量中的一个空格将作为被展开后文件名中的一个空格。


spaced = foo bar
build $spaced/baz other$ file: ...
# The above build line has two outputs: "foo bar/baz" and "other file".

在一个name = value语句中,value前的空白都会被去掉。出现跨行时,后续行起始的空白也会被去掉。


two_words_with_one_space = foo $
bar
one_word_with_no_space = foo$
bar

其他的空白,仅位于行开始处的很重要。如果一行的缩进比前一行多,那么被人为是其父边界的一部分。如果缩进比前一行少,那他就关闭前一个边界

顶层变量

Ninja支持的顶层变量有builddirninja_required_version。具体说明,如下:

  • builddir: 构建的一些输出文件的存放目录。
  • ninja_required_version:指定满足构建需求的最小Ninja版本。

rule变量

一个rule块包含一个key = value的列表声明,这直接影响规则的处理。以下是一些特殊的key:

  • command (required): 待执行的命令。这个字符串($variables被展开之后),被直接传递给sh -c,不经过Ninja翻译。每一个规则只能包含一条command声明。如果有多条命令,需要使用&&符号进行链接。

  • depfile: 指向一个可选的Makefile,其中包含额外的隐式依赖。这个明确的为了支持C/C++的头文件依赖。

  • deps: (1.3版本开始支持)如果存在,必须是gcc或msvc,来指定特殊的依赖。产生的数据库保存在builddir指定目录.ninja_deps文件中。

  • msvc_deps_prefix: (1.5版本开始支持)定义必须从msvc的/showIncludes输出中去掉的字符串。仅在deps = msvc而且使用非英语的Visual Studio版本时使用。

  • description: 命令的简短描述,作为命令运行时更好的打印输出。打印整行还是对应的描述,由-v标记控制。如果一个命令执行失败,整个命令行总是在命令输出之前打印。

  • generator: 如果存在,指明这条规则是用来重复调用生成器程序。通过两种特殊的方式,处理使用生成器规则构建文件:首先,如果命令行修改了,他们不会重新构建;其次,默认不会被清除。

  • in: 空格分割的文件列表被作为一个输入传递给引用此rule的构建行,如果出现在命令中需要使用${in}(shell-quoted)。(提供$in仅仅为了图个方便,如果你需要文件列表的子集或变种,请构建一个新的变量,然后传递新的变量。)

  • in_newline: 和$in一样,只是分割符为换行而不是空格。(仅为了和$rspfile_content一起使用,解决MSVC linker使用固定大小的缓冲区处理输入,而造成的一个bug。)

  • out: 空格分割的文件列表被作为一个输出传递给引用此rule的构建行,如果出现在命令中需要使用${out}

  • restat: 如果存在,引发Ninja在命令行执行完之后,重新统计命令的输出。

  • rspfile, rspfile_content: 如果存在(两个同时),Ninja将为给定命令提供一个响应文件,比如,在调用命令之前将选定的字符串(rspfile_content)写到给定的文件(rspfile),命令执行成功之后阐述文件。

这个在Windows系统非常有用,因为此时命令行的最大长度非常受限,必须使用响应文件替代。具体使用方式,如下:


rule link
command = link.exe /OUT$out [usual link flags here] @$out.rsp
rspfile = $out.rsp
rspfile_content = $in build myapp.exe: link a.obj b.obj [possibly many other .obj files]

构建依赖

Ninja目前支持3种类型的构建依赖。分别是:

  • 罗列在build行中的显式的依赖。他们可以作为规则中的$in变量。这是标准依赖格式。

  • depfile属性或构建语句末尾的| dep1 dep2语法获得的隐式依赖。这个和显式依赖一样,但是不能在$in中使用(不可见)。

  • 通过构建行末|| dep1 dep2语法表示的次序唯一(Order-only)依赖。他们过期的时候,输出不会被重新构建,直到他们被重建,但修改这种依赖不会引发输出重建。

变量展开

变量在路径(在build或default语句)和name = value右边被展开。

name = value语句被执行,右手边的被立即展开(根据以下的规则),从此$name扩展为被展开结果的静态字符串。永远也不会存在,你将需要使用双转译("double-escape")来保护一个值被第二次展开。

所有变量在解析过程,遇到的时候立即被展开,除了一个非常重要的例外:rule块中的变量仅在规则被使用的时候才被展开,而不是声明的时候。在以下的示例中,demo打印出"this is a demo of bar"而不是"this is a demo of $foo"。


rule demo
command = echo "this is a demo of $foo" build out: demo
foo = bar

评估和边界

顶层(Top-level)变量声明的边界,是相关的文件。

subninja关键自,用于包含另一个.ninja文件,其表示新的边界。被包含的subninja文件可以使用父文件中的变量,在文件边界中覆盖他们的值,但是这不影响父文件中变量的值。

同时,可以用#include语句在当前边界内,引入另一个.ninja文件。这个有点像C中的#include语句。

构建块中声明的变量的边界,就是其所属的块。一个构建块中展开的变量的所有查询次序为:

  • 特殊内建变量($in, $out);
  • build/rule块中构建层的变量;
  • 构建行所在文件中的文件层变量(File-level);
  • 使用subninja关键字引入那个文件的(父)文件中的变量。

最后再看一下编译ninja的构建文件


# This file is used to build ninja itself.
# It is generated by configure.py. ninja_required_version = 1.3 # The arguments passed to configure.py, for rerunning it.
configure_args = --platform=linux builddir = build
cxx = g++
ar = ar
cflags = -g -Wall -Wextra -Wno-deprecated -Wno-unused-parameter -fno-rtti $
-fno-exceptions -fvisibility=hidden -pipe $
-Wno-missing-field-initializers '-DNINJA_PYTHON="python"' -O2 -DNDEBUG $
-DUSE_PPOLL
ldflags = -L$builddir rule cxx
command = $cxx -MMD -MT $out -MF $out.d $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc rule ar
command = rm -f $out && $ar crs $out $in
description = AR $out rule link
command = $cxx $ldflags -o $out $in $libs
description = LINK $out # browse_py.h is used to inline browse.py.
rule inline
command = src/inline.sh $varname < $in > $out
description = INLINE $out
build $builddir/browse_py.h: inline src/browse.py | src/inline.sh
varname = kBrowsePy build $builddir/browse.o: cxx src/browse.cc || $builddir/browse_py.h # the depfile parser and ninja lexers are generated using re2c.
rule re2c
command = re2c -b -i --no-generation-date -o $out $in
description = RE2C $out
build src/depfile_parser.cc: re2c src/depfile_parser.in.cc
build src/lexer.cc: re2c src/lexer.in.cc # Core source files all build into ninja library.
build $builddir/build.o: cxx src/build.cc
build $builddir/build_log.o: cxx src/build_log.cc
build $builddir/clean.o: cxx src/clean.cc
build $builddir/debug_flags.o: cxx src/debug_flags.cc
build $builddir/depfile_parser.o: cxx src/depfile_parser.cc
build $builddir/deps_log.o: cxx src/deps_log.cc
build $builddir/disk_interface.o: cxx src/disk_interface.cc
build $builddir/edit_distance.o: cxx src/edit_distance.cc
build $builddir/eval_env.o: cxx src/eval_env.cc
build $builddir/graph.o: cxx src/graph.cc
build $builddir/graphviz.o: cxx src/graphviz.cc
build $builddir/lexer.o: cxx src/lexer.cc
build $builddir/line_printer.o: cxx src/line_printer.cc
build $builddir/manifest_parser.o: cxx src/manifest_parser.cc
build $builddir/metrics.o: cxx src/metrics.cc
build $builddir/state.o: cxx src/state.cc
build $builddir/util.o: cxx src/util.cc
build $builddir/version.o: cxx src/version.cc
build $builddir/subprocess-posix.o: cxx src/subprocess-posix.cc
build $builddir/libninja.a: ar $builddir/browse.o $builddir/build.o $
$builddir/build_log.o $builddir/clean.o $builddir/debug_flags.o $
$builddir/depfile_parser.o $builddir/deps_log.o $
$builddir/disk_interface.o $builddir/edit_distance.o $
$builddir/eval_env.o $builddir/graph.o $builddir/graphviz.o $
$builddir/lexer.o $builddir/line_printer.o $builddir/manifest_parser.o $
$builddir/metrics.o $builddir/state.o $builddir/util.o $
$builddir/version.o $builddir/subprocess-posix.o # Main executable is library plus main() function.
build $builddir/ninja.o: cxx src/ninja.cc
build ninja: link $builddir/ninja.o | $builddir/libninja.a
libs = -lninja # Tests all build into ninja_test executable.
test_cflags = -g -Wall -Wextra -Wno-deprecated -Wno-unused-parameter $
-fno-rtti -fno-exceptions -fvisibility=hidden -pipe $
-Wno-missing-field-initializers -DNINJA_PYTHON="python" -O2 -DNDEBUG $
-DUSE_PPOLL -DGTEST_HAS_RTTI=0
build $builddir/build_log_test.o: cxx src/build_log_test.cc
cflags = $test_cflags
build $builddir/build_test.o: cxx src/build_test.cc
cflags = $test_cflags
build $builddir/clean_test.o: cxx src/clean_test.cc
cflags = $test_cflags
build $builddir/depfile_parser_test.o: cxx src/depfile_parser_test.cc
cflags = $test_cflags
build $builddir/deps_log_test.o: cxx src/deps_log_test.cc
cflags = $test_cflags
build $builddir/disk_interface_test.o: cxx src/disk_interface_test.cc
cflags = $test_cflags
build $builddir/edit_distance_test.o: cxx src/edit_distance_test.cc
cflags = $test_cflags
build $builddir/graph_test.o: cxx src/graph_test.cc
cflags = $test_cflags
build $builddir/lexer_test.o: cxx src/lexer_test.cc
cflags = $test_cflags
build $builddir/manifest_parser_test.o: cxx src/manifest_parser_test.cc
cflags = $test_cflags
build $builddir/ninja_test.o: cxx src/ninja_test.cc
cflags = $test_cflags
build $builddir/state_test.o: cxx src/state_test.cc
cflags = $test_cflags
build $builddir/subprocess_test.o: cxx src/subprocess_test.cc
cflags = $test_cflags
build $builddir/test.o: cxx src/test.cc
cflags = $test_cflags
build $builddir/util_test.o: cxx src/util_test.cc
cflags = $test_cflags
build ninja_test: link $builddir/build_log_test.o $builddir/build_test.o $
$builddir/clean_test.o $builddir/depfile_parser_test.o $
$builddir/deps_log_test.o $builddir/disk_interface_test.o $
$builddir/edit_distance_test.o $builddir/graph_test.o $
$builddir/lexer_test.o $builddir/manifest_parser_test.o $
$builddir/ninja_test.o $builddir/state_test.o $
$builddir/subprocess_test.o $builddir/test.o $builddir/util_test.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread # Ancillary executables.
build $builddir/build_log_perftest.o: cxx src/build_log_perftest.cc
build build_log_perftest: link $builddir/build_log_perftest.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/canon_perftest.o: cxx src/canon_perftest.cc
build canon_perftest: link $builddir/canon_perftest.o | $builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/depfile_parser_perftest.o: cxx src/depfile_parser_perftest.cc
build depfile_parser_perftest: link $builddir/depfile_parser_perftest.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/hash_collision_bench.o: cxx src/hash_collision_bench.cc
build hash_collision_bench: link $builddir/hash_collision_bench.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread
build $builddir/manifest_parser_perftest.o: cxx $
src/manifest_parser_perftest.cc
build manifest_parser_perftest: link $builddir/manifest_parser_perftest.o | $
$builddir/libninja.a
libs = -lninja -lgtest_main -lgtest -lpthread # Generate a graph using the "graph" tool.
rule gendot
command = ./ninja -t graph all > $out
rule gengraph
command = dot -Tpng $in > $out
build $builddir/graph.dot: gendot ninja build.ninja
build graph.png: gengraph $builddir/graph.dot # Generate the manual using asciidoc.
rule asciidoc
command = asciidoc -b docbook -d book -o $out $in
description = ASCIIDOC $out
rule xsltproc
command = xsltproc --nonet doc/docbook.xsl $in > $out
description = XSLTPROC $out
build $builddir/manual.xml: asciidoc doc/manual.asciidoc
build doc/manual.html: xsltproc $builddir/manual.xml | doc/style.css
build manual: phony || doc/manual.html # Generate Doxygen.
rule doxygen
command = doxygen $in
description = DOXYGEN $in
doxygen_mainpage_generator = src/gen_doxygen_mainpage.sh
rule doxygen_mainpage
command = $doxygen_mainpage_generator $in > $out
description = DOXYGEN_MAINPAGE $out
build $builddir/doxygen_mainpage: doxygen_mainpage README COPYING | $
$doxygen_mainpage_generator
build doxygen: doxygen doc/doxygen.config | $builddir/doxygen_mainpage # Regenerate build files if build script changes.
rule configure
command = ${configure_env}python configure.py $configure_args
generator = 1
build build.ninja: configure | configure.py misc/ninja_syntax.py default ninja # Packaging
rule rpmbuild
command = misc/packaging/rpmbuild.sh
description = Building rpms..
build rpm: rpmbuild build all: phony ninja ninja_test build_log_perftest canon_perftest $
depfile_parser_perftest hash_collision_bench manifest_parser_perftest

针对ninja的优化

Ninja是一块非常好的构建工具,其实也是一个特殊的编译器,其中有很多值得学习和借鉴的地方。比如,使用re2c将正则表达式编译为c代码(PHP也是用了这个工具,干了类似的事情),使用graphviz生成dot格式的依赖文件等等。当然,我不太喜欢其对python的依赖。安装了其他依赖工具和库之后,还需要安装python,否则没法编译ninja。经过分析之后,我为ninja定制了一个Makefile编译方案,同时修改了部分python文件,这样在没有python的情况下依然可以编译和使用ninja。如果需要使用ninja -t browse和构建ninja_test等测试目标,那还是需要安装python。当然,要去掉这些依赖也不是很难的事情,如果需要哪天有空我可能就将其修改了。修改后的版本一贯的放在github上,需要的自取。仅将此文作为学习ninja的一个阶段性总结。欢迎交流和反馈。

扩展阅读

祝大家玩的开心

编程之道,就在[编程之美]

Ninja - chromium核心构建工具的更多相关文章

  1. Google软件构建工具Bazel FAQ

    Google软件构建工具Bazel FAQ 本文是我的翻译,原文在这里.欢迎转载,转载请注名本文作者和原始链接 注:如果想了解Bazel的原理,可以看看我之前翻译的Google Blaze原理及使用方 ...

  2. Google软件构建工具Bazel

    转载Google软件构建工具Bazel FAQ 本文是我的翻译,原文在这里.欢迎转载,转载请注名本文作者和原始链接注:如果想了解Bazel的原理,可以看看我之前翻译的Google Blaze原理及使用 ...

  3. Gulp:自动化构建工具

    一.介绍: gulp是一个基于流的构建工具,可以自动执行指定的任务,简洁且高效 二.优点: 开发环境下,想要能够按模块组织代码,监听实时变化 css/js预编译,postcss等方案,浏览器前缀自动补 ...

  4. gulp自动化构建工具

    gulp    自动化构建工具,实时监控.代码合并.压缩... http://www.gulpjs.com.cn/     中文网 http://gulpjs.com/plugins/     英文网 ...

  5. 前端自动化构建工具gulp的使用总结

    前端自动化构建工具gulp的使用总结 博主最近偶的空闲,在此对gulp的使用做一个总结,让小伙伴知道如何合理的使用gulp以及gulp的使用技巧. 谈到gulp,有人可能就会想到另外一个构建工具gru ...

  6. Gulp.js----比Grunt更易用的前端构建工具

    Gulp.js----比Grunt更易用的前端构建工具 Grunt一直是前端构建工具,然而他也不是毫无缺陷的,gulp的作者 Eric Schoffstall 在他介绍 gulp.js 的 prese ...

  7. 深入了解Ant构建工具 命令

    深入了解Ant构建工具 标签: ant工具任务jarjavaclass 2010-05-29 21:16 1346人阅读 评论(2) 收藏 举报 版权声明:本文为博主原创文章,未经博主允许不得转载. ...

  8. 学习笔记——Maven实战(六)Gradle,构建工具的未来?

    Maven面临的挑战 软件行业新旧交替的速度之快往往令人咂舌,不用多少时间,你就会发现曾经大红大紫的技术已经成为了昨日黄花,当然,Maven也不会例外.虽然目前它基本上是Java构建的事实标准,但我们 ...

  9. Gulp, 比Grunt更好用的前端构建工具

    Gulp, 比Grunt更好用的前端构建工具 本文主要从两个方面介绍Gulp:一,Gulp相对于Grunt的优势: 二,Gulp的安装和使用流程 Gulp相对于Grunt的优势 gulp.js 的作者 ...

随机推荐

  1. 如何使用网盘托管git项目

    话说近年来git已经成为项目源代码管理的标准工具,有不少免费托管网站可供使用,详情参考这篇文章: http://www.cnblogs.com/zdz8207/archive/2012/05/20/2 ...

  2. servlet的一个web容器中有且只有一个servlet实例或有多个实例的理解1

    servlet的一个web容器中有且只有一个servlet实例或有多个实例的理解 (2013-06-19 19:30:40) 转载▼     servlet的非线程安全,action的线程安全 对提交 ...

  3. gcc/g++ 参数

    -static  此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么 动态连接库,就可以运行. -share  此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.

  4. BlockingQueue详解

    本例介绍一个特殊的队列:BlockingQueue,如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入 等待状态,直到BlockingQueue进了东西才会被 ...

  5. Java开源数据库管理工具

    SQuirreL SQL Client   SQuirreL SQL Client 是一个用 Java 编写的程序,它允许您查看数据库的内容.发出 SQL 命令,以及如您将看到的,执行许多其他功能.构 ...

  6. C++中的异常处理(二)

    C++中的异常处理(二) 标签: c++C++异常处理 2012-11-24 20:56 1713人阅读 评论(2) 收藏 举报  分类: C++编程语言(24)  版权声明:本文为博主原创文章,未经 ...

  7. Javascript网页摇一摇

    function init(){ if (window.DeviceMotionEvent) { // 移动浏览器支持运动传感事件 window.addEventListener('devicemot ...

  8. XSS 探索

    1. 什么是XSS攻击? 正常的页面被渗出了攻击者的js脚本,这些脚本可以非法地获取用户信息,然后将信息发送到attacked的服务端. XSS是需要充分利用输出环境来构造攻击脚本的 2. 危害 非法 ...

  9. jQuery.isEmptyObject() 函数详解

    所谓"空对象",即不包括任何可枚举(自定义)的属性.简而言之,就是该对象没有属性可以通过for...in迭代. 该函数属于全局jQuery对象. 语法 jQuery 1.4 新增该 ...

  10. WhatsApp值160亿美元,腾讯推大众点评微信支付!

    腾讯前脚刚入股大众点评,FB后脚就将斥资160亿美元收购WhatsApp(40亿美元现金和120亿美元股票). 为什么WhatsApp值160亿美元?这是什么东东呢?WhatsApp这款服务可以帮助用 ...