OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of protoc; | 5 part of protoc; |
6 | 6 |
7 /// Generates the Dart output files for one .proto input file. | 7 /// Generates the Dart output files for one .proto input file. |
8 /// | 8 /// |
9 /// Outputs include .pb.dart, pbenum.dart, and .pbjson.dart. | 9 /// Outputs include .pb.dart, pbenum.dart, and .pbjson.dart. |
10 class FileGenerator extends ProtobufContainer { | 10 class FileGenerator extends ProtobufContainer { |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 String fileName = filePath.pathSegments.last; | 90 String fileName = filePath.pathSegments.last; |
91 int index = fileName.lastIndexOf("."); | 91 int index = fileName.lastIndexOf("."); |
92 return index == -1 ? fileName : fileName.substring(0, index); | 92 return index == -1 ? fileName : fileName.substring(0, index); |
93 } | 93 } |
94 | 94 |
95 String _generateClassName(Uri protoFilePath) { | 95 String _generateClassName(Uri protoFilePath) { |
96 String s = _fileNameWithoutExtension(protoFilePath).replaceAll('-', '_'); | 96 String s = _fileNameWithoutExtension(protoFilePath).replaceAll('-', '_'); |
97 return '${s[0].toUpperCase()}${s.substring(1)}'; | 97 return '${s[0].toUpperCase()}${s.substring(1)}'; |
98 } | 98 } |
99 | 99 |
100 /// Returns the library name at the top of the .pb.dart file. | |
101 /// | |
102 /// (This should be unique to avoid warnings about duplicate Dart libraries.) | |
103 String _generateLibraryName(Uri protoFilePath) { | |
104 var libraryName = | |
105 _fileNameWithoutExtension(protoFilePath).replaceAll('-', '_'); | |
106 | |
107 if (_fileDescriptor.package != '') { | |
108 // Two .protos can be in the same proto package. | |
109 // It isn't unique enough to use as a Dart library name. | |
110 // But we can prepend it. | |
111 return _fileDescriptor.package + "_" + libraryName; | |
112 } | |
113 | |
114 return libraryName; | |
115 } | |
116 | |
117 /// Generates all the Dart files for this .proto file. | 100 /// Generates all the Dart files for this .proto file. |
118 List<CodeGeneratorResponse_File> generateFiles(OutputConfiguration config) { | 101 List<CodeGeneratorResponse_File> generateFiles(OutputConfiguration config) { |
119 if (!_linked) throw new StateError("not linked"); | 102 if (!_linked) throw new StateError("not linked"); |
120 | 103 |
121 makeFile(String extension, String content) { | 104 makeFile(String extension, String content) { |
122 Uri protoUrl = new Uri.file(_fileDescriptor.name); | 105 Uri protoUrl = new Uri.file(_fileDescriptor.name); |
123 Uri dartUrl = config.outputPathFor(protoUrl, extension); | 106 Uri dartUrl = config.outputPathFor(protoUrl, extension); |
124 return new CodeGeneratorResponse_File() | 107 return new CodeGeneratorResponse_File() |
125 ..name = dartUrl.path | 108 ..name = dartUrl.path |
126 ..content = content; | 109 ..content = content; |
127 } | 110 } |
128 | 111 |
129 return [ | 112 return [ |
130 makeFile(".pb.dart", generateMainFile(config)), | 113 makeFile(".pb.dart", generateMainFile(config)), |
131 makeFile(".pbenum.dart", generateEnumFile(config)), | 114 makeFile(".pbenum.dart", generateEnumFile(config)), |
| 115 makeFile(".pbserver.dart", generateServerFile(config)), |
132 makeFile(".pbjson.dart", generateJsonFile(config)), | 116 makeFile(".pbjson.dart", generateJsonFile(config)), |
133 ]; | 117 ]; |
134 } | 118 } |
135 | 119 |
136 /// Returns the contents of the .pb.dart file for this .proto file. | 120 /// Returns the contents of the .pb.dart file for this .proto file. |
137 String generateMainFile( | 121 String generateMainFile( |
138 [OutputConfiguration config = const DefaultOutputConfiguration()]) { | 122 [OutputConfiguration config = const DefaultOutputConfiguration()]) { |
139 if (!_linked) throw new StateError("not linked"); | 123 if (!_linked) throw new StateError("not linked"); |
140 IndentingWriter out = new IndentingWriter(); | 124 IndentingWriter out = new IndentingWriter(); |
141 | 125 |
(...skipping 18 matching lines...) Expand all Loading... |
160 for (ExtensionGenerator x in extensionGenerators) { | 144 for (ExtensionGenerator x in extensionGenerators) { |
161 out.println(' registry.add(${x.name});'); | 145 out.println(' registry.add(${x.name});'); |
162 } | 146 } |
163 out.println('}'); | 147 out.println('}'); |
164 }); | 148 }); |
165 } | 149 } |
166 | 150 |
167 for (ClientApiGenerator c in clientApiGenerators) { | 151 for (ClientApiGenerator c in clientApiGenerators) { |
168 c.generate(out); | 152 c.generate(out); |
169 } | 153 } |
170 for (ServiceGenerator s in serviceGenerators) { | |
171 s.generate(out); | |
172 } | |
173 | |
174 return out.toString(); | 154 return out.toString(); |
175 } | 155 } |
176 | 156 |
177 /// Writes the header and imports for the .pb.dart file. | 157 /// Writes the header and imports for the .pb.dart file. |
178 void writeMainHeader(IndentingWriter out, | 158 void writeMainHeader(IndentingWriter out, |
179 [OutputConfiguration config = const DefaultOutputConfiguration()]) { | 159 [OutputConfiguration config = const DefaultOutputConfiguration()]) { |
180 String libraryName = _generateLibraryName(protoFileUri); | 160 _writeLibraryHeading(out); |
181 out.println('///\n' | |
182 '// Generated code. Do not modify.\n' | |
183 '///\n' | |
184 'library $libraryName;\n'); | |
185 | 161 |
186 // We only add the dart:async import if there are services in the | 162 // We only add the dart:async import if there are services in the |
187 // FileDescriptorProto. | 163 // FileDescriptorProto. |
188 if (_fileDescriptor.service.isNotEmpty) { | 164 if (_fileDescriptor.service.isNotEmpty) { |
189 out.println("import 'dart:async';\n"); | 165 out.println("import 'dart:async';\n"); |
190 } | 166 } |
191 | 167 |
192 if (_needsFixnumImport) { | 168 if (_needsFixnumImport) { |
193 out.println("import 'package:fixnum/fixnum.dart';"); | 169 out.println("import 'package:fixnum/fixnum.dart';"); |
194 } | 170 } |
(...skipping 10 matching lines...) Expand all Loading... |
205 var symbols = mixinImports[imp]; | 181 var symbols = mixinImports[imp]; |
206 out.println("import '${imp}' show ${symbols.join(', ')};"); | 182 out.println("import '${imp}' show ${symbols.join(', ')};"); |
207 } | 183 } |
208 if (mixinImports.isNotEmpty) out.println(); | 184 if (mixinImports.isNotEmpty) out.println(); |
209 | 185 |
210 // Import the .pb.dart files we depend on. | 186 // Import the .pb.dart files we depend on. |
211 var imports = new Set<FileGenerator>.identity(); | 187 var imports = new Set<FileGenerator>.identity(); |
212 var enumImports = new Set<FileGenerator>.identity(); | 188 var enumImports = new Set<FileGenerator>.identity(); |
213 _findProtosToImport(imports, enumImports); | 189 _findProtosToImport(imports, enumImports); |
214 | 190 |
215 void writeImport(FileGenerator target, String extension) { | |
216 Uri resolvedImport = | |
217 config.resolveImport(target.protoFileUri, protoFileUri, extension); | |
218 out.print("import '$resolvedImport'"); | |
219 if (package != target.package && target.package.isNotEmpty) { | |
220 out.print(' as ${target.packageImportPrefix}'); | |
221 } | |
222 out.println(';'); | |
223 } | |
224 | |
225 for (var target in imports) { | 191 for (var target in imports) { |
226 writeImport(target, ".pb.dart"); | 192 _writeImport(out, config, target, ".pb.dart"); |
227 } | 193 } |
228 if (imports.isNotEmpty) out.println(); | 194 if (imports.isNotEmpty) out.println(); |
229 | 195 |
230 for (var target in enumImports) { | 196 for (var target in enumImports) { |
231 writeImport(target, ".pbenum.dart"); | 197 _writeImport(out, config, target, ".pbenum.dart"); |
232 } | 198 } |
233 if (enumImports.isNotEmpty) out.println(); | 199 if (enumImports.isNotEmpty) out.println(); |
234 | 200 |
235 // Services also depend on the json imports. | |
236 if (serviceGenerators.isNotEmpty) { | |
237 Uri resolvedImport = | |
238 config.resolveImport(protoFileUri, protoFileUri, ".pbjson.dart"); | |
239 out.println("import '$resolvedImport';"); | |
240 out.println(); | |
241 } | |
242 | |
243 // Export enums in main file for backward compatibility. | 201 // Export enums in main file for backward compatibility. |
244 if (enumCount > 0) { | 202 if (enumCount > 0) { |
245 Uri resolvedImport = | 203 Uri resolvedImport = |
246 config.resolveImport(protoFileUri, protoFileUri, ".pbenum.dart"); | 204 config.resolveImport(protoFileUri, protoFileUri, ".pbenum.dart"); |
247 out.println("export '$resolvedImport';"); | 205 out.println("export '$resolvedImport';"); |
248 out.println(); | 206 out.println(); |
249 } | 207 } |
250 } | 208 } |
251 | 209 |
252 bool get _needsFixnumImport { | 210 bool get _needsFixnumImport { |
253 for (var m in messageGenerators) { | 211 for (var m in messageGenerators) { |
254 if (m.needsFixnumImport) return true; | 212 if (m.needsFixnumImport) return true; |
255 } | 213 } |
256 for (var x in extensionGenerators) { | 214 for (var x in extensionGenerators) { |
257 if (x.needsFixnumImport) return true; | 215 if (x.needsFixnumImport) return true; |
258 } | 216 } |
259 return false; | 217 return false; |
260 } | 218 } |
261 | 219 |
262 bool get _needsProtobufImport => | 220 bool get _needsProtobufImport => |
263 messageGenerators.isNotEmpty || | 221 messageGenerators.isNotEmpty || |
264 extensionGenerators.isNotEmpty || | 222 extensionGenerators.isNotEmpty || |
265 clientApiGenerators.isNotEmpty || | 223 clientApiGenerators.isNotEmpty; |
266 serviceGenerators.isNotEmpty; | |
267 | 224 |
268 /// Returns the generator for each .pb.dart file we need to import. | 225 /// Returns the generator for each .pb.dart file we need to import. |
269 void _findProtosToImport( | 226 void _findProtosToImport( |
270 Set<FileGenerator> imports, Set<FileGenerator> enumImports) { | 227 Set<FileGenerator> imports, Set<FileGenerator> enumImports) { |
271 for (var m in messageGenerators) { | 228 for (var m in messageGenerators) { |
272 m.addImportsTo(imports, enumImports); | 229 m.addImportsTo(imports, enumImports); |
273 } | 230 } |
274 for (var x in extensionGenerators) { | 231 for (var x in extensionGenerators) { |
275 x.addImportsTo(imports, enumImports); | 232 x.addImportsTo(imports, enumImports); |
276 } | 233 } |
| 234 // Add imports needed for client-side services. |
277 for (var x in serviceGenerators) { | 235 for (var x in serviceGenerators) { |
278 x.addImportsTo(imports); | 236 x.addImportsTo(imports); |
279 } | 237 } |
280 // Don't need to import self. (But we may need to import the enums.) | 238 // Don't need to import self. (But we may need to import the enums.) |
281 imports.remove(this); | 239 imports.remove(this); |
282 } | 240 } |
283 | 241 |
284 /// Returns a map from import names to the Dart symbols to be imported. | 242 /// Returns a map from import names to the Dart symbols to be imported. |
285 Map<String, List<String>> findMixinsToImport() { | 243 Map<String, List<String>> findMixinsToImport() { |
286 var mixins = new Set<PbMixin>(); | 244 var mixins = new Set<PbMixin>(); |
(...skipping 16 matching lines...) Expand all Loading... |
303 imports[imp].sort(); | 261 imports[imp].sort(); |
304 } | 262 } |
305 | 263 |
306 return imports; | 264 return imports; |
307 } | 265 } |
308 | 266 |
309 /// Returns the contents of the .pbenum.dart file for this .proto file. | 267 /// Returns the contents of the .pbenum.dart file for this .proto file. |
310 String generateEnumFile( | 268 String generateEnumFile( |
311 [OutputConfiguration config = const DefaultOutputConfiguration()]) { | 269 [OutputConfiguration config = const DefaultOutputConfiguration()]) { |
312 if (!_linked) throw new StateError("not linked"); | 270 if (!_linked) throw new StateError("not linked"); |
313 Uri filePath = new Uri.file(_fileDescriptor.name); | |
314 if (filePath.isAbsolute) { | |
315 // protoc should never generate a file descriptor with an absolute path. | |
316 throw "FAILURE: File with an absolute path is not supported"; | |
317 } | |
318 | 271 |
319 var baseLibraryName = _generateLibraryName(filePath); | |
320 var libraryName = baseLibraryName + "_pbenum"; | |
321 var out = new IndentingWriter(); | 272 var out = new IndentingWriter(); |
322 out.print(''' | 273 _writeLibraryHeading(out, "pbenum"); |
323 /// | |
324 // Generated code. Do not modify. | |
325 /// | |
326 library $libraryName; | |
327 | |
328 '''); | |
329 | 274 |
330 if (enumCount > 0) { | 275 if (enumCount > 0) { |
331 out.println("import 'package:protobuf/protobuf.dart';"); | 276 out.println("import 'package:protobuf/protobuf.dart';"); |
332 out.println(); | 277 out.println(); |
333 } | 278 } |
334 | 279 |
335 for (EnumGenerator e in enumGenerators) { | 280 for (EnumGenerator e in enumGenerators) { |
336 e.generate(out); | 281 e.generate(out); |
337 } | 282 } |
338 | 283 |
339 for (MessageGenerator m in messageGenerators) { | 284 for (MessageGenerator m in messageGenerators) { |
340 m.generateEnums(out); | 285 m.generateEnums(out); |
341 } | 286 } |
342 | 287 |
343 return out.toString(); | 288 return out.toString(); |
344 } | 289 } |
345 | 290 |
346 /// Returns the number of enum types generated in the .pbenum.dart file. | 291 /// Returns the number of enum types generated in the .pbenum.dart file. |
347 int get enumCount { | 292 int get enumCount { |
348 var count = enumGenerators.length; | 293 var count = enumGenerators.length; |
349 for (MessageGenerator m in messageGenerators) { | 294 for (MessageGenerator m in messageGenerators) { |
350 count += m.enumCount; | 295 count += m.enumCount; |
351 } | 296 } |
352 return count; | 297 return count; |
353 } | 298 } |
354 | 299 |
| 300 /// Returns the contents of the .pbserver.dart file for this .proto file. |
| 301 String generateServerFile( |
| 302 [OutputConfiguration config = const DefaultOutputConfiguration()]) { |
| 303 if (!_linked) throw new StateError("not linked"); |
| 304 var out = new IndentingWriter(); |
| 305 _writeLibraryHeading(out, "pbserver"); |
| 306 |
| 307 if (serviceGenerators.isNotEmpty) { |
| 308 out.println(''' |
| 309 import 'dart:async'; |
| 310 |
| 311 import 'package:protobuf/protobuf.dart'; |
| 312 '''); |
| 313 } |
| 314 |
| 315 // Import .pb.dart files needed for requests and responses. |
| 316 var imports = new Set<FileGenerator>(); |
| 317 for (var x in serviceGenerators) { |
| 318 x.addImportsTo(imports); |
| 319 } |
| 320 for (var target in imports) { |
| 321 _writeImport(out, config, target, ".pb.dart"); |
| 322 } |
| 323 if (imports.isNotEmpty) out.println(); |
| 324 |
| 325 // Import .pbjson.dart file needed for $json and $messageJson. |
| 326 if (serviceGenerators.isNotEmpty) { |
| 327 _writeImport(out, config, this, ".pbjson.dart"); |
| 328 out.println(); |
| 329 } |
| 330 |
| 331 for (ServiceGenerator s in serviceGenerators) { |
| 332 s.generate(out); |
| 333 } |
| 334 |
| 335 return out.toString(); |
| 336 } |
| 337 |
355 /// Returns the contents of the .pbjson.dart file for this .proto file. | 338 /// Returns the contents of the .pbjson.dart file for this .proto file. |
356 String generateJsonFile( | 339 String generateJsonFile( |
357 [OutputConfiguration config = const DefaultOutputConfiguration()]) { | 340 [OutputConfiguration config = const DefaultOutputConfiguration()]) { |
358 if (!_linked) throw new StateError("not linked"); | 341 if (!_linked) throw new StateError("not linked"); |
359 Uri filePath = new Uri.file(_fileDescriptor.name); | |
360 if (filePath.isAbsolute) { | |
361 // protoc should never generate a file descriptor with an absolute path. | |
362 throw "FAILURE: File with an absolute path is not supported"; | |
363 } | |
364 | |
365 var baseLibraryName = _generateLibraryName(filePath); | |
366 var libraryName = baseLibraryName + "_pbjson"; | |
367 var out = new IndentingWriter(); | 342 var out = new IndentingWriter(); |
368 out.print(''' | 343 _writeLibraryHeading(out, "pbjson"); |
369 /// | |
370 // Generated code. Do not modify. | |
371 /// | |
372 library $libraryName; | |
373 | |
374 '''); | |
375 | 344 |
376 // Import the .pbjson.dart files we depend on. | 345 // Import the .pbjson.dart files we depend on. |
377 var importList = _findJsonProtosToImport(); | 346 var imports = _findJsonProtosToImport(); |
378 for (var imported in importList) { | 347 for (var target in imports) { |
379 Uri resolvedImport = config.resolveImport( | 348 _writeImport(out, config, target, ".pbjson.dart"); |
380 imported.protoFileUri, protoFileUri, ".pbjson.dart"); | |
381 out.print("import '$resolvedImport'"); | |
382 if (package != imported.package && imported.package.isNotEmpty) { | |
383 out.print(' as ${imported.packageImportPrefix}'); | |
384 } | |
385 out.println(';'); | |
386 } | 349 } |
387 if (importList.isNotEmpty) out.println(); | 350 if (imports.isNotEmpty) out.println(); |
388 | 351 |
389 for (var e in enumGenerators) { | 352 for (var e in enumGenerators) { |
390 e.generateConstants(out); | 353 e.generateConstants(out); |
391 } | 354 } |
392 for (MessageGenerator m in messageGenerators) { | 355 for (MessageGenerator m in messageGenerators) { |
393 m.generateConstants(out); | 356 m.generateConstants(out); |
394 } | 357 } |
395 for (ServiceGenerator s in serviceGenerators) { | 358 for (ServiceGenerator s in serviceGenerators) { |
396 s.generateConstants(out); | 359 s.generateConstants(out); |
397 } | 360 } |
(...skipping 10 matching lines...) Expand all Loading... |
408 } | 371 } |
409 for (var x in extensionGenerators) { | 372 for (var x in extensionGenerators) { |
410 x.addConstantImportsTo(imports); | 373 x.addConstantImportsTo(imports); |
411 } | 374 } |
412 for (var x in serviceGenerators) { | 375 for (var x in serviceGenerators) { |
413 x.addConstantImportsTo(imports); | 376 x.addConstantImportsTo(imports); |
414 } | 377 } |
415 imports.remove(this); // Don't need to import self. | 378 imports.remove(this); // Don't need to import self. |
416 return imports; | 379 return imports; |
417 } | 380 } |
| 381 |
| 382 /// Writes the library name at the top of the dart file. |
| 383 /// |
| 384 /// (This should be unique to avoid warnings about duplicate Dart libraries.) |
| 385 void _writeLibraryHeading(IndentingWriter out, [String extension]) { |
| 386 Uri filePath = new Uri.file(_fileDescriptor.name); |
| 387 if (filePath.isAbsolute) { |
| 388 // protoc should never generate a file descriptor with an absolute path. |
| 389 throw "FAILURE: File with an absolute path is not supported"; |
| 390 } |
| 391 |
| 392 var libraryName = _fileNameWithoutExtension(filePath).replaceAll('-', '_'); |
| 393 if (extension != null) libraryName += "_$extension"; |
| 394 if (_fileDescriptor.package != '') { |
| 395 // Two .protos can be in the same proto package. |
| 396 // It isn't unique enough to use as a Dart library name. |
| 397 // But we can prepend it. |
| 398 libraryName = _fileDescriptor.package + "_" + libraryName; |
| 399 } |
| 400 out.println(''' |
| 401 /// |
| 402 // Generated code. Do not modify. |
| 403 /// |
| 404 library $libraryName; |
| 405 '''); |
| 406 } |
| 407 |
| 408 /// Writes an import of a .dart file corresponding to a .proto file. |
| 409 /// (Possibly the same .proto file.) |
| 410 void _writeImport(IndentingWriter out, OutputConfiguration config, |
| 411 FileGenerator target, String extension) { |
| 412 Uri resolvedImport = |
| 413 config.resolveImport(target.protoFileUri, protoFileUri, extension); |
| 414 out.print("import '$resolvedImport'"); |
| 415 if (package != target.package && target.package.isNotEmpty) { |
| 416 out.print(' as ${target.packageImportPrefix}'); |
| 417 } |
| 418 out.println(';'); |
| 419 } |
418 } | 420 } |
OLD | NEW |