blob: 508de9f6241ed2bfe4fbeead18cef3a4fff24ba1 [file] [log] [blame] [edit]
// Copyright (c) 2025, 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:archive/archive.dart';
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:http/http.dart' as http;
import 'package:pointycastle/digests/sha3.dart';
void main(List<String> args) async {
await build(args, (input, output) async {
if (input.config.buildCodeAssets) {
final codeConfig = input.config.code;
if (codeConfig.targetOS != OS.current ||
codeConfig.targetArchitecture != Architecture.current) {
throw UnsupportedError(
'This package only supports running on the host platform. '
'Please use a different sqlite package for cross compilation.',
);
}
final file = await _downloadOrFindLibsqlite3(input.outputDirectoryShared);
output.assets.code.add(
// Asset ID: "package:sqlite_prebuilt/src/third_party/sqlite3.g.dart"
CodeAsset(
package: 'sqlite_prebuilt',
name: 'src/third_party/sqlite3.g.dart',
linkMode: DynamicLoadingBundled(),
file: file,
),
);
if (Platform.isLinux || Platform.isMacOS) {
output.dependencies.add(file);
}
}
});
}
const _windowsDownloadInfo = {
Architecture.arm64: (
url: 'https://sqlite.org/2025/sqlite-dll-win-arm64-3500400.zip',
sha256: 'c4f3d245377f4ee2da5c08e882ecaff376b35a609198dc399dd8cec0add1ea43',
),
Architecture.x64: (
url: 'https://sqlite.org/2025/sqlite-dll-win-x64-3500400.zip',
sha256: '8454a8ef362b4b2d5a259a54948ed278ef943128bf1ba74b5cbd87ebc58e5b85',
),
};
const _linuxPaths = {
Architecture.arm64: <String>[],
Architecture.x64: ['/usr/lib/x86_64-linux-gnu/libsqlite3.so'],
};
Future<Uri> _downloadOrFindLibsqlite3(Uri outputDirectory) async {
switch (OS.current) {
case OS.windows:
final extractDir = Directory.fromUri(
outputDirectory.resolve('download/'),
);
final dll = File.fromUri(extractDir.uri.resolve('sqlite3.dll'));
if (await dll.exists()) {
return dll.uri;
}
final url = Uri.parse(_windowsDownloadInfo[Architecture.current]!.url);
print('Downloading $url.');
final response = await http.get(url);
if (response.statusCode != 200) {
throw Exception(
'Error downloading file: Status code ${response.statusCode}',
);
}
final computedHash = SHA3Digest(256)
.process(response.bodyBytes)
.map((b) => b.toRadixString(16).padLeft(2, '0'))
.join('');
final expectedHash = _windowsDownloadInfo[Architecture.current]!.sha256;
if (computedHash != expectedHash) {
throw Exception(
'Download failed, invalid hash: $computedHash, '
'excpected $expectedHash.',
);
}
print('Download complete. Unzipping.');
final archive = ZipDecoder().decodeBytes(response.bodyBytes);
if (!await extractDir.exists()) {
await extractDir.create(recursive: true);
}
for (final file in archive) {
if (!file.isFile) {
throw UnimplementedError('Did not expect directory in zip file');
}
final data = file.content as List<int>;
final targetFile = File.fromUri(extractDir.uri.resolve(file.name));
await targetFile.create(recursive: true);
await targetFile.writeAsBytes(data);
}
print(
'Successfully unzipped files to the "${extractDir.path}" directory.',
);
if (!await dll.exists()) {
throw Exception('Did not find sqlite3.dll in $extractDir');
}
return dll.uri;
case OS.linux:
// Most Linux distros come with a pre-installed libsqlite3.so.
final knownPaths = _linuxPaths[Architecture.current]!;
for (final knownPath in knownPaths) {
final file = File(knownPath);
if (file.existsSync()) {
return File(file.resolveSymbolicLinksSync()).uri;
}
}
case OS.macOS:
// No prebuilt binaries are downloadable on the SQLite website. Let's use
// a package manager instead: Brew. Require the user to install SQLite via
// Brew.
final brewPrefixResult = Process.runSync('brew', ['--prefix', 'sqlite']);
if (brewPrefixResult.exitCode != 0) {
print(brewPrefixResult.stderr);
throw UnsupportedError(
'Install brew and then install sqlite with brew.',
);
}
final sqliteDir = Uri.directory(
File(
(brewPrefixResult.stdout as String).trim(),
).resolveSymbolicLinksSync(),
);
final libsqliteFile = File.fromUri(
sqliteDir.resolve('lib/libsqlite3.dylib'),
).resolveSymbolicLinksSync();
return Uri.file(libsqliteFile);
default:
}
throw UnimplementedError();
}