Skip to content

Commit 180b6fe

Browse files
authored
Add option for Dart protobuf to emit Kythe metdata. (flutter#204)
* Make Dart protobuf emite Kythe metdata. * Fix Travis tests with proper names Set. * Fix other Travis tests, update CHANGELOG and pubspec. * Remove unused _offset from IndentingWriter. * Fix whitespacing in meta goldens. * Remove default explicit null value in IndentingWriter. * Reformat test files to change line wrapping for external paths.
1 parent 7f3cc6d commit 180b6fe

21 files changed

+589
-77
lines changed

protoc_plugin/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 16.1.0
2+
3+
* Add ability to generate Kythe metadata files via the
4+
`generate_kythe_info` option.
5+
16
## 16.0.0
27

38
* Breaking change: Remove the '$checkItem' function from generated message classes and use the new method 'pc' on

protoc_plugin/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ options `<option 1>` and `<option 2>` like this:
6464

6565
--dart_out="<option 1>,<option 2>:."
6666

67+
### Generating Code Info
68+
69+
The plugin includes the `generate_kythe_info` option, which, if passed at run
70+
time, will make the plugin generate metadata files alongside the `.dart` files
71+
generated for the proto messages and their enums. Pass this along with the other
72+
dart_out options:
73+
74+
--dart_out="generate_kythe_info,<other options>:."
75+
6776
Using protocol buffer libraries to build new libraries
6877
------------------------------------------------------
6978

protoc_plugin/lib/code_generator.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ abstract class ProtobufContainer {
1313
String get classname;
1414
String get fullName;
1515

16+
/// The field path contains the field IDs and indices (for repeated fields)
17+
/// that lead to the proto memeber corresponding to a piece of generated code.
18+
/// Repeated fields in the descriptor are further identified by the index of
19+
/// the message in question.
20+
/// For more information see
21+
/// https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto#L728
22+
List<int> get fieldPath;
23+
1624
/// The fully qualified name with a leading '.'.
1725
///
1826
/// This exists because names from protoc come like this.
@@ -98,4 +106,5 @@ class CodeGenerator extends ProtobufContainer {
98106
String get classname => null;
99107
String get fullName => '';
100108
get fileGen => null;
109+
List<int> get fieldPath => [];
101110
}

protoc_plugin/lib/enum_generator.dart

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,63 @@ class EnumGenerator extends ProtobufContainer {
1717
final EnumDescriptorProto _descriptor;
1818
final List<EnumValueDescriptorProto> _canonicalValues =
1919
<EnumValueDescriptorProto>[];
20+
final List<int> _originalCanonicalIndices = <int>[];
2021
final List<EnumAlias> _aliases = <EnumAlias>[];
2122

2223
/// Maps the name of an enum value to the Dart name we will use for it.
2324
final Map<String, String> dartNames = <String, String>{};
25+
final List<int> _originalAliasIndices = <int>[];
26+
List<int> _fieldPath;
27+
final List<int> _fieldPathSegment;
2428

25-
EnumGenerator(EnumDescriptorProto descriptor, ProtobufContainer parent,
26-
Set<String> usedClassNames)
29+
/// See [[ProtobufContainer]
30+
List<int> get fieldPath =>
31+
_fieldPath ??= List.from(_parent.fieldPath)..addAll(_fieldPathSegment);
32+
33+
EnumGenerator._(EnumDescriptorProto descriptor, ProtobufContainer parent,
34+
Set<String> usedClassNames, int repeatedFieldIndex, int fieldIdTag)
2735
: assert(parent != null),
2836
_parent = parent,
37+
_fieldPathSegment = [fieldIdTag, repeatedFieldIndex],
2938
classname = messageOrEnumClassName(descriptor.name, usedClassNames,
3039
parent: parent?.classname ?? ''),
3140
fullName = parent.fullName == ''
3241
? descriptor.name
3342
: '${parent.fullName}.${descriptor.name}',
3443
_descriptor = descriptor {
3544
final usedNames = reservedEnumNames;
36-
for (EnumValueDescriptorProto value in descriptor.value) {
45+
for (var i = 0; i < descriptor.value.length; i++) {
46+
EnumValueDescriptorProto value = descriptor.value[i];
3747
EnumValueDescriptorProto canonicalValue =
3848
descriptor.value.firstWhere((v) => v.number == value.number);
3949
if (value == canonicalValue) {
4050
_canonicalValues.add(value);
51+
_originalCanonicalIndices.add(i);
4152
} else {
4253
_aliases.add(new EnumAlias(value, canonicalValue));
54+
_originalAliasIndices.add(i);
4355
}
4456
dartNames[value.name] = disambiguateName(
4557
avoidInitialUnderscore(value.name), usedNames, enumSuffixes());
4658
}
4759
}
4860

61+
static const _topLevelFieldTag = 5;
62+
static const _nestedFieldTag = 4;
63+
64+
EnumGenerator.topLevel(
65+
EnumDescriptorProto descriptor,
66+
ProtobufContainer parent,
67+
Set<String> usedClassNames,
68+
int repeatedFieldIndex)
69+
: this._(descriptor, parent, usedClassNames, repeatedFieldIndex,
70+
_topLevelFieldTag);
71+
72+
EnumGenerator.nested(EnumDescriptorProto descriptor, ProtobufContainer parent,
73+
Set<String> usedClassNames, int repeatedFieldIndex)
74+
: this._(descriptor, parent, usedClassNames, repeatedFieldIndex,
75+
_nestedFieldTag);
76+
4977
String get package => _parent.package;
5078
FileGenerator get fileGen => _parent.fileGen;
5179

@@ -64,23 +92,58 @@ class EnumGenerator extends ProtobufContainer {
6492
return "$fileImportPrefix.$name";
6593
}
6694

95+
static const int _enumNameTag = 1;
96+
static const int _enumValueTag = 2;
97+
static const int _enumValueNameTag = 1;
98+
6799
void generate(IndentingWriter out) {
68-
out.addBlock(
100+
out.addAnnotatedBlock(
69101
'class ${classname} extends $_protobufImportPrefix.ProtobufEnum {',
70-
'}\n', () {
102+
'}\n', [
103+
new NamedLocation(
104+
name: classname,
105+
fieldPathSegment: new List.from(fieldPath)..add(_enumNameTag),
106+
start: 'class '.length)
107+
], () {
71108
// -----------------------------------------------------------------
72109
// Define enum types.
73-
for (EnumValueDescriptorProto val in _canonicalValues) {
110+
for (var i = 0; i < _canonicalValues.length; i++) {
111+
EnumValueDescriptorProto val = _canonicalValues[i];
74112
final name = dartNames[val.name];
75-
out.println('static const ${classname} $name = '
76-
"const ${classname}._(${val.number}, ${singleQuote(name)});");
113+
out.printlnAnnotated(
114+
'static const ${classname} $name = '
115+
"const ${classname}._(${val.number}, ${singleQuote(name)});",
116+
[
117+
new NamedLocation(
118+
name: name,
119+
fieldPathSegment: new List.from(fieldPath)
120+
..addAll([
121+
_enumValueTag,
122+
_originalCanonicalIndices[i],
123+
_enumValueNameTag
124+
]),
125+
start: 'static const ${classname} '.length)
126+
]);
77127
}
78128
if (_aliases.isNotEmpty) {
79129
out.println();
80-
for (EnumAlias alias in _aliases) {
130+
for (var i = 0; i < _aliases.length; i++) {
131+
EnumAlias alias = _aliases[i];
81132
final name = dartNames[alias.value.name];
82-
out.println('static const ${classname} $name ='
83-
' ${dartNames[alias.canonicalValue.name]};');
133+
out.printlnAnnotated(
134+
'static const ${classname} $name ='
135+
' ${dartNames[alias.canonicalValue.name]};',
136+
[
137+
new NamedLocation(
138+
name: name,
139+
fieldPathSegment: new List.from(fieldPath)
140+
..addAll([
141+
_enumValueTag,
142+
_originalAliasIndices[i],
143+
_enumValueNameTag
144+
]),
145+
start: 'static const ${classname} '.length)
146+
]);
84147
}
85148
}
86149
out.println();

protoc_plugin/lib/extension_generator.dart

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,29 @@ class ExtensionGenerator {
1212
ProtobufField _field;
1313
final String _extensionName;
1414
String _extendedFullName = "";
15-
16-
ExtensionGenerator(this._descriptor, this._parent, Set<String> usedNames)
17-
: _extensionName = extensionName(_descriptor, usedNames);
15+
List<int> _fieldPath;
16+
final List<int> _fieldPathSegment;
17+
18+
/// See [[ProtobufContainer]
19+
List<int> get fieldPath =>
20+
_fieldPath ??= List.from(_parent.fieldPath)..addAll(_fieldPathSegment);
21+
22+
ExtensionGenerator._(this._descriptor, this._parent, Set<String> usedNames,
23+
int repeatedFieldIndex, int fieldIdTag)
24+
: _extensionName = extensionName(_descriptor, usedNames),
25+
_fieldPathSegment = [fieldIdTag, repeatedFieldIndex];
26+
27+
static const _topLevelFieldTag = 7;
28+
static const _nestedFieldTag = 6;
29+
30+
ExtensionGenerator.topLevel(FieldDescriptorProto descriptor,
31+
ProtobufContainer parent, Set<String> usedNames, int repeatedFieldIndex)
32+
: this._(descriptor, parent, usedNames, repeatedFieldIndex,
33+
_topLevelFieldTag);
34+
ExtensionGenerator.nested(FieldDescriptorProto descriptor,
35+
ProtobufContainer parent, Set<String> usedNames, int repeatedFieldIndex)
36+
: this._(
37+
descriptor, parent, usedNames, repeatedFieldIndex, _nestedFieldTag);
1838

1939
void resolve(GenerationContext ctx) {
2040
_field = new ProtobufField.extension(_descriptor, _parent, ctx);
@@ -79,9 +99,16 @@ class ExtensionGenerator {
7999
var dartType = type.getDartType(fileGen);
80100

81101
if (_field.isRepeated) {
82-
out.print('static final $_protobufImportPrefix.Extension $name = '
102+
out.printAnnotated(
103+
'static final $_protobufImportPrefix.Extension $name = '
83104
'new $_protobufImportPrefix.Extension<$dartType>.repeated(\'$_extendedFullName\','
84-
' \'$name\', ${_field.number}, ${_field.typeConstant}');
105+
' \'$name\', ${_field.number}, ${_field.typeConstant}',
106+
[
107+
new NamedLocation(
108+
name: name,
109+
fieldPathSegment: new List.from(fieldPath)..add(1),
110+
start: 'static final $_protobufImportPrefix.Extension '.length)
111+
]);
85112
if (type.isMessage || type.isGroup) {
86113
out.println(
87114
', $_protobufImportPrefix.getCheckFunction(${_field.typeConstant}), $dartType.create);');
@@ -96,9 +123,16 @@ class ExtensionGenerator {
96123
return;
97124
}
98125

99-
out.print('static final $_protobufImportPrefix.Extension $name = '
126+
out.printAnnotated(
127+
'static final $_protobufImportPrefix.Extension $name = '
100128
'new $_protobufImportPrefix.Extension<$dartType>(\'$_extendedFullName\', \'$name\', '
101-
'${_field.number}, ${_field.typeConstant}');
129+
'${_field.number}, ${_field.typeConstant}',
130+
[
131+
new NamedLocation(
132+
name: name,
133+
fieldPathSegment: new List.from(fieldPath)..add(1),
134+
start: 'static final $_protobufImportPrefix.Extension '.length)
135+
]);
102136

103137
String initializer = _field.generateDefaultFunction(fileGen);
104138

protoc_plugin/lib/file_generator.dart

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,22 @@ class FileGenerator extends ProtobufContainer {
145145
}
146146

147147
// Load and register all enum and message types.
148-
for (EnumDescriptorProto enumType in descriptor.enumType) {
149-
enumGenerators.add(new EnumGenerator(enumType, this, usedTopLevelNames));
150-
}
151-
for (DescriptorProto messageType in descriptor.messageType) {
152-
messageGenerators.add(new MessageGenerator(
153-
messageType, this, declaredMixins, defaultMixin, usedTopLevelNames));
154-
}
155-
for (FieldDescriptorProto extension in descriptor.extension) {
156-
extensionGenerators
157-
.add(new ExtensionGenerator(extension, this, usedExtensionNames));
148+
for (var i = 0; i < descriptor.enumType.length; i++) {
149+
enumGenerators.add(new EnumGenerator.topLevel(
150+
descriptor.enumType[i], this, usedTopLevelNames, i));
151+
}
152+
for (var i = 0; i < descriptor.messageType.length; i++) {
153+
messageGenerators.add(new MessageGenerator.topLevel(
154+
descriptor.messageType[i],
155+
this,
156+
declaredMixins,
157+
defaultMixin,
158+
usedTopLevelNames,
159+
i));
160+
}
161+
for (var i = 0; i < descriptor.extension.length; i++) {
162+
extensionGenerators.add(new ExtensionGenerator.topLevel(
163+
descriptor.extension[i], this, usedTopLevelNames, i));
158164
}
159165
for (ServiceDescriptorProto service in descriptor.service) {
160166
if (options.useGrpc) {
@@ -188,6 +194,7 @@ class FileGenerator extends ProtobufContainer {
188194
String get classname => '';
189195
String get fullName => descriptor.package;
190196
FileGenerator get fileGen => this;
197+
List<int> get fieldPath => [];
191198

192199
/// Generates all the Dart files for this .proto file.
193200
List<CodeGeneratorResponse_File> generateFiles(OutputConfiguration config) {
@@ -201,11 +208,27 @@ class FileGenerator extends ProtobufContainer {
201208
..content = content;
202209
}
203210

211+
IndentingWriter mainWriter = generateMainFile(config);
212+
IndentingWriter enumWriter = generateEnumFile(config);
213+
204214
final files = [
205-
makeFile(".pb.dart", generateMainFile(config)),
206-
makeFile(".pbenum.dart", generateEnumFile(config)),
215+
makeFile(".pb.dart", mainWriter.toString()),
216+
makeFile(".pbenum.dart", enumWriter.toString()),
207217
makeFile(".pbjson.dart", generateJsonFile(config)),
208218
];
219+
220+
if (options.generateMetadata) {
221+
files.addAll([
222+
makeFile(
223+
".pb.dart.meta",
224+
new String.fromCharCodes(
225+
mainWriter.sourceLocationInfo.writeToBuffer())),
226+
makeFile(
227+
".pbenum.dart.meta",
228+
new String.fromCharCodes(
229+
enumWriter.sourceLocationInfo.writeToBuffer()))
230+
]);
231+
}
209232
if (options.useGrpc) {
210233
if (grpcGenerators.isNotEmpty) {
211234
files.add(makeFile(".pbgrpc.dart", generateGrpcFile(config)));
@@ -216,11 +239,15 @@ class FileGenerator extends ProtobufContainer {
216239
return files;
217240
}
218241

242+
/// Creates an IndentingWriter with metadata generation enabled or disabled.
243+
IndentingWriter makeWriter() => new IndentingWriter(
244+
filename: options.generateMetadata ? descriptor.name : null);
245+
219246
/// Returns the contents of the .pb.dart file for this .proto file.
220-
String generateMainFile(
247+
IndentingWriter generateMainFile(
221248
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
222249
if (!_linked) throw new StateError("not linked");
223-
IndentingWriter out = new IndentingWriter();
250+
IndentingWriter out = makeWriter();
224251

225252
writeMainHeader(out, config);
226253

@@ -251,7 +278,7 @@ class FileGenerator extends ProtobufContainer {
251278
for (ClientApiGenerator c in clientApiGenerators) {
252279
c.generate(out);
253280
}
254-
return out.toString();
281+
return out;
255282
}
256283

257284
/// Writes the header and imports for the .pb.dart file.
@@ -370,11 +397,11 @@ class FileGenerator extends ProtobufContainer {
370397
}
371398

372399
/// Returns the contents of the .pbenum.dart file for this .proto file.
373-
String generateEnumFile(
400+
IndentingWriter generateEnumFile(
374401
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
375402
if (!_linked) throw new StateError("not linked");
376403

377-
var out = new IndentingWriter();
404+
var out = makeWriter();
378405
_writeHeading(out);
379406

380407
if (enumCount > 0) {
@@ -394,7 +421,7 @@ class FileGenerator extends ProtobufContainer {
394421
m.generateEnums(out);
395422
}
396423

397-
return out.toString();
424+
return out;
398425
}
399426

400427
/// Returns the number of enum types generated in the .pbenum.dart file.
@@ -410,7 +437,7 @@ class FileGenerator extends ProtobufContainer {
410437
String generateServerFile(
411438
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
412439
if (!_linked) throw new StateError("not linked");
413-
var out = new IndentingWriter();
440+
var out = makeWriter();
414441
_writeHeading(out);
415442

416443
if (serviceGenerators.isNotEmpty) {
@@ -450,7 +477,7 @@ class FileGenerator extends ProtobufContainer {
450477
String generateGrpcFile(
451478
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
452479
if (!_linked) throw new StateError("not linked");
453-
var out = new IndentingWriter();
480+
var out = makeWriter();
454481
_writeHeading(out);
455482

456483
out.println(_asyncImport);
@@ -482,7 +509,7 @@ class FileGenerator extends ProtobufContainer {
482509
String generateJsonFile(
483510
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
484511
if (!_linked) throw new StateError("not linked");
485-
var out = new IndentingWriter();
512+
var out = makeWriter();
486513
_writeHeading(out);
487514

488515
// Import the .pbjson.dart files we depend on.

0 commit comments

Comments
 (0)