nanopb_generator.py 94 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360
  1. #!/usr/bin/env python3
  2. # kate: replace-tabs on; indent-width 4;
  3. from __future__ import unicode_literals
  4. '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
  5. nanopb_version = "nanopb-0.4.6-dev"
  6. import sys
  7. import re
  8. import codecs
  9. import copy
  10. import itertools
  11. import tempfile
  12. import shutil
  13. import os
  14. from functools import reduce
  15. try:
  16. # Add some dummy imports to keep packaging tools happy.
  17. import google # bbfreeze seems to need these
  18. import pkg_resources # pyinstaller / protobuf 2.5 seem to need these
  19. import proto.nanopb_pb2 as nanopb_pb2 # pyinstaller seems to need this
  20. import pkg_resources.py2_warn
  21. except:
  22. # Don't care, we will error out later if it is actually important.
  23. pass
  24. try:
  25. # Make sure grpc_tools gets included in binary package if it is available
  26. import grpc_tools.protoc
  27. except:
  28. pass
  29. try:
  30. import google.protobuf.text_format as text_format
  31. import google.protobuf.descriptor_pb2 as descriptor
  32. import google.protobuf.compiler.plugin_pb2 as plugin_pb2
  33. import google.protobuf.reflection as reflection
  34. import google.protobuf.descriptor
  35. except:
  36. sys.stderr.write('''
  37. *************************************************************
  38. *** Could not import the Google protobuf Python libraries ***
  39. *** Try installing package 'python3-protobuf' or similar. ***
  40. *************************************************************
  41. ''' + '\n')
  42. raise
  43. try:
  44. from .proto import nanopb_pb2
  45. from .proto._utils import invoke_protoc
  46. except TypeError:
  47. sys.stderr.write('''
  48. ****************************************************************************
  49. *** Got TypeError when importing the protocol definitions for generator. ***
  50. *** This usually means that the protoc in your path doesn't match the ***
  51. *** Python protobuf library version. ***
  52. *** ***
  53. *** Please check the output of the following commands: ***
  54. *** which protoc ***
  55. *** protoc --version ***
  56. *** python3 -c 'import google.protobuf; print(google.protobuf.__file__)' ***
  57. *** If you are not able to find the python protobuf version using the ***
  58. *** above command, use this command. ***
  59. *** pip freeze | grep -i protobuf ***
  60. ****************************************************************************
  61. ''' + '\n')
  62. raise
  63. except (ValueError, SystemError, ImportError):
  64. # Probably invoked directly instead of via installed scripts.
  65. import proto.nanopb_pb2 as nanopb_pb2
  66. from proto._utils import invoke_protoc
  67. except:
  68. sys.stderr.write('''
  69. ********************************************************************
  70. *** Failed to import the protocol definitions for generator. ***
  71. *** You have to run 'make' in the nanopb/generator/proto folder. ***
  72. ********************************************************************
  73. ''' + '\n')
  74. raise
  75. try:
  76. from tempfile import TemporaryDirectory
  77. except ImportError:
  78. class TemporaryDirectory:
  79. '''TemporaryDirectory fallback for Python 2'''
  80. def __enter__(self):
  81. self.dir = tempfile.mkdtemp()
  82. return self.dir
  83. def __exit__(self, *args):
  84. shutil.rmtree(self.dir)
  85. # ---------------------------------------------------------------------------
  86. # Generation of single fields
  87. # ---------------------------------------------------------------------------
  88. import time
  89. import os.path
  90. # Values are tuple (c type, pb type, encoded size, data_size)
  91. FieldD = descriptor.FieldDescriptorProto
  92. datatypes = {
  93. FieldD.TYPE_BOOL: ('bool', 'BOOL', 1, 4),
  94. FieldD.TYPE_DOUBLE: ('double', 'DOUBLE', 8, 8),
  95. FieldD.TYPE_FIXED32: ('uint32_t', 'FIXED32', 4, 4),
  96. FieldD.TYPE_FIXED64: ('uint64_t', 'FIXED64', 8, 8),
  97. FieldD.TYPE_FLOAT: ('float', 'FLOAT', 4, 4),
  98. FieldD.TYPE_INT32: ('int32_t', 'INT32', 10, 4),
  99. FieldD.TYPE_INT64: ('int64_t', 'INT64', 10, 8),
  100. FieldD.TYPE_SFIXED32: ('int32_t', 'SFIXED32', 4, 4),
  101. FieldD.TYPE_SFIXED64: ('int64_t', 'SFIXED64', 8, 8),
  102. FieldD.TYPE_SINT32: ('int32_t', 'SINT32', 5, 4),
  103. FieldD.TYPE_SINT64: ('int64_t', 'SINT64', 10, 8),
  104. FieldD.TYPE_UINT32: ('uint32_t', 'UINT32', 5, 4),
  105. FieldD.TYPE_UINT64: ('uint64_t', 'UINT64', 10, 8),
  106. # Integer size override options
  107. (FieldD.TYPE_INT32, nanopb_pb2.IS_8): ('int8_t', 'INT32', 10, 1),
  108. (FieldD.TYPE_INT32, nanopb_pb2.IS_16): ('int16_t', 'INT32', 10, 2),
  109. (FieldD.TYPE_INT32, nanopb_pb2.IS_32): ('int32_t', 'INT32', 10, 4),
  110. (FieldD.TYPE_INT32, nanopb_pb2.IS_64): ('int64_t', 'INT32', 10, 8),
  111. (FieldD.TYPE_SINT32, nanopb_pb2.IS_8): ('int8_t', 'SINT32', 2, 1),
  112. (FieldD.TYPE_SINT32, nanopb_pb2.IS_16): ('int16_t', 'SINT32', 3, 2),
  113. (FieldD.TYPE_SINT32, nanopb_pb2.IS_32): ('int32_t', 'SINT32', 5, 4),
  114. (FieldD.TYPE_SINT32, nanopb_pb2.IS_64): ('int64_t', 'SINT32', 10, 8),
  115. (FieldD.TYPE_UINT32, nanopb_pb2.IS_8): ('uint8_t', 'UINT32', 2, 1),
  116. (FieldD.TYPE_UINT32, nanopb_pb2.IS_16): ('uint16_t','UINT32', 3, 2),
  117. (FieldD.TYPE_UINT32, nanopb_pb2.IS_32): ('uint32_t','UINT32', 5, 4),
  118. (FieldD.TYPE_UINT32, nanopb_pb2.IS_64): ('uint64_t','UINT32', 10, 8),
  119. (FieldD.TYPE_INT64, nanopb_pb2.IS_8): ('int8_t', 'INT64', 10, 1),
  120. (FieldD.TYPE_INT64, nanopb_pb2.IS_16): ('int16_t', 'INT64', 10, 2),
  121. (FieldD.TYPE_INT64, nanopb_pb2.IS_32): ('int32_t', 'INT64', 10, 4),
  122. (FieldD.TYPE_INT64, nanopb_pb2.IS_64): ('int64_t', 'INT64', 10, 8),
  123. (FieldD.TYPE_SINT64, nanopb_pb2.IS_8): ('int8_t', 'SINT64', 2, 1),
  124. (FieldD.TYPE_SINT64, nanopb_pb2.IS_16): ('int16_t', 'SINT64', 3, 2),
  125. (FieldD.TYPE_SINT64, nanopb_pb2.IS_32): ('int32_t', 'SINT64', 5, 4),
  126. (FieldD.TYPE_SINT64, nanopb_pb2.IS_64): ('int64_t', 'SINT64', 10, 8),
  127. (FieldD.TYPE_UINT64, nanopb_pb2.IS_8): ('uint8_t', 'UINT64', 2, 1),
  128. (FieldD.TYPE_UINT64, nanopb_pb2.IS_16): ('uint16_t','UINT64', 3, 2),
  129. (FieldD.TYPE_UINT64, nanopb_pb2.IS_32): ('uint32_t','UINT64', 5, 4),
  130. (FieldD.TYPE_UINT64, nanopb_pb2.IS_64): ('uint64_t','UINT64', 10, 8),
  131. }
  132. class Globals:
  133. '''Ugly global variables, should find a good way to pass these.'''
  134. verbose_options = False
  135. separate_options = []
  136. matched_namemasks = set()
  137. protoc_insertion_points = False
  138. # String types (for python 2 / python 3 compatibility)
  139. try:
  140. strtypes = (unicode, str)
  141. openmode_unicode = 'rU'
  142. except NameError:
  143. strtypes = (str, )
  144. openmode_unicode = 'r'
  145. class Names:
  146. '''Keeps a set of nested names and formats them to C identifier.'''
  147. def __init__(self, parts = ()):
  148. if isinstance(parts, Names):
  149. parts = parts.parts
  150. elif isinstance(parts, strtypes):
  151. parts = (parts,)
  152. self.parts = tuple(parts)
  153. def __str__(self):
  154. return '_'.join(self.parts)
  155. def __add__(self, other):
  156. if isinstance(other, strtypes):
  157. return Names(self.parts + (other,))
  158. elif isinstance(other, Names):
  159. return Names(self.parts + other.parts)
  160. elif isinstance(other, tuple):
  161. return Names(self.parts + other)
  162. else:
  163. raise ValueError("Name parts should be of type str")
  164. def __eq__(self, other):
  165. return isinstance(other, Names) and self.parts == other.parts
  166. def __lt__(self, other):
  167. if not isinstance(other, Names):
  168. return NotImplemented
  169. return str(self) < str(other)
  170. def names_from_type_name(type_name):
  171. '''Parse Names() from FieldDescriptorProto type_name'''
  172. if type_name[0] != '.':
  173. raise NotImplementedError("Lookup of non-absolute type names is not supported")
  174. return Names(type_name[1:].split('.'))
  175. def varint_max_size(max_value):
  176. '''Returns the maximum number of bytes a varint can take when encoded.'''
  177. if max_value < 0:
  178. max_value = 2**64 - max_value
  179. for i in range(1, 11):
  180. if (max_value >> (i * 7)) == 0:
  181. return i
  182. raise ValueError("Value too large for varint: " + str(max_value))
  183. assert varint_max_size(-1) == 10
  184. assert varint_max_size(0) == 1
  185. assert varint_max_size(127) == 1
  186. assert varint_max_size(128) == 2
  187. class EncodedSize:
  188. '''Class used to represent the encoded size of a field or a message.
  189. Consists of a combination of symbolic sizes and integer sizes.'''
  190. def __init__(self, value = 0, symbols = [], declarations = [], required_defines = []):
  191. if isinstance(value, EncodedSize):
  192. self.value = value.value
  193. self.symbols = value.symbols
  194. self.declarations = value.declarations
  195. self.required_defines = value.required_defines
  196. elif isinstance(value, strtypes + (Names,)):
  197. self.symbols = [str(value)]
  198. self.value = 0
  199. self.declarations = []
  200. self.required_defines = [str(value)]
  201. else:
  202. self.value = value
  203. self.symbols = symbols
  204. self.declarations = declarations
  205. self.required_defines = required_defines
  206. def __add__(self, other):
  207. if isinstance(other, int):
  208. return EncodedSize(self.value + other, self.symbols, self.declarations, self.required_defines)
  209. elif isinstance(other, strtypes + (Names,)):
  210. return EncodedSize(self.value, self.symbols + [str(other)], self.declarations, self.required_defines + [str(other)])
  211. elif isinstance(other, EncodedSize):
  212. return EncodedSize(self.value + other.value, self.symbols + other.symbols,
  213. self.declarations + other.declarations, self.required_defines + other.required_defines)
  214. else:
  215. raise ValueError("Cannot add size: " + repr(other))
  216. def __mul__(self, other):
  217. if isinstance(other, int):
  218. return EncodedSize(self.value * other, [str(other) + '*' + s for s in self.symbols],
  219. self.declarations, self.required_defines)
  220. else:
  221. raise ValueError("Cannot multiply size: " + repr(other))
  222. def __str__(self):
  223. if not self.symbols:
  224. return str(self.value)
  225. else:
  226. return '(' + str(self.value) + ' + ' + ' + '.join(self.symbols) + ')'
  227. def get_declarations(self):
  228. '''Get any declarations that must appear alongside this encoded size definition,
  229. such as helper union {} types.'''
  230. return '\n'.join(self.declarations)
  231. def get_cpp_guard(self, local_defines):
  232. '''Get an #if preprocessor statement listing all defines that are required for this definition.'''
  233. needed = [x for x in self.required_defines if x not in local_defines]
  234. if needed:
  235. return '#if ' + ' && '.join(['defined(%s)' % x for x in needed]) + "\n"
  236. else:
  237. return ''
  238. def upperlimit(self):
  239. if not self.symbols:
  240. return self.value
  241. else:
  242. return 2**32 - 1
  243. '''
  244. Constants regarding path of proto elements in file descriptor.
  245. They are used to connect proto elements with source code information (comments)
  246. These values come from:
  247. https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto
  248. '''
  249. MESSAGE_PATH = 4
  250. ENUM_PATH = 5
  251. FIELD_PATH = 2
  252. class ProtoElement(object):
  253. def __init__(self, path, index, comments):
  254. '''
  255. path is a predefined value for each element type in proto file.
  256. For example, message == 4, enum == 5, service == 6
  257. index is the N-th occurrence of the `path` in the proto file.
  258. For example, 4-th message in the proto file or 2-nd enum etc ...
  259. comments is a dictionary mapping between element path & SourceCodeInfo.Location
  260. (contains information about source comments).
  261. '''
  262. self.path = path
  263. self.index = index
  264. self.comments = comments
  265. def element_path(self):
  266. '''Get path to proto element.'''
  267. return [self.path, self.index]
  268. def member_path(self, member_index):
  269. '''Get path to member of proto element.
  270. Example paths:
  271. [4, m] - message comments, m: msgIdx in proto from 0
  272. [4, m, 2, f] - field comments in message, f: fieldIdx in message from 0
  273. [6, s] - service comments, s: svcIdx in proto from 0
  274. [6, s, 2, r] - rpc comments in service, r: rpc method def in service from 0
  275. '''
  276. return self.element_path() + [FIELD_PATH, member_index]
  277. def get_comments(self, path, leading_indent=True):
  278. '''Get leading & trailing comments for enum member based on path.
  279. path is the proto path of an element or member (ex. [5 0] or [4 1 2 0])
  280. leading_indent is a flag that indicates if leading comments should be indented
  281. '''
  282. # Obtain SourceCodeInfo.Location object containing comment
  283. # information (based on the member path)
  284. comment = self.comments.get(str(path))
  285. leading_comment = ""
  286. trailing_comment = ""
  287. if not comment:
  288. return leading_comment, trailing_comment
  289. if comment.leading_comments:
  290. leading_comment = " " if leading_indent else ""
  291. leading_comment += "/* %s */" % comment.leading_comments.strip()
  292. if comment.trailing_comments:
  293. trailing_comment = "/* %s */" % comment.trailing_comments.strip()
  294. return leading_comment, trailing_comment
  295. class Enum(ProtoElement):
  296. def __init__(self, names, desc, enum_options, index, comments):
  297. '''
  298. desc is EnumDescriptorProto
  299. index is the index of this enum element inside the file
  300. comments is a dictionary mapping between element path & SourceCodeInfo.Location
  301. (contains information about source comments)
  302. '''
  303. super(Enum, self).__init__(ENUM_PATH, index, comments)
  304. self.options = enum_options
  305. self.names = names
  306. # by definition, `names` include this enum's name
  307. base_name = Names(names.parts[:-1])
  308. if enum_options.long_names:
  309. self.values = [(names + x.name, x.number) for x in desc.value]
  310. else:
  311. self.values = [(base_name + x.name, x.number) for x in desc.value]
  312. self.value_longnames = [self.names + x.name for x in desc.value]
  313. self.packed = enum_options.packed_enum
  314. def has_negative(self):
  315. for n, v in self.values:
  316. if v < 0:
  317. return True
  318. return False
  319. def encoded_size(self):
  320. return max([varint_max_size(v) for n,v in self.values])
  321. def __str__(self):
  322. enum_path = self.element_path()
  323. leading_comment, trailing_comment = self.get_comments(enum_path, leading_indent=False)
  324. result = ''
  325. if leading_comment:
  326. result = '%s\n' % leading_comment
  327. result += 'typedef enum _%s { %s\n' % (self.names, trailing_comment)
  328. enum_length = len(self.values)
  329. enum_values = []
  330. for index, (name, value) in enumerate(self.values):
  331. member_path = self.member_path(index)
  332. leading_comment, trailing_comment = self.get_comments(member_path)
  333. if leading_comment:
  334. enum_values.append(leading_comment)
  335. comma = ","
  336. if index == enum_length - 1:
  337. # last enum member should not end with a comma
  338. comma = ""
  339. enum_values.append(" %s = %d%s %s" % (name, value, comma, trailing_comment))
  340. result += '\n'.join(enum_values)
  341. result += '\n}'
  342. if self.packed:
  343. result += ' pb_packed'
  344. result += ' %s;' % self.names
  345. return result
  346. def auxiliary_defines(self):
  347. # sort the enum by value
  348. sorted_values = sorted(self.values, key = lambda x: (x[1], x[0]))
  349. result = '#define _%s_MIN %s\n' % (self.names, sorted_values[0][0])
  350. result += '#define _%s_MAX %s\n' % (self.names, sorted_values[-1][0])
  351. result += '#define _%s_ARRAYSIZE ((%s)(%s+1))\n' % (self.names, self.names, sorted_values[-1][0])
  352. if not self.options.long_names:
  353. # Define the long names always so that enum value references
  354. # from other files work properly.
  355. for i, x in enumerate(self.values):
  356. result += '#define %s %s\n' % (self.value_longnames[i], x[0])
  357. if self.options.enum_to_string:
  358. result += 'const char *%s_name(%s v);\n' % (self.names, self.names)
  359. return result
  360. def enum_to_string_definition(self):
  361. if not self.options.enum_to_string:
  362. return ""
  363. result = 'const char *%s_name(%s v) {\n' % (self.names, self.names)
  364. result += ' switch (v) {\n'
  365. for ((enumname, _), strname) in zip(self.values, self.value_longnames):
  366. # Strip off the leading type name from the string value.
  367. strval = str(strname)[len(str(self.names)) + 1:]
  368. result += ' case %s: return "%s";\n' % (enumname, strval)
  369. result += ' }\n'
  370. result += ' return "unknown";\n'
  371. result += '}\n'
  372. return result
  373. class FieldMaxSize:
  374. def __init__(self, worst = 0, checks = [], field_name = 'undefined'):
  375. if isinstance(worst, list):
  376. self.worst = max(i for i in worst if i is not None)
  377. else:
  378. self.worst = worst
  379. self.worst_field = field_name
  380. self.checks = list(checks)
  381. def extend(self, extend, field_name = None):
  382. self.worst = max(self.worst, extend.worst)
  383. if self.worst == extend.worst:
  384. self.worst_field = extend.worst_field
  385. self.checks.extend(extend.checks)
  386. class Field:
  387. macro_x_param = 'X'
  388. macro_a_param = 'a'
  389. def __init__(self, struct_name, desc, field_options):
  390. '''desc is FieldDescriptorProto'''
  391. self.tag = desc.number
  392. self.struct_name = struct_name
  393. self.union_name = None
  394. self.name = desc.name
  395. self.default = None
  396. self.max_size = None
  397. self.max_count = None
  398. self.array_decl = ""
  399. self.enc_size = None
  400. self.data_item_size = None
  401. self.ctype = None
  402. self.fixed_count = False
  403. self.callback_datatype = field_options.callback_datatype
  404. self.math_include_required = False
  405. self.sort_by_tag = field_options.sort_by_tag
  406. if field_options.type == nanopb_pb2.FT_INLINE:
  407. # Before nanopb-0.3.8, fixed length bytes arrays were specified
  408. # by setting type to FT_INLINE. But to handle pointer typed fields,
  409. # it makes sense to have it as a separate option.
  410. field_options.type = nanopb_pb2.FT_STATIC
  411. field_options.fixed_length = True
  412. # Parse field options
  413. if field_options.HasField("max_size"):
  414. self.max_size = field_options.max_size
  415. self.default_has = field_options.default_has
  416. if desc.type == FieldD.TYPE_STRING and field_options.HasField("max_length"):
  417. # max_length overrides max_size for strings
  418. self.max_size = field_options.max_length + 1
  419. if field_options.HasField("max_count"):
  420. self.max_count = field_options.max_count
  421. if desc.HasField('default_value'):
  422. self.default = desc.default_value
  423. # Check field rules, i.e. required/optional/repeated.
  424. can_be_static = True
  425. if desc.label == FieldD.LABEL_REPEATED:
  426. self.rules = 'REPEATED'
  427. if self.max_count is None:
  428. can_be_static = False
  429. else:
  430. self.array_decl = '[%d]' % self.max_count
  431. if field_options.fixed_count:
  432. self.rules = 'FIXARRAY'
  433. elif field_options.proto3:
  434. if desc.type == FieldD.TYPE_MESSAGE and not field_options.proto3_singular_msgs:
  435. # In most other protobuf libraries proto3 submessages have
  436. # "null" status. For nanopb, that is implemented as has_ field.
  437. self.rules = 'OPTIONAL'
  438. elif hasattr(desc, "proto3_optional") and desc.proto3_optional:
  439. # Protobuf 3.12 introduced optional fields for proto3 syntax
  440. self.rules = 'OPTIONAL'
  441. else:
  442. # Proto3 singular fields (without has_field)
  443. self.rules = 'SINGULAR'
  444. elif desc.label == FieldD.LABEL_REQUIRED:
  445. self.rules = 'REQUIRED'
  446. elif desc.label == FieldD.LABEL_OPTIONAL:
  447. self.rules = 'OPTIONAL'
  448. else:
  449. raise NotImplementedError(desc.label)
  450. # Check if the field can be implemented with static allocation
  451. # i.e. whether the data size is known.
  452. if desc.type == FieldD.TYPE_STRING and self.max_size is None:
  453. can_be_static = False
  454. if desc.type == FieldD.TYPE_BYTES and self.max_size is None:
  455. can_be_static = False
  456. # Decide how the field data will be allocated
  457. if field_options.type == nanopb_pb2.FT_DEFAULT:
  458. if can_be_static:
  459. field_options.type = nanopb_pb2.FT_STATIC
  460. else:
  461. field_options.type = nanopb_pb2.FT_CALLBACK
  462. if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static:
  463. raise Exception("Field '%s' is defined as static, but max_size or "
  464. "max_count is not given." % self.name)
  465. if field_options.fixed_count and self.max_count is None:
  466. raise Exception("Field '%s' is defined as fixed count, "
  467. "but max_count is not given." % self.name)
  468. if field_options.type == nanopb_pb2.FT_STATIC:
  469. self.allocation = 'STATIC'
  470. elif field_options.type == nanopb_pb2.FT_POINTER:
  471. self.allocation = 'POINTER'
  472. elif field_options.type == nanopb_pb2.FT_CALLBACK:
  473. self.allocation = 'CALLBACK'
  474. else:
  475. raise NotImplementedError(field_options.type)
  476. if field_options.HasField("type_override"):
  477. desc.type = field_options.type_override
  478. # Decide the C data type to use in the struct.
  479. if desc.type in datatypes:
  480. self.ctype, self.pbtype, self.enc_size, self.data_item_size = datatypes[desc.type]
  481. # Override the field size if user wants to use smaller integers
  482. if (desc.type, field_options.int_size) in datatypes:
  483. self.ctype, self.pbtype, self.enc_size, self.data_item_size = datatypes[(desc.type, field_options.int_size)]
  484. elif desc.type == FieldD.TYPE_ENUM:
  485. self.pbtype = 'ENUM'
  486. self.data_item_size = 4
  487. self.ctype = names_from_type_name(desc.type_name)
  488. if self.default is not None:
  489. self.default = self.ctype + self.default
  490. self.enc_size = None # Needs to be filled in when enum values are known
  491. elif desc.type == FieldD.TYPE_STRING:
  492. self.pbtype = 'STRING'
  493. self.ctype = 'char'
  494. if self.allocation == 'STATIC':
  495. self.ctype = 'char'
  496. self.array_decl += '[%d]' % self.max_size
  497. # -1 because of null terminator. Both pb_encode and pb_decode
  498. # check the presence of it.
  499. self.enc_size = varint_max_size(self.max_size) + self.max_size - 1
  500. elif desc.type == FieldD.TYPE_BYTES:
  501. if field_options.fixed_length:
  502. self.pbtype = 'FIXED_LENGTH_BYTES'
  503. if self.max_size is None:
  504. raise Exception("Field '%s' is defined as fixed length, "
  505. "but max_size is not given." % self.name)
  506. self.enc_size = varint_max_size(self.max_size) + self.max_size
  507. self.ctype = 'pb_byte_t'
  508. self.array_decl += '[%d]' % self.max_size
  509. else:
  510. self.pbtype = 'BYTES'
  511. self.ctype = 'pb_bytes_array_t'
  512. if self.allocation == 'STATIC':
  513. self.ctype = self.struct_name + self.name + 't'
  514. self.enc_size = varint_max_size(self.max_size) + self.max_size
  515. elif desc.type == FieldD.TYPE_MESSAGE:
  516. self.pbtype = 'MESSAGE'
  517. self.ctype = self.submsgname = names_from_type_name(desc.type_name)
  518. self.enc_size = None # Needs to be filled in after the message type is available
  519. if field_options.submsg_callback and self.allocation == 'STATIC':
  520. self.pbtype = 'MSG_W_CB'
  521. else:
  522. raise NotImplementedError(desc.type)
  523. if self.default and self.pbtype in ['FLOAT', 'DOUBLE']:
  524. if 'inf' in self.default or 'nan' in self.default:
  525. self.math_include_required = True
  526. def __lt__(self, other):
  527. return self.tag < other.tag
  528. def __str__(self):
  529. result = ''
  530. if self.allocation == 'POINTER':
  531. if self.rules == 'REPEATED':
  532. if self.pbtype == 'MSG_W_CB':
  533. result += ' pb_callback_t cb_' + self.name + ';\n'
  534. result += ' pb_size_t ' + self.name + '_count;\n'
  535. if self.pbtype in ['MESSAGE', 'MSG_W_CB']:
  536. # Use struct definition, so recursive submessages are possible
  537. result += ' struct _%s *%s;' % (self.ctype, self.name)
  538. elif self.pbtype == 'FIXED_LENGTH_BYTES' or self.rules == 'FIXARRAY':
  539. # Pointer to fixed size array
  540. result += ' %s (*%s)%s;' % (self.ctype, self.name, self.array_decl)
  541. elif self.rules in ['REPEATED', 'FIXARRAY'] and self.pbtype in ['STRING', 'BYTES']:
  542. # String/bytes arrays need to be defined as pointers to pointers
  543. result += ' %s **%s;' % (self.ctype, self.name)
  544. else:
  545. result += ' %s *%s;' % (self.ctype, self.name)
  546. elif self.allocation == 'CALLBACK':
  547. result += ' %s %s;' % (self.callback_datatype, self.name)
  548. else:
  549. if self.pbtype == 'MSG_W_CB' and self.rules in ['OPTIONAL', 'REPEATED']:
  550. result += ' pb_callback_t cb_' + self.name + ';\n'
  551. if self.rules == 'OPTIONAL':
  552. result += ' bool has_' + self.name + ';\n'
  553. elif self.rules == 'REPEATED':
  554. result += ' pb_size_t ' + self.name + '_count;\n'
  555. result += ' %s %s%s;' % (self.ctype, self.name, self.array_decl)
  556. return result
  557. def types(self):
  558. '''Return definitions for any special types this field might need.'''
  559. if self.pbtype == 'BYTES' and self.allocation == 'STATIC':
  560. result = 'typedef PB_BYTES_ARRAY_T(%d) %s;\n' % (self.max_size, self.ctype)
  561. else:
  562. result = ''
  563. return result
  564. def get_dependencies(self):
  565. '''Get list of type names used by this field.'''
  566. if self.allocation == 'STATIC':
  567. return [str(self.ctype)]
  568. else:
  569. return []
  570. def get_initializer(self, null_init, inner_init_only = False):
  571. '''Return literal expression for this field's default value.
  572. null_init: If True, initialize to a 0 value instead of default from .proto
  573. inner_init_only: If True, exclude initialization for any count/has fields
  574. '''
  575. inner_init = None
  576. if self.pbtype in ['MESSAGE', 'MSG_W_CB']:
  577. if null_init:
  578. inner_init = '%s_init_zero' % self.ctype
  579. else:
  580. inner_init = '%s_init_default' % self.ctype
  581. elif self.default is None or null_init:
  582. if self.pbtype == 'STRING':
  583. inner_init = '""'
  584. elif self.pbtype == 'BYTES':
  585. inner_init = '{0, {0}}'
  586. elif self.pbtype == 'FIXED_LENGTH_BYTES':
  587. inner_init = '{0}'
  588. elif self.pbtype in ('ENUM', 'UENUM'):
  589. inner_init = '_%s_MIN' % self.ctype
  590. else:
  591. inner_init = '0'
  592. else:
  593. if self.pbtype == 'STRING':
  594. data = codecs.escape_encode(self.default.encode('utf-8'))[0]
  595. inner_init = '"' + data.decode('ascii') + '"'
  596. elif self.pbtype == 'BYTES':
  597. data = codecs.escape_decode(self.default)[0]
  598. data = ["0x%02x" % c for c in bytearray(data)]
  599. if len(data) == 0:
  600. inner_init = '{0, {0}}'
  601. else:
  602. inner_init = '{%d, {%s}}' % (len(data), ','.join(data))
  603. elif self.pbtype == 'FIXED_LENGTH_BYTES':
  604. data = codecs.escape_decode(self.default)[0]
  605. data = ["0x%02x" % c for c in bytearray(data)]
  606. if len(data) == 0:
  607. inner_init = '{0}'
  608. else:
  609. inner_init = '{%s}' % ','.join(data)
  610. elif self.pbtype in ['FIXED32', 'UINT32']:
  611. inner_init = str(self.default) + 'u'
  612. elif self.pbtype in ['FIXED64', 'UINT64']:
  613. inner_init = str(self.default) + 'ull'
  614. elif self.pbtype in ['SFIXED64', 'INT64']:
  615. inner_init = str(self.default) + 'll'
  616. elif self.pbtype in ['FLOAT', 'DOUBLE']:
  617. inner_init = str(self.default)
  618. if 'inf' in inner_init:
  619. inner_init = inner_init.replace('inf', 'INFINITY')
  620. elif 'nan' in inner_init:
  621. inner_init = inner_init.replace('nan', 'NAN')
  622. elif (not '.' in inner_init) and self.pbtype == 'FLOAT':
  623. inner_init += '.0f'
  624. elif self.pbtype == 'FLOAT':
  625. inner_init += 'f'
  626. else:
  627. inner_init = str(self.default)
  628. if inner_init_only:
  629. return inner_init
  630. outer_init = None
  631. if self.allocation == 'STATIC':
  632. if self.rules == 'REPEATED':
  633. outer_init = '0, {' + ', '.join([inner_init] * self.max_count) + '}'
  634. elif self.rules == 'FIXARRAY':
  635. outer_init = '{' + ', '.join([inner_init] * self.max_count) + '}'
  636. elif self.rules == 'OPTIONAL':
  637. if null_init or not self.default_has:
  638. outer_init = 'false, ' + inner_init
  639. else:
  640. outer_init = 'true, ' + inner_init
  641. else:
  642. outer_init = inner_init
  643. elif self.allocation == 'POINTER':
  644. if self.rules == 'REPEATED':
  645. outer_init = '0, NULL'
  646. else:
  647. outer_init = 'NULL'
  648. elif self.allocation == 'CALLBACK':
  649. if self.pbtype == 'EXTENSION':
  650. outer_init = 'NULL'
  651. else:
  652. outer_init = '{{NULL}, NULL}'
  653. if self.pbtype == 'MSG_W_CB' and self.rules in ['REPEATED', 'OPTIONAL']:
  654. outer_init = '{{NULL}, NULL}, ' + outer_init
  655. return outer_init
  656. def tags(self):
  657. '''Return the #define for the tag number of this field.'''
  658. identifier = '%s_%s_tag' % (self.struct_name, self.name)
  659. return '#define %-40s %d\n' % (identifier, self.tag)
  660. def fieldlist(self):
  661. '''Return the FIELDLIST macro entry for this field.
  662. Format is: X(a, ATYPE, HTYPE, LTYPE, field_name, tag)
  663. '''
  664. name = self.name
  665. if self.rules == "ONEOF":
  666. # For oneofs, make a tuple of the union name, union member name,
  667. # and the name inside the parent struct.
  668. if not self.anonymous:
  669. name = '(%s,%s,%s)' % (self.union_name, self.name, self.union_name + '.' + self.name)
  670. else:
  671. name = '(%s,%s,%s)' % (self.union_name, self.name, self.name)
  672. return '%s(%s, %-9s %-9s %-9s %-16s %3d)' % (self.macro_x_param,
  673. self.macro_a_param,
  674. self.allocation + ',',
  675. self.rules + ',',
  676. self.pbtype + ',',
  677. name + ',',
  678. self.tag)
  679. def data_size(self, dependencies):
  680. '''Return estimated size of this field in the C struct.
  681. This is used to try to automatically pick right descriptor size.
  682. If the estimate is wrong, it will result in compile time error and
  683. user having to specify descriptor_width option.
  684. '''
  685. if self.allocation == 'POINTER' or self.pbtype == 'EXTENSION':
  686. size = 8
  687. alignment = 8
  688. elif self.allocation == 'CALLBACK':
  689. size = 16
  690. alignment = 8
  691. elif self.pbtype in ['MESSAGE', 'MSG_W_CB']:
  692. alignment = 8
  693. if str(self.submsgname) in dependencies:
  694. other_dependencies = dict(x for x in dependencies.items() if x[0] != str(self.struct_name))
  695. size = dependencies[str(self.submsgname)].data_size(other_dependencies)
  696. else:
  697. size = 256 # Message is in other file, this is reasonable guess for most cases
  698. if self.pbtype == 'MSG_W_CB':
  699. size += 16
  700. elif self.pbtype in ['STRING', 'FIXED_LENGTH_BYTES']:
  701. size = self.max_size
  702. alignment = 4
  703. elif self.pbtype == 'BYTES':
  704. size = self.max_size + 4
  705. alignment = 4
  706. elif self.data_item_size is not None:
  707. size = self.data_item_size
  708. alignment = 4
  709. if self.data_item_size >= 8:
  710. alignment = 8
  711. else:
  712. raise Exception("Unhandled field type: %s" % self.pbtype)
  713. if self.rules in ['REPEATED', 'FIXARRAY'] and self.allocation == 'STATIC':
  714. size *= self.max_count
  715. if self.rules not in ('REQUIRED', 'SINGULAR'):
  716. size += 4
  717. if size % alignment != 0:
  718. # Estimate how much alignment requirements will increase the size.
  719. size += alignment - (size % alignment)
  720. return size
  721. def encoded_size(self, dependencies):
  722. '''Return the maximum size that this field can take when encoded,
  723. including the field tag. If the size cannot be determined, returns
  724. None.'''
  725. if self.allocation != 'STATIC':
  726. return None
  727. if self.pbtype in ['MESSAGE', 'MSG_W_CB']:
  728. encsize = None
  729. if str(self.submsgname) in dependencies:
  730. submsg = dependencies[str(self.submsgname)]
  731. other_dependencies = dict(x for x in dependencies.items() if x[0] != str(self.struct_name))
  732. encsize = submsg.encoded_size(other_dependencies)
  733. my_msg = dependencies.get(str(self.struct_name))
  734. external = (not my_msg or submsg.protofile != my_msg.protofile)
  735. if encsize and encsize.symbols and external:
  736. # Couldn't fully resolve the size of a dependency from
  737. # another file. Instead of including the symbols directly,
  738. # just use the #define SubMessage_size from the header.
  739. encsize = None
  740. if encsize is not None:
  741. # Include submessage length prefix
  742. encsize += varint_max_size(encsize.upperlimit())
  743. elif not external:
  744. # The dependency is from the same file and size cannot be
  745. # determined for it, thus we know it will not be possible
  746. # in runtime either.
  747. return None
  748. if encsize is None:
  749. # Submessage or its size cannot be found.
  750. # This can occur if submessage is defined in different
  751. # file, and it or its .options could not be found.
  752. # Instead of direct numeric value, reference the size that
  753. # has been #defined in the other file.
  754. encsize = EncodedSize(self.submsgname + 'size')
  755. # We will have to make a conservative assumption on the length
  756. # prefix size, though.
  757. encsize += 5
  758. elif self.pbtype in ['ENUM', 'UENUM']:
  759. if str(self.ctype) in dependencies:
  760. enumtype = dependencies[str(self.ctype)]
  761. encsize = enumtype.encoded_size()
  762. else:
  763. # Conservative assumption
  764. encsize = 10
  765. elif self.enc_size is None:
  766. raise RuntimeError("Could not determine encoded size for %s.%s"
  767. % (self.struct_name, self.name))
  768. else:
  769. encsize = EncodedSize(self.enc_size)
  770. encsize += varint_max_size(self.tag << 3) # Tag + wire type
  771. if self.rules in ['REPEATED', 'FIXARRAY']:
  772. # Decoders must be always able to handle unpacked arrays.
  773. # Therefore we have to reserve space for it, even though
  774. # we emit packed arrays ourselves. For length of 1, packed
  775. # arrays are larger however so we need to add allowance
  776. # for the length byte.
  777. encsize *= self.max_count
  778. if self.max_count == 1:
  779. encsize += 1
  780. return encsize
  781. def has_callbacks(self):
  782. return self.allocation == 'CALLBACK'
  783. def requires_custom_field_callback(self):
  784. return self.allocation == 'CALLBACK' and self.callback_datatype != 'pb_callback_t'
  785. class ExtensionRange(Field):
  786. def __init__(self, struct_name, range_start, field_options):
  787. '''Implements a special pb_extension_t* field in an extensible message
  788. structure. The range_start signifies the index at which the extensions
  789. start. Not necessarily all tags above this are extensions, it is merely
  790. a speed optimization.
  791. '''
  792. self.tag = range_start
  793. self.struct_name = struct_name
  794. self.name = 'extensions'
  795. self.pbtype = 'EXTENSION'
  796. self.rules = 'OPTIONAL'
  797. self.allocation = 'CALLBACK'
  798. self.ctype = 'pb_extension_t'
  799. self.array_decl = ''
  800. self.default = None
  801. self.max_size = 0
  802. self.max_count = 0
  803. self.data_item_size = 0
  804. self.fixed_count = False
  805. self.callback_datatype = 'pb_extension_t*'
  806. def requires_custom_field_callback(self):
  807. return False
  808. def __str__(self):
  809. return ' pb_extension_t *extensions;'
  810. def types(self):
  811. return ''
  812. def tags(self):
  813. return ''
  814. def encoded_size(self, dependencies):
  815. # We exclude extensions from the count, because they cannot be known
  816. # until runtime. Other option would be to return None here, but this
  817. # way the value remains useful if extensions are not used.
  818. return EncodedSize(0)
  819. class ExtensionField(Field):
  820. def __init__(self, fullname, desc, field_options):
  821. self.fullname = fullname
  822. self.extendee_name = names_from_type_name(desc.extendee)
  823. Field.__init__(self, self.fullname + "extmsg", desc, field_options)
  824. if self.rules != 'OPTIONAL':
  825. self.skip = True
  826. else:
  827. self.skip = False
  828. self.rules = 'REQUIRED' # We don't really want the has_field for extensions
  829. # currently no support for comments for extension fields => provide 0, {}
  830. self.msg = Message(self.fullname + "extmsg", None, field_options, 0, {})
  831. self.msg.fields.append(self)
  832. def tags(self):
  833. '''Return the #define for the tag number of this field.'''
  834. identifier = '%s_tag' % self.fullname
  835. return '#define %-40s %d\n' % (identifier, self.tag)
  836. def extension_decl(self):
  837. '''Declaration of the extension type in the .pb.h file'''
  838. if self.skip:
  839. msg = '/* Extension field %s was skipped because only "optional"\n' % self.fullname
  840. msg +=' type of extension fields is currently supported. */\n'
  841. return msg
  842. return ('extern const pb_extension_type_t %s; /* field type: %s */\n' %
  843. (self.fullname, str(self).strip()))
  844. def extension_def(self, dependencies):
  845. '''Definition of the extension type in the .pb.c file'''
  846. if self.skip:
  847. return ''
  848. result = "/* Definition for extension field %s */\n" % self.fullname
  849. result += str(self.msg)
  850. result += self.msg.fields_declaration(dependencies)
  851. result += 'pb_byte_t %s_default[] = {0x00};\n' % self.msg.name
  852. result += self.msg.fields_definition(dependencies)
  853. result += 'const pb_extension_type_t %s = {\n' % self.fullname
  854. result += ' NULL,\n'
  855. result += ' NULL,\n'
  856. result += ' &%s_msg\n' % self.msg.name
  857. result += '};\n'
  858. return result
  859. # ---------------------------------------------------------------------------
  860. # Generation of oneofs (unions)
  861. # ---------------------------------------------------------------------------
  862. class OneOf(Field):
  863. def __init__(self, struct_name, oneof_desc, oneof_options):
  864. self.struct_name = struct_name
  865. self.name = oneof_desc.name
  866. self.ctype = 'union'
  867. self.pbtype = 'oneof'
  868. self.fields = []
  869. self.allocation = 'ONEOF'
  870. self.default = None
  871. self.rules = 'ONEOF'
  872. self.anonymous = oneof_options.anonymous_oneof
  873. self.sort_by_tag = oneof_options.sort_by_tag
  874. self.has_msg_cb = False
  875. def add_field(self, field):
  876. field.union_name = self.name
  877. field.rules = 'ONEOF'
  878. field.anonymous = self.anonymous
  879. self.fields.append(field)
  880. if self.sort_by_tag:
  881. self.fields.sort()
  882. if field.pbtype == 'MSG_W_CB':
  883. self.has_msg_cb = True
  884. # Sort by the lowest tag number inside union
  885. self.tag = min([f.tag for f in self.fields])
  886. def __str__(self):
  887. result = ''
  888. if self.fields:
  889. if self.has_msg_cb:
  890. result += ' pb_callback_t cb_' + self.name + ';\n'
  891. result += ' pb_size_t which_' + self.name + ";\n"
  892. result += ' union {\n'
  893. for f in self.fields:
  894. result += ' ' + str(f).replace('\n', '\n ') + '\n'
  895. if self.anonymous:
  896. result += ' };'
  897. else:
  898. result += ' } ' + self.name + ';'
  899. return result
  900. def types(self):
  901. return ''.join([f.types() for f in self.fields])
  902. def get_dependencies(self):
  903. deps = []
  904. for f in self.fields:
  905. deps += f.get_dependencies()
  906. return deps
  907. def get_initializer(self, null_init):
  908. if self.has_msg_cb:
  909. return '{{NULL}, NULL}, 0, {' + self.fields[0].get_initializer(null_init) + '}'
  910. else:
  911. return '0, {' + self.fields[0].get_initializer(null_init) + '}'
  912. def tags(self):
  913. return ''.join([f.tags() for f in self.fields])
  914. def data_size(self, dependencies):
  915. return max(f.data_size(dependencies) for f in self.fields)
  916. def encoded_size(self, dependencies):
  917. '''Returns the size of the largest oneof field.'''
  918. largest = 0
  919. dynamic_sizes = {}
  920. for f in self.fields:
  921. size = EncodedSize(f.encoded_size(dependencies))
  922. if size is None or size.value is None:
  923. return None
  924. elif size.symbols:
  925. dynamic_sizes[f.tag] = size
  926. elif size.value > largest:
  927. largest = size.value
  928. if not dynamic_sizes:
  929. # Simple case, all sizes were known at generator time
  930. return EncodedSize(largest)
  931. if largest > 0:
  932. # Some sizes were known, some were not
  933. dynamic_sizes[0] = EncodedSize(largest)
  934. # Couldn't find size for submessage at generation time,
  935. # have to rely on macro resolution at compile time.
  936. if len(dynamic_sizes) == 1:
  937. # Only one symbol was needed
  938. return list(dynamic_sizes.values())[0]
  939. else:
  940. # Use sizeof(union{}) construct to find the maximum size of
  941. # submessages.
  942. union_name = "%s_%s_size_union" % (self.struct_name, self.name)
  943. union_def = 'union %s {%s};\n' % (union_name, ' '.join('char f%d[%s];' % (k, s) for k,s in dynamic_sizes.items()))
  944. required_defs = list(itertools.chain.from_iterable(s.required_defines for k,s in dynamic_sizes.items()))
  945. return EncodedSize(0, ['sizeof(union %s)' % union_name], [union_def], required_defs)
  946. def has_callbacks(self):
  947. return bool([f for f in self.fields if f.has_callbacks()])
  948. def requires_custom_field_callback(self):
  949. return bool([f for f in self.fields if f.requires_custom_field_callback()])
  950. # ---------------------------------------------------------------------------
  951. # Generation of messages (structures)
  952. # ---------------------------------------------------------------------------
  953. class Message(ProtoElement):
  954. def __init__(self, names, desc, message_options, index, comments):
  955. super(Message, self).__init__(MESSAGE_PATH, index, comments)
  956. self.name = names
  957. self.fields = []
  958. self.oneofs = {}
  959. self.desc = desc
  960. self.math_include_required = False
  961. self.packed = message_options.packed_struct
  962. self.descriptorsize = message_options.descriptorsize
  963. if message_options.msgid:
  964. self.msgid = message_options.msgid
  965. if desc is not None:
  966. self.load_fields(desc, message_options)
  967. self.callback_function = message_options.callback_function
  968. if not message_options.HasField('callback_function'):
  969. # Automatically assign a per-message callback if any field has
  970. # a special callback_datatype.
  971. for field in self.fields:
  972. if field.requires_custom_field_callback():
  973. self.callback_function = "%s_callback" % self.name
  974. break
  975. def load_fields(self, desc, message_options):
  976. '''Load field list from DescriptorProto'''
  977. no_unions = []
  978. if hasattr(desc, 'oneof_decl'):
  979. for i, f in enumerate(desc.oneof_decl):
  980. oneof_options = get_nanopb_suboptions(desc, message_options, self.name + f.name)
  981. if oneof_options.no_unions:
  982. no_unions.append(i) # No union, but add fields normally
  983. elif oneof_options.type == nanopb_pb2.FT_IGNORE:
  984. pass # No union and skip fields also
  985. else:
  986. oneof = OneOf(self.name, f, oneof_options)
  987. self.oneofs[i] = oneof
  988. else:
  989. sys.stderr.write('Note: This Python protobuf library has no OneOf support\n')
  990. for f in desc.field:
  991. field_options = get_nanopb_suboptions(f, message_options, self.name + f.name)
  992. if field_options.type == nanopb_pb2.FT_IGNORE:
  993. continue
  994. if field_options.descriptorsize > self.descriptorsize:
  995. self.descriptorsize = field_options.descriptorsize
  996. field = Field(self.name, f, field_options)
  997. if hasattr(f, 'oneof_index') and f.HasField('oneof_index'):
  998. if hasattr(f, 'proto3_optional') and f.proto3_optional:
  999. no_unions.append(f.oneof_index)
  1000. if f.oneof_index in no_unions:
  1001. self.fields.append(field)
  1002. elif f.oneof_index in self.oneofs:
  1003. self.oneofs[f.oneof_index].add_field(field)
  1004. if self.oneofs[f.oneof_index] not in self.fields:
  1005. self.fields.append(self.oneofs[f.oneof_index])
  1006. else:
  1007. self.fields.append(field)
  1008. if field.math_include_required:
  1009. self.math_include_required = True
  1010. if len(desc.extension_range) > 0:
  1011. field_options = get_nanopb_suboptions(desc, message_options, self.name + 'extensions')
  1012. range_start = min([r.start for r in desc.extension_range])
  1013. if field_options.type != nanopb_pb2.FT_IGNORE:
  1014. self.fields.append(ExtensionRange(self.name, range_start, field_options))
  1015. if message_options.sort_by_tag:
  1016. self.fields.sort()
  1017. def get_dependencies(self):
  1018. '''Get list of type names that this structure refers to.'''
  1019. deps = []
  1020. for f in self.fields:
  1021. deps += f.get_dependencies()
  1022. return deps
  1023. def __str__(self):
  1024. message_path = self.element_path()
  1025. leading_comment, trailing_comment = self.get_comments(message_path, leading_indent=False)
  1026. result = ''
  1027. if leading_comment:
  1028. result = '%s\n' % leading_comment
  1029. result += 'typedef struct _%s { %s\n' % (self.name, trailing_comment)
  1030. if not self.fields:
  1031. # Empty structs are not allowed in C standard.
  1032. # Therefore add a dummy field if an empty message occurs.
  1033. result += ' char dummy_field;'
  1034. msg_fields = []
  1035. for index, field in enumerate(self.fields):
  1036. member_path = self.member_path(index)
  1037. leading_comment, trailing_comment = self.get_comments(member_path)
  1038. if leading_comment:
  1039. msg_fields.append(leading_comment)
  1040. msg_fields.append("%s %s" % (str(field), trailing_comment))
  1041. result += '\n'.join(msg_fields)
  1042. if Globals.protoc_insertion_points:
  1043. result += '\n/* @@protoc_insertion_point(struct:%s) */' % self.name
  1044. result += '\n}'
  1045. if self.packed:
  1046. result += ' pb_packed'
  1047. result += ' %s;' % self.name
  1048. if self.packed:
  1049. result = 'PB_PACKED_STRUCT_START\n' + result
  1050. result += '\nPB_PACKED_STRUCT_END'
  1051. return result + '\n'
  1052. def types(self):
  1053. return ''.join([f.types() for f in self.fields])
  1054. def get_initializer(self, null_init):
  1055. if not self.fields:
  1056. return '{0}'
  1057. parts = []
  1058. for field in self.fields:
  1059. parts.append(field.get_initializer(null_init))
  1060. return '{' + ', '.join(parts) + '}'
  1061. def count_required_fields(self):
  1062. '''Returns number of required fields inside this message'''
  1063. count = 0
  1064. for f in self.fields:
  1065. if not isinstance(f, OneOf):
  1066. if f.rules == 'REQUIRED':
  1067. count += 1
  1068. return count
  1069. def all_fields(self):
  1070. '''Iterate over all fields in this message, including nested OneOfs.'''
  1071. for f in self.fields:
  1072. if isinstance(f, OneOf):
  1073. for f2 in f.fields:
  1074. yield f2
  1075. else:
  1076. yield f
  1077. def field_for_tag(self, tag):
  1078. '''Given a tag number, return the Field instance.'''
  1079. for field in self.all_fields():
  1080. if field.tag == tag:
  1081. return field
  1082. return None
  1083. def count_all_fields(self):
  1084. '''Count the total number of fields in this message.'''
  1085. count = 0
  1086. for f in self.fields:
  1087. if isinstance(f, OneOf):
  1088. count += len(f.fields)
  1089. else:
  1090. count += 1
  1091. return count
  1092. def fields_declaration(self, dependencies):
  1093. '''Return X-macro declaration of all fields in this message.'''
  1094. Field.macro_x_param = 'X'
  1095. Field.macro_a_param = 'a'
  1096. while any(field.name == Field.macro_x_param for field in self.all_fields()):
  1097. Field.macro_x_param += '_'
  1098. while any(field.name == Field.macro_a_param for field in self.all_fields()):
  1099. Field.macro_a_param += '_'
  1100. # Field descriptor array must be sorted by tag number, pb_common.c relies on it.
  1101. sorted_fields = list(self.all_fields())
  1102. sorted_fields.sort(key = lambda x: x.tag)
  1103. result = '#define %s_FIELDLIST(%s, %s) \\\n' % (self.name,
  1104. Field.macro_x_param,
  1105. Field.macro_a_param)
  1106. result += ' \\\n'.join(x.fieldlist() for x in sorted_fields)
  1107. result += '\n'
  1108. has_callbacks = bool([f for f in self.fields if f.has_callbacks()])
  1109. if has_callbacks:
  1110. if self.callback_function != 'pb_default_field_callback':
  1111. result += "extern bool %s(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);\n" % self.callback_function
  1112. result += "#define %s_CALLBACK %s\n" % (self.name, self.callback_function)
  1113. else:
  1114. result += "#define %s_CALLBACK NULL\n" % self.name
  1115. defval = self.default_value(dependencies)
  1116. if defval:
  1117. hexcoded = ''.join("\\x%02x" % ord(defval[i:i+1]) for i in range(len(defval)))
  1118. result += '#define %s_DEFAULT (const pb_byte_t*)"%s\\x00"\n' % (self.name, hexcoded)
  1119. else:
  1120. result += '#define %s_DEFAULT NULL\n' % self.name
  1121. for field in sorted_fields:
  1122. if field.pbtype in ['MESSAGE', 'MSG_W_CB']:
  1123. if field.rules == 'ONEOF':
  1124. result += "#define %s_%s_%s_MSGTYPE %s\n" % (self.name, field.union_name, field.name, field.ctype)
  1125. else:
  1126. result += "#define %s_%s_MSGTYPE %s\n" % (self.name, field.name, field.ctype)
  1127. return result
  1128. def fields_declaration_cpp_lookup(self):
  1129. result = 'template <>\n'
  1130. result += 'struct MessageDescriptor<%s> {\n' % (self.name)
  1131. result += ' static PB_INLINE_CONSTEXPR const pb_size_t fields_array_length = %d;\n' % (self.count_all_fields())
  1132. result += ' static inline const pb_msgdesc_t* fields() {\n'
  1133. result += ' return &%s_msg;\n' % (self.name)
  1134. result += ' }\n'
  1135. result += '};'
  1136. return result
  1137. def fields_definition(self, dependencies):
  1138. '''Return the field descriptor definition that goes in .pb.c file.'''
  1139. width = self.required_descriptor_width(dependencies)
  1140. if width == 1:
  1141. width = 'AUTO'
  1142. result = 'PB_BIND(%s, %s, %s)\n' % (self.name, self.name, width)
  1143. return result
  1144. def required_descriptor_width(self, dependencies):
  1145. '''Estimate how many words are necessary for each field descriptor.'''
  1146. if self.descriptorsize != nanopb_pb2.DS_AUTO:
  1147. return int(self.descriptorsize)
  1148. if not self.fields:
  1149. return 1
  1150. max_tag = max(field.tag for field in self.all_fields())
  1151. max_offset = self.data_size(dependencies)
  1152. max_arraysize = max((field.max_count or 0) for field in self.all_fields())
  1153. max_datasize = max(field.data_size(dependencies) for field in self.all_fields())
  1154. if max_arraysize > 0xFFFF:
  1155. return 8
  1156. elif (max_tag > 0x3FF or max_offset > 0xFFFF or
  1157. max_arraysize > 0x0FFF or max_datasize > 0x0FFF):
  1158. return 4
  1159. elif max_tag > 0x3F or max_offset > 0xFF:
  1160. return 2
  1161. else:
  1162. # NOTE: Macro logic in pb.h ensures that width 1 will
  1163. # be raised to 2 automatically for string/submsg fields
  1164. # and repeated fields. Thus only tag and offset need to
  1165. # be checked.
  1166. return 1
  1167. def data_size(self, dependencies):
  1168. '''Return approximate sizeof(struct) in the compiled code.'''
  1169. return sum(f.data_size(dependencies) for f in self.fields)
  1170. def encoded_size(self, dependencies):
  1171. '''Return the maximum size that this message can take when encoded.
  1172. If the size cannot be determined, returns None.
  1173. '''
  1174. size = EncodedSize(0)
  1175. for field in self.fields:
  1176. fsize = field.encoded_size(dependencies)
  1177. if fsize is None:
  1178. return None
  1179. size += fsize
  1180. return size
  1181. def default_value(self, dependencies):
  1182. '''Generate serialized protobuf message that contains the
  1183. default values for optional fields.'''
  1184. if not self.desc:
  1185. return b''
  1186. if self.desc.options.map_entry:
  1187. return b''
  1188. optional_only = copy.deepcopy(self.desc)
  1189. # Remove fields without default values
  1190. # The iteration is done in reverse order to avoid remove() messing up iteration.
  1191. for field in reversed(list(optional_only.field)):
  1192. field.ClearField(str('extendee'))
  1193. parsed_field = self.field_for_tag(field.number)
  1194. if parsed_field is None or parsed_field.allocation != 'STATIC':
  1195. optional_only.field.remove(field)
  1196. elif (field.label == FieldD.LABEL_REPEATED or
  1197. field.type == FieldD.TYPE_MESSAGE):
  1198. optional_only.field.remove(field)
  1199. elif hasattr(field, 'oneof_index') and field.HasField('oneof_index'):
  1200. optional_only.field.remove(field)
  1201. elif field.type == FieldD.TYPE_ENUM:
  1202. # The partial descriptor doesn't include the enum type
  1203. # so we fake it with int64.
  1204. enumname = names_from_type_name(field.type_name)
  1205. try:
  1206. enumtype = dependencies[str(enumname)]
  1207. except KeyError:
  1208. raise Exception("Could not find enum type %s while generating default values for %s.\n" % (enumname, self.name)
  1209. + "Try passing all source files to generator at once, or use -I option.")
  1210. if field.HasField('default_value'):
  1211. defvals = [v for n,v in enumtype.values if n.parts[-1] == field.default_value]
  1212. else:
  1213. # If no default is specified, the default is the first value.
  1214. defvals = [v for n,v in enumtype.values]
  1215. if defvals and defvals[0] != 0:
  1216. field.type = FieldD.TYPE_INT64
  1217. field.default_value = str(defvals[0])
  1218. field.ClearField(str('type_name'))
  1219. else:
  1220. optional_only.field.remove(field)
  1221. elif not field.HasField('default_value'):
  1222. optional_only.field.remove(field)
  1223. if len(optional_only.field) == 0:
  1224. return b''
  1225. optional_only.ClearField(str('oneof_decl'))
  1226. optional_only.ClearField(str('nested_type'))
  1227. optional_only.ClearField(str('extension'))
  1228. optional_only.ClearField(str('enum_type'))
  1229. desc = google.protobuf.descriptor.MakeDescriptor(optional_only)
  1230. msg = reflection.MakeClass(desc)()
  1231. for field in optional_only.field:
  1232. if field.type == FieldD.TYPE_STRING:
  1233. setattr(msg, field.name, field.default_value)
  1234. elif field.type == FieldD.TYPE_BYTES:
  1235. setattr(msg, field.name, codecs.escape_decode(field.default_value)[0])
  1236. elif field.type in [FieldD.TYPE_FLOAT, FieldD.TYPE_DOUBLE]:
  1237. setattr(msg, field.name, float(field.default_value))
  1238. elif field.type == FieldD.TYPE_BOOL:
  1239. setattr(msg, field.name, field.default_value == 'true')
  1240. else:
  1241. setattr(msg, field.name, int(field.default_value))
  1242. return msg.SerializeToString()
  1243. # ---------------------------------------------------------------------------
  1244. # Processing of entire .proto files
  1245. # ---------------------------------------------------------------------------
  1246. def iterate_messages(desc, flatten = False, names = Names()):
  1247. '''Recursively find all messages. For each, yield name, DescriptorProto.'''
  1248. if hasattr(desc, 'message_type'):
  1249. submsgs = desc.message_type
  1250. else:
  1251. submsgs = desc.nested_type
  1252. for submsg in submsgs:
  1253. sub_names = names + submsg.name
  1254. if flatten:
  1255. yield Names(submsg.name), submsg
  1256. else:
  1257. yield sub_names, submsg
  1258. for x in iterate_messages(submsg, flatten, sub_names):
  1259. yield x
  1260. def iterate_extensions(desc, flatten = False, names = Names()):
  1261. '''Recursively find all extensions.
  1262. For each, yield name, FieldDescriptorProto.
  1263. '''
  1264. for extension in desc.extension:
  1265. yield names, extension
  1266. for subname, subdesc in iterate_messages(desc, flatten, names):
  1267. for extension in subdesc.extension:
  1268. yield subname, extension
  1269. def toposort2(data):
  1270. '''Topological sort.
  1271. From http://code.activestate.com/recipes/577413-topological-sort/
  1272. This function is under the MIT license.
  1273. '''
  1274. for k, v in list(data.items()):
  1275. v.discard(k) # Ignore self dependencies
  1276. extra_items_in_deps = reduce(set.union, list(data.values()), set()) - set(data.keys())
  1277. data.update(dict([(item, set()) for item in extra_items_in_deps]))
  1278. while True:
  1279. ordered = set(item for item,dep in list(data.items()) if not dep)
  1280. if not ordered:
  1281. break
  1282. for item in sorted(ordered):
  1283. yield item
  1284. data = dict([(item, (dep - ordered)) for item,dep in list(data.items())
  1285. if item not in ordered])
  1286. assert not data, "A cyclic dependency exists amongst %r" % data
  1287. def sort_dependencies(messages):
  1288. '''Sort a list of Messages based on dependencies.'''
  1289. dependencies = {}
  1290. message_by_name = {}
  1291. for message in messages:
  1292. dependencies[str(message.name)] = set(message.get_dependencies())
  1293. message_by_name[str(message.name)] = message
  1294. for msgname in toposort2(dependencies):
  1295. if msgname in message_by_name:
  1296. yield message_by_name[msgname]
  1297. def make_identifier(headername):
  1298. '''Make #ifndef identifier that contains uppercase A-Z and digits 0-9'''
  1299. result = ""
  1300. for c in headername.upper():
  1301. if c.isalnum():
  1302. result += c
  1303. else:
  1304. result += '_'
  1305. return result
  1306. class ProtoFile:
  1307. def __init__(self, fdesc, file_options):
  1308. '''Takes a FileDescriptorProto and parses it.'''
  1309. self.fdesc = fdesc
  1310. self.file_options = file_options
  1311. self.dependencies = {}
  1312. self.math_include_required = False
  1313. self.parse()
  1314. for message in self.messages:
  1315. if message.math_include_required:
  1316. self.math_include_required = True
  1317. break
  1318. # Some of types used in this file probably come from the file itself.
  1319. # Thus it has implicit dependency on itself.
  1320. self.add_dependency(self)
  1321. def parse(self):
  1322. self.enums = []
  1323. self.messages = []
  1324. self.extensions = []
  1325. mangle_names = self.file_options.mangle_names
  1326. flatten = mangle_names == nanopb_pb2.M_FLATTEN
  1327. strip_prefix = None
  1328. replacement_prefix = None
  1329. if mangle_names == nanopb_pb2.M_STRIP_PACKAGE:
  1330. strip_prefix = "." + self.fdesc.package
  1331. elif mangle_names == nanopb_pb2.M_PACKAGE_INITIALS:
  1332. strip_prefix = "." + self.fdesc.package
  1333. replacement_prefix = ""
  1334. for part in self.fdesc.package.split("."):
  1335. replacement_prefix += part[0]
  1336. elif self.file_options.package:
  1337. strip_prefix = "." + self.fdesc.package
  1338. replacement_prefix = self.file_options.package
  1339. def create_name(names):
  1340. if mangle_names in (nanopb_pb2.M_NONE, nanopb_pb2.M_PACKAGE_INITIALS):
  1341. return base_name + names
  1342. if mangle_names == nanopb_pb2.M_STRIP_PACKAGE:
  1343. return Names(names)
  1344. single_name = names
  1345. if isinstance(names, Names):
  1346. single_name = names.parts[-1]
  1347. return Names(single_name)
  1348. def mangle_field_typename(typename):
  1349. if mangle_names == nanopb_pb2.M_FLATTEN:
  1350. return "." + typename.split(".")[-1]
  1351. if strip_prefix is not None and typename.startswith(strip_prefix):
  1352. if replacement_prefix is not None:
  1353. return "." + replacement_prefix + typename[len(strip_prefix):]
  1354. else:
  1355. return typename[len(strip_prefix):]
  1356. if self.file_options.package:
  1357. return "." + replacement_prefix + typename
  1358. return typename
  1359. if replacement_prefix is not None:
  1360. base_name = Names(replacement_prefix.split('.'))
  1361. elif self.fdesc.package:
  1362. base_name = Names(self.fdesc.package.split('.'))
  1363. else:
  1364. base_name = Names()
  1365. # process source code comment locations
  1366. # ignores any locations that do not contain any comment information
  1367. self.comment_locations = {
  1368. str(list(location.path)): location
  1369. for location in self.fdesc.source_code_info.location
  1370. if location.leading_comments or location.leading_detached_comments or location.trailing_comments
  1371. }
  1372. for index, enum in enumerate(self.fdesc.enum_type):
  1373. name = create_name(enum.name)
  1374. enum_options = get_nanopb_suboptions(enum, self.file_options, name)
  1375. self.enums.append(Enum(name, enum, enum_options, index, self.comment_locations))
  1376. for index, (names, message) in enumerate(iterate_messages(self.fdesc, flatten)):
  1377. name = create_name(names)
  1378. message_options = get_nanopb_suboptions(message, self.file_options, name)
  1379. if message_options.skip_message:
  1380. continue
  1381. message = copy.deepcopy(message)
  1382. for field in message.field:
  1383. if field.type in (FieldD.TYPE_MESSAGE, FieldD.TYPE_ENUM):
  1384. field.type_name = mangle_field_typename(field.type_name)
  1385. self.messages.append(Message(name, message, message_options, index, self.comment_locations))
  1386. for index, enum in enumerate(message.enum_type):
  1387. name = create_name(names + enum.name)
  1388. enum_options = get_nanopb_suboptions(enum, message_options, name)
  1389. self.enums.append(Enum(name, enum, enum_options, index, self.comment_locations))
  1390. for names, extension in iterate_extensions(self.fdesc, flatten):
  1391. name = create_name(names + extension.name)
  1392. field_options = get_nanopb_suboptions(extension, self.file_options, name)
  1393. extension = copy.deepcopy(extension)
  1394. if extension.type in (FieldD.TYPE_MESSAGE, FieldD.TYPE_ENUM):
  1395. extension.type_name = mangle_field_typename(extension.type_name)
  1396. if field_options.type != nanopb_pb2.FT_IGNORE:
  1397. self.extensions.append(ExtensionField(name, extension, field_options))
  1398. def add_dependency(self, other):
  1399. for enum in other.enums:
  1400. self.dependencies[str(enum.names)] = enum
  1401. enum.protofile = other
  1402. for msg in other.messages:
  1403. self.dependencies[str(msg.name)] = msg
  1404. msg.protofile = other
  1405. # Fix field default values where enum short names are used.
  1406. for enum in other.enums:
  1407. if not enum.options.long_names:
  1408. for message in self.messages:
  1409. for field in message.all_fields():
  1410. if field.default in enum.value_longnames:
  1411. idx = enum.value_longnames.index(field.default)
  1412. field.default = enum.values[idx][0]
  1413. # Fix field data types where enums have negative values.
  1414. for enum in other.enums:
  1415. if not enum.has_negative():
  1416. for message in self.messages:
  1417. for field in message.all_fields():
  1418. if field.pbtype == 'ENUM' and field.ctype == enum.names:
  1419. field.pbtype = 'UENUM'
  1420. def generate_header(self, includes, headername, options):
  1421. '''Generate content for a header file.
  1422. Generates strings, which should be concatenated and stored to file.
  1423. '''
  1424. yield '/* Automatically generated nanopb header */\n'
  1425. if options.notimestamp:
  1426. yield '/* Generated by %s */\n\n' % (nanopb_version)
  1427. else:
  1428. yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
  1429. if self.fdesc.package:
  1430. symbol = make_identifier(self.fdesc.package + '_' + headername)
  1431. else:
  1432. symbol = make_identifier(headername)
  1433. yield '#ifndef PB_%s_INCLUDED\n' % symbol
  1434. yield '#define PB_%s_INCLUDED\n' % symbol
  1435. if self.math_include_required:
  1436. yield '#include <math.h>\n'
  1437. try:
  1438. yield options.libformat % ('pb.h')
  1439. except TypeError:
  1440. # no %s specified - use whatever was passed in as options.libformat
  1441. yield options.libformat
  1442. yield '\n'
  1443. for incfile in self.file_options.include:
  1444. # allow including system headers
  1445. if (incfile.startswith('<')):
  1446. yield '#include %s\n' % incfile
  1447. else:
  1448. yield options.genformat % incfile
  1449. yield '\n'
  1450. for incfile in includes:
  1451. noext = os.path.splitext(incfile)[0]
  1452. yield options.genformat % (noext + options.extension + options.header_extension)
  1453. yield '\n'
  1454. if Globals.protoc_insertion_points:
  1455. yield '/* @@protoc_insertion_point(includes) */\n'
  1456. yield '\n'
  1457. yield '#if PB_PROTO_HEADER_VERSION != 40\n'
  1458. yield '#error Regenerate this file with the current version of nanopb generator.\n'
  1459. yield '#endif\n'
  1460. yield '\n'
  1461. if self.enums:
  1462. yield '/* Enum definitions */\n'
  1463. for enum in self.enums:
  1464. yield str(enum) + '\n\n'
  1465. if self.messages:
  1466. yield '/* Struct definitions */\n'
  1467. for msg in sort_dependencies(self.messages):
  1468. yield msg.types()
  1469. yield str(msg) + '\n'
  1470. yield '\n'
  1471. if self.extensions:
  1472. yield '/* Extensions */\n'
  1473. for extension in self.extensions:
  1474. yield extension.extension_decl()
  1475. yield '\n'
  1476. if self.enums:
  1477. yield '/* Helper constants for enums */\n'
  1478. for enum in self.enums:
  1479. yield enum.auxiliary_defines() + '\n'
  1480. yield '\n'
  1481. yield '#ifdef __cplusplus\n'
  1482. yield 'extern "C" {\n'
  1483. yield '#endif\n\n'
  1484. if self.messages:
  1485. yield '/* Initializer values for message structs */\n'
  1486. for msg in self.messages:
  1487. identifier = '%s_init_default' % msg.name
  1488. yield '#define %-40s %s\n' % (identifier, msg.get_initializer(False))
  1489. for msg in self.messages:
  1490. identifier = '%s_init_zero' % msg.name
  1491. yield '#define %-40s %s\n' % (identifier, msg.get_initializer(True))
  1492. yield '\n'
  1493. yield '/* Field tags (for use in manual encoding/decoding) */\n'
  1494. for msg in sort_dependencies(self.messages):
  1495. for field in msg.fields:
  1496. yield field.tags()
  1497. for extension in self.extensions:
  1498. yield extension.tags()
  1499. yield '\n'
  1500. yield '/* Struct field encoding specification for nanopb */\n'
  1501. for msg in self.messages:
  1502. yield msg.fields_declaration(self.dependencies) + '\n'
  1503. for msg in self.messages:
  1504. yield 'extern const pb_msgdesc_t %s_msg;\n' % msg.name
  1505. yield '\n'
  1506. yield '/* Defines for backwards compatibility with code written before nanopb-0.4.0 */\n'
  1507. for msg in self.messages:
  1508. yield '#define %s_fields &%s_msg\n' % (msg.name, msg.name)
  1509. yield '\n'
  1510. yield '/* Maximum encoded size of messages (where known) */\n'
  1511. messagesizes = []
  1512. for msg in self.messages:
  1513. identifier = '%s_size' % msg.name
  1514. messagesizes.append((identifier, msg.encoded_size(self.dependencies)))
  1515. # If we require a symbol from another file, put a preprocessor if statement
  1516. # around it to prevent compilation errors if the symbol is not actually available.
  1517. local_defines = [identifier for identifier, msize in messagesizes if msize is not None]
  1518. # emit size_unions, if any
  1519. oneof_sizes = []
  1520. for msg in self.messages:
  1521. for f in msg.fields:
  1522. if isinstance(f, OneOf):
  1523. msize = f.encoded_size(self.dependencies)
  1524. if msize is not None:
  1525. oneof_sizes.append(msize)
  1526. for msize in oneof_sizes:
  1527. guard = msize.get_cpp_guard(local_defines)
  1528. if guard:
  1529. yield guard
  1530. yield msize.get_declarations()
  1531. if guard:
  1532. yield '#endif\n'
  1533. guards = {}
  1534. for identifier, msize in messagesizes:
  1535. if msize is not None:
  1536. cpp_guard = msize.get_cpp_guard(local_defines)
  1537. if cpp_guard not in guards:
  1538. guards[cpp_guard] = set()
  1539. guards[cpp_guard].add('#define %-40s %s' % (identifier, msize))
  1540. else:
  1541. yield '/* %s depends on runtime parameters */\n' % identifier
  1542. for guard, values in guards.items():
  1543. if guard:
  1544. yield guard
  1545. for v in sorted(values):
  1546. yield v
  1547. yield '\n'
  1548. if guard:
  1549. yield '#endif\n'
  1550. yield '\n'
  1551. if [msg for msg in self.messages if hasattr(msg,'msgid')]:
  1552. yield '/* Message IDs (where set with "msgid" option) */\n'
  1553. for msg in self.messages:
  1554. if hasattr(msg,'msgid'):
  1555. yield '#define PB_MSG_%d %s\n' % (msg.msgid, msg.name)
  1556. yield '\n'
  1557. symbol = make_identifier(headername.split('.')[0])
  1558. yield '#define %s_MESSAGES \\\n' % symbol
  1559. for msg in self.messages:
  1560. m = "-1"
  1561. msize = msg.encoded_size(self.dependencies)
  1562. if msize is not None:
  1563. m = msize
  1564. if hasattr(msg,'msgid'):
  1565. yield '\tPB_MSG(%d,%s,%s) \\\n' % (msg.msgid, m, msg.name)
  1566. yield '\n'
  1567. for msg in self.messages:
  1568. if hasattr(msg,'msgid'):
  1569. yield '#define %s_msgid %d\n' % (msg.name, msg.msgid)
  1570. yield '\n'
  1571. yield '#ifdef __cplusplus\n'
  1572. yield '} /* extern "C" */\n'
  1573. yield '#endif\n'
  1574. if options.cpp_descriptors:
  1575. yield '\n'
  1576. yield '#ifdef __cplusplus\n'
  1577. yield '/* Message descriptors for nanopb */\n'
  1578. yield 'namespace nanopb {\n'
  1579. for msg in self.messages:
  1580. yield msg.fields_declaration_cpp_lookup() + '\n'
  1581. yield '} // namespace nanopb\n'
  1582. yield '\n'
  1583. yield '#endif /* __cplusplus */\n'
  1584. yield '\n'
  1585. if Globals.protoc_insertion_points:
  1586. yield '/* @@protoc_insertion_point(eof) */\n'
  1587. # End of header
  1588. yield '\n#endif\n'
  1589. def generate_source(self, headername, options):
  1590. '''Generate content for a source file.'''
  1591. yield '/* Automatically generated nanopb constant definitions */\n'
  1592. if options.notimestamp:
  1593. yield '/* Generated by %s */\n\n' % (nanopb_version)
  1594. else:
  1595. yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
  1596. yield options.genformat % (headername)
  1597. yield '\n'
  1598. if Globals.protoc_insertion_points:
  1599. yield '/* @@protoc_insertion_point(includes) */\n'
  1600. yield '#if PB_PROTO_HEADER_VERSION != 40\n'
  1601. yield '#error Regenerate this file with the current version of nanopb generator.\n'
  1602. yield '#endif\n'
  1603. yield '\n'
  1604. for msg in self.messages:
  1605. yield msg.fields_definition(self.dependencies) + '\n\n'
  1606. for ext in self.extensions:
  1607. yield ext.extension_def(self.dependencies) + '\n'
  1608. for enum in self.enums:
  1609. yield enum.enum_to_string_definition() + '\n'
  1610. # Add checks for numeric limits
  1611. if self.messages:
  1612. largest_msg = max(self.messages, key = lambda m: m.count_required_fields())
  1613. largest_count = largest_msg.count_required_fields()
  1614. if largest_count > 64:
  1615. yield '\n/* Check that missing required fields will be properly detected */\n'
  1616. yield '#if PB_MAX_REQUIRED_FIELDS < %d\n' % largest_count
  1617. yield '#error Properly detecting missing required fields in %s requires \\\n' % largest_msg.name
  1618. yield ' setting PB_MAX_REQUIRED_FIELDS to %d or more.\n' % largest_count
  1619. yield '#endif\n'
  1620. # Add check for sizeof(double)
  1621. has_double = False
  1622. for msg in self.messages:
  1623. for field in msg.all_fields():
  1624. if field.ctype == 'double':
  1625. has_double = True
  1626. if has_double:
  1627. yield '\n'
  1628. yield '#ifndef PB_CONVERT_DOUBLE_FLOAT\n'
  1629. yield '/* On some platforms (such as AVR), double is really float.\n'
  1630. yield ' * To be able to encode/decode double on these platforms, you need.\n'
  1631. yield ' * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line.\n'
  1632. yield ' */\n'
  1633. yield 'PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n'
  1634. yield '#endif\n'
  1635. yield '\n'
  1636. if Globals.protoc_insertion_points:
  1637. yield '/* @@protoc_insertion_point(eof) */\n'
  1638. # ---------------------------------------------------------------------------
  1639. # Options parsing for the .proto files
  1640. # ---------------------------------------------------------------------------
  1641. from fnmatch import fnmatchcase
  1642. def read_options_file(infile):
  1643. '''Parse a separate options file to list:
  1644. [(namemask, options), ...]
  1645. '''
  1646. results = []
  1647. data = infile.read()
  1648. data = re.sub(r'/\*.*?\*/', '', data, flags = re.MULTILINE)
  1649. data = re.sub(r'//.*?$', '', data, flags = re.MULTILINE)
  1650. data = re.sub(r'#.*?$', '', data, flags = re.MULTILINE)
  1651. for i, line in enumerate(data.split('\n')):
  1652. line = line.strip()
  1653. if not line:
  1654. continue
  1655. parts = line.split(None, 1)
  1656. if len(parts) < 2:
  1657. sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
  1658. "Option lines should have space between field name and options. " +
  1659. "Skipping line: '%s'\n" % line)
  1660. sys.exit(1)
  1661. opts = nanopb_pb2.NanoPBOptions()
  1662. try:
  1663. text_format.Merge(parts[1], opts)
  1664. except Exception as e:
  1665. sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
  1666. "Unparsable option line: '%s'. " % line +
  1667. "Error: %s\n" % str(e))
  1668. sys.exit(1)
  1669. results.append((parts[0], opts))
  1670. return results
  1671. def get_nanopb_suboptions(subdesc, options, name):
  1672. '''Get copy of options, and merge information from subdesc.'''
  1673. new_options = nanopb_pb2.NanoPBOptions()
  1674. new_options.CopyFrom(options)
  1675. if hasattr(subdesc, 'syntax') and subdesc.syntax == "proto3":
  1676. new_options.proto3 = True
  1677. # Handle options defined in a separate file
  1678. dotname = '.'.join(name.parts)
  1679. for namemask, options in Globals.separate_options:
  1680. if fnmatchcase(dotname, namemask):
  1681. Globals.matched_namemasks.add(namemask)
  1682. new_options.MergeFrom(options)
  1683. # Handle options defined in .proto
  1684. if isinstance(subdesc.options, descriptor.FieldOptions):
  1685. ext_type = nanopb_pb2.nanopb
  1686. elif isinstance(subdesc.options, descriptor.FileOptions):
  1687. ext_type = nanopb_pb2.nanopb_fileopt
  1688. elif isinstance(subdesc.options, descriptor.MessageOptions):
  1689. ext_type = nanopb_pb2.nanopb_msgopt
  1690. elif isinstance(subdesc.options, descriptor.EnumOptions):
  1691. ext_type = nanopb_pb2.nanopb_enumopt
  1692. else:
  1693. raise Exception("Unknown options type")
  1694. if subdesc.options.HasExtension(ext_type):
  1695. ext = subdesc.options.Extensions[ext_type]
  1696. new_options.MergeFrom(ext)
  1697. if Globals.verbose_options:
  1698. sys.stderr.write("Options for " + dotname + ": ")
  1699. sys.stderr.write(text_format.MessageToString(new_options) + "\n")
  1700. return new_options
  1701. # ---------------------------------------------------------------------------
  1702. # Command line interface
  1703. # ---------------------------------------------------------------------------
  1704. import sys
  1705. import os.path
  1706. from optparse import OptionParser
  1707. optparser = OptionParser(
  1708. usage = "Usage: nanopb_generator.py [options] file.pb ...",
  1709. epilog = "Compile file.pb from file.proto by: 'protoc -ofile.pb file.proto'. " +
  1710. "Output will be written to file.pb.h and file.pb.c.")
  1711. optparser.add_option("--version", dest="version", action="store_true",
  1712. help="Show version info and exit")
  1713. optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[],
  1714. help="Exclude file from generated #include list.")
  1715. optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default=".pb",
  1716. help="Set extension to use instead of '.pb' for generated files. [default: %default]")
  1717. optparser.add_option("-H", "--header-extension", dest="header_extension", metavar="EXTENSION", default=".h",
  1718. help="Set extension to use for generated header files. [default: %default]")
  1719. optparser.add_option("-S", "--source-extension", dest="source_extension", metavar="EXTENSION", default=".c",
  1720. help="Set extension to use for generated source files. [default: %default]")
  1721. optparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE", default="%s.options",
  1722. help="Set name of a separate generator options file.")
  1723. optparser.add_option("-I", "--options-path", dest="options_path", metavar="DIR",
  1724. action="append", default = [],
  1725. help="Search for .options files additionally in this path")
  1726. optparser.add_option("--error-on-unmatched", dest="error_on_unmatched", action="store_true", default=False,
  1727. help ="Stop generation if there are unmatched fields in options file")
  1728. optparser.add_option("--no-error-on-unmatched", dest="error_on_unmatched", action="store_false", default=False,
  1729. help ="Continue generation if there are unmatched fields in options file (default)")
  1730. optparser.add_option("-D", "--output-dir", dest="output_dir",
  1731. metavar="OUTPUTDIR", default=None,
  1732. help="Output directory of .pb.h and .pb.c files")
  1733. optparser.add_option("-Q", "--generated-include-format", dest="genformat",
  1734. metavar="FORMAT", default='#include "%s"',
  1735. help="Set format string to use for including other .pb.h files. [default: %default]")
  1736. optparser.add_option("-L", "--library-include-format", dest="libformat",
  1737. metavar="FORMAT", default='#include <%s>',
  1738. help="Set format string to use for including the nanopb pb.h header. [default: %default]")
  1739. optparser.add_option("--strip-path", dest="strip_path", action="store_true", default=False,
  1740. help="Strip directory path from #included .pb.h file name")
  1741. optparser.add_option("--no-strip-path", dest="strip_path", action="store_false",
  1742. help="Opposite of --strip-path (default since 0.4.0)")
  1743. optparser.add_option("--cpp-descriptors", action="store_true",
  1744. help="Generate C++ descriptors to lookup by type (e.g. pb_field_t for a message)")
  1745. optparser.add_option("-T", "--no-timestamp", dest="notimestamp", action="store_true", default=True,
  1746. help="Don't add timestamp to .pb.h and .pb.c preambles (default since 0.4.0)")
  1747. optparser.add_option("-t", "--timestamp", dest="notimestamp", action="store_false", default=True,
  1748. help="Add timestamp to .pb.h and .pb.c preambles")
  1749. optparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
  1750. help="Don't print anything except errors.")
  1751. optparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
  1752. help="Print more information.")
  1753. optparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[],
  1754. help="Set generator option (max_size, max_count etc.).")
  1755. optparser.add_option("--protoc-insertion-points", dest="protoc_insertion_points", action="store_true", default=False,
  1756. help="Include insertion point comments in output for use by custom protoc plugins")
  1757. def parse_file(filename, fdesc, options):
  1758. '''Parse a single file. Returns a ProtoFile instance.'''
  1759. toplevel_options = nanopb_pb2.NanoPBOptions()
  1760. for s in options.settings:
  1761. text_format.Merge(s, toplevel_options)
  1762. if not fdesc:
  1763. data = open(filename, 'rb').read()
  1764. fdesc = descriptor.FileDescriptorSet.FromString(data).file[0]
  1765. # Check if there is a separate .options file
  1766. had_abspath = False
  1767. try:
  1768. optfilename = options.options_file % os.path.splitext(filename)[0]
  1769. except TypeError:
  1770. # No %s specified, use the filename as-is
  1771. optfilename = options.options_file
  1772. had_abspath = True
  1773. paths = ['.'] + options.options_path
  1774. for p in paths:
  1775. if os.path.isfile(os.path.join(p, optfilename)):
  1776. optfilename = os.path.join(p, optfilename)
  1777. if options.verbose:
  1778. sys.stderr.write('Reading options from ' + optfilename + '\n')
  1779. Globals.separate_options = read_options_file(open(optfilename, openmode_unicode))
  1780. break
  1781. else:
  1782. # If we are given a full filename and it does not exist, give an error.
  1783. # However, don't give error when we automatically look for .options file
  1784. # with the same name as .proto.
  1785. if options.verbose or had_abspath:
  1786. sys.stderr.write('Options file not found: ' + optfilename + '\n')
  1787. Globals.separate_options = []
  1788. Globals.matched_namemasks = set()
  1789. Globals.protoc_insertion_points = options.protoc_insertion_points
  1790. # Parse the file
  1791. file_options = get_nanopb_suboptions(fdesc, toplevel_options, Names([filename]))
  1792. f = ProtoFile(fdesc, file_options)
  1793. f.optfilename = optfilename
  1794. return f
  1795. def process_file(filename, fdesc, options, other_files = {}):
  1796. '''Process a single file.
  1797. filename: The full path to the .proto or .pb source file, as string.
  1798. fdesc: The loaded FileDescriptorSet, or None to read from the input file.
  1799. options: Command line options as they come from OptionsParser.
  1800. Returns a dict:
  1801. {'headername': Name of header file,
  1802. 'headerdata': Data for the .h header file,
  1803. 'sourcename': Name of the source code file,
  1804. 'sourcedata': Data for the .c source code file
  1805. }
  1806. '''
  1807. f = parse_file(filename, fdesc, options)
  1808. # Check the list of dependencies, and if they are available in other_files,
  1809. # add them to be considered for import resolving. Recursively add any files
  1810. # imported by the dependencies.
  1811. deps = list(f.fdesc.dependency)
  1812. while deps:
  1813. dep = deps.pop(0)
  1814. if dep in other_files:
  1815. f.add_dependency(other_files[dep])
  1816. deps += list(other_files[dep].fdesc.dependency)
  1817. # Decide the file names
  1818. noext = os.path.splitext(filename)[0]
  1819. headername = noext + options.extension + options.header_extension
  1820. sourcename = noext + options.extension + options.source_extension
  1821. if options.strip_path:
  1822. headerbasename = os.path.basename(headername)
  1823. else:
  1824. headerbasename = headername
  1825. # List of .proto files that should not be included in the C header file
  1826. # even if they are mentioned in the source .proto.
  1827. excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto'] + options.exclude + list(f.file_options.exclude)
  1828. includes = [d for d in f.fdesc.dependency if d not in excludes]
  1829. headerdata = ''.join(f.generate_header(includes, headerbasename, options))
  1830. sourcedata = ''.join(f.generate_source(headerbasename, options))
  1831. # Check if there were any lines in .options that did not match a member
  1832. unmatched = [n for n,o in Globals.separate_options if n not in Globals.matched_namemasks]
  1833. if unmatched:
  1834. if options.error_on_unmatched:
  1835. raise Exception("Following patterns in " + f.optfilename + " did not match any fields: "
  1836. + ', '.join(unmatched));
  1837. elif not options.quiet:
  1838. sys.stderr.write("Following patterns in " + f.optfilename + " did not match any fields: "
  1839. + ', '.join(unmatched) + "\n")
  1840. if not Globals.verbose_options:
  1841. sys.stderr.write("Use protoc --nanopb-out=-v:. to see a list of the field names.\n")
  1842. return {'headername': headername, 'headerdata': headerdata,
  1843. 'sourcename': sourcename, 'sourcedata': sourcedata}
  1844. def main_cli():
  1845. '''Main function when invoked directly from the command line.'''
  1846. options, filenames = optparser.parse_args()
  1847. if options.version:
  1848. print(nanopb_version)
  1849. sys.exit(0)
  1850. if not filenames:
  1851. optparser.print_help()
  1852. sys.exit(1)
  1853. if options.quiet:
  1854. options.verbose = False
  1855. if options.output_dir and not os.path.exists(options.output_dir):
  1856. optparser.print_help()
  1857. sys.stderr.write("\noutput_dir does not exist: %s\n" % options.output_dir)
  1858. sys.exit(1)
  1859. if options.verbose:
  1860. sys.stderr.write("Nanopb version %s\n" % nanopb_version)
  1861. sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
  1862. % (google.protobuf.__file__, google.protobuf.__version__))
  1863. # Load .pb files into memory and compile any .proto files.
  1864. include_path = ['-I%s' % p for p in options.options_path]
  1865. all_fdescs = {}
  1866. out_fdescs = {}
  1867. for filename in filenames:
  1868. if filename.endswith(".proto"):
  1869. with TemporaryDirectory() as tmpdir:
  1870. tmpname = os.path.join(tmpdir, os.path.basename(filename) + ".pb")
  1871. status = invoke_protoc(["protoc"] + include_path + ['--include_imports', '--include_source_info', '-o' + tmpname, filename])
  1872. if status != 0: sys.exit(status)
  1873. data = open(tmpname, 'rb').read()
  1874. else:
  1875. data = open(filename, 'rb').read()
  1876. fdescs = descriptor.FileDescriptorSet.FromString(data).file
  1877. last_fdesc = fdescs[-1]
  1878. for fdesc in fdescs:
  1879. all_fdescs[fdesc.name] = fdesc
  1880. out_fdescs[last_fdesc.name] = last_fdesc
  1881. # Process any include files first, in order to have them
  1882. # available as dependencies
  1883. other_files = {}
  1884. for fdesc in all_fdescs.values():
  1885. other_files[fdesc.name] = parse_file(fdesc.name, fdesc, options)
  1886. # Then generate the headers / sources
  1887. Globals.verbose_options = options.verbose
  1888. for fdesc in out_fdescs.values():
  1889. results = process_file(fdesc.name, fdesc, options, other_files)
  1890. base_dir = options.output_dir or ''
  1891. to_write = [
  1892. (os.path.join(base_dir, results['headername']), results['headerdata']),
  1893. (os.path.join(base_dir, results['sourcename']), results['sourcedata']),
  1894. ]
  1895. if not options.quiet:
  1896. paths = " and ".join([x[0] for x in to_write])
  1897. sys.stderr.write("Writing to %s\n" % paths)
  1898. for path, data in to_write:
  1899. dirname = os.path.dirname(path)
  1900. if dirname and not os.path.exists(dirname):
  1901. os.makedirs(dirname)
  1902. with open(path, 'w') as f:
  1903. f.write(data)
  1904. def main_plugin():
  1905. '''Main function when invoked as a protoc plugin.'''
  1906. import io, sys
  1907. if sys.platform == "win32":
  1908. import os, msvcrt
  1909. # Set stdin and stdout to binary mode
  1910. msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
  1911. msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
  1912. data = io.open(sys.stdin.fileno(), "rb").read()
  1913. request = plugin_pb2.CodeGeneratorRequest.FromString(data)
  1914. try:
  1915. # Versions of Python prior to 2.7.3 do not support unicode
  1916. # input to shlex.split(). Try to convert to str if possible.
  1917. params = str(request.parameter)
  1918. except UnicodeEncodeError:
  1919. params = request.parameter
  1920. import shlex
  1921. args = shlex.split(params)
  1922. if len(args) == 1 and ',' in args[0]:
  1923. # For compatibility with other protoc plugins, support options
  1924. # separated by comma.
  1925. lex = shlex.shlex(params)
  1926. lex.whitespace_split = True
  1927. lex.whitespace = ','
  1928. lex.commenters = ''
  1929. args = list(lex)
  1930. optparser.usage = "Usage: protoc --nanopb_out=[options][,more_options]:outdir file.proto"
  1931. optparser.epilog = "Output will be written to file.pb.h and file.pb.c."
  1932. if '-h' in args or '--help' in args:
  1933. # By default optparser prints help to stdout, which doesn't work for
  1934. # protoc plugins.
  1935. optparser.print_help(sys.stderr)
  1936. sys.exit(1)
  1937. options, dummy = optparser.parse_args(args)
  1938. if options.version:
  1939. sys.stderr.write('%s\n' % (nanopb_version))
  1940. sys.exit(0)
  1941. Globals.verbose_options = options.verbose
  1942. if options.verbose:
  1943. sys.stderr.write("Nanopb version %s\n" % nanopb_version)
  1944. sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
  1945. % (google.protobuf.__file__, google.protobuf.__version__))
  1946. response = plugin_pb2.CodeGeneratorResponse()
  1947. # Google's protoc does not currently indicate the full path of proto files.
  1948. # Instead always add the main file path to the search dirs, that works for
  1949. # the common case.
  1950. import os.path
  1951. options.options_path.append(os.path.dirname(request.file_to_generate[0]))
  1952. # Process any include files first, in order to have them
  1953. # available as dependencies
  1954. other_files = {}
  1955. for fdesc in request.proto_file:
  1956. other_files[fdesc.name] = parse_file(fdesc.name, fdesc, options)
  1957. for filename in request.file_to_generate:
  1958. for fdesc in request.proto_file:
  1959. if fdesc.name == filename:
  1960. results = process_file(filename, fdesc, options, other_files)
  1961. f = response.file.add()
  1962. f.name = results['headername']
  1963. f.content = results['headerdata']
  1964. f = response.file.add()
  1965. f.name = results['sourcename']
  1966. f.content = results['sourcedata']
  1967. if hasattr(plugin_pb2.CodeGeneratorResponse, "FEATURE_PROTO3_OPTIONAL"):
  1968. response.supported_features = plugin_pb2.CodeGeneratorResponse.FEATURE_PROTO3_OPTIONAL
  1969. io.open(sys.stdout.fileno(), "wb").write(response.SerializeToString())
  1970. if __name__ == '__main__':
  1971. # Check if we are running as a plugin under protoc
  1972. if 'protoc-gen-' in sys.argv[0] or '--protoc-plugin' in sys.argv:
  1973. main_plugin()
  1974. else:
  1975. main_cli()