#!/usr/bin/env dart
// Generated by alef. Do not edit by hand.
// Download platform-specific native library for {{ crate_name }} from GitHub releases.
import 'dart:ffi' show Abi;
import 'dart:io';
import 'dart:typed_data';
import 'package:http/http.dart' as http;
const String _moduleVersion = '{{ version }}';
const String _repoUrl = '{{ repo_url }}';
const String _assetPrefix = '{{ crate_name }}';
const String _libStem = '{{ lib_stem }}';
void main() async {
try {
await _run();
} catch (e) {
stderr.writeln('Error: $e');
exit(1);
}
}
Future<void> _run() async {
final (cacheDir, libPath) = _determinePaths();
// Check if library already exists
if (File(libPath).existsSync()) {
return;
}
// Download the FFI library archive
await _downloadAndExtractLibrary(cacheDir);
}
(String, String) _determinePaths() {
final osName = _osName();
final arch = _arch();
// Use the package root (where pubspec.yaml lives) as base
final packageRoot = _packageRoot();
final libDir = '$packageRoot${Platform.pathSeparator}lib${Platform.pathSeparator}src${Platform.pathSeparator}native${Platform.pathSeparator}$osName-$arch';
final libPath = '$libDir${Platform.pathSeparator}${_libFilename()}';
return (libDir, libPath);
}
String _osName() {
if (Platform.isMacOS) return 'macos';
if (Platform.isWindows) return 'windows';
if (Platform.isLinux) return 'linux';
throw UnsupportedError('Unsupported OS: ${Platform.operatingSystem}');
}
String _arch() {
// `Abi.current()` reports the exact runtime (os, arch) tuple, so derive arch
// from it directly. Prior code grep'd `Platform.resolvedExecutable` for
// 'arm64'/'aarch64'/'x86_64', which broke whenever the dart binary path
// didn't embed the arch (e.g., `/opt/homebrew/Cellar/dart/.../bin/dart` on
// Apple Silicon contains neither 'arm64' nor 'aarch64').
final abi = Abi.current();
switch (abi) {
case Abi.macosArm64:
case Abi.linuxArm64:
case Abi.windowsArm64:
case Abi.androidArm64:
case Abi.iosArm64:
return 'arm64';
case Abi.macosX64:
case Abi.linuxX64:
case Abi.windowsX64:
case Abi.androidX64:
case Abi.iosX64:
return 'x64';
default:
throw UnsupportedError('Unsupported architecture: $abi');
}
}
String _libFilename() {
if (Platform.isMacOS) return 'lib${_libStem}_dart.dylib';
if (Platform.isWindows) return '${_libStem}_dart.dll';
return 'lib${_libStem}_dart.so';
}
String _packageRoot() {
// Use the directory containing this script (bin/) as reference
// to find the package root (parent of bin/).
final scriptPath = Platform.script.toFilePath();
final binDir = File(scriptPath).parent;
return binDir.parent.path;
}
Future<void> _downloadAndExtractLibrary(String cacheDir) async {
final osName = _osName();
var arch = _arch();
// Map Dart arch names to alef platform names used in release asset filenames.
// Go uses x86_64/aarch64; macOS arm64 may differ.
if (arch == 'arm64' && !Platform.isMacOS) {
arch = 'aarch64';
} else if (arch == 'x64') {
arch = 'x86_64';
}
// Clean version for asset name
final version = _moduleVersion.startsWith('v')
? _moduleVersion.substring(1)
: _moduleVersion;
final assetName = '$_assetPrefix-dart-v$version-$osName-$arch.tar.gz';
final downloadUrl = '$_repoUrl/releases/download/v$version/$assetName';
// Create cache directory
await Directory(cacheDir).create(recursive: true);
// Download tarball
final response = await http.get(Uri.parse(downloadUrl));
if (response.statusCode != 200) {
throw Exception('HTTP ${response.statusCode}: Failed to download $downloadUrl');
}
// Extract tarball to cache directory
await _extractTarGz(response.bodyBytes, cacheDir);
}
Future<void> _extractTarGz(Uint8List bytes, String dstDir) async {
// Simple tar.gz extraction via a native tar command (Unix/macOS) or 7-Zip (Windows).
// A pure-Dart implementation would require a tar+gzip library; invoking the OS
// tool is simpler and more reliable.
final tempFile = File('$dstDir/.download.tar.gz');
try {
await tempFile.writeAsBytes(bytes);
if (Platform.isWindows) {
// Windows: use 7z if available (usually bundled), else tar.exe (Win10+)
await Process.run('tar', ['-xzf', tempFile.path, '-C', dstDir]);
} else {
// Unix/macOS: tar command is ubiquitous
await Process.run('tar', ['-xzf', tempFile.path, '-C', dstDir]);
}
} finally {
if (tempFile.existsSync()) {
await tempFile.delete();
}
}
}