Back to home

编译也许可以更快

初次发表于2015/10/23
一、引子
chromium亲密接触了个把月了,对它的编译工具ninja也就停留在能用的基础上。最近需要把ZMQ加到chromium工程中使用,免不了要啃啃这个传说中的神器了。
Ninja是有别于make等的另一个编译系统,它仅仅是google的工程师为了解决编译chromium这个肥仔(近1G源码)速度过慢的问题,受他人的启发而实现的(google真是任性,啥用着不爽就直接造一个,哈哈)。其特点是快和简洁,仅保留最少的特性来提高编译速度。当你正在从事大型的C/C++项目开发,苦恼于编辑完文件后漫长的编译等待的话,不妨考虑下ninja
二、例子
下面以在chromium中添加ZMQ为例,我们略去google各种添加READMELICENSE说明等等步骤,仅仅具体说明下编译环节ninja的用法。
1. 概念
首先,有些基本的概念,我们拿ninjamake系统相关的概念来对比说明:
Ninja——make
build.ninja——makefile
CMakegyp——autoconfautomake
ninja执行一个文件之间的依赖图,通过检测文件修改时间,运行必要的命令来更新你的构建目标。一个构建文件(默认文件名为:build.ninja)提供一个rule(规则)表和运行编译器的方式。同时,附带提供build(构建)语句列表,表明通过rule如何构建文件。
从概念上讲,build语句描述项目的依赖图;而rule语句描述当给定一个图的一条边时,如何生成文件。
2. 编写gyp文件
直接写build.niinja文件会过于繁琐,实际使用过程中可以配合gyp来使用。
GYPgoogle的开源的构建系统,在一个平台上可以生成所有的平台的工程文件(linux上生成vs工程),且生成的工程文件和手工创建的没有区别。
假如有main.cpp源文件
#include
int main(int argc, const char* argv[]) {
std::cout << "Hello World!" << std::endl;
return 0;
}
需要编写.gyp文件
{
'targets': [
{
'target_name': 'main',
'type': 'executable',
'sources': [
'main.cpp',
],
},
],
}
运行gyp命令的式为:
gyp --depth=. --generator-output=build -f ninja main.gyp,其中
-depth指定工程的根目录,
-generator-output指定工程文件的输出目录,默认为当前目录,
-f指定生成工程文件的类型,常用的有'make', ninja, xcode, msvs, scons'。更多的命令行选项可以通过gyp -h查看。
工程实践中还有一种更方便的生成不同平台工程的方法,即使用gyp的全局变量,定义全局变量的好处是可以使用别人封装好的gyp脚本。我们使用的就是chromium封装好的gyp_chromium.py
zeromq的源码拷贝到chromium/src/third_party目录下,在zeromq的目录下创建zeromq.gyp,编辑:
{
'targets': [
{
'target_name': 'zeromq',
'type': 'static_library',
'include_dirs': [
'.',
'<(DEPTH)',
'./include',
],
'direct_dependent_settings': {
'include_dirs': [
'.',
'<(DEPTH)',
],
},
'defines': [
'_GNU_SOURCE'
'_REENTRANT'
'_THREAD_SAFE',
'ZMQ_USE_EPOLL',
],
'sources': [
'src/address.cpp',
'src/address.hpp',
'src/array.hpp',
此处略去几百个文件列表。。。
'src/ypipe.hpp',
'src/yqueue.hpp',
'src/zmq.cpp',
'src/zmq_utils.cpp',
],
},
],
}
target可分为可运行程序、静态库、动态库等,gyp提供了统一的设置方法。
defines为宏定义,对应-D/D
include_dirs对应-I/I
cflags为编译选项,对应类似-Werror/Werror
ldflags为链接选项,对应类似-pthread
这儿不详细阐述gyp的语法等,只列举实际中我们用到的一点东西。我们使用中不像chromium源码需要考虑各个平台,所以编写gyp时,仅保证linux平台可用。
3. 添加依赖
在需要用到zmq的模块对应的gyp文件中依赖项'dependencies'中添加'../third_party/zeromq/zeromq.gyp:*',
4. 可以单独执行gyp_chromium ../third_party/zeromq/zeromq.gyp,生成的build.ninja中就只有zeromq模块单独的内容
cc = ../../third_party/llvm-build/Release+Asserts/bin/clang
cxx = ../../third_party/llvm-build/Release+Asserts/bin/clang++
ld = $cc
ldxx = $cxx
ar = ar
nm = nm
readelf = readelf
pool link_pool
depth = 1
rule cc
command = $cc -MMD -MF $out.d $defines $includes $cflags $cflags_c $cflags_pch_c -c $in -o $out
description = CC $out
depfile = $out.d
deps = gcc
rule cc_s
command = $cc $defines $includes $cflags $cflags_c $cflags_pch_c -c $in -o $out
description = CC $out
rule cxx
command = $cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc $cflags_pch_cc -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
。。。此处省略n行。。。
subninja obj/third_party/zeromq/zeromq.ninja
# Short names for targets.
build zeromq: phony obj/third_party/zeromq/libzeromq.a
build all: phony obj/third_party/zeromq/libzeromq.a
default all
若调用gyp_chromium -Dcomponent=shared_library生成build.ninja文件,就包含有所有的模块。
5. 编译
可以执行ninja -C out/Debug chrome chrome_sandbox,就能生成最终的chrome可执行文件。新的库和依赖已经解决,可以添加代码并进一步调试了,下来就可以体验编辑——编译之间更快的节奏了。