Chromium的Grit工具解析
转载请注明出处:http://www.cnblogs.com/fangkm/p/3405959.html
Chromium项目采用Grit工具来打包生成程序需要的资源,如图片资源、字符串资源等,尤其是字符串资源,牵涉到国际化的问题。Chromium为需要的资源创建单独的项目工程,工程类型为实用工具,自定义工程的生成事件, 在CustomBuild里调用grit命令,根据grd资源描述文件生成相关的资源。如chrome_strings工程生成国际化字符串资源、chrome_resources工程生成除字符串以外的资源,比如图片资源。
Grit工具接受grd资源描述文件,生成.h、.rc、.pak等文件。工具位于src\tools\grit文件夹下,采用python脚本编制。
src\tools\gritsettings下有个resource_ids文件,该文件定义Chromium工程中所有的grd文件所生成的资源编号开始区段,从而确保所有的资源ID不发生冲突。该文件格式描述如下:
{
"SRCDIR": "../..",
"chrome/browser/browser_resources.grd": {
"includes": [500],
"structures": [750],
},
"chrome/app/chromium_strings.grd": {
"messages": [11500],
},
}
整个文件内容就是python的dict的定义,python的eval函数能直接该文件内容以字符串的方式读取,并生成dict结构,省去繁杂的解析过程, python真是一门神奇的语言。
Grit工具使用示例:
python '..\..\..\tools\grit\grit.py' '-i' ' chromium_strings.grd' 'build' '-f' '..\..\..\tools\gritsettings\resource_ids'
'-o' '$(OutDir)obj\global_intermediate\ui\ui_strings' '-D' '_chromium' '-E' 'CHROMIUM_BUILD=chromium' '-D' 'toolkit_views' '-D' 'use_aura' '-D' 'use_ash' '-D' 'remoting' '-D' 'enable_extensions' '-D' 'enable_printing' '-D' 'enable_themes' '-D' 'enable_app_list' '-D' 'enable_settings_app' '-D' 'enable_google_now' '-D' 'use_concatenated_impulse_responses' '-D' 'enable_webrtc' '-D' 'enable_mdns'
命令行参数: -i指定grd源文件;
build指定生成工具,类似的工具还有buildinfo、count等等,
每种工具对应于grit/tool下面的相应python文件,如build对应tool下面的build.py文件,详情请参考grit_runner.py中的相关定义
build后面的命令行参数都是build.py执行的的参数
目前我见到的生成示例,无论是字符串资源还是图片等其他资源,都是用build工具生成,下面就简单走一遍build.py的执行流程。
build.py主要通过提供RcBuilder类来完成解析操作. RcBuilder类派生自interface.Tool, Tool类是各种与build同级的tools的接口类,提供有ShortDescription和Run接口。
其Run传入的命令行参数为:
-o 指定资源生成的目录
-D 指定类似于C语言的预处理宏定义,grd描述中有条件控制生成节点的逻辑,
这些定义宏可以当命令开关
-f 指定first_ids_file,即前面提到的gritsettings\resource_ids文件
-E 设置grit内部使用的环境变量
Run函数内部使用grd_reader.py的Parse方法来解析输入的grd文件,该Parse方法将xml描述的grd文件解析成树状节点结构,方法返回值为树的根节点对象GritNode。grd_reader.py的解析流程容后细说。
在得到Parse函数返回的根节点后,运行根节点的RunGatherers方法,该方法内部遍历所有子节点,调用节点的RunPostSubstitutionGatherer(如果有的话), 目前只发现FileNode节点有此方法,用于解析对应的file元素指定的xtb文件。为了不影响整个框架流程的表述,解析xtb文件的流程也容稍后再表。
在节点树数据准备完毕后,接着就需要生成资源文件了.在RcBuilder的Process方法中遍历outputs节点下的所有output项,根据type属性指定的资源类型来选择grit.format下对应的打包工具。
如: type="data_package" 选择grit.format.data_pack工具;
type="rc_header" 选择grit.format.rc_header工具;
其他格式参见tool/build.py代码中的_format_modules映射
grit.format下对应的工具会将所有的资源节点拼接成格式化的字符串并返回, Process方法将RcBuilder将返回的格式串以相应的编码格式保存到output节点指定的文件中。
资源格式化简介:以rc_header和data_pack为例
rc_header.py解析: rc_header是所有grd文件都必须生成的资源类型,该格式为grd的资源项生成相应的
.h文件,如chromium_strings.h, 里面保存资源的唯一ID值,供C++代码运行期使用.
每一项的形式为: #define name节点值 编号值.
该脚本内部会遍历root节点下的所有资源节点,为每个节点生成一个唯一的编号,
外部可以通过GetIds来访问这个生成的id映射,映射格式为node.GetTextualIds() : id
节点的GetTextualIds返回节点的标志性文本,一般为name属性指定的文本,即.h文件中
的#define宏名部分,所以资源的标识名不能冲突。
node的id生成规则大致如下:
1. 优先采用节点自定义的GetId方法
2. 采用group节点的first_id属性和本节点的offset属性做累加
3. 采用group节点的first_id属性(如果没有,则根据节点的的name
属性值做md5计算)做累加计值
data_pack.py解析: 该格式为grd资源生成.pak格式的文件。其通过遍历每个资源节点,获取到指定语言对应的
文本value(通过节点对象的GetDataPackPair函数获取) ,将节点的id和value值以一定的格式
保存到pak文件中
grd_reader生成节点流程
在介绍生成节点的流程之前,首先介绍下grd中资源节点的类型。在node. mapping.py文件中有如下映射关系:
_ELEMENT_TO_CLASS = {
'identifiers' : empty.IdentifiersNode,
'includes' : empty.IncludesNode,
'messages' : empty.MessagesNode,
'outputs' : empty.OutputsNode,
'structures' : empty.StructuresNode,
'translations' : empty.TranslationsNode,
'include' : include.IncludeNode,
'emit' : io.EmitNode,
'file' : io.FileNode,
'output' : io.OutputNode,
'ex' : message.ExNode,
'message' : message.MessageNode,
'ph' : message.PhNode,
'else' : misc.ElseNode,
'grit' : misc.GritNode,
'identifier' : misc.IdentifierNode,
'if' : misc.IfNode,
'part' : misc.PartNode,
'release' : misc.ReleaseNode,
'then' : misc.ThenNode,
'structure' : structure.StructureNode,
'skeleton' : variant.SkeletonNode,
}
左边的key是grd文件中xml节点名,右边是grit生成的python对象,外部通过ElementToClass方法来访问该映射,从而决定生成什么内存对象。节点的继承结构简略如下:这里仅仅列举等下需要讲解的节点以及节点的方法。
Node为所有节点的基类, GritNode为树的根节点,程序可以通过此入口遍历其所有的子节点。
OutputNode节点对应grd文件中的output节点,表示要生成的资源文件
FileNode节点对应grd文件中的file节点,通常做为translations子节点,指定某种语言本地化需要的xtb翻译文件
MessageNode节点表示需要打包的文本资源项,同样的资源项还有IncludeNode和StructuresNode节点,用于保存图片等其他资源类型,由于精力有限,这里就不讨论 IncludeNode和StructuresNode节点了。
grd_reader使用xml.sax库来解析grd文件,从xml.sax.handler.ContentHandler类派生出一个GrdContentHandler类来接收xml的解析回调. GrdContentHandler在startElement方法中根据读取到的节点名来生成与其对应的内存节点对象,之后调用节点的StartParsing做解析的相关定制处理,再调用节点的HandleAttribute方法读取节点的所有属性值。由于不熟悉python的使用, 我当时阅读此段代码时,很疑惑用栈的方式定位节点之间的父子关系,想了很一会才理解xml.sax.handler.ContentHandler类在解析某个节点开始时调用startElement方法,此时将自己压栈,结束时调用endElement方法,此时将自己出栈。在解析子节点时,父节点还没有解析完成,故父节点对应的endElement还未被调用,所以栈顶就是当前节点的父节点。
解析完毕后, grd_reader调用GritNode根节点的AssignFirstIds方法, 解析之前指定的gritsettings\resource_ids, 遍历所有的GroupingNode子节点(如MessagesNode、IncludesNode等节点),添加相应的first_id属性值(由当前的grd文件名和节点的name属性值在resource_ids中定位)。
字符串本地化的翻译流程:
基类Node节点有一uberclique成员,如果该成员没设定,则向上取父节点的uberclique值,直到根节点(如果没有就创建一个clique.UberClique对象)。当前只有根节点的此成员有值。
在 grd_reader.py解析grd生成节点的时候,资源节点(如message节点)会调用tclib.py为节点的文本部分生成一个tclib.Message对象,该对象为节点的文本value生成一个唯一的标识ID, 即xtb文件中的translation节点的id部分。 如果节点指定use_name_for_id属性为true,则直接用name属性值做translation的id部分, 这样就可以满足在英语中相同的单词在其他语种中需要不同的表达的场景,因为根据相同的文本生成的标识ID肯定是相同的。
文本生成标识ID的规则的代码在grit.extern.tclib.GenerateMessageId函数中,根据node的value文本和meaning属性(如果有的话)调用FP.FingerPrint做md5计算,取计算后的前16位.
根据生成的tclib.Message对象,调用UberClique的MakeClique方法生成一个MessageClique对象,并添加在UberClique内部维护的映射中。外部需要翻译message节点中对应语言的文本时,调用MessageNode的Translate方法,该方法调用MakeClique生成的MessageClique对象的MessageForLanguage方法,传入需要翻译的语言类型,返回MessageClique内部lang对应的 tclib.Translation对象。
下面探讨下MessageClique对象内部lang与tclib.Translation对象的映射是什么时候填充的。
前面提到的RcBuilder的Run方法在节点树结构生成完成后, 调用GritNode的RunGatherers方法,该方法遍历所有FileNode子节点,调用RunPostSubstitutionGatherer, 内部调用xtb_reader.Parse函数,传入根节点uberclique成员的GenerateXtbParserCallback函数生成的回调来保存解析到的id:value,下面看看UberClique类的GenerateXtbParserCallback具体实现:
GenerateXtbParserCallback函数传入当前file节点的语言类型,返回一个回调函数,函数形式为Callback(id, structure)
第一个参数id为translation的id部分,
第二个参数structure参数为translation值部分,单词序列形式,
内部根据id和structure数据生成一个tclib.Translation对象,并保存到UberClique类的cliques_成员里。
cliques_成员是一个字典对象,存放文本串的id 到 MessageClique对象序列的映射
MessageClique对象内部存放{ lang : tclib.Translation }的映射
Chromium的Grit工具解析的更多相关文章
- Ninja - chromium核心构建工具
转自:http://guiquanz.me/2014/07/28/a_intro_to_Ninja/ Ninja - chromium核心构建工具Jul 28, 2014 [在线编辑] 缘由 经过上次 ...
- 命令行工具解析Crash文件,dSYM文件进行符号化
备份 文/爱掏蜂窝的熊(简书作者)原文链接:http://www.jianshu.com/p/0b6f5148dab8著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 序 在日常开发 ...
- 最终版-perl工具解析数据库的报告文件0120
********************需要根据自己的实际环境修改哦**************************** ******************** 1. 收集awr报告样本 a ...
- js之第三方工具解析JSON
1.JSON 仅仅是一种文本字符串.它被存储在 responseText 属性中 为了读取存储在 responseText 属性中的 JSON 数据,须要依据 JavaScript 的 eval 函数 ...
- 5分钟让你学会用最高效的工具解析所有Json
如果你是一个Android开发工程师,学会解析Json字符串是你的必修课,本篇文章主要以实例的方式手把手教你怎么做,花五分钟时间阅读本篇文章你就可以学会解析所有的Json字符串啦. 准备: json字 ...
- chromium的部署工具depot_tools和gclient
depot_tools是个工具包,里面包含gclient.gcl.gn和ninja等工具.其中gclient是代码获取工具,它其实是利用了svn和git.主要涉及的depot_tools文件夹下的文件 ...
- xml之DOM方式解析,DOM4J工具解析原理
DOM解析原理: DOM解析原理:xml解析器一次性把整个xml文档加载进内存,然后在内存中构建一颗Document的对象树,通过Document对象,得到树上的节点对象,通过节点对象访问(操作)到x ...
- Wireshark抓包工具解析HTTPS包
目录 一.遇到的问题 二.解决方案 1. 动态生成签名证书 2. Wireshark配置 3. 最终效果 一.遇到的问题 本学期的计算机网络课程需要使用到Wireshark抓包工具进行网络抓包实验,原 ...
- 通过 bsondump 命令工具 解析备份产生的bson文件
bsondump命令是将BSON格式的文件转换为可读性更强的文件格式,例如转为为JSON 格式的文档,bsondump默认转换为json格式的文档. 当通过mongodump命令进行备份时,如果有参数 ...
随机推荐
- vue.js 常用组件库
vux github ui demo:https://github.com/airyland/vux Mint UI 项目主页:http://mint-ui.github.io/#!/zh-cndem ...
- 读配置文件property文件
import java.io.IOException;import java.util.Properties; import org.springframework.core.io.support.P ...
- 利用koa打造jsonp API
概述 最近学习利用koa搭建API接口,小有所得,现在记录下来,供以后开发时参考,相信对其他人也有用. 就目前我所知道的而言,API有2种,一种是jsonp这种API,前端通过ajax来进行跨域请求获 ...
- [Swift]优先队列PriorityQueue(自定义数据结构)
优先队列[priority queue] 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除. 优先队列特点:在优先队列中,元素被赋予优先级. 当访问元素时,具有最高优先级的元素最先 ...
- 版本控制工具git
公司要求用git,感觉不如svn好使,还是命令行的,暂时记录一下. 服务器是在linux上可以直接安装.我是虚拟机centos6.9版本.yum install -y git 查看版本号是git -- ...
- python粘包分析与解决
TCP与UDP协议 TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket,因此 ...
- 【sping揭秘】13、Spring AOP一世&二世
Spring AOP一世 Spring AOP中的advice Before advice 这个就是在方法执行之前执行,也就是在对应的joinpoint之前 spring AOP二世 开启aspect ...
- numpy中int类型与python中的int
[code] import numpy as np nparr = np.array([[1 ,2, 3, 4]]) np_int32 = nparr[0][0] # np_int=1 py_int ...
- discuz 数据库文件密码修改
网站系统需要修改的位置有两处 Discuz 和 UC-center ①路径:/wwwroot/config/config_global.php 这个根据你网站安装的路径而定. 打开 config_gl ...
- 开发工具 -- Eclipse快捷键
[ALT+/]此快捷键为用户编辑的好帮手,能为用户提供内容的辅助,不要为记不全方法和属性名称犯愁,当记不全类.方法和属性的名字时,多体验一下[ALT+/]快捷键带来的好处吧. [Ctrl+O]显示 ...