UP | HOME

Ping's Tech Notes

gn/ninja: 谷歌的新一代项目构建系统入门
Ping Zhou, 2021-06-25

Table of Contents

[Back To Main Page]​

最近因工作关系,用到了谷歌的项目构建系统gn,这里结合一个简单的例子分析一下gn的基本使用方法。

gn/ninja 背景

gn是谷歌开源的一个元构建系统(meta-build system)。这个”元构建“的意思是,gn并不直接帮你构建项目,而是帮你产生构建项目的ninja文件,然后你再用ninja去构建项目。或者你可以这么理解,gn相当于帮你生成Makefile,然后你再用make去编译构建你的项目。

这么做的原因是,ninja虽然有构建速度快的优点,但它更多是为机器解析设计的,人能看懂ninja文件,但要为项目手写ninja文件就比较繁琐。gn结合ninja,能够让我们方便的创建和维护项目,同时又能享受ninja的编译性能。

目前已经有不少项目使用gn/ninja,其中最著名的之一就是Chromium(谷歌Chrome浏览器的开源版本)。Chromium代码库庞大,依赖关系复杂,需要支持多个编译目标,gn/ninja组合正适合这样的大型C++项目的构建。

安装和基本使用流程

以Ubuntu Linux为例,首先需要先安装ninja:

sudo apt install ninja

gn的安装,可以从官网下载代码编译:

git clone https://gn.googlesource.com/gn
cd gn
python build/gen.py
ninja -C out

然后把二进制文件放到你的路径里即可。

gn的基本使用流程,首先用gn生成ninja文件,然后用ninja来编译构建项目。

gn gen out
ninja -C out/

分析一个简单的例子

gn代码自带一个简单的hello world例子,我们来分析一下它里面的gn文件。为方便理解,我把它们之间的关系画了出来:

gn_simple_build.png

项目有这几个gn文件:

  1. .gn 这个文件有两个作用,一是指示项目的根目录,二是给项目设置构建环境,它需要指定至少一个buildconfig,在这个例子里这个buildconfig指向build目录下的BUILDCONFIG.gn文件。
  2. build/BUILDCONFIG.gn 这个文件被 .gn 引用,给项目设置构建环境,例如编译器和链接器的默认参数(引用自build/BUILD.gn),另外一个重要的设置是默认的工具链 set_default_toolchain ,这里指向build/toolchain目录下的BUILD.gn文件。
  3. build/toolchain/BUILD.gn 从目录名字就可以看出,这个文件定义了项目使用的工具链 “gcc”。如果项目需要用到多个工具链(例如嵌入式系统用到多个交叉编译器),可以在这里定义相应的工具链。
  4. build/BUILD.gn 这个文件被前面的BUIDLCONFIG.gn引用,里面定义了一些编译器和链接器的默认参数。
  5. BUILD.gn 项目的总构建文件,可以看到里面定义了几个target,包括我们要构建的可执行文件hello。

我们来试着编译一下这个项目。

$ cd gn/examples/simple_build

$ gn gen out
Done. Made 3 targets from 4 files in 12ms

$ ninja -C out/
ninja: Entering directory `out/'
[6/6] LINK hello

$ out/hello 
Hello, world

给ninja加上“-v”参数,可以看到详细的编译过程:

$ ninja -v -C out/
ninja: Entering directory `out/'
[1/6] g++ -MMD -MF obj/libhello_static.hello_static.o.d   -fPIC -pthread  -c ../hello_static.cc -o obj/libhello_static.hello_static.o
[2/6] rm -f obj/libhello_static.a && ar rcs obj/libhello_static.a obj/libhello_static.hello_static.o
[3/6] g++ -MMD -MF obj/libhello_shared.hello_shared.o.d -DHELLO_SHARED_IMPLEMENTATION  -fPIC -pthread  -c ../hello_shared.cc -o obj/libhello_shared.hello_shared.o
[4/6] g++ -MMD -MF obj/hello.hello.o.d   -fPIC -pthread  -c ../hello.cc -o obj/hello.hello.o
[5/6] g++ -shared  -o ./libhello_shared.so -Wl,-soname=libhello_shared.so @libhello_shared.so.rsp
[6/6] g++ -Wl,-rpath=\$ORIGIN/ -Wl,-rpath-link= -o hello -Wl,--start-group @hello.rsp  -Wl,--end-group

文档和帮助信息

最简单的方法是用 gn help

gn help <topic>
gn help all

如果需要,还可以让gn输出markdown格式的帮助信息:

gn help --markdown all

在线文档:

Quick start guide: https://gn.googlesource.com/gn/+/main/docs/quick_start.md

Reference: https://gn.googlesource.com/gn/+/main/docs/reference.md