| 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 |