如果有自己编译过AOSP的源码,可能大家都会遇到这样的一个问题:
ninja: error: '*******', needed by '*********', missing and no known rule to make it
在刚刚开始接触AOSP的时候,我基本上都会直奔主题,大概清楚问题后,然后解决问题、重新编译,所以一直都没有注意到一个关键字:"Ninja",今天我们就一起来学习/实践一下这个"熟悉的陌生人",主要介绍一下目前Android的编译系统,以及以一个小Demo为例,实践一下Ninja的使用,如果想了解更多关于Ninja在AOSP中的使用,见参考链接。
一、Android编译系统
早期的Android系统采用Android.mk的配置来编译源码,从Android 7.0开始引入Android.bp。大体梳理一下Android版本相应的发展演变过程:
- Android 7.0引入ninja和kati
- Android 8.0使用Android.bp来替换Android.mk,引入Soong
- Android 9.0强制使用Android.bp

上图就是各个文件之间的转化关系,这里涉及到Ninja, kati, Soong, bp概念,简单介绍一下:
1. Ninja
ninja是一个编译框架,会根据相应的ninja格式的配置文件进行编译,但是ninja文件一般不会手动修改,而是通过将Android.bp文件转换成ninja格文件来编译。
2. Android.bp
Android.bp的出现就是为了替换Android.mk文件。bp跟mk文件不同,它是纯粹的配置,没有分支、循环等流程控制,不能做算数逻辑运算。如果需要控制逻辑,那么只能通过Go语言编写。
3. Soong
Soong类似于之前的Makefile编译系统的核心,负责提供Android.bp语义解析,并将之转换成Ninja文件。Soong还会编译生成一个androidmk命令,用于将Android.mk文件转换为Android.bp文件,不过这个转换功能仅限于没有分支、循环等流程控制的Android.mk才有效。
4. Blueprint
Blueprint是生成、解析Android.bp的工具,是Soong的一部分。Soong负责Android编译而设计的工具,而Blueprint只是解析文件格式,Soong解析内容的具体含义。Blueprint和Soong都是由Golang写的项目,从Android 7.0,prebuilts/go/目录下新增Golang所需的运行环境,在编译时使用。
5. Kati
kati是专为Android开发的一个基于Golang和C++的工具,主要功能是把Android中的Android.mk文件转换成Ninja文件。代码路径是build/kati/,编译后的产物是ckati。
二、Ninja的介绍
从上面我们可以看到,Android最后的编译工作都会交给Ninja,那么它到底是个什么东西呢,为什么Google需要在Android引入Ninja呢?
Ninja is a small build system with a focus on speed. It differs from other build systems in two major respects: it is designed to have its input files generated by a higher-level build system, and it is designed to run builds as fast as possible.
Ninja是一个专注于速度的构建系统,和其他构建系统相比,主要有两点不同:
- Ninja的输入文件一般都是有更高级的构建系统产生的,比如cmake;
- Ninja设计之初就是为了更快的构建;
其实从第一点,我们就能看出来Ninja的设计哲学:相比Makefile是设计出来给人手写的,但是Ninja设计出来是给其它程序生成的。 如果说Makefile是C语言,那么Ninja就是汇编语言。 如果说Makefile是一个DSL,那么Ninja就是一种配置文件。 Makefile支持分支、循环等流程控制,而Ninja只支持一些固定形式的配置。
三、Ninja的实践
工欲善其事,必先利其器,所以我们需要先准备好相关工具:CMake,ninja,以Mac OS为例,安装步骤为:
brew install cmake
brew install ninja
正式开始,以一个最简单的Hello,Ninja为例:
- main.cpp
#include <iostream>
int main() {
std::cout << "Hello, Ninja!" << std::endl;
return 0;
}
- CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(ninja)
set(CMAKE_CXX_STANDARD 14)
add_executable(ninja main.cpp)
OK,一个简单的Hello,Ninja的C++工程已经完成,接下来我们开始编译,使用如下命令,生成build.ninja文件:
[min@bogon:] ninja $ cmake -G Ninja -B build .
-- The C compiler identification is AppleClang 9.1.0.9020039
-- The CXX compiler identification is AppleClang 9.1.0.9020039
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/min/Desktop/workspace/c/ninja/build
可以看到在build目录下,生成了如下文件:
[min@bogon:] build $ tree -L 2
.
├── CMakeCache.txt
├── CMakeFiles
│ ├── 3.15.5
│ ├── CMakeOutput.log
│ ├── CMakeTmp
│ ├── TargetDirectories.txt
│ ├── cmake.check_cache
│ └── ninja.dir
├── build.ninja
├── cmake_install.cmake
└── rules.ninja
其中最重要的就是这个build.ninja文件,接下来,我们使用Ninja命令,开始编译:
[min@bogon:] ninja $ cd build/
[min@bogon:] build $ ninja
[2/2] Linking CXX executable ninja
[min@bogon:] build $ ./ninja
Hello, Ninja!
至此,一个简单的Hello, Ninja!就已经实践完成了,具体关于AOSP中Ninja的编译,希望大家可以自己去摸索一下~