GYP and Chrome

一、简介

GYP是google的一套构建系统,和 cmake 的目的很像。GYP和CMake的主要作用是,从用户编写的一套配置文件,针对不同的工具链生成不同的项目文件(如Makefile/vc projects/xcode projects)。

GYP安装:

svn co http://gyp.googlecode.com/svn/trunk gyp cd gyp ./setup.py build ./setup.py install 

下面是GYP的配置文件的示例:

{ 'targets': [ { 'target_name': 'hello', 'type': 'executable', 'sources': [ 'main.cpp' ] } ] } 

看上去基本就是一个json。它定义了一个名为hello的target。target的类型是executable,表明它是一个可执行文件。如果要编译库,就换成static_library或者shared_library。sources是一个数组,列出所有的源文件。

然后用

# gyp hello.gyp --depth=. -f make

生成makefile。

然后执行make命令即可。

从设计目标上来说,它和autotools还不大一样。autotools只针对make,且仅限于gnu make。autotools的核心是autoconf,如何利用shell脚本在不同的操作系统环境下生成相应的config.h文件。它利用它强大的检测功能很容易适应不同的Unix/Linux环境。

而GYP和CMake都支持各种主流的构建系统。如make/Visual studio/XCode/Ninja。CMake支持构建系统的种类要更多些,比如eclipse cdt、Sublime Text 2、CodeBlocks、Borland C++等等。而这些非主流的东西GYP压根就不会去碰,不敢碰。

按最理想的情况,我们写一套配置规则,在所有平台上都能执行。此时我们可以不关心操作系统是什么。拿autotools来说,假如你要include某个头文件,那么就在autoconf执行的时候检查下有没有这个头文件,然后在真正使用的时候,利用ifdef/else/endif来条件编译,假如有,我们就用它,没有就砍掉某个功能,或者使用替代实现。由于unix种类甚多,差异甚大,按照这样方式写的程序,即便被扔到一个作者从不知晓的陌生环境里,(也许)也能正常运行。

cmake和autotools都是这样的理想主义者,它们试图把不同的工具链的相同部分抽象出来,用一套统一的配置文件来适应不同的平台。迫不得已的时候你可以写 if(OS==WIN) ... else if (OS== Linux) ... 。 于是就这样工作了很多年,很好。而GYP觉得我们不该做这样的过分抽象,构建系统自身应该简洁,把适应不同平台的事情交给程序员自己去做。比如,不同的工具链参数不同,gcc编译多线程程序的时候要加-pthread或-pthreads或-lpthread,而vc则要在4种不同的CRT做出选择。那么,你自己去把不同平台的编译参数挨个标明,GYP不管这事。所以,GYP的项目不可能盲目的去支持Sublime Text 2、eclipse cdt这样的小众玩意儿。为了支持它们,condition会急剧膨胀。

GYP是为Chrome项目开发的,Chrome也是GYP的唯一成功案例。就比如前面编译hello world的时候,加上"--depth=. ",这完全是Chrome的特殊遗留。Chrome是一个有600多万行代码的大型C++项目,它的成功值得借鉴。

虽然GYP和CMake相比还很不成熟,而且很不独立(它几乎是专为Chrome项目服务),但是Chrome本身其实已经给我们贡献了足够多的代码。虽然GYP不像CMake那样自带了很多Module(丰富的FindXXX),但是我们完全可以去Chrome的项目中把那些GYP文件挖出来。

另外,如果你想复用Chrome的代码,那么就得迁就GYP。比如apache的mod_spdy模块,它的SPDY协议的实现就是从Chrome中直接拿去的。为了引用Chrome的代码,mod_spdy就不得不采用GYP做构建。

二、.gyp 文件的格式说明。

.gyp文件基本上就是一个json文件,和标准的json相比,它有两点不同:

  1. 可以用#注释
  2. list或dictionary的最后一个元素后面可以多一个逗号 (便于用程序自动生成这样的文件)

在它最顶层的大括号内,有5种对象:variables、includes、target_defaults、targets、conditions。

  • 'variables': Definitions of variables that can be interpolated and used in various other parts of the file.
  • 'includes': A list of of other files that will be included in this file. By convention, included files have the suffix .gypi (gyp include).
  • 'target_defaults': Settings that will apply to all of the targets defined in this .gyp file.
  • 'targets': The list of targets for which this .gyp file can generate builds. Each target is a dictionary that contains settings describing all the information necessary to build the target.
  • 'conditions': A list of condition specifications that can modify the contents of the items in the global dictionary defined by this .gyp file based on the values of different variablwes. As implied by the above example, the most common use of a conditions section in the top-level dictionary is to add platform-specific targets to the targets list.

Chrome在跨平台问题上采用了一个很有趣的事情,把第三方库的源代码copy到现有项目中,并且静态链接进来。相当于,Chrome为它所有用到的第三方库都做了SVN一个分支,在需要的时候与上游同步。然后它为所有的第三方库都生成了一个GYP项目文件,然后在Chrome中引用这些第三方库的项目文件,完成构建。比如,不管你操作系统有没有装libevent版本是多少,我都用我自己带的这个,然后最终链接成一个无比巨大的exe或ELF文件。

我非常赞同Chrome的这种做法。有些第三方库的接口变动非常快,比如glib,单单为了在linux这一种操作系统下适应不同的glib版本,就得在代码中写大量的条件编译宏。查core dump的时候也困难很多,单是找源代码都能找死一批人(别忘了发行版喜欢 编译前打自己的patch)。另一点是,构建系统本身也得以简化,我不需要去把autoconf/automake/cmake/scons等等全装一遍。最可恨的是,autotools自身还有很多版本,而且各不兼容。要把不同版本的autotools全装上,并且用起来互不干扰,也需要很大技巧。

下面以glib为例演示下如何在一个项目中包含另一个项目。

首先从svn中checkout出zlib的代码

svn co http://src.chromium.org/svn/trunk/src/third\_party/zlib

然后把下面代码保存为test.cpp

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "zlib.h"

#define CHUNK 16384

/* Compress from file source to file dest until EOF on source.
   def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
   allocated for processing, Z_STREAM_ERROR if an invalid compression
   level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
   version of the library linked do not match, or Z_ERRNO if there is
   an error reading or writing the files. */

int main(int argc, char** argv) {
  FILE* source = stdin;
  FILE* dest = stdout;
  int level = Z_DEFAULT_COMPRESSION;

  int flush;
  unsigned have;
  z_stream strm;
  unsigned char in[CHUNK];
  unsigned char out[CHUNK];

  /* allocate deflate state */
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;
  int ret = deflateInit(&strm, level);
  if (ret != Z_OK) return ret;

  /* compress until end of file */
  do {
    strm.avail_in = fread(in, 1, CHUNK, source);
    if (ferror(source)) {
      deflateEnd(&strm);
      return -1;
    }
    flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
    strm.next_in = in;

    /* run deflate() on input until output buffer not full, finish
       compression if all of source has been read in */
    do {
      strm.avail_out = CHUNK;
      strm.next_out = out;
      ret = deflate(&strm, flush);   /* no bad return value */
      assert(ret != Z_STREAM_ERROR); /* state not clobbered */
      have = CHUNK - strm.avail_out;
      if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
        (void) deflateEnd(&strm);
        return Z_ERRNO;
      }
    } while (strm.avail_out == 0);

    /* done when last data in file processed */
  } while (flush != Z_FINISH);

  /* clean up and return */
  deflateEnd(&strm);
  return 0;
}

然后把前面的hello.gyp稍作修改

{
  'targets': [
    { 
      'target_name': 'hello',
      'type': 'executable',     
      'sources': [
        'test.cpp'
      ],
      'dependencies': [
        'zlib/zlib.gyp:zlib'
      ]    
    }
  ]
}

就是在target中加入了一个"dependencies"节。它引用了zlib/zlib.gyp这个文件中的zlib这个target。

然后生成makefile

$ gyp hello.gyp --depth=. -D OS=linux -D os_bsd=0 -D clang=0 -f make

加入了一些新的define,是因为zlib/zlib.gyp中用到了这些variable。

然后make

$ make

用ldd看一下生成的结果文件会发现,

# ldd out/Default/hello linux-vdso.so.1 => (0x00007fff87c1c000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x0000003b0d000000) libm.so.6 => /lib64/libm.so.6 (0x0000003b07000000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003b08400000) libc.so.6 => /lib64/libc.so.6 (0x0000003b06400000) /lib64/ld-linux-x86-64.so.2 (0x0000003b06000000) 

它不依赖于zlib的so/dll。zlib已经被静态链接进去了。

这样当需要在服务器上部署的时候就很容易了。不用再总是去编译、安装第三方的库。

时间: 2024-08-18 03:09:55

GYP and Chrome的相关文章

Chrome Visual Studio 2005下的编译过程_C#教程

编译篇 研究Chrome ,首先得把它编译出来,这对于后续的代码分析和阅读有很大的帮助,想想自己编译出一个 Chrome 浏览器来使用,那是一件很炫的事情. 编译环境准备 Chrome的编译和 WebKit 相比起来,难度上来说,简直是一元二次方程求解和偏微分方程求解的对比(我到现在还没有完整的把 WebKit 编译出来,鄙视一下自己).虽然 Chrome 也是从 WebKit 演进过来,差不多也就是把 WebKit 的 JS 引擎替换成了 V8 .但是不得不承认 Google 把 WebKit

降低Chrome谷歌浏览器内存占用的方法

  由于 Chrome 采用了标签页单进程设计,所以当我们使用 Chrome 同时打开多个标签页浏览网页时,系统内存占用也将大大增加.这就是人们经常抱怨的Chrome内存占用太多的问题.幸亏 Chrome 良好的扩展性产生了不少专门用于减少Chrome内存占用的扩展,在此为大家推荐自认为不错的两个: 1.Tab Hibernation 无论我们打开多少标签页,正在阅读观看的(即活动的)页面也始终只有一个,而其它的即为非活动标签.Tab Hibernation 很显然也认同这一点,当我们安装好Ta

六款实用谷歌Chrome浏览器插件推荐

  1. Infinity:美观实用的新建标签页 安装链接:http://t.cn/RcUOryJ 厌倦了又丑又空的 Chrome 起始页面?Infinity 可以为你量身打造一个美观实用的新建标签页. 你可以在 Chrome 的起始页面添加 Gmail.eBay.支付宝.微信图标实现快速跳转.除此之外,Infinity 还自带笔记和待办项清单,能够让你一打开 Chrome 就可以看到还有多少待办事项,十分方便. 2. Adblock:屏蔽不喜欢的一切 安装链接:http://t.cn/zl9R

支持用 CoffeeScript 调试的 Chrome 扩展

CoffeeScript 越来越流行了,coffee控门想尽一切手段鄙视js的臃肿.这个扩展就可以在console里也能使用coffee script 不会coffee,只能简单的抄这么一句测试下.. 已经实现的功能: - 使用 CoffeeScript 输入 - 格式化输出 - 自动补全 - 命令历史 安装地址:https://chrome.google.com/webstore/detail/amcoffee/eohnbihebhopoapemdbbodaiaagpogik via 谷奥

Chrome浏览器搜索超方便 拖拖文字就搞定

搜索是我们最常用的互联网应用,平时我们搜索互联网信息一般都怎么操作呢?先打开搜索引擎首页,然后在输入框中输入我们需要查询的关键字,或者将拷贝在剪贴板上的文字粘贴到搜索输入框中,然后按下输入框右边的搜索按钮-- 如果你用的是Chrome浏览器,完全不用这么麻烦,有好多种简便的操作方法可以帮助我们快速搜索.下面我们由繁入简来一一介绍,最精彩的最后现身哟! 地址栏兼职搜索栏 在前面的文章中我们介绍过,Chrome浏览器的地址栏是多功能的,搜索就是其中一项重要兼职功能:只需把关键词输入或者拷贝到地址栏里

chrome爬页面上表格某列的数据

有的时候会有这样一个需求,页面上有个大表格,我需要复制里面的一列到本地.比如,我要从表格里面,复制列出来的所有机器IP(这样比数据库导出方便点~) 首先,先用chrome的开发者工具,找到要复制的列中的某一个单元格,然后选择复制xpath.这样会复制下来这个元素的xpath路径,比如: //*[@id="machineGroupTable"]/tbody/tr[2]/td[2] chrome的console,支持用$x()函数直接用xpath来定位元素,因此,可以通过类似这样的js,来

全球第一大浏览器Chrome发展史

自2008年发布以来,Google旗下的Chrome浏览器给网络技术行业带来了一股创新浪潮.在过去四年中,浏览器领域内涌现出了一系列新技术,HTML5.CSS3等等.除了速度一流之外,Chrome对这些新技术是支持的最积极的一个,并且加速了整个行业的浏览器更新速度,这也是为什么Chrome能够如此快速成长的一个重要原因.四年之后的今天,Chrome已经成为了全球第一大浏览器,用户为整个产业的33.8%,那么这期间经历了怎样的发展呢?下面就通过时间轴一起来了解下Chrome的发展史以及这四年来整个

chrome插件远程加载js

chrome插件将js直接注入页面有两种方式,一种是通过Manifest文件中指定js文件,一种是通过chrome.tab.executeScript方式注入.具体可以参考这个官方文档. 由于各种需求,需要将部分js从后端服务器中进行加载.所以只能通过tab.executeScript的方式.具体函数的定义,可以参考官方文档.大致就是能够给定一个文件或者一段代码,在指定的tab中运行. 首先,这个函数必须在background中执行,页面中的content-script本身,是没有权限调用chr

在电脑中如何添加谷歌Chrome浏览器信任站点

  1.双击打开Chrome浏览器,点击右上角三条横线图标,在弹出的菜单中选择"设置"选项; 2.点击最底部的"显示高级设置"; 3.点击隐私设置下的"内容设置"按钮; 4.在内容设置中分别点击Cookie和JavaScript下的"管理例外情况"按钮进行设置; 5.在弹出的设置窗口中输入添加的网站,在右侧"行为"中选择"允许",然后点击"完成"; 6.返回内容设置,