| 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | import '../../artifacts.dart'; |
| 6 | import '../../base/build.dart'; |
| 7 | import '../../base/deferred_component.dart'; |
| 8 | import '../../base/file_system.dart'; |
| 9 | import '../../build_info.dart'; |
| 10 | import '../../devfs.dart'; |
| 11 | import '../../globals.dart' as globals show xcode; |
| 12 | import '../../project.dart'; |
| 13 | import '../build_system.dart'; |
| 14 | import '../depfile.dart'; |
| 15 | import '../exceptions.dart'; |
| 16 | import 'assets.dart'; |
| 17 | import 'common.dart'; |
| 18 | import 'icon_tree_shaker.dart'; |
| 19 | import 'native_assets.dart'; |
| 20 | |
| 21 | /// Prepares the asset bundle in the format expected by flutter.gradle. |
| 22 | /// |
| 23 | /// The vm_snapshot_data, isolate_snapshot_data, and kernel_blob.bin are |
| 24 | /// expected to be in the root output directory. |
| 25 | /// |
| 26 | /// All assets and manifests are included from flutter_assets/**. |
| 27 | abstract class AndroidAssetBundle extends Target { |
| 28 | const AndroidAssetBundle(); |
| 29 | |
| 30 | @override |
| 31 | List get inputs => const [ |
| 32 | Source.pattern('{BUILD_DIR}/app.dill'), |
| 33 | ...IconTreeShaker.inputs, |
| 34 | ]; |
| 35 | |
| 36 | @override |
| 37 | List get outputs => const []; |
| 38 | |
| 39 | @override |
| 40 | List get depfiles => ['flutter_assets.d']; |
| 41 | |
| 42 | @override |
| 43 | Future build(Environment environment) async { |
| 44 | final String? buildModeEnvironment = environment.defines[kBuildMode]; |
| 45 | if (buildModeEnvironment == null) { |
| 46 | throw MissingDefineException(kBuildMode, name); |
| 47 | } |
| 48 | |
| 49 | final buildMode = BuildMode.fromCliName(buildModeEnvironment); |
| 50 | final Directory outputDirectory = environment.outputDir.childDirectory('flutter_assets') |
| 51 | ..createSync(recursive: true); |
| 52 | |
| 53 | // Only copy the prebuilt runtimes and kernel blob in debug mode. |
| 54 | if (buildMode == BuildMode.debug) { |
| 55 | final String vmSnapshotData = environment.artifacts.getArtifactPath( |
| 56 | Artifact.vmSnapshotData, |
| 57 | mode: BuildMode.debug, |
| 58 | ); |
| 59 | final String isolateSnapshotData = environment.artifacts.getArtifactPath( |
| 60 | Artifact.isolateSnapshotData, |
| 61 | mode: BuildMode.debug, |
| 62 | ); |
| 63 | environment.buildDir |
| 64 | .childFile('app.dill') |
| 65 | .copySync(outputDirectory.childFile('kernel_blob.bin').path); |
| 66 | environment.fileSystem |
| 67 | .file(vmSnapshotData) |
| 68 | .copySync(outputDirectory.childFile('vm_snapshot_data').path); |
| 69 | environment.fileSystem |
| 70 | .file(isolateSnapshotData) |
| 71 | .copySync(outputDirectory.childFile('isolate_snapshot_data').path); |
| 72 | } |
| 73 | final Depfile assetDepfile = await copyAssets( |
| 74 | environment, |
| 75 | outputDirectory, |
| 76 | targetPlatform: TargetPlatform.android, |
| 77 | buildMode: buildMode, |
| 78 | flavor: environment.defines[kFlavor], |
| 79 | additionalContent: { |
| 80 | 'NativeAssetsManifest.json': DevFSFileContent( |
| 81 | environment.buildDir.childFile('native_assets.json'), |
| 82 | ), |
| 83 | }, |
| 84 | ); |
| 85 | environment.depFileService.writeToFile( |
| 86 | assetDepfile, |
| 87 | environment.buildDir.childFile('flutter_assets.d'), |
| 88 | ); |
| 89 | } |
| 90 | |
| 91 | @override |
| 92 | List get dependencies => const [KernelSnapshot(), InstallCodeAssets()]; |
| 93 | } |
| 94 | |
| 95 | /// An implementation of [AndroidAssetBundle] that includes dependencies on vm |
| 96 | /// and isolate data. |
| 97 | class DebugAndroidApplication extends AndroidAssetBundle { |
| 98 | const DebugAndroidApplication(); |
| 99 | |
| 100 | @override |
| 101 | String get name => 'debug_android_application'; |
| 102 | |
| 103 | @override |
| 104 | List get inputs => [ |
| 105 | ...super.inputs, |
| 106 | const Source.artifact(Artifact.vmSnapshotData, mode: BuildMode.debug), |
| 107 | const Source.artifact(Artifact.isolateSnapshotData, mode: BuildMode.debug), |
| 108 | ]; |
| 109 | |
| 110 | @override |
| 111 | List get outputs => [ |
| 112 | ...super.outputs, |
| 113 | const Source.pattern('{OUTPUT_DIR}/flutter_assets/vm_snapshot_data'), |
| 114 | const Source.pattern('{OUTPUT_DIR}/flutter_assets/isolate_snapshot_data'), |
| 115 | const Source.pattern('{OUTPUT_DIR}/flutter_assets/kernel_blob.bin'), |
| 116 | ]; |
| 117 | } |
| 118 | |
| 119 | /// An implementation of [AndroidAssetBundle] that only includes assets. |
| 120 | class AotAndroidAssetBundle extends AndroidAssetBundle { |
| 121 | const AotAndroidAssetBundle(); |
| 122 | |
| 123 | @override |
| 124 | String get name => 'aot_android_asset_bundle'; |
| 125 | } |
| 126 | |
| 127 | /// Build a profile android application's Dart artifacts. |
| 128 | class ProfileAndroidApplication extends CopyFlutterAotBundle { |
| 129 | const ProfileAndroidApplication(); |
| 130 | |
| 131 | @override |
| 132 | String get name => 'profile_android_application'; |
| 133 | |
| 134 | @override |
| 135 | List get dependencies => const [ |
| 136 | AotElfProfile(TargetPlatform.android_arm), |
| 137 | AotAndroidAssetBundle(), |
| 138 | ]; |
| 139 | } |
| 140 | |
| 141 | /// Build a release android application's Dart artifacts. |
| 142 | class ReleaseAndroidApplication extends CopyFlutterAotBundle { |
| 143 | const ReleaseAndroidApplication(); |
| 144 | |
| 145 | @override |
| 146 | String get name => 'release_android_application'; |
| 147 | |
| 148 | @override |
| 149 | List get dependencies => const [ |
| 150 | AotElfRelease(TargetPlatform.android_arm), |
| 151 | AotAndroidAssetBundle(), |
| 152 | ]; |
| 153 | } |
| 154 | |
| 155 | /// Generate an ELF binary from a dart kernel file in release mode. |
| 156 | /// |
| 157 | /// This rule implementation outputs the generated so to a unique location |
| 158 | /// based on the Android ABI. This allows concurrent invocations of gen_snapshot |
| 159 | /// to run simultaneously. |
| 160 | /// |
| 161 | /// The name of an instance of this rule would be 'android_aot_profile_android-x64' |
| 162 | /// and is relied upon by flutter.gradle to match the correct rule. |
| 163 | /// |
| 164 | /// It will produce an 'app.so` in the build directory under a folder named with |
| 165 | /// the matching Android ABI. |
| 166 | class AndroidAot extends AotElfBase { |
| 167 | /// Create an [AndroidAot] implementation for a given [targetPlatform] and [buildMode]. |
| 168 | const AndroidAot(this.targetPlatform, this.buildMode); |
| 169 | |
| 170 | /// The name of the produced Android ABI. |
| 171 | String get _androidAbiName { |
| 172 | return getAndroidArchForName(getNameForTargetPlatform(targetPlatform)).archName; |
| 173 | } |
| 174 | |
| 175 | @override |
| 176 | String get name => |
| 177 | 'android_aot_${buildMode.cliName}_' |
| 178 | '${getNameForTargetPlatform(targetPlatform)}'; |
| 179 | |
| 180 | /// The specific Android ABI we are building for. |
| 181 | final TargetPlatform targetPlatform; |
| 182 | |
| 183 | /// The selected build mode. |
| 184 | /// |
| 185 | /// Build mode is restricted to [BuildMode.profile] or [BuildMode.release] for AOT builds. |
| 186 | final BuildMode buildMode; |
| 187 | |
| 188 | @override |
| 189 | List get inputs => [ |
| 190 | const Source.pattern( |
| 191 | '{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/android.dart', |
| 192 | ), |
| 193 | const Source.pattern('{BUILD_DIR}/app.dill'), |
| 194 | const Source.artifact(Artifact.engineDartBinary), |
| 195 | const Source.artifact(Artifact.skyEnginePath), |
| 196 | Source.artifact(Artifact.genSnapshot, mode: buildMode, platform: targetPlatform), |
| 197 | ]; |
| 198 | |
| 199 | @override |
| 200 | List get outputs => [Source.pattern('{BUILD_DIR}/$_androidAbiName/app.so')]; |
| 201 | |
| 202 | @override |
| 203 | List get depfiles => ['flutter_$name.d']; |
| 204 | |
| 205 | @override |
| 206 | List get dependencies => const [KernelSnapshot()]; |
| 207 | |
| 208 | @override |
| 209 | Future build(Environment environment) async { |
| 210 | final snapshotter = AOTSnapshotter( |
| 211 | fileSystem: environment.fileSystem, |
| 212 | logger: environment.logger, |
| 213 | xcode: globals.xcode!, |
| 214 | processManager: environment.processManager, |
| 215 | artifacts: environment.artifacts, |
| 216 | ); |
| 217 | final Directory output = environment.buildDir.childDirectory(_androidAbiName); |
| 218 | final String? buildModeEnvironment = environment.defines[kBuildMode]; |
| 219 | if (buildModeEnvironment == null) { |
| 220 | throw MissingDefineException(kBuildMode, 'aot_elf'); |
| 221 | } |
| 222 | if (!output.existsSync()) { |
| 223 | output.createSync(recursive: true); |
| 224 | } |
| 225 | final List extraGenSnapshotOptions = decodeCommaSeparated( |
| 226 | environment.defines, |
| 227 | kExtraGenSnapshotOptions, |
| 228 | ); |
| 229 | final outputs = []; // outputs for the depfile |
| 230 | final manifestPath = '${output.path}${environment.platform.pathSeparator}manifest.json'; |
| 231 | if (environment.defines[kDeferredComponents] == 'true') { |
| 232 | extraGenSnapshotOptions.add('--loading_unit_manifest=$manifestPath'); |
| 233 | outputs.add(environment.fileSystem.file(manifestPath)); |
| 234 | } |
| 235 | final buildMode = BuildMode.fromCliName(buildModeEnvironment); |
| 236 | final dartObfuscation = environment.defines[kDartObfuscation] == 'true'; |
| 237 | final String? codeSizeDirectory = environment.defines[kCodeSizeDirectory]; |
| 238 | |
| 239 | if (codeSizeDirectory != null) { |
| 240 | final File codeSizeFile = environment.fileSystem |
| 241 | .directory(codeSizeDirectory) |
| 242 | .childFile('snapshot.$_androidAbiName.json'); |
| 243 | final File precompilerTraceFile = environment.fileSystem |
| 244 | .directory(codeSizeDirectory) |
| 245 | .childFile('trace.$_androidAbiName.json'); |
| 246 | extraGenSnapshotOptions.add('--write-v8-snapshot-profile-to=${codeSizeFile.path}'); |
| 247 | extraGenSnapshotOptions.add('--trace-precompiler-to=${precompilerTraceFile.path}'); |
| 248 | } |
| 249 | |
| 250 | final String? splitDebugInfo = environment.defines[kSplitDebugInfo]; |
| 251 | final int snapshotExitCode = await snapshotter.build( |
| 252 | platform: targetPlatform, |
| 253 | buildMode: buildMode, |
| 254 | mainPath: environment.buildDir.childFile('app.dill').path, |
| 255 | outputPath: output.path, |
| 256 | extraGenSnapshotOptions: extraGenSnapshotOptions, |
| 257 | splitDebugInfo: splitDebugInfo, |
| 258 | dartObfuscation: dartObfuscation, |
| 259 | ); |
| 260 | if (snapshotExitCode != 0) { |
| 261 | throw Exception('AOT snapshotter exited with code $snapshotExitCode'); |
| 262 | } |
| 263 | if (environment.defines[kDeferredComponents] == 'true') { |
| 264 | // Parse the manifest for .so paths |
| 265 | final List loadingUnits = LoadingUnit.parseLoadingUnitManifest( |
| 266 | environment.fileSystem.file(manifestPath), |
| 267 | environment.logger, |
| 268 | ); |
| 269 | for (final unit in loadingUnits) { |
| 270 | outputs.add(environment.fileSystem.file(unit.path)); |
| 271 | } |
| 272 | } |
| 273 | environment.depFileService.writeToFile( |
| 274 | Depfile([], outputs), |
| 275 | environment.buildDir.childFile('flutter_$name.d'), |
| 276 | writeEmpty: true, |
| 277 | ); |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | // AndroidAot instances used by the bundle rules below. |
| 282 | const androidArmProfile = AndroidAot(TargetPlatform.android_arm, BuildMode.profile); |
| 283 | const androidArm64Profile = AndroidAot(TargetPlatform.android_arm64, BuildMode.profile); |
| 284 | const androidx64Profile = AndroidAot(TargetPlatform.android_x64, BuildMode.profile); |
| 285 | const androidArmRelease = AndroidAot(TargetPlatform.android_arm, BuildMode.release); |
| 286 | const androidArm64Release = AndroidAot(TargetPlatform.android_arm64, BuildMode.release); |
| 287 | const androidx64Release = AndroidAot(TargetPlatform.android_x64, BuildMode.release); |
| 288 | |
| 289 | /// A rule paired with [AndroidAot] that copies the produced so file and manifest.json (if present) into the output directory. |
| 290 | class AndroidAotBundle extends Target { |
| 291 | /// Create an [AndroidAotBundle] implementation for a given [targetPlatform] and [buildMode]. |
| 292 | const AndroidAotBundle(this.dependency); |
| 293 | |
| 294 | /// The [AndroidAot] instance this bundle rule depends on. |
| 295 | final AndroidAot dependency; |
| 296 | |
| 297 | /// The name of the produced Android ABI. |
| 298 | String get _androidAbiName { |
| 299 | return getAndroidArchForName(getNameForTargetPlatform(dependency.targetPlatform)).archName; |
| 300 | } |
| 301 | |
| 302 | @override |
| 303 | String get name => |
| 304 | 'android_aot_bundle_${dependency.buildMode.cliName}_' |
| 305 | '${getNameForTargetPlatform(dependency.targetPlatform)}'; |
| 306 | |
| 307 | TargetPlatform get targetPlatform => dependency.targetPlatform; |
| 308 | |
| 309 | /// The selected build mode. |
| 310 | /// |
| 311 | /// This is restricted to [BuildMode.profile] or [BuildMode.release]. |
| 312 | BuildMode get buildMode => dependency.buildMode; |
| 313 | |
| 314 | @override |
| 315 | List get inputs => [Source.pattern('{BUILD_DIR}/$_androidAbiName/app.so')]; |
| 316 | |
| 317 | // flutter.gradle has been updated to correctly consume it. |
| 318 | @override |
| 319 | List get outputs => [Source.pattern('{OUTPUT_DIR}/$_androidAbiName/app.so')]; |
| 320 | |
| 321 | @override |
| 322 | List get depfiles => ['flutter_$name.d']; |
| 323 | |
| 324 | @override |
| 325 | List get dependencies => [dependency, const AotAndroidAssetBundle()]; |
| 326 | |
| 327 | @override |
| 328 | Future build(Environment environment) async { |
| 329 | final Directory buildDir = environment.buildDir.childDirectory(_androidAbiName); |
| 330 | final Directory outputDirectory = environment.outputDir.childDirectory(_androidAbiName); |
| 331 | if (!outputDirectory.existsSync()) { |
| 332 | outputDirectory.createSync(recursive: true); |
| 333 | } |
| 334 | final File outputLibFile = buildDir.childFile('app.so'); |
| 335 | outputLibFile.copySync(outputDirectory.childFile('app.so').path); |
| 336 | |
| 337 | final inputs = []; |
| 338 | final outputs = []; |
| 339 | final File manifestFile = buildDir.childFile('manifest.json'); |
| 340 | if (manifestFile.existsSync()) { |
| 341 | final File destinationFile = outputDirectory.childFile('manifest.json'); |
| 342 | manifestFile.copySync(destinationFile.path); |
| 343 | inputs.add(manifestFile); |
| 344 | outputs.add(destinationFile); |
| 345 | } |
| 346 | environment.depFileService.writeToFile( |
| 347 | Depfile(inputs, outputs), |
| 348 | environment.buildDir.childFile('flutter_$name.d'), |
| 349 | writeEmpty: true, |
| 350 | ); |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | // AndroidBundleAot instances. |
| 355 | const androidArmProfileBundle = AndroidAotBundle(androidArmProfile); |
| 356 | const androidArm64ProfileBundle = AndroidAotBundle(androidArm64Profile); |
| 357 | const androidx64ProfileBundle = AndroidAotBundle(androidx64Profile); |
| 358 | const androidArmReleaseBundle = AndroidAotBundle(androidArmRelease); |
| 359 | const androidArm64ReleaseBundle = AndroidAotBundle(androidArm64Release); |
| 360 | const androidx64ReleaseBundle = AndroidAotBundle(androidx64Release); |
| 361 | |
| 362 | // Rule that copies split aot library files to the intermediate dirs of each deferred component. |
| 363 | class AndroidAotDeferredComponentsBundle extends Target { |
| 364 | /// Create an [AndroidAotDeferredComponentsBundle] implementation for a given [targetPlatform] and [BuildInfo.mode]. |
| 365 | /// |
| 366 | /// If [components] is not provided, it will be read from the `pubspec.yaml` manifest. |
| 367 | AndroidAotDeferredComponentsBundle(this.dependency, {List? components}) |
| 368 | : _components = components; |
| 369 | |
| 370 | /// The [AndroidAotBundle] instance this bundle rule depends on. |
| 371 | final AndroidAotBundle dependency; |
| 372 | |
| 373 | List? _components; |
| 374 | |
| 375 | /// The name of the produced Android ABI. |
| 376 | String get _androidAbiName { |
| 377 | return getAndroidArchForName(getNameForTargetPlatform(dependency.targetPlatform)).archName; |
| 378 | } |
| 379 | |
| 380 | @override |
| 381 | String get name => |
| 382 | 'android_aot_deferred_components_bundle_${dependency.buildMode.cliName}_' |
| 383 | '${getNameForTargetPlatform(dependency.targetPlatform)}'; |
| 384 | |
| 385 | TargetPlatform get targetPlatform => dependency.targetPlatform; |
| 386 | |
| 387 | @override |
| 388 | List get inputs => [ |
| 389 | // Tracking app.so is enough to invalidate the dynamically named |
| 390 | // loading unit libs as changes to loading units guarantee |
| 391 | // changes to app.so as well. This task does not actually |
| 392 | // copy app.so. |
| 393 | Source.pattern('{OUTPUT_DIR}/$_androidAbiName/app.so'), |
| 394 | const Source.pattern('{PROJECT_DIR}/pubspec.yaml'), |
| 395 | ]; |
| 396 | |
| 397 | @override |
| 398 | List get outputs => const []; |
| 399 | |
| 400 | @override |
| 401 | List get depfiles => ['flutter_$name.d']; |
| 402 | |
| 403 | @override |
| 404 | List get dependencies => [dependency]; |
| 405 | |
| 406 | @override |
| 407 | Future build(Environment environment) async { |
| 408 | _components ??= FlutterProject.current().manifest.deferredComponents ?? []; |
| 409 | final abis = [_androidAbiName]; |
| 410 | final List generatedLoadingUnits = LoadingUnit.parseGeneratedLoadingUnits( |
| 411 | environment.outputDir, |
| 412 | environment.logger, |
| 413 | abis: abis, |
| 414 | ); |
| 415 | for (final DeferredComponent component in _components!) { |
| 416 | component.assignLoadingUnits(generatedLoadingUnits); |
| 417 | } |
| 418 | final Depfile libDepfile = copyDeferredComponentSoFiles( |
| 419 | environment, |
| 420 | _components!, |
| 421 | generatedLoadingUnits, |
| 422 | environment.projectDir.childDirectory('build'), |
| 423 | abis, |
| 424 | dependency.buildMode, |
| 425 | ); |
| 426 | |
| 427 | final File manifestFile = environment.outputDir |
| 428 | .childDirectory(_androidAbiName) |
| 429 | .childFile('manifest.json'); |
| 430 | if (manifestFile.existsSync()) { |
| 431 | libDepfile.inputs.add(manifestFile); |
| 432 | } |
| 433 | |
| 434 | environment.depFileService.writeToFile( |
| 435 | libDepfile, |
| 436 | environment.buildDir.childFile('flutter_$name.d'), |
| 437 | writeEmpty: true, |
| 438 | ); |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | Target androidArmProfileDeferredComponentsBundle = AndroidAotDeferredComponentsBundle( |
| 443 | androidArmProfileBundle, |
| 444 | ); |
| 445 | Target androidArm64ProfileDeferredComponentsBundle = AndroidAotDeferredComponentsBundle( |
| 446 | androidArm64ProfileBundle, |
| 447 | ); |
| 448 | Target androidx64ProfileDeferredComponentsBundle = AndroidAotDeferredComponentsBundle( |
| 449 | androidx64ProfileBundle, |
| 450 | ); |
| 451 | Target androidArmReleaseDeferredComponentsBundle = AndroidAotDeferredComponentsBundle( |
| 452 | androidArmReleaseBundle, |
| 453 | ); |
| 454 | Target androidArm64ReleaseDeferredComponentsBundle = AndroidAotDeferredComponentsBundle( |
| 455 | androidArm64ReleaseBundle, |
| 456 | ); |
| 457 | Target androidx64ReleaseDeferredComponentsBundle = AndroidAotDeferredComponentsBundle( |
| 458 | androidx64ReleaseBundle, |
| 459 | ); |
| 460 | |
| 461 | /// A set of all target names that build deferred component apps. |
| 462 | var deferredComponentsTargets = { |
| 463 | androidArmProfileDeferredComponentsBundle.name, |
| 464 | androidArm64ProfileDeferredComponentsBundle.name, |
| 465 | androidx64ProfileDeferredComponentsBundle.name, |
| 466 | androidArmReleaseDeferredComponentsBundle.name, |
| 467 | androidArm64ReleaseDeferredComponentsBundle.name, |
| 468 | androidx64ReleaseDeferredComponentsBundle.name, |
| 469 | }; |
| 470 | |
| 471 | /// Utility method to copy and rename the required .so shared libs from the build output |
| 472 | /// to the correct component intermediate directory. |
| 473 | /// |
| 474 | /// The [DeferredComponent]s passed to this method must have had loading units assigned. |
| 475 | /// Assigned components are components that have determined which loading units contains |
| 476 | /// the dart libraries it has via the DeferredComponent.assignLoadingUnits method. |
| 477 | Depfile copyDeferredComponentSoFiles( |
| 478 | Environment env, |
| 479 | List components, |
| 480 | List loadingUnits, |
| 481 | Directory buildDir, // generally `/build` |
| 482 | List abis, |
| 483 | BuildMode buildMode, |
| 484 | ) { |
| 485 | final inputs = []; |
| 486 | final outputs = []; |
| 487 | final usedLoadingUnits = {}; |
| 488 | // Copy all .so files for loading units that are paired with a deferred component. |
| 489 | for (final abi in abis) { |
| 490 | for (final component in components) { |
| 491 | final Set? loadingUnits = component.loadingUnits; |
| 492 | if (loadingUnits == null || !component.assigned) { |
| 493 | env.logger.printError('Deferred component require loading units to be assigned.'); |
| 494 | return Depfile(inputs, outputs); |
| 495 | } |
| 496 | for (final LoadingUnit unit in loadingUnits) { |
| 497 | // ensure the abi for the unit is one of the abis we build for. |
| 498 | final List? splitPath = unit.path?.split(env.fileSystem.path.separator); |
| 499 | if (splitPath == null || splitPath[splitPath.length - 2] != abi) { |
| 500 | continue; |
| 501 | } |
| 502 | usedLoadingUnits.add(unit.id); |
| 503 | // the deferred_libs directory is added as a source set for the component. |
| 504 | final File destination = buildDir |
| 505 | .childDirectory(component.name) |
| 506 | .childDirectory('intermediates') |
| 507 | .childDirectory('flutter') |
| 508 | .childDirectory(buildMode.cliName) |
| 509 | .childDirectory('deferred_libs') |
| 510 | .childDirectory(abi) |
| 511 | .childFile('libapp.so-${unit.id}.part.so'); |
| 512 | if (!destination.existsSync()) { |
| 513 | destination.createSync(recursive: true); |
| 514 | } |
| 515 | final File source = env.fileSystem.file(unit.path); |
| 516 | source.copySync(destination.path); |
| 517 | inputs.add(source); |
| 518 | outputs.add(destination); |
| 519 | } |
| 520 | } |
| 521 | } |
| 522 | // Copy unused loading units, which are included in the base module. |
| 523 | for (final abi in abis) { |
| 524 | for (final unit in loadingUnits) { |
| 525 | if (usedLoadingUnits.contains(unit.id)) { |
| 526 | continue; |
| 527 | } |
| 528 | // ensure the abi for the unit is one of the abis we build for. |
| 529 | final List? splitPath = unit.path?.split(env.fileSystem.path.separator); |
| 530 | if (splitPath == null || splitPath[splitPath.length - 2] != abi) { |
| 531 | continue; |
| 532 | } |
| 533 | final File destination = env.outputDir |
| 534 | .childDirectory(abi) |
| 535 | // Omit 'lib' prefix here as it is added by the gradle task that adds 'lib' to 'app.so'. |
| 536 | .childFile('app.so-${unit.id}.part.so'); |
| 537 | if (!destination.existsSync()) { |
| 538 | destination.createSync(recursive: true); |
| 539 | } |
| 540 | final File source = env.fileSystem.file(unit.path); |
| 541 | source.copySync(destination.path); |
| 542 | inputs.add(source); |
| 543 | outputs.add(destination); |
| 544 | } |
| 545 | } |
| 546 | return Depfile(inputs, outputs); |
| 547 | } |
| 548 | |