blob: 1cfd58e86eaa27002879749b28b23fd763829dcb [file]
// 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.
import 'dart:io';
import 'package:jnigen/jnigen.dart';
import 'package:jnigen/src/logging/logging.dart';
import 'package:jnigen/src/util/find_package.dart';
import 'package:jnigen/src/bindings/c_bindings.dart';
abstract class BindingsWriter {
Future<void> writeBindings(List<ClassDecl> classes);
}
/// Run dart format command on [path].
Future<void> runDartFormat(String path) async {
log.info('Running dart format...');
final formatRes = await Process.run('dart', ['format', path]);
// if negative exit code, likely due to an interrupt.
if (formatRes.exitCode > 0) {
log.fatal('Dart format completed with exit code ${formatRes.exitCode} '
'This usually means there\'s a syntax error in bindings.\n'
'Please look at the generated files and report a bug.');
}
}
Future<void> _copyFileFromPackage(String package, String relPath, Uri target,
{String Function(String)? transform}) async {
final packagePath = await findPackageRoot(package);
if (packagePath != null) {
final sourceFile = File.fromUri(packagePath.resolve(relPath));
final targetFile = await File.fromUri(target).create(recursive: true);
var source = await sourceFile.readAsString();
if (transform != null) {
source = transform(source);
}
await targetFile.writeAsString(source);
} else {
log.warning('package $package not found! '
'skipped copying ${target.toFilePath()}');
}
}
Future<void> writeCBindings(Config config, List<ClassDecl> classes) async {
// write C file and init file
final cConfig = config.outputConfig.cConfig!;
final cRoot = cConfig.path;
final preamble = config.preamble;
log.info("Using c root = $cRoot");
final libraryName = cConfig.libraryName;
log.info('Creating dart init file ...');
// Create C file
final subdir = cConfig.subdir ?? '.';
final cFileRelativePath = '$subdir/$libraryName.c';
final cFile = await File.fromUri(cRoot.resolve(cFileRelativePath))
.create(recursive: true);
final cFileStream = cFile.openWrite();
// Write C Bindings
if (preamble != null) {
cFileStream.writeln(preamble);
}
cFileStream.write(CPreludes.prelude);
final cgen = CBindingGenerator(config);
final cBindings = classes.map(cgen.generateBinding).toList();
log.info('writing c bindings to $cFile');
cBindings.forEach(cFileStream.write);
await cFileStream.close();
log.info('Copying auxiliary files...');
await _copyFileFromPackage(
'jni', 'src/dartjni.h', cRoot.resolve('$subdir/dartjni.h'));
await _copyFileFromPackage(
'jni', 'src/.clang-format', cRoot.resolve('$subdir/.clang-format'));
await _copyFileFromPackage(
'jnigen', 'cmake/CMakeLists.txt.tmpl', cRoot.resolve('CMakeLists.txt'),
transform: (s) {
return s
.replaceAll('{{LIBRARY_NAME}}', libraryName)
.replaceAll('{{SUBDIR}}', subdir);
});
log.info('Running clang-format on C bindings');
try {
final clangFormat = Process.runSync('clang-format', ['-i', cFile.path]);
if (clangFormat.exitCode != 0) {
printError(clangFormat.stderr);
log.warning('clang-format exited with $exitCode');
}
} on ProcessException catch (e) {
log.warning('cannot run clang-format: $e');
}
}