blob: 3e983c24400510e0b53118306ef34228e7c84e52 [file] [log] [blame]
// 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.
// This is a script to run a subset of CI checks on development machine before
// submitting PR. This prevents from accidentally forgetting to format or
// remove an unused import.
// This is rough around the edges. This script may give false positives such
// as some gitignore'd temporary files failing the comparison.
// On windows, please install 'diffutils' using your favorite package installer
// for this script to work correctly. Also ensure that clang-format is on your
// PATH.
import 'dart:async';
import 'dart:io';
import 'package:args/args.dart';
import 'command_runner.dart';
// Flags
const _clone = 'clone';
void main(List<String> arguments) async {
final parser = ArgParser()
..addFlag(
_clone,
help: 'Run checks in a cloned copy of the project.',
defaultsTo: true,
negatable: true,
);
final argResults = parser.parse(arguments);
final shouldClone = argResults[_clone] as bool;
final gitRoot = getRepositoryRoot();
// change to project root
Directory.current = gitRoot.toFilePath();
var tempDir = Directory.current;
if (shouldClone) {
tempDir = Directory.current.createTempSync('jnigen_checks_clone_');
final gitClone = Runner("Clone jni", Directory.current.uri)
..chainCommand('git', ['clone', '.', tempDir.path]);
await gitClone.run();
}
final jniPath = tempDir.uri.resolve("jni/");
final jnigenPath = tempDir.uri.resolve("jnigen/");
final pubGet = Runner("Pub get", tempDir.uri)
..chainCommand("flutter", ["pub", "get", "--offline"],
workingDirectory: jniPath)
..chainCommand("dart", ["pub", "get", "--offline"],
workingDirectory: jnigenPath);
await pubGet.run();
final jniAnalyze = Runner("Analyze JNI", jniPath);
jniAnalyze
..chainCommand("dart", ["analyze", "--fatal-infos"])
..chainCommand(
"dart", ["format", "--output=none", "--set-exit-if-changed", "."])
..chainCommand(
"clang-format",
[
"--dry-run",
"-Werror",
"dartjni.c",
"dartjni.h",
"third_party/global_jni_env.c",
"third_party/global_jni_env.h",
],
workingDirectory: jniPath.resolve("src/"));
final jniTest = Runner("Test JNI", jniPath)
..chainCommand("dart", ["run", "jni:setup"])
..chainCommand("dart", ["test", "-j", "1"]);
unawaited(jniAnalyze.run().then((f) => jniTest.run()));
List<String> getJavaFiles(String relativePath) =>
Directory.fromUri(jnigenPath.resolve(relativePath))
.listSync(recursive: true)
.where((file) => file.path.endsWith(".java"))
.map((file) => file.path)
.toList();
final javaFiles = getJavaFiles("java/") +
getJavaFiles("test/simple_package_test") +
getJavaFiles("example/in_app_java") +
getJavaFiles("example/notification_plugin");
final checkJavaFormat = Runner("Check java format", jnigenPath)
..chainCommand(
"google-java-format", ["-n", "--set-exit-if-changed", ...javaFiles]);
unawaited(checkJavaFormat.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", jniPath)
..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", jnigenPath)
..chainCommand("dart", ["analyze", "--fatal-infos"])
..chainCommand(
"dart", ["format", "--output=none", "--set-exit-if-changed", "."])
..chainCommand("dart", ["run", "jnigen:setup"]);
// 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",
gitRoot.resolve("jnigen/example/in_app_java"))
..chainCommand("dart", [
"run",
"jnigen",
"--config",
"jnigen.yaml",
"-Doutput.c.path=src_temp/",
"-Doutput.dart.path=_temp.dart",
])
..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",
gitRoot.resolve("jnigen/example/pdfbox_plugin"))
..chainCommand("dart", [
"run",
"jnigen",
"--config",
"jnigen.yaml",
"-Doutput.c.path=src_temp/",
"-Doutput.dart.path=lib_temp/",
])
..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",
gitRoot.resolve("jnigen/example/notification_plugin"))
..chainCommand("dart", [
"run",
"jnigen",
"--config",
"jnigen.yaml",
"-Doutput.c.path=src_temp/",
"-Doutput.dart.path=_temp.dart",
])
..chainCommand("diff", ["lib/notifications.dart", "_temp.dart"])
..chainCommand("diff", ["-qr", "src/", "src_temp/"])
..chainCleanupCommand("rm", ["-r", "_temp.dart", "src_temp"]);
unawaited(jnigenAnalyze.run().then((_) {
final test = jnigenTest.run();
final inAppJava = compareInAppJavaBindings.run();
final pdfBox = comparePdfboxBindings.run();
final notificationPlugin = compareNotificationPluginBindings.run();
return Future.wait([test, inAppJava, pdfBox, notificationPlugin]);
}).then((_) {
if (shouldClone) {
tempDir.deleteSync(recursive: true);
}
}));
}