import 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
String targetTripleFromCodeConfig(CodeConfig codeConfig) {
return switch ((codeConfig.targetOS, codeConfig.targetArchitecture)) {
// Android
(OS.android, Architecture.arm64) => 'aarch64-linux-android',
(OS.android, Architecture.arm) => 'armv7-linux-androideabi',
(OS.android, Architecture.x64) => 'x86_64-linux-android',
// iOS
(OS.iOS, Architecture.arm64)
when codeConfig.iOS.targetSdk == IOSSdk.iPhoneSimulator =>
'aarch64-apple-ios-sim',
(OS.iOS, Architecture.arm64)
when codeConfig.iOS.targetSdk == IOSSdk.iPhoneOS =>
'aarch64-apple-ios',
(OS.iOS, Architecture.arm64) => throw UnsupportedError(
'Unknown IOSSdk: ${codeConfig.iOS.targetSdk}',
),
(OS.iOS, Architecture.x64) => 'x86_64-apple-ios',
// // Windows
// (OS.windows, Architecture.arm64) => 'aarch64-pc-windows-msvc',
// (OS.windows, Architecture.x64) => 'x86_64-pc-windows-msvc',
// Linux
(OS.linux, Architecture.arm64) => 'aarch64-unknown-linux-gnu',
(OS.linux, Architecture.x64) => 'x86_64-unknown-linux-gnu',
// macOS
(OS.macOS, Architecture.arm64) => 'aarch64-apple-darwin',
(OS.macOS, Architecture.x64) => 'x86_64-apple-darwin',
(_, _) => throw UnsupportedError(
'Unsupported target: $codeConfig.targetOS on $codeConfig.targetArchitecture',
),
};
}
LinkMode linkModeFromCodeConfig(CodeConfig codeConfig) {
return switch (codeConfig.linkModePreference) {
LinkModePreference.dynamic ||
LinkModePreference.preferDynamic => DynamicLoadingBundled(),
LinkModePreference.static ||
LinkModePreference.preferStatic => StaticLinking(),
_ => throw UnsupportedError(
'Unsupported LinkModePreference: $codeConfig.linkModePreference',
),
};
}
final class BoltFFIBuilder implements Builder {
@override
Future<void> run({
required BuildInput input,
required BuildOutputBuilder output,
Logger? logger,
}) async {
logger ??= Logger("BoltFFI::build");
if (!input.config.buildCodeAssets) {
logger.info(
'buildCodeAssets is false; '
'skipping build of Rust code assets',
);
return;
}
const artifactName = "{{ artifact_name }}";
const assetName = "{{ artifact_name }}.dart";
final codeConfig = input.config.code;
final targetTriple = targetTripleFromCodeConfig(codeConfig);
final linkMode = linkModeFromCodeConfig(codeConfig);
final nativeDir = input.packageRoot.resolve("native");
final libFile = Uri.file(
path.join(
nativeDir.toFilePath(),
targetTriple,
codeConfig.targetOS
.libraryFileName(artifactName, linkMode)
.replaceAll('-', '_'),
),
);
if (!await File(libFile.toFilePath()).exists()) {
throw Exception('''
Library for target `$targetTriple` not found.
''');
}
final outputNativeDir = input.outputDirectory.resolve("native");
await Directory(outputNativeDir.toFilePath()).create();
final outputLibFile = Uri.file(
path.join(
outputNativeDir.toFilePath(),
codeConfig.targetOS
.libraryFileName(artifactName, linkMode)
.replaceAll('-', '_'),
),
);
await File(libFile.toFilePath()).copy(outputLibFile.toFilePath());
output.dependencies.addAll(
await Directory(nativeDir.toFilePath())
.list(recursive: true)
.where((e) => e is File)
.map((f) => f.uri)
.toList(),
);
output.assets.code.add(
CodeAsset(
package: input.packageName,
name: assetName,
linkMode: linkMode,
file: outputLibFile,
),
routing: const ToAppBundle(),
);
}
}
void main(List<String> args) async {
await build(args, (input, output) async {
await BoltFFIBuilder().run(input: input, output: output);
});
}