Ninja


Ninja是一个小巧、高效的构建系统,专注于快速执行构建规则
它不负责生成构建规则,而是执行已有规则,规则通常由CMake之类的工具生成,Ninja本身只关注如何根据描述的依赖关系来执行命令
Make是既要写规则,又要执行;Ninja是只管执行,生成规则交给别的工具\

Ninja的核心特点

  • 极快:它的目标就是比make快很多,特别是在增量构建时;相比于传统的make,Ninja在启动开销、依赖检查、任务调度等方面都进行了极致优化
  • 并行化:天生支持多核运行(ninja -j N控制线程数)
  • 最小化I/O:Ninja避免过度扫描文件,依赖检查逻辑很轻量
  • 专注单一目标:不像CMake或Make那样很多花哨功能,Ninja只有一个任务:尽快跑完编译命令;Ninja的构建文件(build.ninja)由高级生成器(如CMake)输出。这意味着依赖关系显式且精确,避免了make中可能因隐式导致的错误构建
  • 设计简单:Ninja的语法和功能极其简单,它不包含复杂的逻辑(如条件判断、函数调用)。所有复杂逻辑都留给上一级的生成器,Ninja只负责执行。这使得它非常稳定和可预测

使用

  1. 使用CMake生成Ninja构建文件 在配置CMake项目时,使用-G Ninja参数指定生成器未Ninja
# 在项目构建目录中
mkdir build && cd build
cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Release

这条命令会读取上一级目录(..)的CMakeLists.txt,并根据其内容生成build.ninja文件和各种.ninja片段,而不是Makefile

  1. 使用Ninja进行构建 在上一步的build目录中,直接运行ninja命令
ninja

Ninja会读取build.ninja文件,开始编译项目。会看到它的输出非常简洁,直接显示正在进行的任务和进度,没有冗余信息

  1. 常用Ninja命令
  • ninja:构建默认目标(通常是all
  • ninja target_name:构建指定的目标(例如ninja hello_world
  • ninja -j N:指定并行编译的任务数(例如ninja -j 8使用8个线程)。Ninja会自动检测CPU核心数,但有时手动指定可以获得更好性能
  • ninja -C builddir:指定在builddir目录下执行构建(等价于cd builddir && ninja
  • ninja clean:清理所有构建处的文件
  • ninja -t clean:这是Ninja自带的清理命令,有时比生成器生成的clean规则更高效
  • ninja -t targets:列出所有可构建的目标
  • ninja -t graph:以graphviz格式输出所有目标的依赖图,可用于可视化依赖关系
  • ninja -n--dry-run,显示将要执行的命令但实际上不执行,用于调试

Ninja构建文件

虽然不推荐手写,但了解其基本语法有助于调试。build.ninja文件主要由rule(规则),build statement(构建语句)和variable(变量)组成

  • rule:定义了如何执行一个任务,类似于Makefile中的模式规则
# 定义一个名为'compile'的规则
rule compile
    command = gcc -c -o $out $in # 要执行的命令
    description = Compiling $out # 构建时显示的信息
    depfile = $out.d # 用于存储头文件等依赖信息
  • $in代表输入文件(如.c文件)

  • $out代表输出文件(如.o文件)

  • build:是Ninja的核心,它定义了具体的文件依赖关系和使用的规则

# 构建语句格式:build output_file: rule_name input_file
build hello.o: compile hello.c

这表示:要构建hello.o,需要使用compile这个规则,并且它依赖于hello.c文件

  • variable
cflags = -Wall -O2
rule compile
    command = gcc $cflags -c -o $out $in
  • 默认目标
default hello
  • 伪目标
build all: phony hello.exe other_target

phony表示all不是一个真实的文件,它总是需要被构建

完整示例

cflags = -Wall

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

rule link
  command = gcc -o $out $in

build main.o: compile main.c
build utils.o: compile utils.c

build myapp: link main.o utils.o

default myapp