diff --git a/packages/pigeon/bin/pigeon.dart b/packages/pigeon/bin/pigeon.dart index 1a424e092f0..650716a0ad3 100644 --- a/packages/pigeon/bin/pigeon.dart +++ b/packages/pigeon/bin/pigeon.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.2 +// @dart = 2.3 import 'dart:async'; import 'dart:io'; @@ -26,18 +26,122 @@ String _posixRelative(String input, {String from}) { return context.relative(posixAbsInputPath, from: posixTempPath); } +/// aa_bb_cc -> AaBbCc +String _underLineToHump(String str) { + return str + .split('_') + .map((String e) => e.replaceRange(0, 1, e[0].toUpperCase())) + .join(); +} + +/// AaBbCc -> aa_bb_cc +String _humpToUnderLine(String str) { + return str.replaceAllMapped(RegExp(r'(^[A-Z])|([A-Z])'), (Match match) { + if (match[2] != null) { + return '_${match[2]?.toLowerCase()}'; + } else { + return match[1]?.toLowerCase() ?? ''; + } + }); +} + Future main(List args) async { final PigeonOptions opts = Pigeon.parseArgs(args); final Directory tempDir = Directory.systemTemp.createTempSync( 'flutter_pigeon.', ); + if (opts.isDir) { + final Directory inputDir = Directory(opts.inputDir); + if (!inputDir.existsSync()) { + tempDir.deleteSync(recursive: true); + exit(exitCode); + } + + final List list = inputDir.listSync(recursive: true); + final List inputDirs = path.split(opts.inputDir); + for (int i = 0; i < list.length; i++) { + final FileSystemEntity element = list[i]; + if (element is File) { + final String input = element.path; + final List fileParents = path.split(input); + final String fileName = path.basenameWithoutExtension(input); + final String outName = _humpToUnderLine('${fileName}_${opts.suffit}'); + final String humpName = _underLineToHump(outName); + opts.input = input; + + final List middle = + fileParents.sublist(inputDirs.length, fileParents.length - 1); + final List javaPackage = middle.isNotEmpty + ? [ + '--java_package', + '${opts.javaOptions.package}.${middle.join('.')}', + ] + : [ + '--java_package', + opts.javaOptions.package, + ]; + + final List objcPrefix = opts.objcOptions.prefix != null + ? [ + '--objc_prefix', + opts.objcOptions.prefix, + ] + : List.empty(); + final List fileArgs = [ + '--input', + input, + '--dart_out', + path.join( + opts.dartOutDir.replaceAll('/', path.separator), + middle.join(path.separator), + '$outName.dart', + ), + '--java_out', + path.join( + opts.javaOutDir.replaceAll('/', path.separator), + opts.javaOptions.package?.replaceAll('.', path.separator) ?? '', + middle.join(path.separator), + '$humpName.java', + ), + ...javaPackage, + '--objc_header_out', + path.join( + opts.objcOutDir.replaceAll('/', path.separator), + middle.join(path.separator), + '$humpName.h', + ), + '--objc_source_out', + path.join( + opts.objcOutDir.replaceAll('/', path.separator), + middle.join(path.separator), + '$humpName.m', + ), + ...objcPrefix, + ]; + + print('generate file $input'); + print('generate args $fileArgs'); + final int code = await _genFile(fileArgs, opts, tempDir); + print('generate resule $code $input'); + print('============================'); + } + } + } else { + await _genFile(args, opts, tempDir); + } + tempDir.deleteSync(recursive: true); + exit(exitCode); +} + +Future _genFile( + List args, PigeonOptions opts, Directory tempDir) async { String importLine = ''; if (opts.input != null) { final String relInputPath = _posixRelative(opts.input, from: tempDir.path); importLine = 'import \'$relInputPath\';\n'; } - final String code = """// @dart = 2.2 + final String code = """// @dart = 2.3 $importLine import 'dart:io'; import 'dart:isolate'; @@ -68,6 +172,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/example/README.md b/packages/pigeon/example/README.md index 8b8409d84cf..91d1efb5cdb 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -41,6 +41,26 @@ flutter pub run pigeon \ --java_out ./android/app/src/main/java/dev/flutter/pigeon/Pigeon.java \ --java_package "dev.flutter.pigeon" ``` +### Directory model generated + +Enable Directory model generated + +```sh +flutter pub run pigeon --dir +``` + +Other args + +```sh +flutter pub run pigeon --dir \ + --input_dir pigeons \ + --dart_out_dir lib \ + --objc_out_dir ios/Runner \ + --objc_prefix FL \ + --java_out_dir android/app/src/main/java \ + --java_package "dev.flutter.pigeon" +``` + ### AppDelegate.m diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 80f7cb5e616..bf8926433e7 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -134,14 +134,14 @@ void _writeFlutterApi( : func.returnType == 'void' ? 'return;' : 'return null;'; - indent.write('if (message == null) '); - indent.scoped('{', '}', () { - indent.writeln(emptyReturnStatement); - }); String call; if (argType == 'void') { call = 'api.${func.name}()'; } else { + indent.write('if (message == null) '); + indent.scoped('{', '}', () { + indent.writeln(emptyReturnStatement); + }); indent.writeln( 'final $argType input = $argType.decode(message);', ); diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 22d3871d6b2..5bb69122595 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -117,6 +117,24 @@ bool _isFlutterApi(ClassMirror apiMirror) { /// Options used when running the code generator. class PigeonOptions { + /// Enable directory model + bool isDir; + + /// Directory model suffit that will be appended after all generated classes or file. + String suffit; + + /// Path to the directory which will be processed. + String inputDir; + + /// Path to the dart direcotry that will be generated. + String dartOutDir; + + /// Path to the Objective-C source and header direcotry will be generated. + String objcOutDir; + + /// Path to the java direcotry that will be generated. + String javaOutDir; + /// Path to the file which will be processed. String input; @@ -284,6 +302,19 @@ options: } static final ArgParser _argParser = ArgParser() + ..addFlag('dir', abbr: 'd', help: 'Enable directory model') + ..addOption('suffit', + defaultsTo: 'pigeon', + help: 'Directory model suffix for generated classes or files.') + ..addOption('input_dir', defaultsTo: 'pigeons', help: 'Path to pigeon.') + ..addOption('dart_out_dir', + defaultsTo: 'lib', help: 'Path to generated Dart source directory.') + ..addOption('java_out_dir', + defaultsTo: 'android/src/main/java', + help: 'Path to generated Java directory.') + ..addOption('objc_out_dir', + defaultsTo: 'ios/Classes', + help: 'Path to generated Objective-C source and header directory.') ..addOption('input', help: 'REQUIRED: Path to pigeon file.') ..addOption('dart_out', help: 'REQUIRED: Path to generated Dart source file (.dart).') @@ -307,6 +338,12 @@ options: final ArgResults results = _argParser.parse(args); final PigeonOptions opts = PigeonOptions(); + opts.isDir = results['dir']; + opts.suffit = results['suffit']; + opts.inputDir = results['input_dir']; + opts.dartOutDir = results['dart_out_dir']; + opts.objcOutDir = results['objc_out_dir']; + opts.javaOutDir = results['java_out_dir']; opts.input = results['input']; opts.dartOut = results['dart_out']; opts.dartTestOut = results['dart_test_out']; @@ -327,6 +364,7 @@ options: sink = stdout; } else { file = File(output); + file.parent.createSync(recursive: true); sink = file.openWrite(); } func(sink); diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index b3ea3c2fefc..71f82909500 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -8,4 +8,4 @@ dependencies: dev_dependencies: test: ^1.11.1 environment: - sdk: '>=2.2.0 <3.0.0' + sdk: '>=2.3.0 <3.0.0'