| // Copyright 2014 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Logic for native assets shared between all host OSes. | |
| import 'package:logging/logging.dart' as logging; | |
| import 'package:native_assets_builder/native_assets_builder.dart' hide NativeAssetsBuildRunner; | |
| import 'package:native_assets_builder/native_assets_builder.dart' as native_assets_builder show NativeAssetsBuildRunner; | |
| import 'package:native_assets_cli/native_assets_cli.dart'; | |
| import 'package:package_config/package_config_types.dart'; | |
| import 'base/common.dart'; | |
| import 'base/file_system.dart'; | |
| import 'base/logger.dart'; | |
| import 'base/platform.dart'; | |
| import 'build_info.dart' as build_info; | |
| import 'cache.dart'; | |
| import 'features.dart'; | |
| import 'globals.dart' as globals; | |
| import 'ios/native_assets.dart'; | |
| import 'linux/native_assets.dart'; | |
| import 'macos/native_assets.dart'; | |
| import 'macos/native_assets_host.dart'; | |
| import 'resident_runner.dart'; | |
| import 'windows/native_assets.dart'; | |
| /// Programmatic API to be used by Dart launchers to invoke native builds. | |
| /// | |
| /// It enables mocking `package:native_assets_builder` package. | |
| /// It also enables mocking native toolchain discovery via [cCompilerConfig]. | |
| abstract class NativeAssetsBuildRunner { | |
| /// Whether the project has a `.dart_tools/package_config.json`. | |
| /// | |
| /// If there is no package config, [packagesWithNativeAssets], [build], and | |
| /// [dryRun] must not be invoked. | |
| Future<bool> hasPackageConfig(); | |
| /// All packages in the transitive dependencies that have a `build.dart`. | |
| Future<List<Package>> packagesWithNativeAssets(); | |
| /// Runs all [packagesWithNativeAssets] `build.dart` in dry run. | |
| Future<DryRunResult> dryRun({ | |
| required bool includeParentEnvironment, | |
| required LinkModePreference linkModePreference, | |
| required OS targetOS, | |
| required Uri workingDirectory, | |
| }); | |
| /// Runs all [packagesWithNativeAssets] `build.dart`. | |
| Future<BuildResult> build({ | |
| required bool includeParentEnvironment, | |
| required BuildMode buildMode, | |
| required LinkModePreference linkModePreference, | |
| required Target target, | |
| required Uri workingDirectory, | |
| CCompilerConfig? cCompilerConfig, | |
| int? targetAndroidNdkApi, | |
| IOSSdk? targetIOSSdk, | |
| }); | |
| /// The C compiler config to use for compilation. | |
| Future<CCompilerConfig> get cCompilerConfig; | |
| } | |
| /// Uses `package:native_assets_builder` for its implementation. | |
| class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { | |
| NativeAssetsBuildRunnerImpl( | |
| this.projectUri, | |
| this.packageConfig, | |
| this.fileSystem, | |
| this.logger, | |
| ); | |
| final Uri projectUri; | |
| final PackageConfig packageConfig; | |
| final FileSystem fileSystem; | |
| final Logger logger; | |
| late final logging.Logger _logger = logging.Logger('') | |
| ..onRecord.listen((logging.LogRecord record) { | |
| final int levelValue = record.level.value; | |
| final String message = record.message; | |
| if (levelValue >= logging.Level.SEVERE.value) { | |
| logger.printError(message); | |
| } else if (levelValue >= logging.Level.WARNING.value) { | |
| logger.printWarning(message); | |
| } else if (levelValue >= logging.Level.INFO.value) { | |
| logger.printTrace(message); | |
| } else { | |
| logger.printTrace(message); | |
| } | |
| }); | |
| late final Uri _dartExecutable = fileSystem.directory(Cache.flutterRoot).uri.resolve('bin/dart'); | |
| late final native_assets_builder.NativeAssetsBuildRunner _buildRunner = native_assets_builder.NativeAssetsBuildRunner( | |
| logger: _logger, | |
| dartExecutable: _dartExecutable, | |
| ); | |
| @override | |
| Future<bool> hasPackageConfig() { | |
| final File packageConfigJson = | |
| fileSystem.directory(projectUri.toFilePath()).childDirectory('.dart_tool').childFile('package_config.json'); | |
| return packageConfigJson.exists(); | |
| } | |
| @override | |
| Future<List<Package>> packagesWithNativeAssets() async { | |
| final PackageLayout packageLayout = PackageLayout.fromPackageConfig( | |
| packageConfig, | |
| projectUri.resolve('.dart_tool/package_config.json'), | |
| ); | |
| return packageLayout.packagesWithNativeAssets; | |
| } | |
| @override | |
| Future<DryRunResult> dryRun({ | |
| required bool includeParentEnvironment, | |
| required LinkModePreference linkModePreference, | |
| required OS targetOS, | |
| required Uri workingDirectory, | |
| }) { | |
| final PackageLayout packageLayout = PackageLayout.fromPackageConfig( | |
| packageConfig, | |
| projectUri.resolve('.dart_tool/package_config.json'), | |
| ); | |
| return _buildRunner.dryRun( | |
| includeParentEnvironment: includeParentEnvironment, | |
| linkModePreference: linkModePreference, | |
| targetOs: targetOS, | |
| workingDirectory: workingDirectory, | |
| packageLayout: packageLayout, | |
| ); | |
| } | |
| @override | |
| Future<BuildResult> build({ | |
| required bool includeParentEnvironment, | |
| required BuildMode buildMode, | |
| required LinkModePreference linkModePreference, | |
| required Target target, | |
| required Uri workingDirectory, | |
| CCompilerConfig? cCompilerConfig, | |
| int? targetAndroidNdkApi, | |
| IOSSdk? targetIOSSdk, | |
| }) { | |
| final PackageLayout packageLayout = PackageLayout.fromPackageConfig( | |
| packageConfig, | |
| projectUri.resolve('.dart_tool/package_config.json'), | |
| ); | |
| return _buildRunner.build( | |
| buildMode: buildMode, | |
| cCompilerConfig: cCompilerConfig, | |
| includeParentEnvironment: includeParentEnvironment, | |
| linkModePreference: linkModePreference, | |
| target: target, | |
| targetAndroidNdkApi: targetAndroidNdkApi, | |
| targetIOSSdk: targetIOSSdk, | |
| workingDirectory: workingDirectory, | |
| packageLayout: packageLayout, | |
| ); | |
| } | |
| @override | |
| late final Future<CCompilerConfig> cCompilerConfig = () { | |
| if (globals.platform.isMacOS || globals.platform.isIOS) { | |
| return cCompilerConfigMacOS(); | |
| } | |
| if (globals.platform.isLinux) { | |
| return cCompilerConfigLinux(); | |
| } | |
| if (globals.platform.isWindows) { | |
| return cCompilerConfigWindows(); | |
| } | |
| throwToolExit( | |
| 'Native assets feature not yet implemented for Android.', | |
| ); | |
| }(); | |
| } | |
| /// Write [assets] to `native_assets.yaml` in [yamlParentDirectory]. | |
| Future<Uri> writeNativeAssetsYaml( | |
| Iterable<Asset> assets, | |
| Uri yamlParentDirectory, | |
| FileSystem fileSystem, | |
| ) async { | |
| globals.logger.printTrace('Writing native_assets.yaml.'); | |
| final String nativeAssetsDartContents = assets.toNativeAssetsFile(); | |
| final Directory parentDirectory = fileSystem.directory(yamlParentDirectory); | |
| if (!await parentDirectory.exists()) { | |
| await parentDirectory.create(recursive: true); | |
| } | |
| final File nativeAssetsFile = parentDirectory.childFile('native_assets.yaml'); | |
| await nativeAssetsFile.writeAsString(nativeAssetsDartContents); | |
| globals.logger.printTrace('Writing ${nativeAssetsFile.path} done.'); | |
| return nativeAssetsFile.uri; | |
| } | |
| /// Select the native asset build mode for a given Flutter build mode. | |
| BuildMode nativeAssetsBuildMode(build_info.BuildMode buildMode) { | |
| switch (buildMode) { | |
| case build_info.BuildMode.debug: | |
| return BuildMode.debug; | |
| case build_info.BuildMode.jitRelease: | |
| case build_info.BuildMode.profile: | |
| case build_info.BuildMode.release: | |
| return BuildMode.release; | |
| } | |
| } | |
| /// Checks whether this project does not yet have a package config file. | |
| /// | |
| /// A project has no package config when `pub get` has not yet been run. | |
| /// | |
| /// Native asset builds cannot be run without a package config. If there is | |
| /// no package config, leave a logging trace about that. | |
| Future<bool> _hasNoPackageConfig(NativeAssetsBuildRunner buildRunner) async { | |
| final bool packageConfigExists = await buildRunner.hasPackageConfig(); | |
| if (!packageConfigExists) { | |
| globals.logger.printTrace('No package config found. Skipping native assets compilation.'); | |
| } | |
| return !packageConfigExists; | |
| } | |
| Future<bool> nativeBuildRequired(NativeAssetsBuildRunner buildRunner) async { | |
| if (await _hasNoPackageConfig(buildRunner)) { | |
| return false; | |
| } | |
| final List<Package> packagesWithNativeAssets = await buildRunner.packagesWithNativeAssets(); | |
| if (packagesWithNativeAssets.isEmpty) { | |
| return false; | |
| } | |
| if (!featureFlags.isNativeAssetsEnabled) { | |
| final String packageNames = packagesWithNativeAssets.map((Package p) => p.name).join(' '); | |
| throwToolExit( | |
| 'Package(s) $packageNames require the native assets feature to be enabled. ' | |
| 'Enable using `flutter config --enable-native-assets`.', | |
| ); | |
| } | |
| return true; | |
| } | |
| /// Ensures that either this project has no native assets, or that native assets | |
| /// are supported on that operating system. | |
| /// | |
| /// Exits the tool if the above condition is not satisfied. | |
| Future<void> ensureNoNativeAssetsOrOsIsSupported( | |
| Uri workingDirectory, | |
| String os, | |
| FileSystem fileSystem, | |
| NativeAssetsBuildRunner buildRunner, | |
| ) async { | |
| if (await _hasNoPackageConfig(buildRunner)) { | |
| return; | |
| } | |
| final List<Package> packagesWithNativeAssets = await buildRunner.packagesWithNativeAssets(); | |
| if (packagesWithNativeAssets.isEmpty) { | |
| return; | |
| } | |
| final String packageNames = packagesWithNativeAssets.map((Package p) => p.name).join(' '); | |
| throwToolExit( | |
| 'Package(s) $packageNames require the native assets feature. ' | |
| 'This feature has not yet been implemented for `$os`. ' | |
| 'For more info see https://github.com/flutter/flutter/issues/129757.', | |
| ); | |
| } | |
| /// Ensure all native assets have a linkmode declared to be dynamic loading. | |
| /// | |
| /// In JIT, the link mode must always be dynamic linking. | |
| /// In AOT, the static linking has not yet been implemented in Dart: | |
| /// https://github.com/dart-lang/sdk/issues/49418. | |
| /// | |
| /// Therefore, ensure all `build.dart` scripts return only dynamic libraries. | |
| void ensureNoLinkModeStatic(List<Asset> nativeAssets) { | |
| final Iterable<Asset> staticAssets = nativeAssets.whereLinkMode(LinkMode.static); | |
| if (staticAssets.isNotEmpty) { | |
| final String assetIds = staticAssets.map((Asset a) => a.id).toSet().join(', '); | |
| throwToolExit( | |
| 'Native asset(s) $assetIds have their link mode set to static, ' | |
| 'but this is not yet supported. ' | |
| 'For more info see https://github.com/dart-lang/sdk/issues/49418.', | |
| ); | |
| } | |
| } | |
| /// This should be the same for different archs, debug/release, etc. | |
| /// It should work for all macOS. | |
| Uri nativeAssetsBuildUri(Uri projectUri, OS os) { | |
| final String buildDir = build_info.getBuildDirectory(); | |
| return projectUri.resolve('$buildDir/native_assets/$os/'); | |
| } | |
| /// Gets the native asset id to dylib mapping to embed in the kernel file. | |
| /// | |
| /// Run hot compiles a kernel file that is pushed to the device after hot | |
| /// restart. We need to embed the native assets mapping in order to access | |
| /// native assets after hot restart. | |
| Future<Uri?> dryRunNativeAssets({ | |
| required Uri projectUri, | |
| required FileSystem fileSystem, | |
| required NativeAssetsBuildRunner buildRunner, | |
| required List<FlutterDevice> flutterDevices, | |
| }) async { | |
| if (flutterDevices.length != 1) { | |
| return dryRunNativeAssetsMultipeOSes( | |
| projectUri: projectUri, | |
| fileSystem: fileSystem, | |
| targetPlatforms: flutterDevices.map((FlutterDevice d) => d.targetPlatform).nonNulls, | |
| buildRunner: buildRunner, | |
| ); | |
| } | |
| final FlutterDevice flutterDevice = flutterDevices.single; | |
| final build_info.TargetPlatform targetPlatform = flutterDevice.targetPlatform!; | |
| final Uri? nativeAssetsYaml; | |
| switch (targetPlatform) { | |
| case build_info.TargetPlatform.darwin: | |
| nativeAssetsYaml = await dryRunNativeAssetsMacOS( | |
| projectUri: projectUri, | |
| fileSystem: fileSystem, | |
| buildRunner: buildRunner, | |
| ); | |
| case build_info.TargetPlatform.ios: | |
| nativeAssetsYaml = await dryRunNativeAssetsIOS( | |
| projectUri: projectUri, | |
| fileSystem: fileSystem, | |
| buildRunner: buildRunner, | |
| ); | |
| case build_info.TargetPlatform.tester: | |
| if (const LocalPlatform().isMacOS) { | |
| nativeAssetsYaml = await dryRunNativeAssetsMacOS( | |
| projectUri: projectUri, | |
| flutterTester: true, | |
| fileSystem: fileSystem, | |
| buildRunner: buildRunner, | |
| ); | |
| } else if (const LocalPlatform().isLinux) { | |
| nativeAssetsYaml = await dryRunNativeAssetsLinux( | |
| projectUri: projectUri, | |
| flutterTester: true, | |
| fileSystem: fileSystem, | |
| buildRunner: buildRunner, | |
| ); | |
| } else if (const LocalPlatform().isWindows) { | |
| nativeAssetsYaml = await dryRunNativeAssetsWindows( | |
| projectUri: projectUri, | |
| flutterTester: true, | |
| fileSystem: fileSystem, | |
| buildRunner: buildRunner, | |
| ); | |
| } else { | |
| await nativeBuildRequired(buildRunner); | |
| nativeAssetsYaml = null; | |
| } | |
| case build_info.TargetPlatform.linux_arm64: | |
| case build_info.TargetPlatform.linux_x64: | |
| nativeAssetsYaml = await dryRunNativeAssetsLinux( | |
| projectUri: projectUri, | |
| fileSystem: fileSystem, | |
| buildRunner: buildRunner, | |
| ); | |
| case build_info.TargetPlatform.windows_x64: | |
| nativeAssetsYaml = await dryRunNativeAssetsWindows( | |
| projectUri: projectUri, | |
| fileSystem: fileSystem, | |
| buildRunner: buildRunner, | |
| ); | |
| case build_info.TargetPlatform.android_arm: | |
| case build_info.TargetPlatform.android_arm64: | |
| case build_info.TargetPlatform.android_x64: | |
| case build_info.TargetPlatform.android_x86: | |
| case build_info.TargetPlatform.android: | |
| case build_info.TargetPlatform.fuchsia_arm64: | |
| case build_info.TargetPlatform.fuchsia_x64: | |
| case build_info.TargetPlatform.web_javascript: | |
| await ensureNoNativeAssetsOrOsIsSupported( | |
| projectUri, | |
| targetPlatform.toString(), | |
| fileSystem, | |
| buildRunner, | |
| ); | |
| nativeAssetsYaml = null; | |
| } | |
| return nativeAssetsYaml; | |
| } | |
| /// Dry run the native builds for multiple OSes. | |
| /// | |
| /// Needed for `flutter run -d all`. | |
| Future<Uri?> dryRunNativeAssetsMultipeOSes({ | |
| required NativeAssetsBuildRunner buildRunner, | |
| required Uri projectUri, | |
| required FileSystem fileSystem, | |
| required Iterable<build_info.TargetPlatform> targetPlatforms, | |
| }) async { | |
| if (await nativeBuildRequired(buildRunner)) { | |
| return null; | |
| } | |
| final Uri buildUri = buildUriMultiple(projectUri); | |
| final Iterable<Asset> nativeAssetPaths = <Asset>[ | |
| if (targetPlatforms.contains(build_info.TargetPlatform.darwin) || | |
| (targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.macOS)) | |
| ...await dryRunNativeAssetsMacOSInternal( | |
| fileSystem, | |
| projectUri, | |
| false, | |
| buildRunner, | |
| ), | |
| if (targetPlatforms.contains(build_info.TargetPlatform.linux_arm64) || | |
| targetPlatforms.contains(build_info.TargetPlatform.linux_x64) || | |
| (targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.linux)) | |
| ...await dryRunNativeAssetsLinuxInternal( | |
| fileSystem, | |
| projectUri, | |
| false, | |
| buildRunner, | |
| ), | |
| if (targetPlatforms.contains(build_info.TargetPlatform.windows_x64) || | |
| (targetPlatforms.contains(build_info.TargetPlatform.tester) && OS.current == OS.windows)) | |
| ...await dryRunNativeAssetsWindowsInternal( | |
| fileSystem, | |
| projectUri, | |
| false, | |
| buildRunner, | |
| ), | |
| if (targetPlatforms.contains(build_info.TargetPlatform.ios)) | |
| ...await dryRunNativeAssetsIOSInternal( | |
| fileSystem, | |
| projectUri, | |
| buildRunner, | |
| ) | |
| ]; | |
| final Uri nativeAssetsUri = await writeNativeAssetsYaml(nativeAssetPaths, buildUri, fileSystem); | |
| return nativeAssetsUri; | |
| } | |
| /// With `flutter run -d all` we need a place to store the native assets | |
| /// mapping for multiple OSes combined. | |
| Uri buildUriMultiple(Uri projectUri) { | |
| final String buildDir = build_info.getBuildDirectory(); | |
| return projectUri.resolve('$buildDir/native_assets/multiple/'); | |
| } | |
| /// Dry run the native builds. | |
| /// | |
| /// This does not build native assets, it only simulates what the final paths | |
| /// of all assets will be so that this can be embedded in the kernel file. | |
| Future<Uri?> dryRunNativeAssetsSingleArchitecture({ | |
| required NativeAssetsBuildRunner buildRunner, | |
| required Uri projectUri, | |
| bool flutterTester = false, | |
| required FileSystem fileSystem, | |
| required OS os, | |
| }) async { | |
| if (!await nativeBuildRequired(buildRunner)) { | |
| return null; | |
| } | |
| final Uri buildUri = nativeAssetsBuildUri(projectUri, os); | |
| final Iterable<Asset> nativeAssetPaths = await dryRunNativeAssetsSingleArchitectureInternal( | |
| fileSystem, | |
| projectUri, | |
| flutterTester, | |
| buildRunner, | |
| os, | |
| ); | |
| final Uri nativeAssetsUri = await writeNativeAssetsYaml( | |
| nativeAssetPaths, | |
| buildUri, | |
| fileSystem, | |
| ); | |
| return nativeAssetsUri; | |
| } | |
| Future<Iterable<Asset>> dryRunNativeAssetsSingleArchitectureInternal( | |
| FileSystem fileSystem, | |
| Uri projectUri, | |
| bool flutterTester, | |
| NativeAssetsBuildRunner buildRunner, | |
| OS targetOS, | |
| ) async { | |
| final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); | |
| globals.logger.printTrace('Dry running native assets for $targetOS.'); | |
| final DryRunResult dryRunResult = await buildRunner.dryRun( | |
| linkModePreference: LinkModePreference.dynamic, | |
| targetOS: targetOS, | |
| workingDirectory: projectUri, | |
| includeParentEnvironment: true, | |
| ); | |
| ensureNativeAssetsBuildSucceed(dryRunResult); | |
| final List<Asset> nativeAssets = dryRunResult.assets; | |
| ensureNoLinkModeStatic(nativeAssets); | |
| globals.logger.printTrace('Dry running native assets for $targetOS done.'); | |
| final Uri? absolutePath = flutterTester ? buildUri : null; | |
| final Map<Asset, Asset> assetTargetLocations = _assetTargetLocationsSingleArchitecture( | |
| nativeAssets, | |
| absolutePath, | |
| ); | |
| final Iterable<Asset> nativeAssetPaths = assetTargetLocations.values; | |
| return nativeAssetPaths; | |
| } | |
| /// Builds native assets. | |
| /// | |
| /// If [targetPlatform] is omitted, the current target architecture is used. | |
| /// | |
| /// If [flutterTester] is true, absolute paths are emitted in the native | |
| /// assets mapping. This can be used for JIT mode without sandbox on the host. | |
| /// This is used in `flutter test` and `flutter run -d flutter-tester`. | |
| Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsSingleArchitecture({ | |
| required NativeAssetsBuildRunner buildRunner, | |
| build_info.TargetPlatform? targetPlatform, | |
| required Uri projectUri, | |
| required build_info.BuildMode buildMode, | |
| bool flutterTester = false, | |
| Uri? yamlParentDirectory, | |
| required FileSystem fileSystem, | |
| }) async { | |
| final Target target = targetPlatform != null ? _getNativeTarget(targetPlatform) : Target.current; | |
| final OS targetOS = target.os; | |
| final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); | |
| final Directory buildDir = fileSystem.directory(buildUri); | |
| if (!await buildDir.exists()) { | |
| // CMake requires the folder to exist to do copying. | |
| await buildDir.create(recursive: true); | |
| } | |
| if (!await nativeBuildRequired(buildRunner)) { | |
| final Uri nativeAssetsYaml = await writeNativeAssetsYaml( | |
| <Asset>[], | |
| yamlParentDirectory ?? buildUri, | |
| fileSystem, | |
| ); | |
| return (nativeAssetsYaml, <Uri>[]); | |
| } | |
| final BuildMode buildModeCli = nativeAssetsBuildMode(buildMode); | |
| globals.logger.printTrace('Building native assets for $target $buildModeCli.'); | |
| final BuildResult result = await buildRunner.build( | |
| linkModePreference: LinkModePreference.dynamic, | |
| target: target, | |
| buildMode: buildModeCli, | |
| workingDirectory: projectUri, | |
| includeParentEnvironment: true, | |
| cCompilerConfig: await buildRunner.cCompilerConfig, | |
| ); | |
| ensureNativeAssetsBuildSucceed(result); | |
| final List<Asset> nativeAssets = result.assets; | |
| final Set<Uri> dependencies = result.dependencies.toSet(); | |
| ensureNoLinkModeStatic(nativeAssets); | |
| globals.logger.printTrace('Building native assets for $target done.'); | |
| final Uri? absolutePath = flutterTester ? buildUri : null; | |
| final Map<Asset, Asset> assetTargetLocations = _assetTargetLocationsSingleArchitecture(nativeAssets, absolutePath); | |
| await _copyNativeAssetsSingleArchitecture( | |
| buildUri, | |
| assetTargetLocations, | |
| buildMode, | |
| fileSystem, | |
| ); | |
| final Uri nativeAssetsUri = await writeNativeAssetsYaml( | |
| assetTargetLocations.values, | |
| yamlParentDirectory ?? buildUri, | |
| fileSystem, | |
| ); | |
| return (nativeAssetsUri, dependencies.toList()); | |
| } | |
| Map<Asset, Asset> _assetTargetLocationsSingleArchitecture( | |
| List<Asset> nativeAssets, | |
| Uri? absolutePath, | |
| ) { | |
| return <Asset, Asset>{ | |
| for (final Asset asset in nativeAssets) | |
| asset: _targetLocationSingleArchitecture( | |
| asset, | |
| absolutePath, | |
| ), | |
| }; | |
| } | |
| Asset _targetLocationSingleArchitecture(Asset asset, Uri? absolutePath) { | |
| final AssetPath path = asset.path; | |
| switch (path) { | |
| case AssetSystemPath _: | |
| case AssetInExecutable _: | |
| case AssetInProcess _: | |
| return asset; | |
| case AssetAbsolutePath _: | |
| final String fileName = path.uri.pathSegments.last; | |
| Uri uri; | |
| if (absolutePath != null) { | |
| // Flutter tester needs full host paths. | |
| uri = absolutePath.resolve(fileName); | |
| } else { | |
| // Flutter Desktop needs "absolute" paths inside the app. | |
| // "relative" in the context of native assets would be relative to the | |
| // kernel or aot snapshot. | |
| uri = Uri(path: fileName); | |
| } | |
| return asset.copyWith(path: AssetAbsolutePath(uri)); | |
| } | |
| throw Exception('Unsupported asset path type ${path.runtimeType} in asset $asset'); | |
| } | |
| /// Extract the [Target] from a [TargetPlatform]. | |
| /// | |
| /// Does not cover MacOS, iOS, and Android as these pass the architecture | |
| /// in other enums. | |
| Target _getNativeTarget(build_info.TargetPlatform targetPlatform) { | |
| switch (targetPlatform) { | |
| case build_info.TargetPlatform.linux_x64: | |
| return Target.linuxX64; | |
| case build_info.TargetPlatform.linux_arm64: | |
| return Target.linuxArm64; | |
| case build_info.TargetPlatform.windows_x64: | |
| return Target.windowsX64; | |
| case build_info.TargetPlatform.android: | |
| case build_info.TargetPlatform.ios: | |
| case build_info.TargetPlatform.darwin: | |
| case build_info.TargetPlatform.fuchsia_arm64: | |
| case build_info.TargetPlatform.fuchsia_x64: | |
| case build_info.TargetPlatform.tester: | |
| case build_info.TargetPlatform.web_javascript: | |
| case build_info.TargetPlatform.android_arm: | |
| case build_info.TargetPlatform.android_arm64: | |
| case build_info.TargetPlatform.android_x64: | |
| case build_info.TargetPlatform.android_x86: | |
| throw Exception('Unknown targetPlatform: $targetPlatform.'); | |
| } | |
| } | |
| Future<void> _copyNativeAssetsSingleArchitecture( | |
| Uri buildUri, | |
| Map<Asset, Asset> assetTargetLocations, | |
| build_info.BuildMode buildMode, | |
| FileSystem fileSystem, | |
| ) async { | |
| if (assetTargetLocations.isNotEmpty) { | |
| globals.logger.printTrace('Copying native assets to ${buildUri.toFilePath()}.'); | |
| final Directory buildDir = fileSystem.directory(buildUri.toFilePath()); | |
| if (!buildDir.existsSync()) { | |
| buildDir.createSync(recursive: true); | |
| } | |
| for (final MapEntry<Asset, Asset> assetMapping in assetTargetLocations.entries) { | |
| final Uri source = (assetMapping.key.path as AssetAbsolutePath).uri; | |
| final Uri target = (assetMapping.value.path as AssetAbsolutePath).uri; | |
| final Uri targetUri = buildUri.resolveUri(target); | |
| final String targetFullPath = targetUri.toFilePath(); | |
| await fileSystem.file(source).copy(targetFullPath); | |
| } | |
| globals.logger.printTrace('Copying native assets done.'); | |
| } | |
| } | |
| void ensureNativeAssetsBuildSucceed(DryRunResult result) { | |
| if (!result.success) { | |
| throwToolExit( | |
| 'Building native assets failed. See the logs for more details.', | |
| ); | |
| } | |
| } |