|
37 | 37 |
|
38 | 38 |
|
39 | 39 | def main():
|
40 |
| - # Parse request |
41 |
| - data = io.open(sys.stdin.fileno(), 'rb').read() |
42 |
| - request = plugin_pb2.CodeGeneratorRequest.FromString(data) |
| 40 | + # Parse request |
| 41 | + data = io.open(sys.stdin.fileno(), 'rb').read() |
| 42 | + request = plugin_pb2.CodeGeneratorRequest.FromString(data) |
43 | 43 |
|
44 |
| - # Preprocess inputs, changing types and nanopb defaults |
45 |
| - options = nanopb_parse_options(request) |
46 |
| - use_anonymous_oneof(request) |
47 |
| - use_bytes_for_strings(request) |
| 44 | + # Preprocess inputs, changing types and nanopb defaults |
| 45 | + options = nanopb_parse_options(request) |
| 46 | + use_anonymous_oneof(request) |
| 47 | + use_bytes_for_strings(request) |
48 | 48 |
|
49 |
| - # Generate code |
50 |
| - parsed_files = nanopb_parse_files(request, options) |
51 |
| - results = nanopb_generate(request, options, parsed_files) |
52 |
| - response = nanopb_write(results) |
| 49 | + # Generate code |
| 50 | + parsed_files = nanopb_parse_files(request, options) |
| 51 | + results = nanopb_generate(request, options, parsed_files) |
| 52 | + response = nanopb_write(results) |
53 | 53 |
|
54 |
| - # Write to stdout |
55 |
| - io.open(sys.stdout.fileno(), 'wb').write(response.SerializeToString()) |
| 54 | + # Write to stdout |
| 55 | + io.open(sys.stdout.fileno(), 'wb').write(response.SerializeToString()) |
56 | 56 |
|
57 | 57 |
|
58 | 58 | def use_anonymous_oneof(request):
|
59 |
| - """Use anonymous unions for oneofs if they're the only one in a message. |
| 59 | + """Use anonymous unions for oneofs if they're the only one in a message. |
60 | 60 |
|
61 |
| - Equivalent to setting this option on messages where it applies: |
| 61 | + Equivalent to setting this option on messages where it applies: |
62 | 62 |
|
63 |
| - option (nanopb).anonymous_oneof = true; |
| 63 | + option (nanopb).anonymous_oneof = true; |
64 | 64 |
|
65 |
| - Args: |
66 |
| - request: A CodeGeneratorRequest from protoc. The descriptors are modified |
67 |
| - in place. |
68 |
| - """ |
69 |
| - for _, message_type in iterate_messages(request): |
70 |
| - if len(message_type.oneof_decl) == 1: |
71 |
| - ext = message_type.options.Extensions[nanopb_pb2.nanopb_msgopt] |
72 |
| - ext.anonymous_oneof = True |
| 65 | + Args: |
| 66 | + request: A CodeGeneratorRequest from protoc. The descriptors are modified |
| 67 | + in place. |
| 68 | + """ |
| 69 | + for _, message_type in iterate_messages(request): |
| 70 | + if len(message_type.oneof_decl) == 1: |
| 71 | + ext = message_type.options.Extensions[nanopb_pb2.nanopb_msgopt] |
| 72 | + ext.anonymous_oneof = True |
73 | 73 |
|
74 | 74 |
|
75 | 75 | def use_bytes_for_strings(request):
|
76 |
| - """Always use the bytes type instead of string. |
| 76 | + """Always use the bytes type instead of string. |
77 | 77 |
|
78 |
| - By default, nanopb renders proto strings as having the C type char* and does |
79 |
| - not include a separate size field, getting the length of the string via |
80 |
| - strlen(). Unfortunately this prevents using strings with embedded nulls, |
81 |
| - which is something the wire format supports. |
| 78 | + By default, nanopb renders proto strings as having the C type char* and does |
| 79 | + not include a separate size field, getting the length of the string via |
| 80 | + strlen(). Unfortunately this prevents using strings with embedded nulls, |
| 81 | + which is something the wire format supports. |
82 | 82 |
|
83 |
| - Fortunately, string and bytes proto fields are identical on the wire and |
84 |
| - nanopb's bytes representation does have an explicit length, so this function |
85 |
| - changes the types of all string fields to bytes. The generated code will now |
86 |
| - contain pb_bytes_array_t. |
| 83 | + Fortunately, string and bytes proto fields are identical on the wire and |
| 84 | + nanopb's bytes representation does have an explicit length, so this function |
| 85 | + changes the types of all string fields to bytes. The generated code will now |
| 86 | + contain pb_bytes_array_t. |
87 | 87 |
|
88 |
| - There's no nanopb or proto option to control this behavior. The equivalent |
89 |
| - would be to hand edit all the .proto files :-(. |
| 88 | + There's no nanopb or proto option to control this behavior. The equivalent |
| 89 | + would be to hand edit all the .proto files :-(. |
90 | 90 |
|
91 |
| - Args: |
92 |
| - request: A CodeGeneratorRequest from protoc. The descriptors are modified |
93 |
| - in place. |
94 |
| - """ |
95 |
| - for names, message_type in iterate_messages(request): |
96 |
| - for field in message_type.field: |
97 |
| - if field.type == FieldDescriptorProto.TYPE_STRING: |
98 |
| - field.type = FieldDescriptorProto.TYPE_BYTES |
| 91 | + Args: |
| 92 | + request: A CodeGeneratorRequest from protoc. The descriptors are modified |
| 93 | + in place. |
| 94 | + """ |
| 95 | + for names, message_type in iterate_messages(request): |
| 96 | + for field in message_type.field: |
| 97 | + if field.type == FieldDescriptorProto.TYPE_STRING: |
| 98 | + field.type = FieldDescriptorProto.TYPE_BYTES |
99 | 99 |
|
100 | 100 |
|
101 | 101 | def iterate_messages(request):
|
102 |
| - """Iterates over all messages in all files in the request. |
| 102 | + """Iterates over all messages in all files in the request. |
103 | 103 |
|
104 |
| - Args: |
105 |
| - request: A CodeGeneratorRequest passed by protoc. |
| 104 | + Args: |
| 105 | + request: A CodeGeneratorRequest passed by protoc. |
106 | 106 |
|
107 |
| - Yields: |
108 |
| - names: a nanopb.Names object giving a qualified name for the message |
109 |
| - message_type: a DescriptorProto for the message. |
110 |
| - """ |
111 |
| - for fdesc in request.proto_file: |
112 |
| - for names, message_type in nanopb.iterate_messages(fdesc): |
113 |
| - yield names, message_type |
| 107 | + Yields: |
| 108 | + names: a nanopb.Names object giving a qualified name for the message |
| 109 | + message_type: a DescriptorProto for the message. |
| 110 | + """ |
| 111 | + for fdesc in request.proto_file: |
| 112 | + for names, message_type in nanopb.iterate_messages(fdesc): |
| 113 | + yield names, message_type |
114 | 114 |
|
115 | 115 |
|
116 | 116 | def nanopb_parse_options(request):
|
117 |
| - """Parses nanopb_generator command-line options from the given request. |
| 117 | + """Parses nanopb_generator command-line options from the given request. |
118 | 118 |
|
119 |
| - Args: |
120 |
| - request: A CodeGeneratorRequest passed by protoc. |
| 119 | + Args: |
| 120 | + request: A CodeGeneratorRequest passed by protoc. |
121 | 121 |
|
122 |
| - Returns: |
123 |
| - Nanopb's options object, obtained via optparser. |
124 |
| - """ |
125 |
| - # Parse options the same as nanopb_generator.main_plugin() does. |
126 |
| - args = shlex.split(request.parameter) |
127 |
| - options, _ = nanopb.optparser.parse_args(args) |
| 122 | + Returns: |
| 123 | + Nanopb's options object, obtained via optparser. |
| 124 | + """ |
| 125 | + # Parse options the same as nanopb_generator.main_plugin() does. |
| 126 | + args = shlex.split(request.parameter) |
| 127 | + options, _ = nanopb.optparser.parse_args(args) |
128 | 128 |
|
129 |
| - # Force certain options |
130 |
| - options.extension = '.nanopb' |
131 |
| - options.verbose = True |
| 129 | + # Force certain options |
| 130 | + options.extension = '.nanopb' |
| 131 | + options.verbose = True |
132 | 132 |
|
133 |
| - # Replicate options setup from nanopb_generator.main_plugin. |
134 |
| - nanopb.Globals.verbose_options = options.verbose |
| 133 | + # Replicate options setup from nanopb_generator.main_plugin. |
| 134 | + nanopb.Globals.verbose_options = options.verbose |
135 | 135 |
|
136 |
| - # Google's protoc does not currently indicate the full path of proto files. |
137 |
| - # Instead always add the main file path to the search dirs, that works for |
138 |
| - # the common case. |
139 |
| - options.options_path.append(os.path.dirname(request.file_to_generate[0])) |
140 |
| - return options |
| 136 | + # Google's protoc does not currently indicate the full path of proto files. |
| 137 | + # Instead always add the main file path to the search dirs, that works for |
| 138 | + # the common case. |
| 139 | + options.options_path.append(os.path.dirname(request.file_to_generate[0])) |
| 140 | + return options |
141 | 141 |
|
142 | 142 |
|
143 | 143 | def nanopb_parse_files(request, options):
|
144 |
| - """Parses the files in the given request into nanopb ProtoFile objects. |
145 |
| -
|
146 |
| - Args: |
147 |
| - request: A CodeGeneratorRequest, as passed by protoc. |
148 |
| - options: The command-line options from nanopb_parse_options. |
149 |
| -
|
150 |
| - Returns: |
151 |
| - A dictionary of filename to nanopb.ProtoFile objects, each one representing |
152 |
| - the parsed form of a FileDescriptor in the request. |
153 |
| - """ |
154 |
| - # Process any include files first, in order to have them |
155 |
| - # available as dependencies |
156 |
| - parsed_files = {} |
157 |
| - for fdesc in request.proto_file: |
158 |
| - parsed_files[fdesc.name] = nanopb.parse_file(fdesc.name, fdesc, options) |
| 144 | + """Parses the files in the given request into nanopb ProtoFile objects. |
| 145 | +
|
| 146 | + Args: |
| 147 | + request: A CodeGeneratorRequest, as passed by protoc. |
| 148 | + options: The command-line options from nanopb_parse_options. |
| 149 | +
|
| 150 | + Returns: |
| 151 | + A dictionary of filename to nanopb.ProtoFile objects, each one |
| 152 | + representing the parsed form of a FileDescriptor in the request. |
| 153 | + """ |
| 154 | + # Process any include files first, in order to have them |
| 155 | + # available as dependencies |
| 156 | + parsed_files = {} |
| 157 | + for fdesc in request.proto_file: |
| 158 | + parsed_files[fdesc.name] = nanopb.parse_file(fdesc.name, fdesc, options) |
159 | 159 |
|
160 |
| - return parsed_files |
| 160 | + return parsed_files |
161 | 161 |
|
162 | 162 |
|
163 | 163 | def nanopb_generate(request, options, parsed_files):
|
164 |
| - """Generates C sources from the given parsed files. |
165 |
| -
|
166 |
| - Args: |
167 |
| - request: A CodeGeneratorRequest, as passed by protoc. |
168 |
| - options: The command-line options from nanopb_parse_options. |
169 |
| - parsed_files: A dictionary of filename to nanopb.ProtoFile, as returned by |
170 |
| - nanopb_parse_files(). |
171 |
| -
|
172 |
| - Returns: |
173 |
| - A list of nanopb output dictionaries, each one representing the code |
174 |
| - generation result for each file to generate. The output dictionaries have |
175 |
| - the following form: |
176 |
| -
|
177 |
| - { |
178 |
| - 'headername': Name of header file, ending in .h, |
179 |
| - 'headerdata': Contents of the header file, |
180 |
| - 'sourcename': Name of the source code file, ending in .c, |
181 |
| - 'sourcedata': Contents of the source code file |
182 |
| - } |
183 |
| - """ |
184 |
| - output = [] |
185 |
| - |
186 |
| - for filename in request.file_to_generate: |
187 |
| - for fdesc in request.proto_file: |
188 |
| - if fdesc.name == filename: |
189 |
| - results = nanopb.process_file(filename, fdesc, options, parsed_files) |
190 |
| - output.append(results) |
191 |
| - |
192 |
| - return output |
| 164 | + """Generates C sources from the given parsed files. |
| 165 | +
|
| 166 | + Args: |
| 167 | + request: A CodeGeneratorRequest, as passed by protoc. |
| 168 | + options: The command-line options from nanopb_parse_options. |
| 169 | + parsed_files: A dictionary of filename to nanopb.ProtoFile, as returned by |
| 170 | + nanopb_parse_files(). |
| 171 | +
|
| 172 | + Returns: |
| 173 | + A list of nanopb output dictionaries, each one representing the code |
| 174 | + generation result for each file to generate. The output dictionaries have |
| 175 | + the following form: |
| 176 | +
|
| 177 | + { |
| 178 | + 'headername': Name of header file, ending in .h, |
| 179 | + 'headerdata': Contents of the header file, |
| 180 | + 'sourcename': Name of the source code file, ending in .c, |
| 181 | + 'sourcedata': Contents of the source code file |
| 182 | + } |
| 183 | + """ |
| 184 | + output = [] |
| 185 | + |
| 186 | + for filename in request.file_to_generate: |
| 187 | + for fdesc in request.proto_file: |
| 188 | + if fdesc.name == filename: |
| 189 | + results = nanopb.process_file( |
| 190 | + filename, fdesc, options, parsed_files) |
| 191 | + output.append(results) |
| 192 | + |
| 193 | + return output |
193 | 194 |
|
194 | 195 |
|
195 | 196 | def nanopb_write(results):
|
196 |
| - """Translates nanopb output dictionaries to a CodeGeneratorResponse. |
| 197 | + """Translates nanopb output dictionaries to a CodeGeneratorResponse. |
197 | 198 |
|
198 |
| - Args: |
199 |
| - results: A list of generated source dictionaries, as returned by |
200 |
| - nanopb_generate(). |
| 199 | + Args: |
| 200 | + results: A list of generated source dictionaries, as returned by |
| 201 | + nanopb_generate(). |
201 | 202 |
|
202 |
| - Returns: |
203 |
| - A CodeGeneratorResponse describing the result of the code generation |
204 |
| - process to protoc. |
205 |
| - """ |
206 |
| - response = plugin_pb2.CodeGeneratorResponse() |
| 203 | + Returns: |
| 204 | + A CodeGeneratorResponse describing the result of the code generation |
| 205 | + process to protoc. |
| 206 | + """ |
| 207 | + response = plugin_pb2.CodeGeneratorResponse() |
207 | 208 |
|
208 |
| - for result in results: |
209 |
| - f = response.file.add() |
210 |
| - f.name = result['headername'] |
211 |
| - f.content = result['headerdata'] |
| 209 | + for result in results: |
| 210 | + f = response.file.add() |
| 211 | + f.name = result['headername'] |
| 212 | + f.content = result['headerdata'] |
212 | 213 |
|
213 |
| - f = response.file.add() |
214 |
| - f.name = result['sourcename'] |
215 |
| - f.content = result['sourcedata'] |
| 214 | + f = response.file.add() |
| 215 | + f.name = result['sourcename'] |
| 216 | + f.content = result['sourcedata'] |
216 | 217 |
|
217 |
| - return response |
| 218 | + return response |
218 | 219 |
|
219 | 220 |
|
220 | 221 | if __name__ == '__main__':
|
221 |
| - main() |
| 222 | + main() |
0 commit comments