[jnigen] Support customizing the command to call ApiSummarizer (#3579)
diff --git a/pkgs/jnigen/CHANGELOG.md b/pkgs/jnigen/CHANGELOG.md
index a98c9a6..611739b 100644
--- a/pkgs/jnigen/CHANGELOG.md
+++ b/pkgs/jnigen/CHANGELOG.md
@@ -22,6 +22,8 @@
the `generate` function.
- Change remaining `String` paths to `Uri`.
- Allow interface mixin names to be customized using the visitor API.
+- Support customizing the command `java -jar ApiSummarizer.jar` via the
+ `summarizer.command` field in config.
## 0.17.0
diff --git a/pkgs/jnigen/lib/src/config/config_types.dart b/pkgs/jnigen/lib/src/config/config_types.dart
index 75da56f..e60daec 100644
--- a/pkgs/jnigen/lib/src/config/config_types.dart
+++ b/pkgs/jnigen/lib/src/config/config_types.dart
@@ -326,6 +326,13 @@
/// disabled.
final AndroidSdk? androidSdk;
+ /// Command used to run the API summarizer.
+ ///
+ /// This should only be used if the system uses a prebuilt `ApiSummarizer.jar`
+ /// file. If provided, building ApiSummarizer using Gradle is skipped and this
+ /// command is used directly to invoke the summarizer.
+ final String? summarizerCommand;
+
Input({
this.sourcePath = const [],
this.classPath = const [],
@@ -335,6 +342,7 @@
this.backend,
this.mavenDownloads,
this.androidSdk,
+ this.summarizerCommand,
}) : workingDirectory = workingDirectory ?? Uri.directory('.') {
for (final className in classes) {
_validateClassName(className);
@@ -459,6 +467,7 @@
extraArgs: prov.getStringList(_Props.summarizerArgs) ?? const [],
backend: getSummarizerBackend(prov.getString(_Props.backend), null),
workingDirectory: prov.getPath(_Props.summarizerWorkingDir),
+ summarizerCommand: prov.getString(_Props.summarizerCommand),
mavenDownloads: prov.hasValue(_Props.mavenDownloads)
? MavenDownloads(
sourceDeps: prov.getStringList(_Props.sourceDeps) ?? const [],
@@ -641,6 +650,7 @@
static const summarizer = 'summarizer';
static const summarizerArgs = '$summarizer.extra_args';
static const summarizerWorkingDir = '$summarizer.working_dir';
+ static const summarizerCommand = '$summarizer.command';
static const backend = '$summarizer.backend';
static const sourcePath = 'source_path';
diff --git a/pkgs/jnigen/lib/src/generate_bindings.dart b/pkgs/jnigen/lib/src/generate_bindings.dart
index bf6875d..9605d25 100644
--- a/pkgs/jnigen/lib/src/generate_bindings.dart
+++ b/pkgs/jnigen/lib/src/generate_bindings.dart
@@ -45,7 +45,9 @@
..addAll(Annotated.defaultNullableAnnotations)
..addAll(nullability.nullable);
- await buildSummarizerIfNotExists();
+ if (input.summarizerCommand == null) {
+ await buildSummarizerIfNotExists();
+ }
final Classes classes;
diff --git a/pkgs/jnigen/lib/src/summary/summary.dart b/pkgs/jnigen/lib/src/summary/summary.dart
index 55accff..66862d2 100644
--- a/pkgs/jnigen/lib/src/summary/summary.dart
+++ b/pkgs/jnigen/lib/src/summary/summary.dart
@@ -57,7 +57,7 @@
/// script before this API is used.
class SummarizerCommand {
SummarizerCommand({
- this.command = 'java -jar .dart_tool/jnigen/ApiSummarizer.jar',
+ required this.command,
List<Uri>? sourcePath,
List<Uri>? classPath,
this.extraArgs = const [],
@@ -126,6 +126,8 @@
Future<Classes> getSummary(JniGenerator config) async {
final summarizer = SummarizerCommand(
+ command: config.input.summarizerCommand ??
+ 'java -jar .dart_tool/jnigen/ApiSummarizer.jar',
sourcePath: config.input.sourcePath,
classPath: config.input.classPath,
classes: config.input.classes,
diff --git a/pkgs/jnigen/test/config_test.dart b/pkgs/jnigen/test/config_test.dart
index c22354a..2e9ad8d 100644
--- a/pkgs/jnigen/test/config_test.dart
+++ b/pkgs/jnigen/test/config_test.dart
@@ -55,6 +55,8 @@
expect(a.input.workingDirectory, b.input.workingDirectory,
reason: 'workingDirectory');
expect(a.input.backend, b.input.backend, reason: 'backend');
+ expect(a.input.summarizerCommand, equals(b.input.summarizerCommand),
+ reason: 'summarizerCommand');
expect(a.imports.symbolFiles, b.imports.symbolFiles,
reason: 'imports.symbolFiles');
expect(a.imports.hide, b.imports.hide, reason: 'imports.hide');
diff --git a/pkgs/jnigen/test/generation_test.dart b/pkgs/jnigen/test/generation_test.dart
index 66ac2e2..da55bcf 100644
--- a/pkgs/jnigen/test/generation_test.dart
+++ b/pkgs/jnigen/test/generation_test.dart
@@ -5,9 +5,21 @@
import 'dart:io';
import 'package:jnigen/jnigen.dart';
+import 'package:jnigen/src/logging/logging.dart';
+import 'package:jnigen/src/summary/summary.dart';
import 'package:path/path.dart';
import 'package:test/test.dart';
+void expectBuildSummarizerTriggered(List<String> log, {bool triggered = true}) {
+ final matcher = contains(predicate<String>(
+ (msg) =>
+ msg.contains('ApiSummarizer.jar exists') ||
+ msg.contains('Building ApiSummarizer component') ||
+ msg.contains('Rebuilding ApiSummarizer component'),
+ ));
+ expect(log, triggered ? matcher : isNot(matcher));
+}
+
void main() {
test('Warn if non-jnigen-generated files exist in directory', () async {
final root = await Directory.current.createTemp();
@@ -20,6 +32,8 @@
final sourcePath =
join(Directory.current.path, 'test', 'simple_package_test', 'java');
print(sourcePath);
+ final logMessages = <String>[];
+ final logSub = log.onRecord.listen((r) => logMessages.add(r.message));
await expectLater(
() => JniGenerator(
input: Input(
@@ -40,6 +54,55 @@
predicate((error) => error.toString().contains('non_gen.dart')),
])),
);
+ await logSub.cancel();
+ expectBuildSummarizerTriggered(logMessages, triggered: true);
+ await root.delete(recursive: true);
+ });
+
+ test('buildSummarizerIfNotExists is skipped if summarizerCommand is passed',
+ () async {
+ final root = await Directory.current.createTemp();
+ final fakeSummarizer = File(join(root.path, 'fake_summarizer.dart'));
+ const errorMessage = 'Custom summarizer error output';
+ await fakeSummarizer.writeAsString('''
+import 'dart:io';
+
+void main() {
+ stderr.writeln('$errorMessage');
+ exit(1);
+}
+''');
+ final outDir = Directory(join(root.path, 'out'))..createSync();
+
+ final config = JniGenerator(
+ input: Input(
+ classes: ['com.example.Test'],
+ summarizerCommand: '${Platform.executable} ${fakeSummarizer.path}',
+ ),
+ output: Output(
+ dart: DartCodeOutput(
+ path: outDir.uri,
+ structure: OutputStructure.packageStructure,
+ ),
+ ),
+ );
+
+ final logMessages = <String>[];
+ final logSub = log.onRecord.listen((r) => logMessages.add(r.message));
+ try {
+ await getSummary(config);
+ fail('Expected SummaryParseException');
+ } on SummaryParseException catch (e) {
+ expect(e.stderr, contains(errorMessage));
+ }
+ await logSub.cancel();
+
+ expectBuildSummarizerTriggered(logMessages, triggered: false);
+ expect(
+ logMessages,
+ contains(predicate<String>(
+ (msg) => msg.contains('execute ${Platform.executable}'))),
+ );
await root.delete(recursive: true);
});
}