| OLD | NEW |
| (Empty) | |
| 1 #!/usr/bin/env python |
| 2 |
| 3 # Hack required to give precedence to the Chromium's protobuf library instead |
| 4 # of the debian's package. |
| 5 import os |
| 6 _CHROME_SRC = os.path.abspath(os.path.join(os.path.abspath(__file__), os.pardir, |
| 7 os.pardir, os.pardir, os.pardir)) |
| 8 _PROTOBUF_DIR = os.path.join(_CHROME_SRC, 'third_party', 'protobuf', 'python') |
| 9 import pkg_resources |
| 10 pkg_resources.declare_namespace('google') |
| 11 pkg_resources.fixup_namespace_packages(_PROTOBUF_DIR) |
| 12 import google |
| 13 assert _PROTOBUF_DIR in google.__path__[0] |
| 14 import google.protobuf |
| 15 assert _PROTOBUF_DIR in google.protobuf.__path__[0] |
| 16 import google.protobuf.descriptor |
| 17 from google.protobuf.descriptor import FieldDescriptor |
| 18 |
| 19 import argparse |
| 20 import shutil |
| 21 import subprocess |
| 22 import sys |
| 23 |
| 24 |
| 25 class CppStubGenerator(object): |
| 26 def __init__(self, proto_file_name): |
| 27 self.proto_file_name = proto_file_name |
| 28 self.processed_classes = set() |
| 29 self.includes = set() |
| 30 self.namespace = None |
| 31 self.cpp_namespace = None |
| 32 |
| 33 |
| 34 def GetCppClassName(self, full_name): |
| 35 assert(full_name.startswith(self.namespace)) |
| 36 return full_name[len(self.namespace) + 1:].replace('.', '_') |
| 37 |
| 38 |
| 39 def GenerateTranslationUnit(self): |
| 40 db = google.protobuf.symbol_database.Default() |
| 41 descriptors = [x.DESCRIPTOR for x in |
| 42 db.GetMessages([self.proto_file_name]).itervalues()] |
| 43 namespace_parts = descriptors[0].file.package.split('.') |
| 44 self.namespace = '.'.join(namespace_parts) |
| 45 self.cpp_namespace = '::' + '::'.join(namespace_parts) |
| 46 ifdef_guard = '%s_%s_H' % (self.namespace, self.proto_file_name) |
| 47 ifdef_guard = ifdef_guard.upper().replace('.', '_') |
| 48 |
| 49 descriptors_code = '' |
| 50 for descriptor in descriptors: |
| 51 descriptors_code += self.GenerateClassRecursive(descriptor) |
| 52 |
| 53 code = '// Autogenerated. DO NOT EDIT.\n' |
| 54 code += '#ifndef %s\n' % ifdef_guard |
| 55 code += '#define %s\n\n' % ifdef_guard |
| 56 |
| 57 code += '#include "base/trace_event/v2/append_only_proto_message.h"\n' |
| 58 |
| 59 for inc in self.includes: |
| 60 code += '#include "%s"\n' % inc |
| 61 code += '\n' |
| 62 |
| 63 for ns in namespace_parts: |
| 64 code += 'namespace %s {\n' % ns |
| 65 |
| 66 code += '\n' |
| 67 |
| 68 for full_name in self.processed_classes: |
| 69 cpp_class_name = self.GetCppClassName(full_name) |
| 70 if not cpp_class_name: |
| 71 continue |
| 72 code += 'class %s;\n' % cpp_class_name |
| 73 code += descriptors_code |
| 74 |
| 75 for ns in namespace_parts: |
| 76 code += '} // namespace %s\n' % ns |
| 77 code += '#endif // %s\n' % ifdef_guard |
| 78 code += '\n' |
| 79 return code |
| 80 |
| 81 |
| 82 def GenerateClassRecursive(self, descriptor): |
| 83 assert(isinstance(descriptor, google.protobuf.descriptor.Descriptor)) |
| 84 if descriptor.full_name in self.processed_classes: |
| 85 return '' |
| 86 self.processed_classes.add(descriptor.full_name) |
| 87 code = '' |
| 88 for nested_descriptor in descriptor.nested_types: |
| 89 code += self.GenerateClassRecursive(nested_descriptor) |
| 90 code += self.GenerateClass(descriptor) |
| 91 return code |
| 92 |
| 93 |
| 94 def GenerateClass(self, descriptor): |
| 95 assert(isinstance(descriptor, google.protobuf.descriptor.Descriptor)) |
| 96 assert(descriptor.file.name == self.proto_file_name) |
| 97 assert(descriptor.full_name.startswith(self.namespace)) |
| 98 _BASE_CLASS = '::base::trace_event::v2::AppendOnlyProtoMessage' |
| 99 _SIMPLE_TYPES = { |
| 100 FieldDescriptor.TYPE_INT32: ('int32_t', 'AppendVarIntSigned'), |
| 101 FieldDescriptor.TYPE_INT64: ('int64_t', 'AppendVarIntSigned'), |
| 102 FieldDescriptor.TYPE_UINT64: ('uint64_t', 'AppendVarIntUnsigned'), |
| 103 FieldDescriptor.TYPE_UINT32: ('uint32_t', 'AppendVarIntUnsigned'), |
| 104 FieldDescriptor.TYPE_BOOL: ('bool', 'AppendVarIntUnsigned'), |
| 105 FieldDescriptor.TYPE_FLOAT: ('float', 'AppendFloat'), |
| 106 FieldDescriptor.TYPE_DOUBLE: ('double', 'AppendDouble'), |
| 107 FieldDescriptor.TYPE_STRING: ('const char*', 'AppendString'), |
| 108 } |
| 109 |
| 110 class_name = self.GetCppClassName(descriptor.full_name) |
| 111 code = '\n' |
| 112 |
| 113 for enum_descriptor in descriptor.enum_types: |
| 114 code += self.GenerateEnum(class_name, enum_descriptor) |
| 115 |
| 116 # Generate enum types. |
| 117 code += 'class %s : public %s {\n' % (class_name, _BASE_CLASS) |
| 118 code += ' public:\n' |
| 119 for nested_type in descriptor.nested_types: |
| 120 code += ' using %s = %s;\n' % ( |
| 121 nested_type.name, self.GetCppClassName(nested_type.full_name)) |
| 122 for enum_descriptor in descriptor.enum_types: |
| 123 code += ' using %(enum_name)s = %(class_name)s_%(enum_name)s;\n' % { |
| 124 'enum_name': enum_descriptor.name, 'class_name': class_name} |
| 125 |
| 126 # Generate C++ class. |
| 127 code += '\n' |
| 128 code += ' explicit %(class_name)s() : %(base_class)s() {}\n' % { |
| 129 'class_name': class_name, |
| 130 'base_class': _BASE_CLASS } |
| 131 code += ' ~%s() override {}\n' % class_name |
| 132 |
| 133 for field in descriptor.fields: |
| 134 assert not field.has_options or not field.GetOptions().packed, 'Packed fie
lds are not supported' |
| 135 |
| 136 camel_name = field.camelcase_name[:1].upper() + field.camelcase_name[1:] |
| 137 code += ' static const int k%sFieldNumber = %d;\n' % (camel_name, field.n
umber) |
| 138 |
| 139 if field.type in _SIMPLE_TYPES: |
| 140 cpp_type, marshal_fn = _SIMPLE_TYPES[field.type] |
| 141 code += ' void set_%s(%s value) { %s(%d, value); }\n' % ( |
| 142 field.name, cpp_type, marshal_fn, field.number) |
| 143 |
| 144 elif field.type == FieldDescriptor.TYPE_BYTES: |
| 145 code += ' void set_%s(const void* value, size_t size) { AppendBytes(%d,
value, size); }\n' % ( |
| 146 field.name, field.number) |
| 147 |
| 148 elif field.type == FieldDescriptor.TYPE_ENUM: |
| 149 cpp_type = '%s::%s' % (self.cpp_namespace, self.GetCppClassName(field.en
um_type.full_name)) |
| 150 code += ' void set_%s(%s value) { AppendVarIntSigned(%d, static_cast<in
t32_t>(value)); }\n' % ( |
| 151 field.name, cpp_type, field.number) |
| 152 |
| 153 elif field.type == FieldDescriptor.TYPE_MESSAGE: |
| 154 if field.message_type.file.name != descriptor.file.name: |
| 155 self.includes.add(ProtoToHppFileName(field.message_type.file.name)) |
| 156 |
| 157 props = { |
| 158 'base_class': _BASE_CLASS, |
| 159 'full_class_name': '%s::%s' % ( |
| 160 self.cpp_namespace, |
| 161 self.GetCppClassName(field.message_type.full_name)), |
| 162 'field_name': field.name, |
| 163 'field_number': field.number, |
| 164 } |
| 165 code += ''' |
| 166 %(full_class_name)s* add_%(field_name)s() { |
| 167 return BeginNestedMessage<%(full_class_name)s>(%(field_number)d); |
| 168 // %(full_class_name)s* inst = new %(full_class_name)s(); |
| 169 // inst->set_buffer_writer(buffer_writer()); |
| 170 // BeginNestedMessage(%(field_number)d, ::std::unique_ptr<%(base_class)s>(in
st)); |
| 171 // return inst; |
| 172 }\n''' % props |
| 173 # if field.label != FieldDescriptor.LABEL_REPEATED: |
| 174 # code += ''' |
| 175 # void set_allocated_%(field_name)s(%(full_class_name)s* value) { |
| 176 # BeginNestedMessage(%(field_number)d, ::std::unique_ptr<%(base_class)s>(val
ue)); |
| 177 # }\n''' % props |
| 178 else: |
| 179 assert False, 'Field "%s" not supported' % field.name |
| 180 |
| 181 code += '};\n\n' |
| 182 return code |
| 183 |
| 184 |
| 185 def GenerateEnum(self, class_name, enum_descriptor): |
| 186 assert(isinstance(enum_descriptor, google.protobuf.descriptor.EnumDescriptor
)) |
| 187 code = 'enum %s_%s {\n' % (class_name, enum_descriptor.name) |
| 188 for enum_value in enum_descriptor.values: |
| 189 code += ' %s_%s_%s = %d,\n' % (class_name, enum_descriptor.name, |
| 190 enum_value.name, enum_value.number) |
| 191 code += '};\n\n' |
| 192 return code |
| 193 |
| 194 def ProtoToHppFileName(proto_file_name): |
| 195 return proto_file_name.replace('.proto', '.tracing-pb.h') |
| 196 |
| 197 def main(): |
| 198 parser = argparse.ArgumentParser() |
| 199 parser.add_argument('--protoc', help='Path to the standard protoc compiler.') |
| 200 parser.add_argument('--proto-out-dir', help='Path for generated stubs.') |
| 201 parser.add_argument('--proto-in-dir', help='Path for source protos.') |
| 202 parser.add_argument('input_protos', nargs='+') |
| 203 |
| 204 args = parser.parse_args() |
| 205 protoc = os.path.abspath(args.protoc) |
| 206 assert(os.path.exists(protoc)) |
| 207 out_dir = os.path.abspath(args.proto_out_dir) |
| 208 if not os.path.isdir(out_dir): |
| 209 os.makedirs(out_dir) |
| 210 |
| 211 print 'Stage 1/2: .proto -> python (using official protoc)' |
| 212 for input_proto_path in args.input_protos: |
| 213 print ' ' + input_proto_path |
| 214 cmd = [protoc, '--proto_path=' + args.proto_in_dir, |
| 215 input_proto_path, |
| 216 '--python_out=' + out_dir, |
| 217 '--cpp_out=' + out_dir, |
| 218 '--js_out=' + out_dir] |
| 219 subprocess.check_call(cmd) |
| 220 |
| 221 print '\nStage 2/2: python -> Fast C++ stubs' |
| 222 sys.path.append(out_dir) |
| 223 for input_proto_path in args.input_protos: |
| 224 fname = os.path.basename(input_proto_path) |
| 225 module_name = fname[0:-6] + '_pb2' |
| 226 assert os.path.exists(os.path.join(out_dir, module_name + '.py')) |
| 227 out_cpp_file_path = os.path.join(out_dir, ProtoToHppFileName(fname)) |
| 228 print ' %-32s -> %s' % (fname, out_cpp_file_path) |
| 229 __import__(module_name) |
| 230 with open(out_cpp_file_path, 'w') as fout: |
| 231 gen = CppStubGenerator(fname) |
| 232 fout.write(gen.GenerateTranslationUnit()) |
| 233 |
| 234 if __name__ == '__main__': |
| 235 main() |
| OLD | NEW |