• Ninja


    转自: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的一个阶段性总结。欢迎交流和反馈。

    扩展阅读

    祝大家玩的开心

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

    编程之美

  • 相关阅读:
    P4396 [AHOI2013]作业
    NOIP2018普及T2暨洛谷P5016 龙虎斗
    NOIP2018普及T1暨洛谷P5015 标题统计 题解
    【交题大桥】团队信息存档
    markdown浅谈
    洛谷P1690 贪婪的Copy 题解
    洛谷P4994 终于结束的起点 题解
    洛谷P4995 跳跳!题解
    这么多都变了,洛谷4还会远吗?
    洛谷P1396 营救 题解
  • 原文地址:https://www.cnblogs.com/x_wukong/p/4846179.html
Copyright © 2020-2023  润新知