/// This is copied from Cargokit (which is the official way to use it currently) /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin import 'package:collection/collection.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'android_environment.dart'; import 'cargo.dart'; import 'environment.dart'; import 'options.dart'; import 'rustup.dart'; import 'target.dart'; import 'util.dart'; final _log = Logger('builder'); enum BuildConfiguration { debug, release, profile, } extension on BuildConfiguration { bool get isDebug => this == BuildConfiguration.debug; String get rustName => switch (this) { BuildConfiguration.debug => 'debug', BuildConfiguration.release => 'release', BuildConfiguration.profile => 'release', }; } class BuildException implements Exception { final String message; BuildException(this.message); @override String toString() { return 'BuildException: $message'; } } class BuildEnvironment { final BuildConfiguration configuration; final CargokitCrateOptions crateOptions; final String targetTempDir; final String manifestDir; final CrateInfo crateInfo; final bool isAndroid; final String? androidSdkPath; final String? androidNdkVersion; final int? androidMinSdkVersion; final String? javaHome; BuildEnvironment({ required this.configuration, required this.crateOptions, required this.targetTempDir, required this.manifestDir, required this.crateInfo, required this.isAndroid, this.androidSdkPath, this.androidNdkVersion, this.androidMinSdkVersion, this.javaHome, }); static BuildConfiguration parseBuildConfiguration(String value) { // XCode configuration adds the flavor to configuration name. final firstSegment = value.split('-').first; final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( (e) => e.name == firstSegment, ); if (buildConfiguration == null) { _log.warning('Unknown build configuraiton $value, will assume release'); return BuildConfiguration.release; } return buildConfiguration; } static BuildEnvironment fromEnvironment({ required bool isAndroid, }) { final buildConfiguration = parseBuildConfiguration(Environment.configuration); final manifestDir = Environment.manifestDir; final crateOptions = CargokitCrateOptions.load( manifestDir: manifestDir, ); final crateInfo = CrateInfo.load(manifestDir); return BuildEnvironment( configuration: buildConfiguration, crateOptions: crateOptions, targetTempDir: Environment.targetTempDir, manifestDir: manifestDir, crateInfo: crateInfo, isAndroid: isAndroid, androidSdkPath: isAndroid ? Environment.sdkPath : null, androidNdkVersion: isAndroid ? Environment.ndkVersion : null, androidMinSdkVersion: isAndroid ? int.parse(Environment.minSdkVersion) : null, javaHome: isAndroid ? Environment.javaHome : null, ); } } class RustBuilder { final Target target; final BuildEnvironment environment; RustBuilder({ required this.target, required this.environment, }); void prepare( Rustup rustup, ) { final toolchain = _toolchain; if (rustup.installedTargets(toolchain) == null) { rustup.installToolchain(toolchain); } if (toolchain == 'nightly') { rustup.installRustSrcForNightly(); } if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { rustup.installTarget(target.rust, toolchain: toolchain); } } CargoBuildOptions? get _buildOptions => environment.crateOptions.cargo[environment.configuration]; String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; /// Returns the path of directory containing build artifacts. Future build() async { final extraArgs = _buildOptions?.flags ?? []; final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); runCommand( 'rustup', [ 'run', _toolchain, 'cargo', 'build', ...extraArgs, '--manifest-path', manifestPath, '-p', environment.crateInfo.packageName, if (!environment.configuration.isDebug) '--release', '--target', target.rust, '--target-dir', environment.targetTempDir, ], environment: await _buildEnvironment(), ); return path.join( environment.targetTempDir, target.rust, environment.configuration.rustName, ); } Future> _buildEnvironment() async { if (target.android == null) { return {}; } else { final sdkPath = environment.androidSdkPath; final ndkVersion = environment.androidNdkVersion; final minSdkVersion = environment.androidMinSdkVersion; if (sdkPath == null) { throw BuildException('androidSdkPath is not set'); } if (ndkVersion == null) { throw BuildException('androidNdkVersion is not set'); } if (minSdkVersion == null) { throw BuildException('androidMinSdkVersion is not set'); } final env = AndroidEnvironment( sdkPath: sdkPath, ndkVersion: ndkVersion, minSdkVersion: minSdkVersion, targetTempDir: environment.targetTempDir, target: target, ); if (!env.ndkIsInstalled() && environment.javaHome != null) { env.installNdk(javaHome: environment.javaHome!); } return env.buildEnvironment(); } } }