| // 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(); |
| } |