[jnigen] Add regenerate script (https://github.com/dart-lang/jnigen/issues/106)
diff --git a/pkgs/jnigen/tool/command_runner.dart b/pkgs/jnigen/tool/command_runner.dart new file mode 100644 index 0000000..7bf925e --- /dev/null +++ b/pkgs/jnigen/tool/command_runner.dart
@@ -0,0 +1,109 @@ +import 'dart:io'; + +const ansiRed = '\x1b[31m'; +const ansiDefault = '\x1b[39;49m'; + +void printError(Object? message) { + if (stderr.supportsAnsiEscapes) { + message = '$ansiRed$message$ansiDefault'; + } + stderr.writeln(message); +} + +class StepFailure implements Exception { + StepFailure(this.name); + String name; + @override + String toString() => 'step failed: $name'; +} + +abstract class Step { + /// Runs this step, raises an exception if something fails. + Future<void> run(); +} + +class Callback implements Step { + Callback(this.name, this.function); + String name; + Future<void> Function() function; + @override + Future<void> run() => function(); +} + +class Command implements Step { + Command(this.exec, this.args, this.workingDirectory); + final String exec; + final List<String> args; + final Uri workingDirectory; + + @override + Future<void> run() async { + final result = await Process.run( + exec, + args, + workingDirectory: workingDirectory.toFilePath(), + runInShell: true, + ); + if (result.exitCode != 0) { + printError(result.stdout); + printError(result.stderr); + final commandString = "$exec ${args.join(" ")}"; + stderr.writeln("failure executing command: $commandString"); + throw StepFailure(commandString); + } + } +} + +class Runner { + static final gitRoot = getRepositoryRoot(); + Runner(this.name, this.defaultWorkingDir); + String name; + Uri defaultWorkingDir; + final steps = <Step>[]; + final cleanupSteps = <Step>[]; + + void chainCommand(String exec, List<String> args, {Uri? workingDirectory}) => + _addCommand(steps, exec, args, workingDirectory: workingDirectory); + + void chainCleanupCommand(String exec, List<String> args, + {Uri? workingDirectory}) => + _addCommand(cleanupSteps, exec, args, workingDirectory: workingDirectory); + + void _addCommand(List<Step> list, String exec, List<String> args, + {Uri? workingDirectory}) { + list.add(Command(exec, args, (workingDirectory ?? defaultWorkingDir))); + } + + void chainCallback(String name, Future<void> Function() callback) { + steps.add(Callback(name, callback)); + } + + Future<void> run() async { + stderr.writeln("started: $name"); + var error = false; + for (var step in steps) { + try { + await step.run(); + } on StepFailure catch (e) { + stderr.writeln(e); + error = true; + exitCode = 1; + break; + } + } + stderr.writeln('${error ? "failed" : "complete"}: $name'); + for (var step in cleanupSteps) { + try { + await step.run(); + } on Exception catch (e) { + printError("ERROR: $e"); + } + } + } +} + +Uri getRepositoryRoot() { + final gitCommand = Process.runSync("git", ["rev-parse", "--show-toplevel"]); + final output = gitCommand.stdout as String; + return Uri.directory(output.trim()); +}
diff --git a/pkgs/jnigen/tool/pre_commit_checks.dart b/pkgs/jnigen/tool/pre_commit_checks.dart index 6575e8a..07eb0b6 100644 --- a/pkgs/jnigen/tool/pre_commit_checks.dart +++ b/pkgs/jnigen/tool/pre_commit_checks.dart
@@ -16,115 +16,26 @@ import 'dart:async'; import 'dart:io'; -const ansiRed = '\x1b[31m'; -const ansiDefault = '\x1b[39;49m'; - -void printError(Object? message) { - if (stderr.supportsAnsiEscapes) { - message = '$ansiRed$message$ansiDefault'; - } - stderr.writeln(message); -} - -class StepFailure implements Exception { - StepFailure(this.name); - String name; - @override - String toString() => 'step failed: $name'; -} - -abstract class Step { - /// Runs this step, raises an exception if something fails. - Future<void> run(); -} - -class Callback implements Step { - Callback(this.name, this.function); - String name; - Future<void> Function() function; - @override - Future<void> run() => function(); -} - -class Command implements Step { - Command(this.exec, this.args, this.workingDirectory); - final String exec; - final List<String> args; - final String workingDirectory; - - @override - Future<void> run() async { - final result = - await Process.run(exec, args, workingDirectory: workingDirectory); - if (result.exitCode != 0) { - printError(result.stdout); - printError(result.stderr); - final commandString = "$exec ${args.join(" ")}"; - stderr.writeln("failure executing command: $commandString"); - throw StepFailure(commandString); - } - } -} - -class Runner { - static final gitRoot = getRepositoryRoot(); - Runner(this.name, this.defaultWorkingDir); - String name; - String defaultWorkingDir; - final steps = <Step>[]; - final cleanupSteps = <Step>[]; - - void chainCommand(String exec, List<String> args, - {String? workingDirectory}) => - _addCommand(steps, exec, args, workingDirectory: workingDirectory); - - void chainCleanupCommand(String exec, List<String> args, - {String? workingDirectory}) => - _addCommand(cleanupSteps, exec, args, workingDirectory: workingDirectory); - - void _addCommand(List<Step> list, String exec, List<String> args, - {String? workingDirectory}) { - final resolvedWorkingDirectory = - gitRoot.resolve(workingDirectory ?? defaultWorkingDir); - list.add(Command(exec, args, resolvedWorkingDirectory.toFilePath())); - } - - void chainCallback(String name, Future<void> Function() callback) { - steps.add(Callback(name, callback)); - } - - Future<void> run() async { - stderr.writeln("started: $name"); - var error = false; - for (var step in steps) { - try { - await step.run(); - } on StepFailure catch (e) { - stderr.writeln(e); - error = true; - exitCode = 1; - break; - } - } - stderr.writeln('${error ? "failed" : "complete"}: $name'); - for (var step in cleanupSteps) { - try { - await step.run(); - } on Exception catch (e) { - printError("ERROR: $e"); - } - } - } -} - -Uri getRepositoryRoot() { - final gitCommand = Process.runSync("git", ["rev-parse", "--show-toplevel"]); - final output = gitCommand.stdout as String; - return Uri.directory(output.trim()); -} +import 'command_runner.dart'; void main() async { - final jniAnalyze = Runner("Analyze JNI", "jni"); + final gitRoot = getRepositoryRoot(); + + // change to project root + Directory.current = gitRoot.toFilePath(); + + final tempDir = Directory.current.createTempSync('jnigen_checks_clone_'); + final tempJniPath = tempDir.uri.resolve("jni/"); + final tempJnigenPath = tempDir.uri.resolve("jnigen/"); + final gitClone = Runner("Clone jni", Directory.current.uri) + ..chainCommand('git', ['clone', '.', tempDir.path]) + ..chainCommand("flutter", ["pub", "get", "--offline"], + workingDirectory: tempJniPath) + ..chainCommand("dart", ["pub", "get", "--offline"], + workingDirectory: tempJnigenPath); + await gitClone.run(); + + final jniAnalyze = Runner("Analyze JNI", tempJniPath); jniAnalyze ..chainCommand("dart", ["analyze", "--fatal-infos"]) ..chainCommand( @@ -139,35 +50,44 @@ "third_party/global_jni_env.c", "third_party/global_jni_env.h", ], - workingDirectory: "jni/src"); - final jniTest = Runner("Test JNI", "jni") + workingDirectory: tempJniPath.resolve("src/")); + final jniTest = Runner("Test JNI", tempJniPath) ..chainCommand("dart", ["run", "jni:setup"]) ..chainCommand("dart", ["test", "-j", "1"]); unawaited(jniAnalyze.run().then((f) => jniTest.run())); + final ffigenBindingsPath = getRepositoryRoot() .resolve("jni/lib/src/third_party/jni_bindings_generated.dart"); final ffigenBindings = File.fromUri(ffigenBindingsPath); final oldBindingsText = ffigenBindings.readAsStringSync(); - final ffigenCompare = Runner("Generate & Compare FFIGEN bindings", "jni") - ..chainCommand("dart", ["run", "ffigen", "--config", "ffigen.yaml"]) - ..chainCallback("compare bindings", () async { - final newBindingsText = await ffigenBindings.readAsString(); - if (newBindingsText != oldBindingsText) { - await ffigenBindings.writeAsString(oldBindingsText); - throw "new JNI.h bindings differ from old bindings"; - } - }); + final ffigenCompare = + Runner("Generate & Compare FFIGEN bindings", tempJniPath) + ..chainCommand("dart", ["run", "ffigen", "--config", "ffigen.yaml"]) + ..chainCallback("compare bindings", () async { + final newBindingsText = await ffigenBindings.readAsString(); + if (newBindingsText != oldBindingsText) { + await ffigenBindings.writeAsString(oldBindingsText); + throw "new JNI.h bindings differ from old bindings"; + } + }); unawaited(ffigenCompare.run()); - final jnigenAnalyze = Runner("Analyze jnigen", "jnigen") + final jnigenAnalyze = Runner("Analyze jnigen", tempJnigenPath) ..chainCommand("dart", ["analyze", "--fatal-infos"]) ..chainCommand( "dart", ["format", "--output=none", "--set-exit-if-changed", "."]) ..chainCommand("dart", ["run", "jnigen:setup"]); - final jnigenTest = Runner("Test jnigen", "jnigen") - ..chainCommand("dart", ["test"]); + + // Tests may need more time when running on systems with less cores. + // So '--timeout 2x' is specified. + final jnigenTest = Runner("Test jnigen", gitRoot.resolve("jnigen/")) + ..chainCommand("dart", ["test", "--timeout", "2x"]); + + // Note: Running in_app_java and notification_plugin checks on source dir + // itself, because running flutter build in cloned dir will take time. final compareInAppJavaBindings = Runner( - "Generate & compare InAppJava bindings", "jnigen/example/in_app_java") + "Generate & compare InAppJava bindings", + gitRoot.resolve("jnigen/example/in_app_java")) ..chainCommand("dart", [ "run", "jnigen", @@ -179,8 +99,9 @@ ..chainCommand("diff", ["lib/android_utils.dart", "_temp.dart"]) ..chainCommand("diff", ["-qr", "src/android_utils/", "src_temp/"]) ..chainCleanupCommand("rm", ["-r", "_temp.dart", "src_temp"]); - final comparePdfboxBindings = Runner( - "Generate & compare PdfBox Bindings", "jnigen/example/pdfbox_plugin") + + final comparePdfboxBindings = Runner("Generate & compare PdfBox Bindings", + gitRoot.resolve("jnigen/example/pdfbox_plugin")) ..chainCommand("dart", [ "run", "jnigen", @@ -192,9 +113,10 @@ ..chainCommand("diff", ["-qr", "lib/src/third_party/", "lib_temp/"]) ..chainCommand("diff", ["-qr", "src/", "src_temp/"]) ..chainCleanupCommand("rm", ["-r", "lib_temp", "src_temp"]); + final compareNotificationPluginBindings = Runner( "Generate & compare NotificationPlugin Bindings", - "jnigen/example/notification_plugin") + gitRoot.resolve("jnigen/example/notification_plugin")) ..chainCommand("dart", [ "run", "jnigen", @@ -206,10 +128,14 @@ ..chainCommand("diff", ["lib/notifications.dart", "_temp.dart"]) ..chainCommand("diff", ["-qr", "src/", "src_temp/"]) ..chainCleanupCommand("rm", ["-r", "_temp.dart", "src_temp"]); + unawaited(jnigenAnalyze.run().then((_) { - jnigenTest.run(); - compareInAppJavaBindings.run(); - comparePdfboxBindings.run(); - compareNotificationPluginBindings.run(); + final test = jnigenTest.run(); + final inAppJava = compareInAppJavaBindings.run(); + final pdfBox = comparePdfboxBindings.run(); + final notificationPlugin = compareNotificationPluginBindings.run(); + return Future.wait([test, inAppJava, pdfBox, notificationPlugin]); + }).then((_) { + tempDir.deleteSync(recursive: true); })); }
diff --git a/pkgs/jnigen/tool/regenerate_all_bindings.dart b/pkgs/jnigen/tool/regenerate_all_bindings.dart new file mode 100644 index 0000000..b99fb01 --- /dev/null +++ b/pkgs/jnigen/tool/regenerate_all_bindings.dart
@@ -0,0 +1,39 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Run this script after any change which affects generated bindings. +// +// This will update all generated bindings in the whole repository. + +import 'dart:io'; + +import 'command_runner.dart'; + +const scripts = [ + "test/jackson_core_test/generate.dart", + "test/simple_package_test/generate.dart", +]; + +const yamlBasedExamples = [ + "example/in_app_java", + "example/pdfbox_plugin", + "example/notification_plugin", +]; + +void main() async { + final runners = <Runner>[]; + final current = Directory.current.uri; + for (var script in scripts) { + runners.add(Runner("Run generate script: $script", current) + ..chainCommand("dart", ["run", script])); + } + + for (var yamlDir in yamlBasedExamples) { + runners.add( + Runner("Regenerate bindings in $yamlDir", current.resolve(yamlDir)) + ..chainCommand("dart", ["run", "jnigen", "--config", "jnigen.yaml"])); + } + + await Future.wait(runners.map((runner) => runner.run()).toList()); +}