diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 721ab01fc459..a6abdc031301 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 26.1.8 + +* [kotlin] Adds option to add javax.annotation.Generated annotation. + ## 26.1.7 * [objc] Updates to use module imports. diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index e00fdf2063dd..b5484d5c9b21 100644 --- a/packages/pigeon/lib/src/generator_tools.dart +++ b/packages/pigeon/lib/src/generator_tools.dart @@ -15,7 +15,11 @@ import 'generator.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '26.1.7'; +const String pigeonVersion = '26.1.8'; + +/// Annotation for generated code. +const String generatedAnnotation = + '@javax.annotation.Generated("dev.flutter.pigeon")'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/src/java/java_generator.dart b/packages/pigeon/lib/src/java/java_generator.dart index 2f08bc3a0c61..bf54254ec46f 100644 --- a/packages/pigeon/lib/src/java/java_generator.dart +++ b/packages/pigeon/lib/src/java/java_generator.dart @@ -202,7 +202,7 @@ class JavaGenerator extends StructuredGenerator { '@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})', ); if (generatorOptions.useGeneratedAnnotation ?? false) { - indent.writeln('@javax.annotation.Generated("dev.flutter.pigeon")'); + indent.writeln(generatedAnnotation); } indent.writeln('public class ${generatorOptions.className!} {'); indent.inc(); diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index 7df50a975990..bb9f7896676e 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -35,6 +35,9 @@ const String _pigeonMethodChannelCodec = 'PigeonMethodCodec'; const String _overflowClassName = '${classNamePrefix}CodecOverflow'; +/// Kotlin file-level annotation for generated code. +const String kotlinGeneratedAnnotation = '@file:$generatedAnnotation'; + /// Options that control how Kotlin code will be generated. class KotlinOptions { /// Creates a [KotlinOptions] object @@ -44,6 +47,7 @@ class KotlinOptions { this.errorClassName, this.includeErrorClass = true, this.fileSpecificClassNameComponent, + this.useGeneratedAnnotation = false, }); /// The package where the generated class will live. @@ -64,6 +68,11 @@ class KotlinOptions { /// A String to augment class names to avoid cross file collisions. final String? fileSpecificClassNameComponent; + /// Determines if the `javax.annotation.Generated` is used in the output. This + /// is false by default since that dependency isn't available in plugins by + /// default . + final bool useGeneratedAnnotation; + /// Creates a [KotlinOptions] from a Map representation where: /// `x = KotlinOptions.fromMap(x.toMap())`. static KotlinOptions fromMap(Map map) { @@ -74,6 +83,7 @@ class KotlinOptions { includeErrorClass: map['includeErrorClass'] as bool? ?? true, fileSpecificClassNameComponent: map['fileSpecificClassNameComponent'] as String?, + useGeneratedAnnotation: map['useGeneratedAnnotation'] as bool? ?? false, ); } @@ -87,6 +97,7 @@ class KotlinOptions { 'includeErrorClass': includeErrorClass, if (fileSpecificClassNameComponent != null) 'fileSpecificClassNameComponent': fileSpecificClassNameComponent!, + 'useGeneratedAnnotation': useGeneratedAnnotation, }; return result; } @@ -108,6 +119,7 @@ class InternalKotlinOptions extends InternalOptions { this.errorClassName, this.includeErrorClass = true, this.fileSpecificClassNameComponent, + this.useGeneratedAnnotation = false, }); /// Creates InternalKotlinOptions from KotlinOptions. @@ -119,6 +131,7 @@ class InternalKotlinOptions extends InternalOptions { copyrightHeader = options.copyrightHeader ?? copyrightHeader, errorClassName = options.errorClassName, includeErrorClass = options.includeErrorClass, + useGeneratedAnnotation = options.useGeneratedAnnotation, fileSpecificClassNameComponent = options.fileSpecificClassNameComponent ?? kotlinOut.split('/').lastOrNull?.split('.').first; @@ -143,6 +156,11 @@ class InternalKotlinOptions extends InternalOptions { /// A String to augment class names to avoid cross file collisions. final String? fileSpecificClassNameComponent; + + /// Determines if the `javax.annotation.Generated` is used in the output. This + /// is false by default since that dependency isn't available in plugins by + /// default . + final bool useGeneratedAnnotation; } /// Options that control how Kotlin code will be generated for a specific @@ -192,6 +210,9 @@ class KotlinGenerator extends StructuredGenerator { indent.writeln('// ${getGeneratedCodeWarning()}'); indent.writeln('// $seeAlsoWarning'); indent.writeln('@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")'); + if (generatorOptions.useGeneratedAnnotation) { + indent.writeln(kotlinGeneratedAnnotation); + } } @override diff --git a/packages/pigeon/lib/src/pigeon_lib.dart b/packages/pigeon/lib/src/pigeon_lib.dart index 75216141dc59..e638629f33ad 100644 --- a/packages/pigeon/lib/src/pigeon_lib.dart +++ b/packages/pigeon/lib/src/pigeon_lib.dart @@ -547,6 +547,10 @@ ${_argParser.usage}'''; help: 'The package that generated Kotlin code will be in.', aliases: const ['experimental_kotlin_package'], ) + ..addFlag( + 'kotlin_use_generated_annotation', + help: 'Adds javax.annotation.Generated annotation to the output.', + ) ..addOption( 'cpp_header_out', help: 'Path to generated C++ header file (.h).', @@ -638,6 +642,8 @@ ${_argParser.usage}'''; kotlinOut: results['kotlin_out'] as String?, kotlinOptions: KotlinOptions( package: results['kotlin_package'] as String?, + useGeneratedAnnotation: + results['kotlin_use_generated_annotation'] as bool? ?? false, ), cppHeaderOut: results['cpp_header_out'] as String?, cppSourceOut: results['cpp_source_out'] as String?, diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index d01fb9466690..a2b22e26f34c 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 26.1.7 # This must match the version in lib/src/generator_tools.dart +version: 26.1.8 # This must match the version in lib/src/generator_tools.dart environment: sdk: ^3.9.0 diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart index a7dd1bbfbcbc..664607bcdaa7 100644 --- a/packages/pigeon/test/java_generator_test.dart +++ b/packages/pigeon/test/java_generator_test.dart @@ -4,6 +4,7 @@ import 'package:pigeon/pigeon.dart'; import 'package:pigeon/src/ast.dart'; +import 'package:pigeon/src/generator_tools.dart'; import 'package:pigeon/src/java/java_generator.dart'; import 'package:test/test.dart'; @@ -1611,51 +1612,53 @@ void main() { ); }); - test('generated annotation', () { - final classDefinition = Class(name: 'Foobar', fields: []); - final root = Root( - apis: [], - classes: [classDefinition], - enums: [], - ); - final sink = StringBuffer(); - const javaOptions = InternalJavaOptions( - className: 'Messages', - useGeneratedAnnotation: true, - javaOut: '', - ); - const generator = JavaGenerator(); - generator.generate( - javaOptions, - root, - sink, - dartPackageName: DEFAULT_PACKAGE_NAME, - ); - final code = sink.toString(); - expect(code, contains('@javax.annotation.Generated("dev.flutter.pigeon")')); - }); + group('generated annotation', () { + late Class classDefinition; + late Root root; + late StringBuffer sink; + late JavaGenerator generator; - test('no generated annotation', () { - final classDefinition = Class(name: 'Foobar', fields: []); - final root = Root( - apis: [], - classes: [classDefinition], - enums: [], - ); - final sink = StringBuffer(); - const javaOptions = InternalJavaOptions(className: 'Messages', javaOut: ''); - const generator = JavaGenerator(); - generator.generate( - javaOptions, - root, - sink, - dartPackageName: DEFAULT_PACKAGE_NAME, - ); - final code = sink.toString(); - expect( - code, - isNot(contains('@javax.annotation.Generated("dev.flutter.pigeon")')), - ); + setUp(() { + classDefinition = Class(name: 'Foobar', fields: []); + root = Root( + apis: [], + classes: [classDefinition], + enums: [], + ); + sink = StringBuffer(); + generator = const JavaGenerator(); + }); + + test('with generated annotation', () { + const javaOptions = InternalJavaOptions( + className: 'Messages', + useGeneratedAnnotation: true, + javaOut: '', + ); + generator.generate( + javaOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final code = sink.toString(); + expect(code, contains(generatedAnnotation)); + }); + + test('without generated annotation', () { + const javaOptions = InternalJavaOptions( + className: 'Messages', + javaOut: '', + ); + generator.generate( + javaOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final code = sink.toString(); + expect(code, isNot(contains(generatedAnnotation))); + }); }); test('transfers documentation comments', () { diff --git a/packages/pigeon/test/kotlin_generator_test.dart b/packages/pigeon/test/kotlin_generator_test.dart index 10e5193dae96..1de8ae95e8ae 100644 --- a/packages/pigeon/test/kotlin_generator_test.dart +++ b/packages/pigeon/test/kotlin_generator_test.dart @@ -982,6 +982,49 @@ void main() { expect(code, startsWith('// hello world')); }); + group('generated annotation', () { + late Root root; + late KotlinGenerator generator; + late StringBuffer sink; + + setUp(() { + root = Root(apis: [], classes: [], enums: []); + generator = const KotlinGenerator(); + sink = StringBuffer(); + }); + + test('with generated annotation', () { + final kotlinOptions = InternalKotlinOptions( + copyrightHeader: makeIterable('hello world'), + kotlinOut: '', + useGeneratedAnnotation: true, + ); + generator.generate( + kotlinOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final code = sink.toString(); + expect(code, contains(kotlinGeneratedAnnotation)); + }); + + test('without generated annotation', () { + final kotlinOptions = InternalKotlinOptions( + copyrightHeader: makeIterable('hello world'), + kotlinOut: '', + ); + generator.generate( + kotlinOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final code = sink.toString(); + expect(code, isNot(contains(kotlinGeneratedAnnotation))); + }); + }); + test('generics - list', () { final classDefinition = Class( name: 'Foobar', diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart index 2627787c4f03..86b0498554a2 100644 --- a/packages/pigeon/test/pigeon_lib_test.dart +++ b/packages/pigeon/test/pigeon_lib_test.dart @@ -137,6 +137,13 @@ void main() { expect(opts.kotlinOptions?.package, equals('com.google.foo')); }); + test('parse args - kotlin_use_generated_annotation', () { + final PigeonOptions opts = Pigeon.parseArgs([ + '--kotlin_use_generated_annotation', + ]); + expect(opts.kotlinOptions!.useGeneratedAnnotation, isTrue); + }); + test('parse args - cpp_header_out', () { final PigeonOptions opts = Pigeon.parseArgs([ '--cpp_header_out', diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 99a19cb1fb71..a183a44c344d 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -208,6 +208,21 @@ Future generateTestPigeons({ return generateCode; } } + + // Test case for useGeneratedAnnotation feature with core_tests + final String corePascalCaseName = _snakeToPascalCase('core_tests'); + final int generateCodeWithAnnotation = await runPigeon( + input: './pigeons/core_tests.dart', + kotlinOut: + '$outputBase/android/src/main/kotlin/com/example/test_plugin/${corePascalCaseName}WithAnnotation.gen.kt', + kotlinPackage: 'com.example.test_plugin', + kotlinErrorClassName: 'FlutterError', + kotlinUseGeneratedAnnotation: true, + ); + if (generateCodeWithAnnotation != 0) { + return generateCodeWithAnnotation; + } + return 0; } @@ -217,6 +232,7 @@ Future runPigeon({ String? kotlinPackage, String? kotlinErrorClassName, bool kotlinIncludeErrorClass = true, + bool kotlinUseGeneratedAnnotation = false, bool swiftIncludeErrorClass = true, String? swiftOut, String? swiftErrorClassName, @@ -230,6 +246,7 @@ Future runPigeon({ String? gobjectModule, String? javaOut, String? javaPackage, + bool javaUseGeneratedAnnotation = false, String? objcHeaderOut, String? objcSourceOut, String objcPrefix = '', @@ -285,12 +302,16 @@ Future runPigeon({ ? null : GObjectOptions(module: gobjectModule), javaOut: javaOut, - javaOptions: JavaOptions(package: javaPackage), + javaOptions: JavaOptions( + package: javaPackage, + useGeneratedAnnotation: javaUseGeneratedAnnotation, + ), kotlinOut: kotlinOut, kotlinOptions: KotlinOptions( package: kotlinPackage, errorClassName: kotlinErrorClassName, includeErrorClass: kotlinIncludeErrorClass, + useGeneratedAnnotation: kotlinUseGeneratedAnnotation, ), objcHeaderOut: objcHeaderOut, objcSourceOut: objcSourceOut,