builder.dart
1 /// This is copied from Cargokit (which is the official way to use it currently) 2 /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 4 import 'package:collection/collection.dart'; 5 import 'package:logging/logging.dart'; 6 import 'package:path/path.dart' as path; 7 8 import 'android_environment.dart'; 9 import 'cargo.dart'; 10 import 'environment.dart'; 11 import 'options.dart'; 12 import 'rustup.dart'; 13 import 'target.dart'; 14 import 'util.dart'; 15 16 final _log = Logger('builder'); 17 18 enum BuildConfiguration { 19 debug, 20 release, 21 profile, 22 } 23 24 extension on BuildConfiguration { 25 bool get isDebug => this == BuildConfiguration.debug; 26 String get rustName => switch (this) { 27 BuildConfiguration.debug => 'debug', 28 BuildConfiguration.release => 'release', 29 BuildConfiguration.profile => 'release', 30 }; 31 } 32 33 class BuildException implements Exception { 34 final String message; 35 36 BuildException(this.message); 37 38 @override 39 String toString() { 40 return 'BuildException: $message'; 41 } 42 } 43 44 class BuildEnvironment { 45 final BuildConfiguration configuration; 46 final CargokitCrateOptions crateOptions; 47 final String targetTempDir; 48 final String manifestDir; 49 final CrateInfo crateInfo; 50 51 final bool isAndroid; 52 final String? androidSdkPath; 53 final String? androidNdkVersion; 54 final int? androidMinSdkVersion; 55 final String? javaHome; 56 57 BuildEnvironment({ 58 required this.configuration, 59 required this.crateOptions, 60 required this.targetTempDir, 61 required this.manifestDir, 62 required this.crateInfo, 63 required this.isAndroid, 64 this.androidSdkPath, 65 this.androidNdkVersion, 66 this.androidMinSdkVersion, 67 this.javaHome, 68 }); 69 70 static BuildConfiguration parseBuildConfiguration(String value) { 71 // XCode configuration adds the flavor to configuration name. 72 final firstSegment = value.split('-').first; 73 final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( 74 (e) => e.name == firstSegment, 75 ); 76 if (buildConfiguration == null) { 77 _log.warning('Unknown build configuraiton $value, will assume release'); 78 return BuildConfiguration.release; 79 } 80 return buildConfiguration; 81 } 82 83 static BuildEnvironment fromEnvironment({ 84 required bool isAndroid, 85 }) { 86 final buildConfiguration = 87 parseBuildConfiguration(Environment.configuration); 88 final manifestDir = Environment.manifestDir; 89 final crateOptions = CargokitCrateOptions.load( 90 manifestDir: manifestDir, 91 ); 92 final crateInfo = CrateInfo.load(manifestDir); 93 return BuildEnvironment( 94 configuration: buildConfiguration, 95 crateOptions: crateOptions, 96 targetTempDir: Environment.targetTempDir, 97 manifestDir: manifestDir, 98 crateInfo: crateInfo, 99 isAndroid: isAndroid, 100 androidSdkPath: isAndroid ? Environment.sdkPath : null, 101 androidNdkVersion: isAndroid ? Environment.ndkVersion : null, 102 androidMinSdkVersion: 103 isAndroid ? int.parse(Environment.minSdkVersion) : null, 104 javaHome: isAndroid ? Environment.javaHome : null, 105 ); 106 } 107 } 108 109 class RustBuilder { 110 final Target target; 111 final BuildEnvironment environment; 112 113 RustBuilder({ 114 required this.target, 115 required this.environment, 116 }); 117 118 void prepare( 119 Rustup rustup, 120 ) { 121 final toolchain = _toolchain; 122 if (rustup.installedTargets(toolchain) == null) { 123 rustup.installToolchain(toolchain); 124 } 125 if (toolchain == 'nightly') { 126 rustup.installRustSrcForNightly(); 127 } 128 if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { 129 rustup.installTarget(target.rust, toolchain: toolchain); 130 } 131 } 132 133 CargoBuildOptions? get _buildOptions => 134 environment.crateOptions.cargo[environment.configuration]; 135 136 String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; 137 138 /// Returns the path of directory containing build artifacts. 139 Future<String> build() async { 140 final extraArgs = _buildOptions?.flags ?? []; 141 final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); 142 runCommand( 143 'rustup', 144 [ 145 'run', 146 _toolchain, 147 'cargo', 148 'build', 149 ...extraArgs, 150 '--manifest-path', 151 manifestPath, 152 '-p', 153 environment.crateInfo.packageName, 154 if (!environment.configuration.isDebug) '--release', 155 '--target', 156 target.rust, 157 '--target-dir', 158 environment.targetTempDir, 159 ], 160 environment: await _buildEnvironment(), 161 ); 162 return path.join( 163 environment.targetTempDir, 164 target.rust, 165 environment.configuration.rustName, 166 ); 167 } 168 169 Future<Map<String, String>> _buildEnvironment() async { 170 if (target.android == null) { 171 return {}; 172 } else { 173 final sdkPath = environment.androidSdkPath; 174 final ndkVersion = environment.androidNdkVersion; 175 final minSdkVersion = environment.androidMinSdkVersion; 176 if (sdkPath == null) { 177 throw BuildException('androidSdkPath is not set'); 178 } 179 if (ndkVersion == null) { 180 throw BuildException('androidNdkVersion is not set'); 181 } 182 if (minSdkVersion == null) { 183 throw BuildException('androidMinSdkVersion is not set'); 184 } 185 final env = AndroidEnvironment( 186 sdkPath: sdkPath, 187 ndkVersion: ndkVersion, 188 minSdkVersion: minSdkVersion, 189 targetTempDir: environment.targetTempDir, 190 target: target, 191 ); 192 if (!env.ndkIsInstalled() && environment.javaHome != null) { 193 env.installNdk(javaHome: environment.javaHome!); 194 } 195 return env.buildEnvironment(); 196 } 197 } 198 }