Move server-side service stubs to .pbserver.dart
This should break all dependencies on .pbjson.dart files for client-side
code, reducing the number of files loaded into Dartium.
However, it is a breaking API change: server-side code needs to be modified
to import .pbserver.dart files as well as .pb.dart files.
BUG=
R=sgjesse@google.com
Review URL: https://chromiumcodereview.appspot.com//2013343002 .
diff --git a/Makefile b/Makefile
index 03df907..60575c4 100644
--- a/Makefile
+++ b/Makefile
@@ -33,9 +33,14 @@
toplevel_import \
toplevel
TEST_PROTO_DIR=$(OUTPUT_DIR)/protos
-TEST_PROTO_LIBS=$(foreach proto, $(TEST_PROTO_LIST), $(TEST_PROTO_DIR)/$(proto).pb.dart $(TEST_PROTO_DIR)/$(proto).pbjson.dart)
+TEST_PROTO_LIBS=$(foreach f, $(TEST_PROTO_LIST), \
+ $(TEST_PROTO_DIR)/$(f).pb.dart \
+ $(TEST_PROTO_DIR)/$(f).pbenum.dart \
+ $(TEST_PROTO_DIR)/$(f).pbserver.dart \
+ $(TEST_PROTO_DIR)/$(f).pbjson.dart)
TEST_PROTO_SRC_DIR=test/protos
-TEST_PROTO_SRCS=$(foreach proto, $(TEST_PROTO_LIST), $(TEST_PROTO_SRC_DIR)/$(proto).proto)
+TEST_PROTO_SRCS=$(foreach proto, $(TEST_PROTO_LIST), \
+ $(TEST_PROTO_SRC_DIR)/$(proto).proto)
PREGENERATED_SRCS=lib/descriptor.proto lib/plugin.proto
diff --git a/lib/file_generator.dart b/lib/file_generator.dart
index 148e2a6..65cba47 100644
--- a/lib/file_generator.dart
+++ b/lib/file_generator.dart
@@ -97,23 +97,6 @@
return '${s[0].toUpperCase()}${s.substring(1)}';
}
- /// Returns the library name at the top of the .pb.dart file.
- ///
- /// (This should be unique to avoid warnings about duplicate Dart libraries.)
- String _generateLibraryName(Uri protoFilePath) {
- var libraryName =
- _fileNameWithoutExtension(protoFilePath).replaceAll('-', '_');
-
- if (_fileDescriptor.package != '') {
- // Two .protos can be in the same proto package.
- // It isn't unique enough to use as a Dart library name.
- // But we can prepend it.
- return _fileDescriptor.package + "_" + libraryName;
- }
-
- return libraryName;
- }
-
/// Generates all the Dart files for this .proto file.
List<CodeGeneratorResponse_File> generateFiles(OutputConfiguration config) {
if (!_linked) throw new StateError("not linked");
@@ -129,6 +112,7 @@
return [
makeFile(".pb.dart", generateMainFile(config)),
makeFile(".pbenum.dart", generateEnumFile(config)),
+ makeFile(".pbserver.dart", generateServerFile(config)),
makeFile(".pbjson.dart", generateJsonFile(config)),
];
}
@@ -167,21 +151,13 @@
for (ClientApiGenerator c in clientApiGenerators) {
c.generate(out);
}
- for (ServiceGenerator s in serviceGenerators) {
- s.generate(out);
- }
-
return out.toString();
}
/// Writes the header and imports for the .pb.dart file.
void writeMainHeader(IndentingWriter out,
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
- String libraryName = _generateLibraryName(protoFileUri);
- out.println('///\n'
- '// Generated code. Do not modify.\n'
- '///\n'
- 'library $libraryName;\n');
+ _writeLibraryHeading(out);
// We only add the dart:async import if there are services in the
// FileDescriptorProto.
@@ -212,34 +188,16 @@
var enumImports = new Set<FileGenerator>.identity();
_findProtosToImport(imports, enumImports);
- void writeImport(FileGenerator target, String extension) {
- Uri resolvedImport =
- config.resolveImport(target.protoFileUri, protoFileUri, extension);
- out.print("import '$resolvedImport'");
- if (package != target.package && target.package.isNotEmpty) {
- out.print(' as ${target.packageImportPrefix}');
- }
- out.println(';');
- }
-
for (var target in imports) {
- writeImport(target, ".pb.dart");
+ _writeImport(out, config, target, ".pb.dart");
}
if (imports.isNotEmpty) out.println();
for (var target in enumImports) {
- writeImport(target, ".pbenum.dart");
+ _writeImport(out, config, target, ".pbenum.dart");
}
if (enumImports.isNotEmpty) out.println();
- // Services also depend on the json imports.
- if (serviceGenerators.isNotEmpty) {
- Uri resolvedImport =
- config.resolveImport(protoFileUri, protoFileUri, ".pbjson.dart");
- out.println("import '$resolvedImport';");
- out.println();
- }
-
// Export enums in main file for backward compatibility.
if (enumCount > 0) {
Uri resolvedImport =
@@ -262,8 +220,7 @@
bool get _needsProtobufImport =>
messageGenerators.isNotEmpty ||
extensionGenerators.isNotEmpty ||
- clientApiGenerators.isNotEmpty ||
- serviceGenerators.isNotEmpty;
+ clientApiGenerators.isNotEmpty;
/// Returns the generator for each .pb.dart file we need to import.
void _findProtosToImport(
@@ -274,6 +231,7 @@
for (var x in extensionGenerators) {
x.addImportsTo(imports, enumImports);
}
+ // Add imports needed for client-side services.
for (var x in serviceGenerators) {
x.addImportsTo(imports);
}
@@ -310,22 +268,9 @@
String generateEnumFile(
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
if (!_linked) throw new StateError("not linked");
- Uri filePath = new Uri.file(_fileDescriptor.name);
- if (filePath.isAbsolute) {
- // protoc should never generate a file descriptor with an absolute path.
- throw "FAILURE: File with an absolute path is not supported";
- }
- var baseLibraryName = _generateLibraryName(filePath);
- var libraryName = baseLibraryName + "_pbenum";
var out = new IndentingWriter();
- out.print('''
-///
-// Generated code. Do not modify.
-///
-library $libraryName;
-
-''');
+ _writeLibraryHeading(out, "pbenum");
if (enumCount > 0) {
out.println("import 'package:protobuf/protobuf.dart';");
@@ -352,39 +297,61 @@
return count;
}
+ /// Returns the contents of the .pbserver.dart file for this .proto file.
+ String generateServerFile(
+ [OutputConfiguration config = const DefaultOutputConfiguration()]) {
+ if (!_linked) throw new StateError("not linked");
+ var out = new IndentingWriter();
+ _writeLibraryHeading(out, "pbserver");
+
+ if (serviceGenerators.isNotEmpty) {
+ out.println('''
+import 'dart:async';
+
+import 'package:protobuf/protobuf.dart';
+''');
+ }
+
+ // Import .pb.dart files needed for requests and responses.
+ var imports = new Set<FileGenerator>();
+ for (var x in serviceGenerators) {
+ x.addImportsTo(imports);
+ }
+ for (var target in imports) {
+ _writeImport(out, config, target, ".pb.dart");
+ }
+
+ // Import .pbjson.dart file needed for $json and $messageJson.
+ if (serviceGenerators.isNotEmpty) {
+ _writeImport(out, config, this, ".pbjson.dart");
+ out.println();
+ }
+
+ Uri resolvedImport =
+ config.resolveImport(protoFileUri, protoFileUri, ".pb.dart");
+ out.println("export '$resolvedImport';");
+ out.println();
+
+ for (ServiceGenerator s in serviceGenerators) {
+ s.generate(out);
+ }
+
+ return out.toString();
+ }
+
/// Returns the contents of the .pbjson.dart file for this .proto file.
String generateJsonFile(
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
if (!_linked) throw new StateError("not linked");
- Uri filePath = new Uri.file(_fileDescriptor.name);
- if (filePath.isAbsolute) {
- // protoc should never generate a file descriptor with an absolute path.
- throw "FAILURE: File with an absolute path is not supported";
- }
-
- var baseLibraryName = _generateLibraryName(filePath);
- var libraryName = baseLibraryName + "_pbjson";
var out = new IndentingWriter();
- out.print('''
-///
-// Generated code. Do not modify.
-///
-library $libraryName;
-
-''');
+ _writeLibraryHeading(out, "pbjson");
// Import the .pbjson.dart files we depend on.
- var importList = _findJsonProtosToImport();
- for (var imported in importList) {
- Uri resolvedImport = config.resolveImport(
- imported.protoFileUri, protoFileUri, ".pbjson.dart");
- out.print("import '$resolvedImport'");
- if (package != imported.package && imported.package.isNotEmpty) {
- out.print(' as ${imported.packageImportPrefix}');
- }
- out.println(';');
+ var imports = _findJsonProtosToImport();
+ for (var target in imports) {
+ _writeImport(out, config, target, ".pbjson.dart");
}
- if (importList.isNotEmpty) out.println();
+ if (imports.isNotEmpty) out.println();
for (var e in enumGenerators) {
e.generateConstants(out);
@@ -415,4 +382,43 @@
imports.remove(this); // Don't need to import self.
return imports;
}
+
+ /// Writes the library name at the top of the dart file.
+ ///
+ /// (This should be unique to avoid warnings about duplicate Dart libraries.)
+ void _writeLibraryHeading(IndentingWriter out, [String extension]) {
+ Uri filePath = new Uri.file(_fileDescriptor.name);
+ if (filePath.isAbsolute) {
+ // protoc should never generate a file descriptor with an absolute path.
+ throw "FAILURE: File with an absolute path is not supported";
+ }
+
+ var libraryName = _fileNameWithoutExtension(filePath).replaceAll('-', '_');
+ if (extension != null) libraryName += "_$extension";
+ if (_fileDescriptor.package != '') {
+ // Two .protos can be in the same proto package.
+ // It isn't unique enough to use as a Dart library name.
+ // But we can prepend it.
+ libraryName = _fileDescriptor.package + "_" + libraryName;
+ }
+ out.println('''
+///
+// Generated code. Do not modify.
+///
+library $libraryName;
+''');
+ }
+
+ /// Writes an import of a .dart file corresponding to a .proto file.
+ /// (Possibly the same .proto file.)
+ void _writeImport(IndentingWriter out, OutputConfiguration config,
+ FileGenerator target, String extension) {
+ Uri resolvedImport =
+ config.resolveImport(target.protoFileUri, protoFileUri, extension);
+ out.print("import '$resolvedImport'");
+ if (package != target.package && target.package.isNotEmpty) {
+ out.print(' as ${target.packageImportPrefix}');
+ }
+ out.println(';');
+ }
}
diff --git a/pubspec.yaml b/pubspec.yaml
index f913957..9c52c02 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: protoc_plugin
-version: 0.5.2
+version: 0.6
author: Dart Team <misc@dartlang.org>
description: Protoc compiler plugin to generate Dart code
homepage: https://github.com/dart-lang/dart-protoc-plugin
diff --git a/test/file_generator_test.dart b/test/file_generator_test.dart
index bac4d15..380647d 100644
--- a/test/file_generator_test.dart
+++ b/test/file_generator_test.dart
@@ -297,8 +297,8 @@
expect(writer.toString(), expected);
});
- test('FileGenerator outputs extra imports for a service', () {
- String expected = r'''
+ test('FileGenerator outputs files for a service', () {
+ String expectedClient = r'''
///
// Generated code. Do not modify.
///
@@ -308,12 +308,93 @@
import 'package:protobuf/protobuf.dart';
-import 'test.pbjson.dart';
+class Empty extends GeneratedMessage {
+ static final BuilderInfo _i = new BuilderInfo('Empty')
+ ..hasRequiredFields = false
+ ;
+
+ Empty() : super();
+ Empty.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
+ Empty.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
+ Empty clone() => new Empty()..mergeFromMessage(this);
+ BuilderInfo get info_ => _i;
+ static Empty create() => new Empty();
+ static PbList<Empty> createRepeated() => new PbList<Empty>();
+ static Empty getDefault() {
+ if (_defaultInstance == null) _defaultInstance = new _ReadonlyEmpty();
+ return _defaultInstance;
+ }
+ static Empty _defaultInstance;
+ static void $checkItem(Empty v) {
+ if (v is !Empty) checkItemFailed(v, 'Empty');
+ }
+}
+
+class _ReadonlyEmpty extends Empty with ReadonlyMessageMixin {}
+
+class TestApi {
+ RpcClient _client;
+ TestApi(this._client);
+
+ Future<Empty> ping(ClientContext ctx, Empty request) {
+ var emptyResponse = new Empty();
+ return _client.invoke(ctx, 'Test', 'Ping', request, emptyResponse);
+ }
+}
''';
+
+ String expectedServer = r'''
+///
+// Generated code. Do not modify.
+///
+library test_pbserver;
+
+import 'dart:async';
+
+import 'package:protobuf/protobuf.dart';
+
+import 'test.pb.dart';
+import 'test.pbjson.dart';
+
+export 'test.pb.dart';
+
+abstract class TestServiceBase extends GeneratedService {
+ Future<Empty> ping(ServerContext ctx, Empty request);
+
+ GeneratedMessage createRequest(String method) {
+ switch (method) {
+ case 'Ping': return new Empty();
+ default: throw new ArgumentError('Unknown method: $method');
+ }
+ }
+
+ Future<GeneratedMessage> handleCall(ServerContext ctx, String method, GeneratedMessage request) {
+ switch (method) {
+ case 'Ping': return ping(ctx, request);
+ default: throw new ArgumentError('Unknown method: $method');
+ }
+ }
+
+ Map<String, dynamic> get $json => Test$json;
+ Map<String, dynamic> get $messageJson => Test$messageJson;
+}
+
+''';
+
+ DescriptorProto empty = new DescriptorProto()..name = "Empty";
+
+ ServiceDescriptorProto sd = new ServiceDescriptorProto()
+ ..name = 'Test'
+ ..method.add(new MethodDescriptorProto()
+ ..name = 'Ping'
+ ..inputType = '.Empty'
+ ..outputType = '.Empty');
+
FileDescriptorProto fd = new FileDescriptorProto()
..name = 'test'
- ..service.add(new ServiceDescriptorProto());
+ ..messageType.add(empty)
+ ..service.add(sd);
var options = parseGenerationOptions(
new CodeGeneratorRequest(), new CodeGeneratorResponse());
@@ -323,7 +404,8 @@
var writer = new IndentingWriter();
fg.writeMainHeader(writer);
- expect(writer.toString(), expected);
+ expect(fg.generateMainFile(), expectedClient);
+ expect(fg.generateServerFile(), expectedServer);
});
test('FileGenerator handles field_name options', () {
diff --git a/test/service_test.dart b/test/service_test.dart
index 2628a1f..a4aedd7 100644
--- a/test/service_test.dart
+++ b/test/service_test.dart
@@ -5,7 +5,7 @@
import 'package:protobuf/protobuf.dart';
import 'package:test/test.dart';
-import '../out/protos/service.pb.dart' as pb;
+import '../out/protos/service.pbserver.dart' as pb;
import '../out/protos/service2.pb.dart' as pb2;
import '../out/protos/service3.pb.dart' as pb3;