17 """A dumb configuration.rst generator that relies on source comments."""    22 TARGET = 
'docs/source/configuration.rst'    24 PREFIX = 
""".. Copyright 2016 The Cartographer Authors    26 .. Licensed under the Apache License, Version 2.0 (the "License");    27    you may not use this file except in compliance with the License.    28    You may obtain a copy of the License at    30 ..      http://www.apache.org/licenses/LICENSE-2.0    32 .. Unless required by applicable law or agreed to in writing, software    33    distributed under the License is distributed on an "AS IS" BASIS,    34    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.    35    See the License for the specific language governing permissions and    36    limitations under the License.    42 .. DO NOT EDIT! This documentation is AUTOGENERATED, please edit .proto files as    43 .. needed and run scripts/update_configuration_doc.py.    48 NODOC = 
'Not yet documented.'    52   def __init__(self, name, package, preceding_comments):
    63     self.options.append((option_type, name, comments))
    67   """Computes the list of Message objects of the option messages in a file."""    68   line_iter = iter(proto_file)
    71   for line 
in line_iter:
    73     if line.startswith(
'package'):
    74       assert line[-1] == 
';'    75       package = line[7:-1].strip()
    78       assert '}' not in line
    84     for line 
in line_iter:
    89       elif line.startswith(
'//'):
    91         comment = line[2:].strip()
    92         if not comment.startswith(
'NEXT ID:'):
    93           message_comments.append(comment)
    94       elif line.startswith(
'message') 
and line.endswith(
'Options {'):
    95         message_name = package + 
'.' + line[7:-1].strip()
   100     print(
" Found '%s'." % message_name)
   101     message = 
Message(message_name, package, message_comments)
   102     message_list.append(message)
   107     for line 
in line_iter:
   111         message.AddTrailingComments(option_comments)
   113       elif line.startswith(
'//'):
   114         comment = line[2:].strip()
   115         if not comment.startswith(
'NEXT ID:'):
   116           option_comments.append(comment)
   118         assert not line.startswith(
'required')
   119         if multiline 
is None:
   120           if line.startswith(
'optional'):
   125           multiline += 
' ' + line
   126         if not multiline.endswith(
';'):
   128         assert len(multiline) < 200
   129         option = multiline[8:-1].strip().rstrip(
'0123456789').strip()
   130         assert option.endswith(
'=')
   131         option_type, option_name = option[:-1].strip().split();
   132         print(
"  Option '%s'." % option_name)
   134         message.AddOption(option_type, option_name, option_comments)
   141   """Recursively parses all proto files into a list of Message objects."""   143   for dirpath, dirnames, filenames 
in os.walk(root):
   144     for name 
in filenames:
   145       if name.endswith(
'.proto'):
   146         path = os.path.join(dirpath, name)
   147         print(
"Found '%s'..." % path)
   148         assert not os.path.islink(path)
   149         message_list.extend(
ParseProtoFile(io.open(path, encoding=
'UTF-8')))
   154   """Raised when resolving a message name fails."""   157 class Resolver(object):
   161   def Resolve(self, message_name, package_name):
   162     if message_name 
in (
'bool', 
'double', 
'float', 
'int32'):
   164     if message_name.startswith(
'.'):
   165       return message_name[1:]
   166     package = package_name.split(
'.')
   167     for levels 
in range(len(package), -1, -1):
   168       candidate = 
'.'.join(package[0:levels]) + 
'.' + message_name
   172         'Resolving %s in %s failed.' % (message_name, package_name))
   176   """Recursively generates documentation, sorts and writes it."""   178   resolver = 
Resolver(message.name 
for message 
in message_list)
   181   for message 
in message_list:
   182     content = [message.name, 
'=' * len(message.name), 
'']
   183     assert message.name 
not in output_dict
   184     output_dict[message.name] = content
   185     if message.preceding_comments:
   186       content.extend(preceding_comments)
   188     for option_type, option_name, option_comments 
in message.options:
   190           resolver.Resolve(option_type, message.package) + 
' ' + option_name)
   191       if not option_comments:
   192         option_comments.append(NODOC)
   193       for comment 
in option_comments:
   194         content.append(
'  ' + comment)
   196     if message.trailing_comments:
   197       content.extend(message.trailing_comments)
   200   output = [
'\n'.join(doc) 
for key, doc 
in sorted(list(output_dict.items()))]
   201   print(
'\n\n'.join(output), file=output_file)
   205   assert not os.path.islink(TARGET) 
and os.path.isfile(TARGET)
   206   assert not os.path.islink(ROOT) 
and os.path.isdir(ROOT)
   207   output_file = io.open(TARGET, mode=
'w', encoding=
'UTF-8', newline=
'\n')
   208   output_file.write(PREFIX)
   210   output_file.write(SUFFIX)
   214 if __name__ == 
"__main__":
 def Resolve(self, message_name, package_name)
def __init__(self, name_set)
def AddTrailingComments(self, comments)
def GenerateDocumentation(output_file, root)
def ParseProtoFile(proto_file)
def ParseProtoFilesRecursively(root)
def AddOption(self, option_type, name, comments)
def __init__(self, name, package, preceding_comments)