gn/ninja: 谷歌的新一代项目构建系统入门
Ping Zhou, 2021-06-25
Table of Contents
最近因工作关系,用到了谷歌的项目构建系统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文件:
.gn
这个文件有两个作用,一是指示项目的根目录,二是给项目设置构建环境,它需要指定至少一个buildconfig,在这个例子里这个buildconfig指向build目录下的BUILDCONFIG.gn文件。build/BUILDCONFIG.gn
这个文件被.gn
引用,给项目设置构建环境,例如编译器和链接器的默认参数(引用自build/BUILD.gn),另外一个重要的设置是默认的工具链set_default_toolchain
,这里指向build/toolchain目录下的BUILD.gn文件。build/toolchain/BUILD.gn
从目录名字就可以看出,这个文件定义了项目使用的工具链 “gcc”。如果项目需要用到多个工具链(例如嵌入式系统用到多个交叉编译器),可以在这里定义相应的工具链。build/BUILD.gn
这个文件被前面的BUIDLCONFIG.gn引用,里面定义了一些编译器和链接器的默认参数。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