00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 """A dumb configuration.rst generator that relies on source comments."""
00018
00019 import io
00020 import os
00021
00022 TARGET = 'docs/source/configuration.rst'
00023 ROOT = 'cartographer'
00024 PREFIX = """.. Copyright 2016 The Cartographer Authors
00025
00026 .. Licensed under the Apache License, Version 2.0 (the "License");
00027 you may not use this file except in compliance with the License.
00028 You may obtain a copy of the License at
00029
00030 .. http://www.apache.org/licenses/LICENSE-2.0
00031
00032 .. Unless required by applicable law or agreed to in writing, software
00033 distributed under the License is distributed on an "AS IS" BASIS,
00034 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00035 See the License for the specific language governing permissions and
00036 limitations under the License.
00037
00038 =============
00039 Configuration
00040 =============
00041
00042 .. DO NOT EDIT! This documentation is AUTOGENERATED, please edit .proto files as
00043 .. needed and run scripts/update_configuration_doc.py.
00044
00045 """
00046 SUFFIX = """
00047 """
00048 NODOC = 'Not yet documented.'
00049
00050
00051 class Message(object):
00052 def __init__(self, name, package, preceding_comments):
00053 self.name = name
00054 self.package = package
00055 self.preceding_comments = preceding_comments
00056 self.trailing_comments = None
00057 self.options = []
00058
00059 def AddTrailingComments(self, comments):
00060 self.trailing_comments = comments
00061
00062 def AddOption(self, option_type, name, comments):
00063 self.options.append((option_type, name, comments))
00064
00065
00066 def ParseProtoFile(proto_file):
00067 """Computes the list of Message objects of the option messages in a file."""
00068 line_iter = iter(proto_file)
00069
00070
00071 for line in line_iter:
00072 line = line.strip()
00073 if line.startswith('package'):
00074 assert line[-1] == ';'
00075 package = line[7:-1].strip()
00076 break
00077 else:
00078 assert '}' not in line
00079
00080 message_list = []
00081 while True:
00082
00083 message_comments = []
00084 for line in line_iter:
00085 line = line.strip()
00086 if '}' in line:
00087
00088 message_comments = []
00089 elif line.startswith('//'):
00090
00091 comment = line[2:].strip()
00092 if not comment.startswith('NEXT ID:'):
00093 message_comments.append(comment)
00094 elif line.startswith('message') and line.endswith('Options {'):
00095 message_name = package + '.' + line[7:-1].strip()
00096 break
00097 else:
00098
00099 break
00100 print(" Found '%s'." % message_name)
00101 message = Message(message_name, package, message_comments)
00102 message_list.append(message)
00103
00104
00105 option_comments = []
00106 multiline = ''
00107 for line in line_iter:
00108 line = line.strip()
00109 if '}' in line:
00110
00111 message.AddTrailingComments(option_comments)
00112 break
00113 elif line.startswith('//'):
00114 comment = line[2:].strip()
00115 if not comment.startswith('NEXT ID:'):
00116 option_comments.append(comment)
00117 else:
00118 assert not line.startswith('required')
00119 multiline += ' ' + line
00120 if not multiline.endswith(';'):
00121 continue
00122 assert len(multiline) < 200
00123 option = multiline[:-1].strip().rstrip('0123456789').strip()
00124 assert option.endswith('=')
00125 if option.startswith('repeated'):
00126 option = option[8:]
00127 option_type, option_name = option[:-1].strip().split();
00128 print(" Option '%s'." % option_name)
00129 multiline = ''
00130 message.AddOption(option_type, option_name, option_comments)
00131 option_comments = []
00132
00133 return message_list
00134
00135
00136 def ParseProtoFilesRecursively(root):
00137 """Recursively parses all proto files into a list of Message objects."""
00138 message_list = []
00139 for dirpath, dirnames, filenames in os.walk(root):
00140 for name in filenames:
00141 if name.endswith('.proto'):
00142 path = os.path.join(dirpath, name)
00143 print("Found '%s'..." % path)
00144 assert not os.path.islink(path)
00145 message_list.extend(ParseProtoFile(io.open(path, encoding='UTF-8')))
00146 return message_list
00147
00148
00149 class ResolutionError(Exception):
00150 """Raised when resolving a message name fails."""
00151
00152
00153 class Resolver(object):
00154 def __init__(self, name_set):
00155 self.name_set = set(iter(name_set))
00156
00157 def Resolve(self, message_name, package_name):
00158 if message_name in ('bool', 'double', 'float', 'int32'):
00159 return message_name
00160 if message_name.startswith('.'):
00161 return message_name[1:]
00162 package = package_name.split('.')
00163 for levels in range(len(package), -1, -1):
00164 candidate = '.'.join(package[0:levels]) + '.' + message_name
00165 if candidate in self.name_set:
00166 return candidate
00167 raise ResolutionError(
00168 'Resolving %s in %s failed.' % (message_name, package_name))
00169
00170
00171 def GenerateDocumentation(output_file, root):
00172 """Recursively generates documentation, sorts and writes it."""
00173 message_list = ParseProtoFilesRecursively(root)
00174 resolver = Resolver(message.name for message in message_list)
00175
00176 output_dict = {}
00177 for message in message_list:
00178 content = [message.name, '=' * len(message.name), '']
00179 assert message.name not in output_dict
00180 output_dict[message.name] = content
00181 if message.preceding_comments:
00182 content.extend(preceding_comments)
00183 content.append('')
00184 for option_type, option_name, option_comments in message.options:
00185
00186
00187
00188 if option_type in ('InitialTrajectoryPose',):
00189 continue
00190 content.append(
00191 resolver.Resolve(option_type, message.package) + ' ' + option_name)
00192 if not option_comments:
00193 option_comments.append(NODOC)
00194 for comment in option_comments:
00195 content.append(' ' + comment)
00196 content.append('')
00197 if message.trailing_comments:
00198 content.extend(message.trailing_comments)
00199 content.append('')
00200
00201 output = ['\n'.join(doc) for key, doc in sorted(list(output_dict.items()))]
00202 print('\n\n'.join(output), file=output_file)
00203
00204
00205 def main():
00206 assert not os.path.islink(TARGET) and os.path.isfile(TARGET)
00207 assert not os.path.islink(ROOT) and os.path.isdir(ROOT)
00208 output_file = io.open(TARGET, mode='w', encoding='UTF-8', newline='\n')
00209 output_file.write(PREFIX)
00210 GenerateDocumentation(output_file, ROOT)
00211 output_file.write(SUFFIX)
00212 output_file.close()
00213
00214
00215 if __name__ == "__main__":
00216 main()