Document Protobuf

最近在做一个给google protobuf文件产生html文档的任务。我们的系统是SOA架构,通讯协议是基于protobuf的,然而其他Team的开发者认为protobuf不好理解,要求一个类似docxgen产生的文档,PSE认为protobuf没有条理,最好能有一个格式化好的PDF文档以便于他审阅和签署protocol。其实protobuf已经是对通信协议的最直观的描述了,service和message的定义与文档结合一体,结构清晰。Google并没有提供给protobuf产生文档的工具,docxgen也没有迹象要支持protobuf, 显然大部分人不认为protobuf需要文档,但是世事就是这样的,客户的需求总是要满足的。幸好protobuf提供了足够的反射能力,自己造轮子的材料也是足够的。

基础结构和效果如上图所示,由两个C++写的binary和一个python script组成。搞的这么复杂主要是为了不给系统增加任何第三方库依赖。ProtoFileConverter只依赖于已有的protobuf库,htmlPrinter只需要用到Qt,而我系统定制的protobuf库和Qt只有C++部分,所以这两个组件只能用C++实现。

protobuf代码解析

ProtoFileConverter的核心是对*.proto文件进行语义分析,protobuf提供了compiler API可以很方便的解析proto文件。

1
2
3
4
5
6
7
8
google::protobuf::compiler::DiskSourceTree sourceTree;
sourceTree.MapPath("", protoRoot);

// 定义一个google::protobuf::compiler::MultiFileErrorCollector的实现类 FileErrorCollector
FileErrorCollector errorCollector;
google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector);
// import proto file到google::protobuf::FileDescriptor结构
const google::protobuf::FileDescriptor* fileDescriptor = importer.Import(protoFile);

从FileDescriptor可以得到当前proto文件中定义的所有service method message enum的Descriptor。Descriptor的定义参考官方文档。用下面的函数就可以得到proto文件各元素的注释。

1
2
3
4
5
6
7
8
9
10
11
12
template <typename DescriptorType>
static std::string GetDescriptorComment(const DescriptorType* descriptor) {
google::protobuf::SourceLocation location;
std::string comments;
if (descriptor->GetSourceLocation(&location)) {
comments = location.leading_comments;
comments += " ";
comments += location.trailing_comments;
}

return comments;
}