diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index eaa7efc036..0e3ace1687 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -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. diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index a4e5a0c8f9..5283dc0404 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -8,8 +8,9 @@ host platform type-safe and easier. ## Supported Platforms 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 @@ -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. @@ -112,9 +114,12 @@ public interface Api2Host { ## Null Safety (NNBD) -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 diff --git a/packages/pigeon/bin/pigeon.dart b/packages/pigeon/bin/pigeon.dart index ec65f9d3eb..3566badd68 100644 --- a/packages/pigeon/bin/pigeon.dart +++ b/packages/pigeon/bin/pigeon.dart @@ -4,8 +4,10 @@ // @dart = 2.2 +import 'dart:io' show exit; + import 'package:pigeon/pigeon_cl.dart'; Future main(List args) async { - await runCommandLine(args); + exit(await runCommandLine(args)); } diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index f36b62e79b..41c1dcbf23 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -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) { diff --git a/packages/pigeon/lib/pigeon_cl.dart b/packages/pigeon/lib/pigeon_cl.dart index de70005bfc..096c4820a2 100644 --- a/packages/pigeon/lib/pigeon_cl.dart +++ b/packages/pigeon/lib/pigeon_cl.dart @@ -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 runCommandLine(List args, {Uri? packageConfig}) async { +Future runCommandLine(List args, {Uri? packageConfig}) async { final PigeonOptions opts = Pigeon.parseArgs(args); final Directory tempDir = Directory.systemTemp.createTempSync( 'flutter_pigeon.', @@ -71,5 +71,5 @@ void main(List args, SendPort sendPort) async { }); final int exitCode = await completer.future; tempDir.deleteSync(recursive: true); - exit(exitCode); + return exitCode; } diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 9d3bc2ba35..5228bb9751 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -313,7 +313,8 @@ 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', @@ -321,6 +322,10 @@ options: /// Convert command-line arguments to [PigeonOptions]. static PigeonOptions parseArgs(List 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(); @@ -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']; @@ -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( diff --git a/packages/pigeon/run_tests.sh b/packages/pigeon/run_tests.sh index ab4610e38d..f48cd409ea 100755 --- a/packages/pigeon/run_tests.sh +++ b/packages/pigeon/run_tests.sh @@ -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 \ @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 \ @@ -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 \ diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart index 6ef917238c..5463793311 100644 --- a/packages/pigeon/test/dart_generator_test.dart +++ b/packages/pigeon/test/dart_generator_test.dart @@ -23,7 +23,7 @@ void main() { classes: [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;')); @@ -48,7 +48,7 @@ void main() { fields: [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')); @@ -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, @@ -101,7 +101,7 @@ void main() { fields: [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')); @@ -123,7 +123,7 @@ void main() { fields: [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 doSomething')); expect(code, contains('// noop')); @@ -145,7 +145,7 @@ void main() { fields: [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 @@ -171,7 +171,7 @@ void main() { fields: [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();')); @@ -193,7 +193,7 @@ void main() { fields: [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[)]')); }); @@ -228,7 +228,7 @@ 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 {')); @@ -236,7 +236,8 @@ void main() { expect(mainCode, isNot(contains('.ApiMock.doSomething'))); expect(mainCode, isNot(contains('\'${Keys.result}\': output.encode()'))); expect(mainCode, isNot(contains('return {};'))); - 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 {'))); @@ -261,7 +262,7 @@ void main() { classes: [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')); }); @@ -285,7 +286,7 @@ void main() { fields: [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 doSomething(Input arg);')); @@ -312,7 +313,7 @@ void main() { fields: [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(')); @@ -338,7 +339,7 @@ void main() { fields: [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')); @@ -360,7 +361,7 @@ void main() { fields: [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[)]')); });