Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
df851d5
Rename generator class to Adapter
tarrinneal Dec 21, 2022
e40d679
create new generator class and dart subclass
tarrinneal Dec 21, 2022
8c4e2a0
cpp and dart test gen
tarrinneal Dec 21, 2022
bcdbdbc
added files
tarrinneal Dec 21, 2022
31bb701
Adds Generator class to all generators
tarrinneal Dec 21, 2022
06315ce
adds swift
tarrinneal Dec 21, 2022
5675c64
Updates tests to use new Adapter naming scheme
tarrinneal Dec 21, 2022
a319b48
Dart generate methods
tarrinneal Dec 21, 2022
6d7405d
convert all generate functions to use new method
tarrinneal Dec 21, 2022
72a380c
Merge branch 'main' of github.com:flutter/packages into skeleton2
tarrinneal Dec 21, 2022
1e59fef
chagngelog
tarrinneal Dec 21, 2022
0964cd3
remove Generator class fields
tarrinneal Dec 21, 2022
f589da4
move paths to options
tarrinneal Dec 22, 2022
637679d
remove dartTestOptions
tarrinneal Dec 22, 2022
68a23e2
Nits and combines source and header generators
tarrinneal Dec 27, 2022
67282cf
renames Adapter to GeneratorAdapter
tarrinneal Dec 27, 2022
d08606c
Update version number for breaking changes
tarrinneal Dec 27, 2022
90fcb67
nits
tarrinneal Dec 27, 2022
ea0ec6c
more personal nits
tarrinneal Dec 27, 2022
6206bad
Fixes dart header bug
tarrinneal Dec 27, 2022
7c3d35c
add gen files for clarity
tarrinneal Dec 27, 2022
ef0b71a
Merge branch 'main' of github.com:flutter/packages into skeleton2
tarrinneal Dec 27, 2022
8bf2dfb
better field naming
tarrinneal Dec 28, 2022
8c6abf2
better field naming
tarrinneal Dec 28, 2022
93a5d1b
removed unneeded dart test generator
tarrinneal Dec 28, 2022
710d1a1
Add filetype to generator
tarrinneal Dec 28, 2022
d189d11
Adds filetype as field to generatorAdapters
tarrinneal Dec 29, 2022
daae569
Merge branch 'main' of github.com:flutter/packages into skeleton2
tarrinneal Dec 29, 2022
88905e3
Merge branch 'main' of github.com:flutter/packages into skeleton2
tarrinneal Dec 30, 2022
29decb9
nits
tarrinneal Dec 30, 2022
b7abbe4
assert
tarrinneal Jan 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 5.0.0

* Creates new Generator classes for each language.

## 4.2.15

* Relocates generator classes. (Reverted)
Expand Down
37 changes: 30 additions & 7 deletions packages/pigeon/lib/cpp_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'ast.dart';
import 'functional.dart';
import 'generator.dart';
import 'generator_tools.dart';
import 'pigeon_lib.dart' show Error;

Expand All @@ -21,36 +22,41 @@ const String _defaultCodecSerializer = 'flutter::StandardCodecSerializer';
class CppOptions {
/// Creates a [CppOptions] object
const CppOptions({
this.header,
this.headerIncludePath,
this.namespace,
this.copyrightHeader,
this.headerOutPath,
});

/// The path to the header that will get placed in the source filed (example:
/// "foo.h").
final String? header;
final String? headerIncludePath;

/// The namespace where the generated class will live.
final String? namespace;

/// A copyright header that will get prepended to generated code.
final Iterable<String>? copyrightHeader;

/// The path to the output header file location.
final String? headerOutPath;

/// Creates a [CppOptions] from a Map representation where:
/// `x = CppOptions.fromMap(x.toMap())`.
static CppOptions fromMap(Map<String, Object> map) {
return CppOptions(
header: map['header'] as String?,
headerIncludePath: map['header'] as String?,
namespace: map['namespace'] as String?,
copyrightHeader: map['copyrightHeader'] as Iterable<String>?,
headerOutPath: map['cppHeaderOut'] as String?,
);
}

/// Converts a [CppOptions] to a Map representation where:
/// `x = CppOptions.fromMap(x.toMap())`.
Map<String, Object> toMap() {
final Map<String, Object> result = <String, Object>{
if (header != null) 'header': header!,
if (headerIncludePath != null) 'header': headerIncludePath!,
if (namespace != null) 'namespace': namespace!,
if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!,
};
Expand All @@ -64,6 +70,23 @@ class CppOptions {
}
}

/// Class that manages all Cpp code generation.
class CppGenerator extends Generator<CppOptions> {
/// Instantiates a Cpp Generator.
CppGenerator();

/// Generates Cpp files with specified [CppOptions]
@override
void generate(CppOptions languageOptions, Root root, StringSink sink,
FileType fileType) {
if (fileType == FileType.header) {
generateCppHeader(languageOptions, root, sink);
} else {
generateCppSource(languageOptions, root, sink);
}
}
}

String _getCodecSerializerName(Api api) => '${api.name}CodecSerializer';

const String _pointerPrefix = 'pointer';
Expand Down Expand Up @@ -1011,8 +1034,8 @@ void _writeSystemHeaderIncludeBlock(Indent indent, List<String> headers) {

/// Generates the ".h" file for the AST represented by [root] to [sink] with the
/// provided [options] and [headerFileName].
void generateCppHeader(
String? headerFileName, CppOptions options, Root root, StringSink sink) {
void generateCppHeader(CppOptions options, Root root, StringSink sink) {
final String? headerFileName = options.headerOutPath;
final Indent indent = Indent(sink);
if (options.copyrightHeader != null) {
addLines(indent, options.copyrightHeader!, linePrefix: '// ');
Expand Down Expand Up @@ -1113,7 +1136,7 @@ void generateCppSource(CppOptions options, Root root, StringSink sink) {
indent.addln('#undef _HAS_EXCEPTIONS');
indent.addln('');

indent.writeln('#include "${options.header}"');
indent.writeln('#include "${options.headerIncludePath}"');
indent.addln('');
_writeSystemHeaderIncludeBlock(indent, <String>[
'flutter/basic_message_channel.h',
Expand Down
52 changes: 47 additions & 5 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:yaml/yaml.dart' as yaml;

import 'ast.dart';
import 'functional.dart';
import 'generator.dart';
import 'generator_tools.dart';

/// Documentation comment open symbol.
Expand All @@ -24,18 +25,30 @@ const String _standardMessageCodec = 'StandardMessageCodec';
/// Options that control how Dart code will be generated.
class DartOptions {
/// Constructor for DartOptions.
const DartOptions({this.copyrightHeader});
DartOptions({
this.copyrightHeader,
this.sourceOutPath,
this.testOutPath,
});

/// A copyright header that will get prepended to generated code.
final Iterable<String>? copyrightHeader;

/// Path to output generated Dart file.
String? sourceOutPath;

/// Path to output generated Test file for tests.
String? testOutPath;

/// Creates a [DartOptions] from a Map representation where:
/// `x = DartOptions.fromMap(x.toMap())`.
static DartOptions fromMap(Map<String, Object> map) {
final Iterable<dynamic>? copyrightHeader =
map['copyrightHeader'] as Iterable<dynamic>?;
return DartOptions(
copyrightHeader: copyrightHeader?.cast<String>(),
sourceOutPath: map['sourceOutPath'] as String?,
testOutPath: map['testOutPath'] as String?,
);
}

Expand All @@ -44,6 +57,8 @@ class DartOptions {
Map<String, Object> toMap() {
final Map<String, Object> result = <String, Object>{
if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!,
if (sourceOutPath != null) 'sourceOutPath': sourceOutPath!,
if (testOutPath != null) 'testOutPath': testOutPath!,
};
return result;
}
Expand All @@ -55,6 +70,33 @@ class DartOptions {
}
}

/// Class that manages all Dart code generation.
class DartGenerator extends Generator<DartOptions> {
/// Instantiates a Dart Generator.
DartGenerator();

/// Generates Dart files with specified [DartOptions]
@override
void generate(DartOptions languageOptions, Root root, StringSink sink,
FileType fileType) {
assert(fileType == FileType.source);
generateDart(languageOptions, root, sink);
}

/// Generates Dart files for testing with specified [DartOptions]
void generateTest(DartOptions languageOptions, Root root, StringSink sink) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: Why not make this a file type as well so the structure is the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was considering that, I just didn't think about it when I wrote the code. I'll change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue with this, upon thinking about it more, is that it would generate the test files when generating non test dart code as well. I don't know if that is super important to avoid, but it seems unnecessary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might be able to avoid that by making the set of types option-driven, but let's not worry about it now and just leave it as you have it; we can always revisit later and it's a minor thing.

final String sourceOutPath = languageOptions.sourceOutPath ?? '';
final String testOutPath = languageOptions.testOutPath ?? '';
generateTestDart(
languageOptions,
root,
sink,
sourceOutPath: sourceOutPath,
testOutPath: testOutPath,
);
}
}

String _escapeForDartSingleQuotedString(String raw) {
return raw
.replaceAll(r'\', r'\\')
Expand Down Expand Up @@ -695,14 +737,14 @@ String _posixify(String inputPath) {
}

/// Generates Dart source code for test support libraries based on the given AST
/// represented by [root], outputting the code to [sink]. [dartOutPath] is the
/// represented by [root], outputting the code to [sink]. [sourceOutPath] is the
/// path of the generated dart code to be tested. [testOutPath] is where the
/// test code will be generated.
void generateTestDart(
DartOptions opt,
Root root,
StringSink sink, {
required String dartOutPath,
required String sourceOutPath,
required String testOutPath,
}) {
final Indent indent = Indent(sink);
Expand All @@ -726,10 +768,10 @@ void generateTestDart(
indent.writeln('');
final String relativeDartPath =
path.Context(style: path.Style.posix).relative(
_posixify(dartOutPath),
_posixify(sourceOutPath),
from: _posixify(path.dirname(testOutPath)),
);
late final String? packageName = _deducePackageName(dartOutPath);
late final String? packageName = _deducePackageName(sourceOutPath);
if (!relativeDartPath.contains('/lib/') || packageName == null) {
// If we can't figure out the package name or the relative path doesn't
// include a 'lib' directory, try relative path import which only works in
Expand Down
15 changes: 15 additions & 0 deletions packages/pigeon/lib/generator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'ast.dart';
import 'generator_tools.dart';

/// A superclass of generator classes.
///
/// This provides the structure that is common across generators for different languages.
abstract class Generator<T> {
/// Generates files for specified language with specified [languageOptions]
void generate(
T languageOptions, Root root, StringSink sink, FileType fileType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making the adjustment. I think having a Generator interface and maintaining the Adapter keeps the generator files simple. Everything looks good but one thing. I think I missed some discussion on FileType. I don't think you've really gained anything by its inclusion. I think it's a design mistake for the following reasons:

  1. Implementing an abstract function and asserting that one parameter is exactly one value means the abstraction doesn't match the implementations well.
  2. The FileType already has 2 omissions: The AST Generator and the Dart Test generator whose output is not a "source" or a "header"
  3. Considering input to some generators being "header" doesn't make sense in many of the generators.
  4. The design isn't modular in that new types of output has to be added to a finite list of outputs and all Generators would have to consider what it means to them.

What do we think we gained by adding them? Now there is a grouping of similar generators: CppHeader and CppSource. That seems like it complicates the design with little benefit. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback.

Would the inclusion of a default option solve some of these issues?

This allows for generators that don't have multiple file types to ignore the fileType parameter without mislabeling them. It would also avoid the need to add more options to the FileType enum in the future (unless there is some other multi file system that isn't covered by source and header).

Is there a better way to handle combining the generator classes without the use of a parameter similar to this implementation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Generator abstraction is already powerful enough to capture the idea of a group.

VP9DJiCm48NtSufHLxJW2qHHQ8LGMI21b5p0YeTYDTYMnqP2qBlZ_BGDeUacSjvxy_IDvJK7wKFyQ02q2UJVP4su9KDU1klpgi1lGBp5NI_HZNL1MyCPLdSeEMIuATE9jTbdMARl4WuNgpPAkYGETVnkOwEsnz9bFtlVUP-oqQfLZ_N9VTOSyaVlhIzcS5xrZgnvwN-leFq3MWbnfWIe6ycC3ywTj8HyE8zX_fC6nZxy2Qzw

or

VP1D3e8m44RtSug9Aq6v02482yF6H1EuG4ChDjQMjCMDUdSB5I7nPzDCt_Hx-TBCMA9jTn40N5gcZwHcM339DB5A9rMADq1SOUCHMwhMSYLDZDKQYR4nvgMR39Vd64jt1l3ugiefQHrywSn9TO8MepJmsSsmknB1QKz7lTlkkB79Lckbqnzr3hnXIkxzzK-rZq9X54qj0Mf1Z9b0eLNNkjBpf6TzRX4kPjdtXCbzBXtU0sHC

I still don't think that the group is buying you much in terms of making the codebase easier to understand or work with though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we think we gained by adding them? Now there is a grouping of similar generators: CppHeader and CppSource.

The issue is that they aren't "similar" they are two fundamentally linked things that can't meaningfully operate on their own. To be useful, each one must be paired with a call to the other with identical inputs. And changes to the header generator almost always require exactly corresponding changes to the source generator or everything breaks.

That's why I would like to see combined classes for all of the linked groups; the goal here is to move all of the generator logic into the subclasses, and inherently codependent logic should be in the same class.

(Having two parallel classes, each with half the logic, feels like when someone has two Ivar arrays where the object indexes must match, instead of using a map or an array of structures. Technically it works, but it's really fragile and harder to understand.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on a design doc for this to allow for more effective communication about our goals here. https://docs.google.com/document/d/1ABr5yZM_sOZTd66bIVpsCJ7_koKmH0tZuLH4ZT1cs8c/edit?usp=sharing

}
11 changes: 10 additions & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:mirrors';
import 'ast.dart';

/// The current version of pigeon. This must match the version in pubspec.yaml.
const String pigeonVersion = '4.2.15';
const String pigeonVersion = '5.0.0';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down Expand Up @@ -497,3 +497,12 @@ Iterable<NamedType> getFieldsInSerializationOrder(Class klass) {
// This returns the fields in the order they are declared in the pigeon file.
return klass.fields;
}

/// Enum to specify which file will be generated for multi-file generators
enum FileType {
/// header file.
header,

/// source file.
source,
}
15 changes: 15 additions & 0 deletions packages/pigeon/lib/java_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'ast.dart';
import 'functional.dart';
import 'generator.dart';
import 'generator_tools.dart';
import 'pigeon_lib.dart' show TaskQueueType;

Expand Down Expand Up @@ -84,6 +85,20 @@ class JavaOptions {
}
}

/// Class that manages all Java code generation.
class JavaGenerator extends Generator<JavaOptions> {
/// Instantiates a Java Generator.
JavaGenerator();

/// Generates Java files with specified [JavaOptions]
@override
void generate(JavaOptions languageOptions, Root root, StringSink sink,
FileType fileType) {
assert(fileType == FileType.source);
generateJava(languageOptions, root, sink);
}
}

/// Calculates the name of the codec that will be generated for [api].
String _getCodecName(Api api) => '${api.name}Codec';

Expand Down
15 changes: 15 additions & 0 deletions packages/pigeon/lib/kotlin_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'ast.dart';
import 'functional.dart';
import 'generator.dart';
import 'generator_tools.dart';
import 'pigeon_lib.dart' show TaskQueueType;

Expand Down Expand Up @@ -64,6 +65,20 @@ class KotlinOptions {
}
}

/// Class that manages all Kotlin code generation.
class KotlinGenerator extends Generator<KotlinOptions> {
/// Instantiates a Kotlin Generator.
KotlinGenerator();

/// Generates Kotlin files with specified [KotlinOptions]
@override
void generate(KotlinOptions languageOptions, Root root, StringSink sink,
FileType fileType) {
assert(fileType == FileType.source);
generateKotlin(languageOptions, root, sink);
}
}

/// Calculates the name of the codec that will be generated for [api].
String _getCodecName(Api api) => '${api.name}Codec';

Expand Down
28 changes: 23 additions & 5 deletions packages/pigeon/lib/objc_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'ast.dart';
import 'functional.dart';
import 'generator.dart';
import 'generator_tools.dart';
import 'pigeon_lib.dart' show Error, TaskQueueType;

Expand All @@ -18,14 +19,14 @@ const DocumentCommentSpecification _docCommentSpec =
class ObjcOptions {
/// Parametric constructor for ObjcOptions.
const ObjcOptions({
this.header,
this.headerIncludePath,
this.prefix,
this.copyrightHeader,
});

/// The path to the header that will get placed in the source filed (example:
/// "foo.h").
final String? header;
final String? headerIncludePath;

/// Prefix that will be appended before all generated classes and protocols.
final String? prefix;
Expand All @@ -39,7 +40,7 @@ class ObjcOptions {
final Iterable<dynamic>? copyrightHeader =
map['copyrightHeader'] as Iterable<dynamic>?;
return ObjcOptions(
header: map['header'] as String?,
headerIncludePath: map['header'] as String?,
prefix: map['prefix'] as String?,
copyrightHeader: copyrightHeader?.cast<String>(),
);
Expand All @@ -49,7 +50,7 @@ class ObjcOptions {
/// `x = ObjcOptions.fromMap(x.toMap())`.
Map<String, Object> toMap() {
final Map<String, Object> result = <String, Object>{
if (header != null) 'header': header!,
if (headerIncludePath != null) 'header': headerIncludePath!,
if (prefix != null) 'prefix': prefix!,
if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!,
};
Expand All @@ -63,6 +64,23 @@ class ObjcOptions {
}
}

/// Class that manages all Objc header code generation.
class ObjcGenerator extends Generator<ObjcOptions> {
/// Instantiates a Objc Generator.
ObjcGenerator();

/// Generates Objc files with specified [ObjcOptions]
@override
void generate(ObjcOptions languageOptions, Root root, StringSink sink,
FileType fileType) {
if (fileType == FileType.header) {
generateObjcHeader(languageOptions, root, sink);
} else {
generateObjcSource(languageOptions, root, sink);
}
}
}

/// Calculates the ObjC class name, possibly prefixed.
String _className(String? prefix, String className) {
if (prefix != null) {
Expand Down Expand Up @@ -890,7 +908,7 @@ void generateObjcSource(ObjcOptions options, Root root, StringSink sink) {
}

void writeImports() {
indent.writeln('#import "${options.header}"');
indent.writeln('#import "${options.headerIncludePath}"');
indent.writeln('#import <Flutter/Flutter.h>');
}

Expand Down
Loading