Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 27 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
## 0.2.0

* **BREAKING CHANGE** - Pigeon files must be null-safe now. That means the
fields inside of the classes must be declared nullable (
[non-null fields](https://github.com/flutter/flutter/issues/59118) arent't yet
supported). Migration example:

```dart
// Version 0.1.x
class Foo {
int bar;
String baz;
}

// Version 0.2.x
class Foo {
int? bar;
String? baz;
}
```

* **BREAKING CHANGE** - The default output from Pigeon is now null-safe. If you
want non-null-safe code you must provide the `--no-dart_null_safety` flag.
* The Pigeon source code is now null-safe.
* Fixed niladic non-value returning async functions in the Java generator.
* Made `runCommandLine` return an the status code.

## 0.1.24

* Moved logic from bin/ to lib/ to help customers wrap up the behavior.
Expand Down
17 changes: 11 additions & 6 deletions packages/pigeon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ host platform type-safe and easier.
## Supported Platforms

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since it's a breaking change, add a banner at the top of this page too to describe where the break is


Currently Pigeon only supports generating Objective-C code for usage on iOS and
Java code for Android. The Objective-C code is [accessible to Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift) and the Java
code is accessible to Kotlin.
Java code for Android. The Objective-C code is
[accessible to Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift)
and the Java code is accessible to Kotlin.

## Runtime Requirements

Expand Down Expand Up @@ -64,7 +65,8 @@ See the "Example" tab. A full working example can also be found in the

Pigeon uses the `StandardMessageCodec` so it supports any data-type platform
channels supports
[[documentation](https://flutter.dev/docs/development/platform-integration/platform-channels#codec)]. Nested data-types are supported, too.
[[documentation](https://flutter.dev/docs/development/platform-integration/platform-channels#codec)].
Nested data-types are supported, too.

Note: Generics for List and Map aren't supported yet.

Expand Down Expand Up @@ -112,9 +114,12 @@ public interface Api2Host {

## Null Safety (NNBD)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should caveat this a bit more clearly. If we're highlighting null safety more prominently, we should be clear there are 2 layers. There's the general null safety concept and there we're not doing too much yet. Things can't be defined as non-null and the nullable types aren't specifically annotated as so in kotlin and swift. There's also the Dart 2.12 null safety mechanism. We don't support non-null neither but we properly tag everything as nullable now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done


In order to generate null-safe code run the command line with the extra argument
`--dart_null_safety`. For example:
`flutter pub run pigeon --input ./pigeons/messages.dart --dart_null_safety`.
Right now Pigeon supports generating null-safe code, but it doesn't yet support
[non-null fields](https://github.com/flutter/flutter/issues/59118).

The default is to generate null-safe code but in order to generate non-null-safe
code run Pigeon with the extra argument `--no-dart_null_safety`. For example:
`flutter pub run pigeon --input ./pigeons/messages.dart --no-dart_null_safety --dart_out stdout`.

## Feedback

Expand Down
4 changes: 3 additions & 1 deletion packages/pigeon/bin/pigeon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

// @dart = 2.2

import 'dart:io' show exit;

import 'package:pigeon/pigeon_cl.dart';

Future<void> main(List<String> args) async {
await runCommandLine(args);
exit(await runCommandLine(args));
}
5 changes: 4 additions & 1 deletion packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import 'generator_tools.dart';

/// Options that control how Dart code will be generated.
class DartOptions {
/// Constructor for DartOptions.
DartOptions({this.isNullSafe = true});

/// Determines if the generated code has null safety annotations (Dart >=2.12 required).
bool isNullSafe = false;
bool isNullSafe;
}

String _escapeForDartSingleQuotedString(String raw) {
Expand Down
4 changes: 2 additions & 2 deletions packages/pigeon/lib/pigeon_cl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ String _posixRelative(String input, {required String from}) {
/// This is the main entrypoint for the command-line tool. [args] are the
/// commmand line arguments and there is an optional [packageConfig] to
/// accomodate users that want to integrate pigeon with other build systems.
Future<void> runCommandLine(List<String> args, {Uri? packageConfig}) async {
Future<int> runCommandLine(List<String> args, {Uri? packageConfig}) async {
final PigeonOptions opts = Pigeon.parseArgs(args);
final Directory tempDir = Directory.systemTemp.createTempSync(
'flutter_pigeon.',
Expand Down Expand Up @@ -71,5 +71,5 @@ void main(List<String> args, SendPort sendPort) async {
});
final int exitCode = await completer.future;
tempDir.deleteSync(recursive: true);
exit(exitCode);
return exitCode;
}
14 changes: 10 additions & 4 deletions packages/pigeon/lib/pigeon_lib.dart
Original file line number Diff line number Diff line change
Expand Up @@ -313,14 +313,19 @@ options:
..addOption('java_package',
help: 'The package that generated Java code will be in.')
..addFlag('dart_null_safety',
help: 'Makes generated Dart code have null safety annotations')
help: 'Makes generated Dart code have null safety annotations',
defaultsTo: true)
..addOption('objc_header_out',
help: 'Path to generated Objective-C header file (.h).')
..addOption('objc_prefix',
help: 'Prefix for generated Objective-C classes and protocols.');

/// Convert command-line arguments to [PigeonOptions].
static PigeonOptions parseArgs(List<String> args) {
// Note: This function shouldn't perform any logic, just translate the args
// to PigeonOptions. Synthesized values inside of the PigeonOption should
// get set in the `run` function to accomodate users that are using the
// `configurePigeon` function.
final ArgResults results = _argParser.parse(args);

final PigeonOptions opts = PigeonOptions();
Expand All @@ -334,9 +339,6 @@ options:
);
opts.javaOut = results['java_out'];
opts.javaOptions = JavaOptions(
className: (opts.javaOut == null)
? null
: path.basenameWithoutExtension(opts.javaOut!),
package: results['java_package'],
);
opts.dartOptions = DartOptions()..isNullSafe = results['dart_null_safety'];
Expand Down Expand Up @@ -480,6 +482,10 @@ options:
options.objcOptions ?? ObjcOptions(), parseResults.root, sink));
}
if (options.javaOut != null) {
if (options.javaOptions!.className == null) {
options.javaOptions!.className =
path.basenameWithoutExtension(options.javaOut!);
}
await _runGenerator(
options.javaOut!,
(StringSink sink) => generateJava(
Expand Down
8 changes: 3 additions & 5 deletions packages/pigeon/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ test_pigeon_ios() {
temp_dir=$(mktmpdir)

pub run pigeon \
--no-dart_null_safety \
--input $1 \
--dart_out $temp_dir/pigeon.dart \
--objc_header_out $temp_dir/pigeon.h \
Expand All @@ -80,7 +81,6 @@ test_pigeon_android() {

pub run pigeon \
--input $1 \
--dart_null_safety \
--dart_out $temp_dir/pigeon.dart \
--java_out $temp_dir/Pigeon.java \
--java_package foo
Expand Down Expand Up @@ -113,7 +113,6 @@ test_null_safe_dart() {

pub run pigeon \
--input $1 \
--dart_null_safety \
--dart_out $temp_dir/pigeon.dart

dartanalyzer $temp_dir/pigeon.dart --fatal-infos --fatal-warnings --packages ./e2e_tests/test_objc/.packages
Expand Down Expand Up @@ -165,7 +164,6 @@ pub run pigeon 1> /dev/null
pushd $PWD
pub run pigeon \
--input pigeons/message.dart \
--dart_null_safety \
--dart_out platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
cd platform_tests/flutter_null_safe_unit_tests
flutter pub get
Expand All @@ -178,7 +176,6 @@ popd
pushd $PWD
pub run pigeon \
--input pigeons/message.dart \
--dart_null_safety \
--dart_out mock_handler_tester/test/message.dart \
--dart_test_out mock_handler_tester/test/test.dart
dartfmt -w mock_handler_tester/test/message.dart
Expand Down Expand Up @@ -221,11 +218,13 @@ test_pigeon_ios ./pigeons/all_datatypes.dart
# iOS unit tests on generated code.
###############################################################################
pub run pigeon \
--no-dart_null_safety \
--input pigeons/message.dart \
--dart_out /dev/null \
--objc_header_out platform_tests/ios_unit_tests/ios/Runner/messages.h \
--objc_source_out platform_tests/ios_unit_tests/ios/Runner/messages.m
pub run pigeon \
--no-dart_null_safety \
--input pigeons/async_handlers.dart \
--dart_out /dev/null \
--objc_header_out platform_tests/ios_unit_tests/ios/Runner/async_handlers.h \
Expand Down Expand Up @@ -255,7 +254,6 @@ DARTLE_DART="e2e_tests/test_objc/lib/dartle.dart"
PIGEON_JAVA="e2e_tests/test_objc/android/app/src/main/java/io/flutter/plugins/Pigeon.java"
pub run pigeon \
--input pigeons/message.dart \
--dart_null_safety \
--dart_out $DARTLE_DART \
--objc_header_out $DARTLE_H \
--objc_source_out $DARTLE_M \
Expand Down
31 changes: 16 additions & 15 deletions packages/pigeon/test/dart_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ void main() {
classes: <Class>[klass],
);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('class Foobar'));
expect(code, contains(' dataType1 field1;'));
Expand All @@ -48,7 +48,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')])
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, matches('Output.*doSomething.*Input'));
Expand All @@ -66,7 +66,7 @@ void main() {
)
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(
code,
Expand Down Expand Up @@ -101,7 +101,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')])
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('abstract class Api'));
expect(code, contains('static void setup(Api'));
Expand All @@ -123,7 +123,7 @@ void main() {
fields: <Field>[Field(name: 'input', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('Future<void> doSomething'));
expect(code, contains('// noop'));
Expand All @@ -145,7 +145,7 @@ void main() {
fields: <Field>[Field(name: 'input', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
// The next line verifies that we're not setting a variable to the value of "doSomething", but
// ignores the line where we assert the value of the argument isn't null, since on that line
Expand All @@ -171,7 +171,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, matches('output.*=.*doSomething[(][)]'));
expect(code, contains('Output doSomething();'));
Expand All @@ -193,7 +193,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, matches('channel.send[(]null[)]'));
});
Expand Down Expand Up @@ -228,15 +228,16 @@ void main() {
]);
final StringBuffer mainCodeSink = StringBuffer();
final StringBuffer testCodeSink = StringBuffer();
generateDart(DartOptions(), root, mainCodeSink);
generateDart(DartOptions(isNullSafe: false), root, mainCodeSink);
final String mainCode = mainCodeSink.toString();
expect(mainCode, isNot(contains('import \'fo\\\'o.dart\';')));
expect(mainCode, contains('class Api {'));
expect(mainCode, isNot(contains('abstract class ApiMock')));
expect(mainCode, isNot(contains('.ApiMock.doSomething')));
expect(mainCode, isNot(contains('\'${Keys.result}\': output.encode()')));
expect(mainCode, isNot(contains('return <Object, Object>{};')));
generateTestDart(DartOptions(), root, testCodeSink, "fo'o.dart");
generateTestDart(
DartOptions(isNullSafe: false), root, testCodeSink, "fo'o.dart");
final String testCode = testCodeSink.toString();
expect(testCode, contains('import \'fo\\\'o.dart\';'));
expect(testCode, isNot(contains('class Api {')));
Expand All @@ -261,7 +262,7 @@ void main() {
classes: <Class>[klass],
);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('// @dart = 2.8'));
});
Expand All @@ -285,7 +286,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')])
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('abstract class Api'));
expect(code, contains('Future<Output> doSomething(Input arg);'));
Expand All @@ -312,7 +313,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')])
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, isNot(matches('=.s*doSomething')));
expect(code, contains('await api.doSomething('));
Expand All @@ -338,7 +339,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')])
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, matches('Output.*doSomething.*Input'));
Expand All @@ -360,7 +361,7 @@ void main() {
fields: <Field>[Field(name: 'output', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
generateDart(DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, matches('channel.send[(]null[)]'));
});
Expand Down