From abcba1fa7e2a4d9efa4716c5ca3ae2b44cabcbd7 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 26 Sep 2024 12:21:29 +0200 Subject: [PATCH] Reland "[flutter_tools] Cleanup of native asset related code (removes around 50% of the native asset related code) (#155430)" (#155745) Changes to original CL: The code that issues an error on unsupported operating system in the dry-run case was missing a case for iOS and Android Original CL description tl;dr Removes 50% (>1650 locs) of native asset related code in `packages/flutter_tools` Before this PR the invocation of dart build/link/dry-run was implemented per OS. This lead to very large code duplication of almost identical, but slightly different code. It also led to similarly duplicated test code. Almost the entire dart build/link/dry-run implementation is identical across OSes. There's small variations: - configuration of the build (e.g. android/macos/ios version, ios sdk, ...) - determining target locations & copying the final shared libraries This PR unifies the implementation by reducing the code to basically two main functions: * `runFlutterSpecificDartBuild` which is responsible for - obtain flutter configuration - perform dart build (& link) - determine target location & install binaries * `runFlutterSpecificDartDryRunOnPlatforms` which is responsible for a similar (but not same): - obtain flutter configuration - perform dart dry run - determine target location these two functions will call out to helpers for the OS specific functionality: * `_assetTargetLocationsForOS` for determining the location of the code assets * `_copyNativeCodeAssetsForOS` for copying the code assets (and possibly overriting the install name, etc) => Since we get rid of the code duplication across OSes and have only a single code path for the build/link/dry-run, we can also remove the duplicated tests that were pretty much identical across OSes. We also harden the building code by adding asserts, e.g. * the dry fun functionality should never be used by `flutter test` * the `build/native_assets//native_assets.yaml` should only be used by `flutter test` and the dry-run of `flutter run` => We change the tests to also comply with these invariants (so the tests are not testing things that cannot happen in reality) We also rename `{,Flutter}NativeAssetsBuildRunner` to disambiguate it from the `package:native_asset_builder`'s `NativeAssetsBuildRunner`. --- .../build_system/targets/native_assets.dart | 312 +----- .../native_assets/android/native_assets.dart | 162 +-- .../native_assets/ios/native_assets.dart | 199 +--- .../native_assets/linux/native_assets.dart | 58 -- .../native_assets/macos/native_assets.dart | 226 +---- .../macos/native_assets_host.dart | 18 +- .../isolated/native_assets/native_assets.dart | 933 ++++++++++-------- .../native_assets/test/native_assets.dart | 83 +- .../native_assets/windows/native_assets.dart | 60 -- .../isolated/android/native_assets_test.dart | 284 +----- .../targets/native_assets_test.dart | 24 +- .../fake_native_assets_build_runner.dart | 39 +- .../test/general.shard/isolated/hot_test.dart | 8 +- .../isolated/ios/native_assets_test.dart | 261 +---- .../isolated/linux/native_assets_test.dart | 348 +------ .../isolated/macos/native_assets_test.dart | 358 ++----- .../isolated/native_assets_test.dart | 308 ++++++ .../isolated/resident_runner_test.dart | 2 +- .../isolated/windows/native_assets_test.dart | 304 +----- 19 files changed, 1171 insertions(+), 2816 deletions(-) create mode 100644 packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart diff --git a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart index fae9b56cf59..12efadc5dbf 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart @@ -3,22 +3,14 @@ // found in the LICENSE file. import 'package:meta/meta.dart'; -import 'package:native_assets_builder/native_assets_builder.dart' hide NativeAssetsBuildRunner; +import 'package:native_assets_builder/native_assets_builder.dart'; import 'package:package_config/package_config_types.dart'; -import '../../android/gradle_utils.dart'; import '../../base/common.dart'; import '../../base/file_system.dart'; -import '../../base/platform.dart'; import '../../build_info.dart'; import '../../dart/package_map.dart'; -import '../../isolated/native_assets/android/native_assets.dart'; -import '../../isolated/native_assets/ios/native_assets.dart'; -import '../../isolated/native_assets/linux/native_assets.dart'; -import '../../isolated/native_assets/macos/native_assets.dart'; import '../../isolated/native_assets/native_assets.dart'; -import '../../isolated/native_assets/windows/native_assets.dart'; -import '../../macos/xcode.dart'; import '../build_system.dart'; import '../depfile.dart'; import '../exceptions.dart'; @@ -43,20 +35,21 @@ import 'common.dart'; /// rebuild. class NativeAssets extends Target { const NativeAssets({ - @visibleForTesting NativeAssetsBuildRunner? buildRunner, + @visibleForTesting FlutterNativeAssetsBuildRunner? buildRunner, }) : _buildRunner = buildRunner; - final NativeAssetsBuildRunner? _buildRunner; + final FlutterNativeAssetsBuildRunner? _buildRunner; @override Future build(Environment environment) async { final String? nativeAssetsEnvironment = environment.defines[kNativeAssets]; - final List dependencies; final FileSystem fileSystem = environment.fileSystem; - final File nativeAssetsFile = environment.buildDir.childFile('native_assets.yaml'); + final Uri nativeAssetsFileUri = environment.buildDir.childFile('native_assets.yaml').uri; + + final DartBuildResult result; if (nativeAssetsEnvironment == 'false') { - dependencies = []; - await writeNativeAssetsYaml(KernelAssets(), environment.buildDir.uri, fileSystem); + result = const DartBuildResult.empty(); + await writeNativeAssetsYaml(KernelAssets(), nativeAssetsFileUri, fileSystem); } else { final String? targetPlatformEnvironment = environment.defines[kTargetPlatform]; if (targetPlatformEnvironment == null) { @@ -69,8 +62,8 @@ class NativeAssets extends Target { fileSystem.file(environment.packageConfigPath), logger: environment.logger, ); - final NativeAssetsBuildRunner buildRunner = _buildRunner ?? - NativeAssetsBuildRunnerImpl( + final FlutterNativeAssetsBuildRunner buildRunner = _buildRunner ?? + FlutterNativeAssetsBuildRunnerImpl( projectUri, environment.packageConfigPath, packageConfig, @@ -78,102 +71,22 @@ class NativeAssets extends Target { environment.logger, ); - switch (targetPlatform) { - case TargetPlatform.ios: - dependencies = await _buildIOS( - environment, - projectUri, - fileSystem, - buildRunner, - ); - case TargetPlatform.darwin: - dependencies = await _buildMacOS( - environment, - projectUri, - fileSystem, - buildRunner, - ); - case TargetPlatform.linux_arm64: - case TargetPlatform.linux_x64: - dependencies = await _buildLinux( - environment, - targetPlatform, - projectUri, - fileSystem, - buildRunner, - ); - case TargetPlatform.windows_arm64: - case TargetPlatform.windows_x64: - dependencies = await _buildWindows( - environment, - targetPlatform, - projectUri, - fileSystem, - buildRunner, - ); - case TargetPlatform.tester: - if (const LocalPlatform().isMacOS) { - (_, dependencies) = await buildNativeAssetsMacOS( - buildMode: BuildMode.debug, - projectUri: projectUri, - codesignIdentity: environment.defines[kCodesignIdentity], - yamlParentDirectory: environment.buildDir.uri, - fileSystem: fileSystem, - buildRunner: buildRunner, - flutterTester: true, - ); - } else if (const LocalPlatform().isLinux) { - (_, dependencies) = await buildNativeAssetsLinux( - buildMode: BuildMode.debug, - projectUri: projectUri, - yamlParentDirectory: environment.buildDir.uri, - fileSystem: fileSystem, - buildRunner: buildRunner, - flutterTester: true, - ); - } else if (const LocalPlatform().isWindows) { - (_, dependencies) = await buildNativeAssetsWindows( - buildMode: BuildMode.debug, - projectUri: projectUri, - yamlParentDirectory: environment.buildDir.uri, - fileSystem: fileSystem, - buildRunner: buildRunner, - flutterTester: true, - ); - } else { - // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 - // Write the file we claim to have in the [outputs]. - await writeNativeAssetsYaml(KernelAssets(), environment.buildDir.uri, fileSystem); - dependencies = []; - } - case TargetPlatform.android_arm: - case TargetPlatform.android_arm64: - case TargetPlatform.android_x64: - case TargetPlatform.android_x86: - case TargetPlatform.android: - (_, dependencies) = await _buildAndroid( - environment, - targetPlatform, - projectUri, - fileSystem, - buildRunner, - ); - case TargetPlatform.fuchsia_arm64: - case TargetPlatform.fuchsia_x64: - case TargetPlatform.web_javascript: - // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 - // Write the file we claim to have in the [outputs]. - await writeNativeAssetsYaml(KernelAssets(), environment.buildDir.uri, fileSystem); - dependencies = []; - } + (result, _) = await runFlutterSpecificDartBuild( + environmentDefines: environment.defines, + buildRunner: buildRunner, + targetPlatform: targetPlatform, + projectUri: projectUri, + nativeAssetsYamlUri : nativeAssetsFileUri, + fileSystem: fileSystem, + ); } final Depfile depfile = Depfile( [ - for (final Uri dependency in dependencies) fileSystem.file(dependency), + for (final Uri dependency in result.dependencies) fileSystem.file(dependency), ], [ - nativeAssetsFile, + fileSystem.file(nativeAssetsFileUri), ], ); final File outputDepfile = environment.buildDir.childFile('native_assets.d'); @@ -181,188 +94,14 @@ class NativeAssets extends Target { outputDepfile.parent.createSync(recursive: true); } environment.depFileService.writeToFile(depfile, outputDepfile); - if (!await nativeAssetsFile.exists()) { - throwToolExit("${nativeAssetsFile.path} doesn't exist."); + if (!await fileSystem.file(nativeAssetsFileUri).exists()) { + throwToolExit("${nativeAssetsFileUri.path} doesn't exist."); } if (!await outputDepfile.exists()) { throwToolExit("${outputDepfile.path} doesn't exist."); } } - Future> _buildWindows( - Environment environment, - TargetPlatform targetPlatform, - Uri projectUri, - FileSystem fileSystem, - NativeAssetsBuildRunner buildRunner, - ) async { - final String? environmentBuildMode = environment.defines[kBuildMode]; - if (environmentBuildMode == null) { - throw MissingDefineException(kBuildMode, name); - } - final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode); - final (_, List dependencies) = await buildNativeAssetsWindows( - targetPlatform: targetPlatform, - buildMode: buildMode, - projectUri: projectUri, - yamlParentDirectory: environment.buildDir.uri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - return dependencies; - } - - Future> _buildLinux( - Environment environment, - TargetPlatform targetPlatform, - Uri projectUri, - FileSystem fileSystem, - NativeAssetsBuildRunner buildRunner, - ) async { - final String? environmentBuildMode = environment.defines[kBuildMode]; - if (environmentBuildMode == null) { - throw MissingDefineException(kBuildMode, name); - } - final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode); - final (_, List dependencies) = await buildNativeAssetsLinux( - targetPlatform: targetPlatform, - buildMode: buildMode, - projectUri: projectUri, - yamlParentDirectory: environment.buildDir.uri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - return dependencies; - } - - Future> _buildMacOS( - Environment environment, - Uri projectUri, - FileSystem fileSystem, - NativeAssetsBuildRunner buildRunner, - ) async { - final List darwinArchs = - _emptyToNull(environment.defines[kDarwinArchs]) - ?.split(' ') - .map(getDarwinArchForName) - .toList() ?? - [DarwinArch.x86_64, DarwinArch.arm64]; - final String? environmentBuildMode = environment.defines[kBuildMode]; - if (environmentBuildMode == null) { - throw MissingDefineException(kBuildMode, name); - } - final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode); - final (_, List dependencies) = await buildNativeAssetsMacOS( - darwinArchs: darwinArchs, - buildMode: buildMode, - projectUri: projectUri, - codesignIdentity: environment.defines[kCodesignIdentity], - yamlParentDirectory: environment.buildDir.uri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - return dependencies; - } - - Future> _buildIOS( - Environment environment, - Uri projectUri, - FileSystem fileSystem, - NativeAssetsBuildRunner buildRunner, - ) { - final List iosArchs = - _emptyToNull(environment.defines[kIosArchs]) - ?.split(' ') - .map(getIOSArchForName) - .toList() ?? - [DarwinArch.arm64]; - final String? environmentBuildMode = environment.defines[kBuildMode]; - if (environmentBuildMode == null) { - throw MissingDefineException(kBuildMode, name); - } - final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode); - final String? sdkRoot = environment.defines[kSdkRoot]; - if (sdkRoot == null) { - throw MissingDefineException(kSdkRoot, name); - } - final EnvironmentType environmentType = - environmentTypeFromSdkroot(sdkRoot, environment.fileSystem)!; - return buildNativeAssetsIOS( - environmentType: environmentType, - darwinArchs: iosArchs, - buildMode: buildMode, - projectUri: projectUri, - codesignIdentity: environment.defines[kCodesignIdentity], - fileSystem: fileSystem, - buildRunner: buildRunner, - yamlParentDirectory: environment.buildDir.uri, - ); - } - - Future<(Uri? nativeAssetsYaml, List dependencies)> _buildAndroid( - Environment environment, - TargetPlatform targetPlatform, - Uri projectUri, - FileSystem fileSystem, - NativeAssetsBuildRunner buildRunner) { - final String? androidArchsEnvironment = environment.defines[kAndroidArchs]; - final List androidArchs = _androidArchs( - targetPlatform, - androidArchsEnvironment, - ); - final int targetAndroidNdkApi = - int.parse(environment.defines[kMinSdkVersion] ?? minSdkVersion); - final String? environmentBuildMode = environment.defines[kBuildMode]; - if (environmentBuildMode == null) { - throw MissingDefineException(kBuildMode, name); - } - final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode); - return buildNativeAssetsAndroid( - buildMode: buildMode, - projectUri: projectUri, - yamlParentDirectory: environment.buildDir.uri, - fileSystem: fileSystem, - buildRunner: buildRunner, - androidArchs: androidArchs, - targetAndroidNdkApi: targetAndroidNdkApi, - ); - } - - List _androidArchs( - TargetPlatform targetPlatform, - String? androidArchsEnvironment, - ) { - switch (targetPlatform) { - case TargetPlatform.android_arm: - return [AndroidArch.armeabi_v7a]; - case TargetPlatform.android_arm64: - return [AndroidArch.arm64_v8a]; - case TargetPlatform.android_x64: - return [AndroidArch.x86_64]; - case TargetPlatform.android_x86: - return [AndroidArch.x86]; - case TargetPlatform.android: - if (androidArchsEnvironment == null) { - throw MissingDefineException(kAndroidArchs, name); - } - return androidArchsEnvironment - .split(' ') - .map(getAndroidArchForName) - .toList(); - case TargetPlatform.darwin: - case TargetPlatform.fuchsia_arm64: - case TargetPlatform.fuchsia_x64: - case TargetPlatform.ios: - case TargetPlatform.linux_arm64: - case TargetPlatform.linux_x64: - case TargetPlatform.tester: - case TargetPlatform.web_javascript: - case TargetPlatform.windows_x64: - case TargetPlatform.windows_arm64: - throwToolExit('Unsupported Android target platform: $targetPlatform.'); - } - } - @override List get depfiles => [ 'native_assets.d', @@ -390,10 +129,3 @@ class NativeAssets extends Target { Source.pattern('{BUILD_DIR}/native_assets.yaml'), ]; } - -String? _emptyToNull(String? input) { - if (input == null || input.isEmpty) { - return null; - } - return input; -} diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart index 1a8b2fcf6bb..c5fde536ced 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart @@ -2,145 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:native_assets_builder/native_assets_builder.dart' - hide NativeAssetsBuildRunner; +import 'package:native_assets_builder/native_assets_builder.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../../../android/android_sdk.dart'; +import '../../../android/gradle_utils.dart'; import '../../../base/common.dart'; import '../../../base/file_system.dart'; -import '../../../build_info.dart'; +import '../../../build_info.dart' hide BuildMode; import '../../../globals.dart' as globals; -import '../native_assets.dart'; -/// 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 dryRunNativeAssetsAndroid({ - required NativeAssetsBuildRunner buildRunner, - required Uri projectUri, - bool flutterTester = false, - required FileSystem fileSystem, -}) async { - if (!await nativeBuildRequired(buildRunner)) { - return null; - } - - final Uri buildUri_ = nativeAssetsBuildUri(projectUri, OSImpl.android); - final Iterable nativeAssetPaths = - await dryRunNativeAssetsAndroidInternal( - fileSystem, - projectUri, - buildRunner, - ); - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(nativeAssetPaths), - buildUri_, - fileSystem, - ); - return nativeAssetsUri; +int targetAndroidNdkApi(Map environmentDefines) { + return int.parse(environmentDefines[kMinSdkVersion] ?? minSdkVersion); } -Future> dryRunNativeAssetsAndroidInternal( - FileSystem fileSystem, - Uri projectUri, - NativeAssetsBuildRunner buildRunner, -) async { - const OSImpl targetOS = OSImpl.android; - - globals.logger.printTrace('Dry running native assets for $targetOS.'); - final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( - linkModePreference: LinkModePreferenceImpl.dynamic, - targetOS: targetOS, - workingDirectory: projectUri, - includeParentEnvironment: true, - ); - ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); - // No link hooks in JIT mode. - final List nativeAssets = buildDryRunResult.assets; - globals.logger.printTrace('Dry running native assets for $targetOS done.'); - final Map assetTargetLocations = - _assetTargetLocations(nativeAssets); - return assetTargetLocations.values; -} - -/// Builds native assets. -Future<(Uri? nativeAssetsYaml, List dependencies)> - buildNativeAssetsAndroid({ - required NativeAssetsBuildRunner buildRunner, - required Iterable androidArchs, - required Uri projectUri, - required BuildMode buildMode, - String? codesignIdentity, - Uri? yamlParentDirectory, - required FileSystem fileSystem, - required int targetAndroidNdkApi, -}) async { - const OSImpl targetOS = OSImpl.android; - final Uri buildUri_ = nativeAssetsBuildUri(projectUri, targetOS); - if (!await nativeBuildRequired(buildRunner)) { - final Uri nativeAssetsYaml = await writeNativeAssetsYaml( - KernelAssets(), - yamlParentDirectory ?? buildUri_, - fileSystem, - ); - return (nativeAssetsYaml, []); - } - - final List targets = androidArchs.map(_getNativeTarget).toList(); - final BuildModeImpl buildModeCli = - nativeAssetsBuildMode(buildMode); - final bool linkingEnabled = buildModeCli == BuildModeImpl.release; - - globals.logger - .printTrace('Building native assets for $targets $buildModeCli.'); - final List nativeAssets = []; - final Set dependencies = {}; - for (final Target target in targets) { - final BuildResult buildResult = await buildRunner.build( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl, - targetAndroidNdkApi: targetAndroidNdkApi, - linkingEnabled: linkingEnabled, - ); - ensureNativeAssetsBuildSucceed(buildResult); - nativeAssets.addAll(buildResult.assets); - dependencies.addAll(buildResult.dependencies); - if (linkingEnabled) { - final LinkResult linkResult = await buildRunner.link( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl, - targetAndroidNdkApi: targetAndroidNdkApi, - buildResult: buildResult, - ); - ensureNativeAssetsLinkSucceed(linkResult); - nativeAssets.addAll(linkResult.assets); - dependencies.addAll(linkResult.dependencies); - } - } - globals.logger.printTrace('Building native assets for $targets done.'); - final Map assetTargetLocations = - _assetTargetLocations(nativeAssets); - await _copyNativeAssetsAndroid(buildUri_, assetTargetLocations, fileSystem); - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(assetTargetLocations.values), - yamlParentDirectory ?? buildUri_, - fileSystem); - return (nativeAssetsUri, dependencies.toList()); -} - -Future _copyNativeAssetsAndroid( +Future copyNativeCodeAssetsAndroid( Uri buildUri, - Map assetTargetLocations, + Map assetTargetLocations, FileSystem fileSystem, ) async { if (assetTargetLocations.isNotEmpty) { @@ -154,7 +33,7 @@ Future _copyNativeAssetsAndroid( final Uri archUri = buildUri.resolve('jniLibs/lib/$jniArchDir/'); await fileSystem.directory(archUri).create(recursive: true); } - for (final MapEntry assetMapping + for (final MapEntry assetMapping in assetTargetLocations.entries) { final Uri source = assetMapping.key.file!; final Uri target = (assetMapping.value.path as KernelAssetAbsolutePath).uri; @@ -171,7 +50,7 @@ Future _copyNativeAssetsAndroid( } /// Get the [Target] for [androidArch]. -Target _getNativeTarget(AndroidArch androidArch) { +Target getNativeAndroidTarget(AndroidArch androidArch) { return switch (androidArch) { AndroidArch.armeabi_v7a => Target.androidArm, AndroidArch.arm64_v8a => Target.androidArm64, @@ -192,27 +71,27 @@ AndroidArch _getAndroidArch(Target target) { }; } -Map _assetTargetLocations( - List nativeAssets) { - return { - for (final AssetImpl asset in nativeAssets) +Map assetTargetLocationsAndroid( + List nativeAssets) { + return { + for (final NativeCodeAssetImpl asset in nativeAssets) asset: _targetLocationAndroid(asset), }; } /// Converts the `path` of [asset] as output from a `build.dart` invocation to /// the path used inside the Flutter app bundle. -KernelAsset _targetLocationAndroid(AssetImpl asset) { - final LinkModeImpl linkMode = (asset as NativeCodeAssetImpl).linkMode; +KernelAsset _targetLocationAndroid(NativeCodeAssetImpl asset) { + final LinkMode linkMode = asset.linkMode; final KernelAssetPath kernelAssetPath; switch (linkMode) { - case DynamicLoadingSystemImpl _: + case DynamicLoadingSystem _: kernelAssetPath = KernelAssetSystemPath(linkMode.uri); - case LookupInExecutableImpl _: + case LookupInExecutable _: kernelAssetPath = KernelAssetInExecutable(); - case LookupInProcessImpl _: + case LookupInProcess _: kernelAssetPath = KernelAssetInProcess(); - case DynamicLoadingBundledImpl _: + case DynamicLoadingBundled _: final String fileName = asset.file!.pathSegments.last; kernelAssetPath = KernelAssetAbsolutePath(Uri(path: fileName)); default: @@ -234,7 +113,6 @@ KernelAsset _targetLocationAndroid(AssetImpl asset) { /// Should only be invoked if a native assets build is performed. If the native /// assets feature is disabled, or none of the packages have native assets, a /// missing NDK is okay. -@override Future cCompilerConfigAndroid() async { final AndroidSdk? androidSdk = AndroidSdk.locateAndroidSdk(); if (androidSdk == null) { diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart index 427ccc45fd5..20b5cb7c89f 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart @@ -2,147 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:native_assets_builder/native_assets_builder.dart' - hide NativeAssetsBuildRunner; +import 'package:native_assets_builder/native_assets_builder.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../../../base/file_system.dart'; -import '../../../build_info.dart'; +import '../../../build_info.dart' hide BuildMode; +import '../../../build_info.dart' as build_info; import '../../../globals.dart' as globals; import '../macos/native_assets_host.dart'; -import '../native_assets.dart'; -/// 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 and -/// the Xcode project. -Future dryRunNativeAssetsIOS({ - required NativeAssetsBuildRunner buildRunner, - required Uri projectUri, - required FileSystem fileSystem, -}) async { - if (!await nativeBuildRequired(buildRunner)) { - return null; - } +// TODO(dcharkes): Fetch minimum iOS version from somewhere. https://github.com/flutter/flutter/issues/145104 +const int targetIOSVersion = 12; - final Uri buildUri = nativeAssetsBuildUri(projectUri, OSImpl.iOS); - final Iterable assetTargetLocations = await dryRunNativeAssetsIOSInternal( - fileSystem, - projectUri, - buildRunner, - ); - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(assetTargetLocations), - buildUri, - fileSystem, - ); - return nativeAssetsUri; -} - -Future> dryRunNativeAssetsIOSInternal( - FileSystem fileSystem, - Uri projectUri, - NativeAssetsBuildRunner buildRunner, -) async { - const OSImpl targetOS = OSImpl.iOS; - globals.logger.printTrace('Dry running native assets for $targetOS.'); - final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( - linkModePreference: LinkModePreferenceImpl.dynamic, - targetOS: targetOS, - workingDirectory: projectUri, - includeParentEnvironment: true, - ); - ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); - // No link hooks in JIT. - final List nativeAssets = buildDryRunResult.assets; - globals.logger.printTrace('Dry running native assets for $targetOS done.'); - return _assetTargetLocations(nativeAssets).values; -} - -/// Builds native assets. -Future> buildNativeAssetsIOS({ - required NativeAssetsBuildRunner buildRunner, - required List darwinArchs, - required EnvironmentType environmentType, - required Uri projectUri, - required BuildMode buildMode, - String? codesignIdentity, - required Uri yamlParentDirectory, - required FileSystem fileSystem, -}) async { - if (!await nativeBuildRequired(buildRunner)) { - await writeNativeAssetsYaml(KernelAssets(), yamlParentDirectory, fileSystem); - return []; - } - - final List targets = darwinArchs.map(_getNativeTarget).toList(); - final BuildModeImpl buildModeCli = nativeAssetsBuildMode(buildMode); - final bool linkingEnabled = buildModeCli == BuildModeImpl.release; - - const OSImpl targetOS = OSImpl.iOS; - final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); - final IOSSdkImpl iosSdk = _getIOSSdkImpl(environmentType); - - globals.logger.printTrace('Building native assets for $targets $buildModeCli.'); - final List nativeAssets = []; - final Set dependencies = {}; - for (final Target target in targets) { - final BuildResult buildResult = await buildRunner.build( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - targetIOSSdkImpl: iosSdk, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.cCompilerConfig, - // TODO(dcharkes): Fetch minimum iOS version from somewhere. https://github.com/flutter/flutter/issues/145104 - targetIOSVersion: 12, - linkingEnabled: linkingEnabled, - ); - ensureNativeAssetsBuildSucceed(buildResult); - nativeAssets.addAll(buildResult.assets); - dependencies.addAll(buildResult.dependencies); - if (linkingEnabled) { - final LinkResult linkResult = await buildRunner.link( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - targetIOSSdkImpl: iosSdk, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.cCompilerConfig, - buildResult: buildResult, - // TODO(dcharkes): Fetch minimum iOS version from somewhere. https://github.com/flutter/flutter/issues/145104 - targetIOSVersion: 12, - ); - ensureNativeAssetsLinkSucceed(linkResult); - nativeAssets.addAll(linkResult.assets); - dependencies.addAll(linkResult.dependencies); - } - } - globals.logger.printTrace('Building native assets for $targets done.'); - final Map> fatAssetTargetLocations = - _fatAssetTargetLocations(nativeAssets); - await _copyNativeAssetsIOS( - buildUri, - fatAssetTargetLocations, - codesignIdentity, - buildMode, - fileSystem, - ); - - final Map assetTargetLocations = - _assetTargetLocations(nativeAssets); - await writeNativeAssetsYaml( - KernelAssets(assetTargetLocations.values), - yamlParentDirectory, - fileSystem, - ); - return dependencies.toList(); -} - -IOSSdkImpl _getIOSSdkImpl(EnvironmentType environmentType) { +IOSSdkImpl getIOSSdk(EnvironmentType environmentType) { return switch (environmentType) { EnvironmentType.physical => IOSSdkImpl.iPhoneOS, EnvironmentType.simulator => IOSSdkImpl.iPhoneSimulator, @@ -150,7 +23,7 @@ IOSSdkImpl _getIOSSdkImpl(EnvironmentType environmentType) { } /// Extract the [Target] from a [DarwinArch]. -Target _getNativeTarget(DarwinArch darwinArch) { +Target getNativeIOSTarget(DarwinArch darwinArch) { return switch (darwinArch) { DarwinArch.armv7 => Target.iOSArm, DarwinArch.arm64 => Target.iOSArm64, @@ -158,13 +31,13 @@ Target _getNativeTarget(DarwinArch darwinArch) { }; } -Map> _fatAssetTargetLocations( - List nativeAssets) { +Map> fatAssetTargetLocationsIOS( + List nativeAssets) { final Set alreadyTakenNames = {}; - final Map> result = - >{}; + final Map> result = + >{}; final Map idToPath = {}; - for (final AssetImpl asset in nativeAssets) { + for (final NativeCodeAssetImpl asset in nativeAssets) { // Use same target path for all assets with the same id. final KernelAssetPath path = idToPath[asset.id] ?? _targetLocationIOS( @@ -172,23 +45,24 @@ Map> _fatAssetTargetLocations( alreadyTakenNames, ).path; idToPath[asset.id] = path; - result[path] ??= []; + result[path] ??= []; result[path]!.add(asset); } return result; } -Map _assetTargetLocations( - List nativeAssets) { +Map assetTargetLocationsIOS( + List nativeAssets) { final Set alreadyTakenNames = {}; final Map idToPath = {}; - final Map result = {}; - for (final AssetImpl asset in nativeAssets) { - final KernelAssetPath path = idToPath[asset.id] ?? - _targetLocationIOS(asset, alreadyTakenNames).path; + final Map result = + {}; + for (final NativeCodeAssetImpl asset in nativeAssets) { + final KernelAssetPath path = + idToPath[asset.id] ?? _targetLocationIOS(asset, alreadyTakenNames).path; idToPath[asset.id] = path; result[asset] = KernelAsset( - id: (asset as NativeCodeAssetImpl).id, + id: asset.id, target: Target.fromArchitectureAndOS(asset.architecture!, asset.os), path: path, ); @@ -196,17 +70,18 @@ Map _assetTargetLocations( return result; } -KernelAsset _targetLocationIOS(AssetImpl asset, Set alreadyTakenNames) { -final LinkModeImpl linkMode = (asset as NativeCodeAssetImpl).linkMode; -final KernelAssetPath kernelAssetPath; +KernelAsset _targetLocationIOS( + NativeCodeAssetImpl asset, Set alreadyTakenNames) { + final LinkMode linkMode = asset.linkMode; + final KernelAssetPath kernelAssetPath; switch (linkMode) { - case DynamicLoadingSystemImpl _: + case DynamicLoadingSystem _: kernelAssetPath = KernelAssetSystemPath(linkMode.uri); - case LookupInExecutableImpl _: + case LookupInExecutable _: kernelAssetPath = KernelAssetInExecutable(); - case LookupInProcessImpl _: + case LookupInProcess _: kernelAssetPath = KernelAssetInProcess(); - case DynamicLoadingBundledImpl _: + case DynamicLoadingBundled _: final String fileName = asset.file!.pathSegments.last; kernelAssetPath = KernelAssetAbsolutePath(frameworkUri( fileName, @@ -236,11 +111,11 @@ final KernelAssetPath kernelAssetPath; /// /// Code signing is also done here, so that it doesn't have to be done in /// in xcode_backend.dart. -Future _copyNativeAssetsIOS( +Future copyNativeCodeAssetsIOS( Uri buildUri, - Map> assetTargetLocations, + Map> assetTargetLocations, String? codesignIdentity, - BuildMode buildMode, + build_info.BuildMode buildMode, FileSystem fileSystem, ) async { if (assetTargetLocations.isNotEmpty) { @@ -250,11 +125,12 @@ Future _copyNativeAssetsIOS( final Map oldToNewInstallNames = {}; final List<(File, String, Directory)> dylibs = <(File, String, Directory)>[]; - for (final MapEntry> assetMapping + for (final MapEntry> assetMapping in assetTargetLocations.entries) { final Uri target = (assetMapping.key as KernelAssetAbsolutePath).uri; final List sources = [ - for (final AssetImpl source in assetMapping.value) fileSystem.file(source.file) + for (final NativeCodeAssetImpl source in assetMapping.value) + fileSystem.file(source.file) ]; final Uri targetUri = buildUri.resolveUri(target); final File dylibFile = fileSystem.file(targetUri); @@ -265,7 +141,8 @@ Future _copyNativeAssetsIOS( await lipoDylibs(dylibFile, sources); final String dylibFileName = dylibFile.basename; - final String newInstallName = '@rpath/$dylibFileName.framework/$dylibFileName'; + final String newInstallName = + '@rpath/$dylibFileName.framework/$dylibFileName'; final Set oldInstallNames = await getInstallNamesDylib(dylibFile); for (final String oldInstallName in oldInstallNames) { oldToNewInstallNames[oldInstallName] = newInstallName; diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart index ee4be081dc6..815146e96ff 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart @@ -2,70 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:native_assets_builder/native_assets_builder.dart' - hide NativeAssetsBuildRunner; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../../../base/common.dart'; import '../../../base/file_system.dart'; import '../../../base/io.dart'; -import '../../../build_info.dart'; import '../../../globals.dart' as globals; -import '../native_assets.dart'; - -/// 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 dryRunNativeAssetsLinux({ - required NativeAssetsBuildRunner buildRunner, - required Uri projectUri, - bool flutterTester = false, - required FileSystem fileSystem, -}) { - return dryRunNativeAssetsSingleArchitecture( - buildRunner: buildRunner, - projectUri: projectUri, - flutterTester: flutterTester, - fileSystem: fileSystem, - os: OSImpl.linux, - ); -} - -Future> dryRunNativeAssetsLinuxInternal( - FileSystem fileSystem, - Uri projectUri, - bool flutterTester, - NativeAssetsBuildRunner buildRunner, -) { - return dryRunNativeAssetsSingleArchitectureInternal( - fileSystem, - projectUri, - flutterTester, - buildRunner, - OSImpl.linux, - ); -} - -Future<(Uri? nativeAssetsYaml, List dependencies)> buildNativeAssetsLinux({ - required NativeAssetsBuildRunner buildRunner, - TargetPlatform? targetPlatform, - required Uri projectUri, - required BuildMode buildMode, - bool flutterTester = false, - Uri? yamlParentDirectory, - required FileSystem fileSystem, -}) { - return buildNativeAssetsSingleArchitecture( - buildRunner: buildRunner, - targetPlatform: targetPlatform, - projectUri: projectUri, - buildMode: buildMode, - flutterTester: flutterTester, - yamlParentDirectory: yamlParentDirectory, - fileSystem: fileSystem, - ); -} /// Flutter expects `clang++` to be on the path on Linux hosts. /// diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart index e6d681aa451..6878797a2c3 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart @@ -2,179 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:native_assets_builder/native_assets_builder.dart' - hide NativeAssetsBuildRunner; +import 'package:native_assets_builder/native_assets_builder.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../../../base/file_system.dart'; -import '../../../build_info.dart'; +import '../../../build_info.dart' hide BuildMode; +import '../../../build_info.dart' as build_info; import '../../../globals.dart' as globals; -import '../native_assets.dart'; import 'native_assets_host.dart'; -/// 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 and -/// the Xcode project. -Future dryRunNativeAssetsMacOS({ - required NativeAssetsBuildRunner buildRunner, - required Uri projectUri, - bool flutterTester = false, - required FileSystem fileSystem, -}) async { - if (!await nativeBuildRequired(buildRunner)) { - return null; - } - - final Uri buildUri = nativeAssetsBuildUri(projectUri, OSImpl.macOS); - final Iterable nativeAssetPaths = await dryRunNativeAssetsMacOSInternal( - fileSystem, - projectUri, - flutterTester, - buildRunner, - ); - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(nativeAssetPaths), - buildUri, - fileSystem, - ); - return nativeAssetsUri; -} - -Future> dryRunNativeAssetsMacOSInternal( - FileSystem fileSystem, - Uri projectUri, - bool flutterTester, - NativeAssetsBuildRunner buildRunner, -) async { - const OSImpl targetOS = OSImpl.macOS; - final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); - - globals.logger.printTrace('Dry running native assets for $targetOS.'); - final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( - linkModePreference: LinkModePreferenceImpl.dynamic, - targetOS: targetOS, - workingDirectory: projectUri, - includeParentEnvironment: true, - ); - ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); - // No link hooks in JIT mode. - final List nativeAssets = buildDryRunResult.assets; - globals.logger.printTrace('Dry running native assets for $targetOS done.'); - final Uri? absolutePath = flutterTester ? buildUri : null; - final Map assetTargetLocations = - _assetTargetLocations( - nativeAssets, - absolutePath, - ); - return assetTargetLocations.values; -} - -/// Builds native assets. -/// -/// If [darwinArchs] 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 dependencies)> buildNativeAssetsMacOS({ - required NativeAssetsBuildRunner buildRunner, - List? darwinArchs, - required Uri projectUri, - required BuildMode buildMode, - bool flutterTester = false, - String? codesignIdentity, - Uri? yamlParentDirectory, - required FileSystem fileSystem, -}) async { - const OSImpl targetOS = OSImpl.macOS; - final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); - if (!await nativeBuildRequired(buildRunner)) { - final Uri nativeAssetsYaml = await writeNativeAssetsYaml( - KernelAssets(), - yamlParentDirectory ?? buildUri, - fileSystem, - ); - return (nativeAssetsYaml, []); - } - - final List targets = darwinArchs != null - ? darwinArchs.map(_getNativeTarget).toList() - : [Target.current]; - final BuildModeImpl buildModeCli = - nativeAssetsBuildMode(buildMode); - final bool linkingEnabled = buildModeCli == BuildModeImpl.release; - - globals.logger - .printTrace('Building native assets for $targets $buildModeCli.'); - final List nativeAssets = []; - final Set dependencies = {}; - for (final Target target in targets) { - final BuildResult buildResult = await buildRunner.build( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.cCompilerConfig, - // TODO(dcharkes): Fetch minimum MacOS version from somewhere. https://github.com/flutter/flutter/issues/145104 - targetMacOSVersion: 13, - linkingEnabled: linkingEnabled, - ); - ensureNativeAssetsBuildSucceed(buildResult); - nativeAssets.addAll(buildResult.assets); - dependencies.addAll(buildResult.dependencies); - if (linkingEnabled) { - final LinkResult linkResult = await buildRunner.link( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.cCompilerConfig, - buildResult: buildResult, - // TODO(dcharkes): Fetch minimum MacOS version from somewhere. https://github.com/flutter/flutter/issues/145104 - targetMacOSVersion: 13, - ); - ensureNativeAssetsLinkSucceed(linkResult); - nativeAssets.addAll(linkResult.assets); - dependencies.addAll(linkResult.dependencies); - } - } - globals.logger.printTrace('Building native assets for $targets done.'); - final Uri? absolutePath = flutterTester ? buildUri : null; - final Map assetTargetLocations = - _assetTargetLocations(nativeAssets, absolutePath); - final Map> fatAssetTargetLocations = - _fatAssetTargetLocations(nativeAssets, absolutePath); - if (flutterTester) { - await _copyNativeAssetsMacOSFlutterTester( - buildUri, - fatAssetTargetLocations, - codesignIdentity, - buildMode, - fileSystem, - ); - } else { - await _copyNativeAssetsMacOS( - buildUri, - fatAssetTargetLocations, - codesignIdentity, - buildMode, - fileSystem, - ); - } - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(assetTargetLocations.values), - yamlParentDirectory ?? buildUri, - fileSystem, - ); - return (nativeAssetsUri, dependencies.toList()); -} +// TODO(dcharkes): Fetch minimum MacOS version from somewhere. https://github.com/flutter/flutter/issues/145104 +const int targetMacOSVersion = 13; /// Extract the [Target] from a [DarwinArch]. -Target _getNativeTarget(DarwinArch darwinArch) { +Target getNativeMacOSTarget(DarwinArch darwinArch) { return switch (darwinArch) { DarwinArch.arm64 => Target.macOSArm64, DarwinArch.x86_64 => Target.macOSX64, @@ -182,15 +24,15 @@ Target _getNativeTarget(DarwinArch darwinArch) { }; } -Map> _fatAssetTargetLocations( - List nativeAssets, +Map> fatAssetTargetLocationsMacOS( + List nativeAssets, Uri? absolutePath, ) { final Set alreadyTakenNames = {}; - final Map> result = - >{}; + final Map> result = + >{}; final Map idToPath = {}; - for (final AssetImpl asset in nativeAssets) { + for (final NativeCodeAssetImpl asset in nativeAssets) { // Use same target path for all assets with the same id. final KernelAssetPath path = idToPath[asset.id] ?? _targetLocationMacOS( @@ -199,25 +41,25 @@ Map> _fatAssetTargetLocations( alreadyTakenNames, ).path; idToPath[asset.id] = path; - result[path] ??= []; + result[path] ??= []; result[path]!.add(asset); } return result; } -Map _assetTargetLocations( - List nativeAssets, +Map assetTargetLocationsMacOS( + List nativeAssets, Uri? absolutePath, ) { final Set alreadyTakenNames = {}; final Map idToPath = {}; - final Map result = {}; - for (final AssetImpl asset in nativeAssets) { + final Map result = {}; + for (final NativeCodeAssetImpl asset in nativeAssets) { final KernelAssetPath path = idToPath[asset.id] ?? _targetLocationMacOS(asset, absolutePath, alreadyTakenNames).path; idToPath[asset.id] = path; result[asset] = KernelAsset( - id: (asset as NativeCodeAssetImpl).id, + id: asset.id, target: Target.fromArchitectureAndOS(asset.architecture!, asset.os), path: path, ); @@ -226,20 +68,20 @@ Map _assetTargetLocations( } KernelAsset _targetLocationMacOS( - AssetImpl asset, + NativeCodeAssetImpl asset, Uri? absolutePath, Set alreadyTakenNames, ) { - final LinkModeImpl linkMode = (asset as NativeCodeAssetImpl).linkMode; + final LinkMode linkMode = asset.linkMode; final KernelAssetPath kernelAssetPath; switch (linkMode) { - case DynamicLoadingSystemImpl _: + case DynamicLoadingSystem _: kernelAssetPath = KernelAssetSystemPath(linkMode.uri); - case LookupInExecutableImpl _: + case LookupInExecutable _: kernelAssetPath = KernelAssetInExecutable(); - case LookupInProcessImpl _: + case LookupInProcess _: kernelAssetPath = KernelAssetInProcess(); - case DynamicLoadingBundledImpl _: + case DynamicLoadingBundled _: final String fileName = asset.file!.pathSegments.last; Uri uri; if (absolutePath != null) { @@ -279,11 +121,11 @@ KernelAsset _targetLocationMacOS( /// /// Code signing is also done here, so that it doesn't have to be done in /// in macos_assemble.sh. -Future _copyNativeAssetsMacOS( +Future copyNativeCodeAssetsMacOS( Uri buildUri, - Map> assetTargetLocations, + Map> assetTargetLocations, String? codesignIdentity, - BuildMode buildMode, + build_info.BuildMode buildMode, FileSystem fileSystem, ) async { if (assetTargetLocations.isNotEmpty) { @@ -294,11 +136,11 @@ Future _copyNativeAssetsMacOS( final Map oldToNewInstallNames = {}; final List<(File, String, Directory)> dylibs = <(File, String, Directory)>[]; - for (final MapEntry> assetMapping + for (final MapEntry> assetMapping in assetTargetLocations.entries) { final Uri target = (assetMapping.key as KernelAssetAbsolutePath).uri; final List sources = [ - for (final AssetImpl source in assetMapping.value) fileSystem.file(source.file), + for (final NativeCodeAssetImpl source in assetMapping.value) fileSystem.file(source.file), ]; final Uri targetUri = buildUri.resolveUri(target); final String name = targetUri.pathSegments.last; @@ -373,11 +215,11 @@ Future _copyNativeAssetsMacOS( /// so that the referenced library can be found the dynamic linker. /// /// Code signing is also done here. -Future _copyNativeAssetsMacOSFlutterTester( +Future copyNativeCodeAssetsMacOSFlutterTester( Uri buildUri, - Map> assetTargetLocations, + Map> assetTargetLocations, String? codesignIdentity, - BuildMode buildMode, + build_info.BuildMode buildMode, FileSystem fileSystem, ) async { if (assetTargetLocations.isNotEmpty) { @@ -388,11 +230,11 @@ Future _copyNativeAssetsMacOSFlutterTester( final Map oldToNewInstallNames = {}; final List<(File, String)> dylibs = <(File, String)>[]; - for (final MapEntry> assetMapping + for (final MapEntry> assetMapping in assetTargetLocations.entries) { final Uri target = (assetMapping.key as KernelAssetAbsolutePath).uri; final List sources = [ - for (final AssetImpl source in assetMapping.value) fileSystem.file(source.file), + for (final NativeCodeAssetImpl source in assetMapping.value) fileSystem.file(source.file), ]; final Uri targetUri = buildUri.resolveUri(target); final File dylibFile = fileSystem.file(targetUri); diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets_host.dart b/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets_host.dart index 4ce136c74d5..503f9ae2a0c 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets_host.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets_host.dart @@ -4,13 +4,13 @@ // Shared logic between iOS and macOS implementations of native assets. -import 'package:native_assets_cli/native_assets_cli.dart' show Architecture; +import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../../../base/common.dart'; import '../../../base/file_system.dart'; import '../../../base/io.dart'; -import '../../../build_info.dart'; +import '../../../build_info.dart' as build_info; import '../../../convert.dart'; import '../../../globals.dart' as globals; @@ -99,13 +99,13 @@ Future setInstallNamesDylib( String newInstallName, Map oldToNewInstallNames, ) async { - final ProcessResult setInstallNamesResult = await globals.processManager.run( + final ProcessResult setInstallNamesResult = await globals.processManager.run( [ 'install_name_tool', '-id', newInstallName, - for (final MapEntry entry in oldToNewInstallNames.entries) - ...['-change', entry.key, entry.value], + for (final MapEntry entry in oldToNewInstallNames + .entries) ...['-change', entry.key, entry.value], dylibFile.path, ], ); @@ -135,18 +135,16 @@ Future> getInstallNamesDylib(File dylibFile) async { return { for (final List architectureSection - in parseOtoolArchitectureSections(installNameResult.stdout as String).values) + in parseOtoolArchitectureSections(installNameResult.stdout as String).values) // For each architecture, a separate install name is reported, which are // not necessarily the same. architectureSection.single, }; } - - Future codesignDylib( String? codesignIdentity, - BuildMode buildMode, + build_info.BuildMode buildMode, FileSystemEntity target, ) async { if (codesignIdentity == null || codesignIdentity.isEmpty) { @@ -157,7 +155,7 @@ Future codesignDylib( '--force', '--sign', codesignIdentity, - if (buildMode != BuildMode.release) ...[ + if (buildMode != build_info.BuildMode.release) ...[ // Mimic Xcode's timestamp codesigning behavior on non-release binaries. '--timestamp=none', ], diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart index 99e1a6f5225..fd8952e7d98 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart @@ -5,10 +5,7 @@ // 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' - as native_assets_builder show NativeAssetsBuildRunner; -import 'package:native_assets_builder/native_assets_builder.dart' - hide NativeAssetsBuildRunner; +import 'package:native_assets_builder/native_assets_builder.dart'; import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:package_config/package_config_types.dart'; @@ -18,9 +15,11 @@ import '../../base/file_system.dart'; import '../../base/logger.dart'; import '../../base/platform.dart'; import '../../build_info.dart' as build_info; +import '../../build_system/exceptions.dart'; import '../../cache.dart'; import '../../features.dart'; import '../../globals.dart' as globals; +import '../../macos/xcode.dart' as xcode; import '../../resident_runner.dart'; import '../../run_hot.dart'; import 'android/native_assets.dart'; @@ -30,11 +29,161 @@ import 'macos/native_assets.dart'; import 'macos/native_assets_host.dart'; import 'windows/native_assets.dart'; +/// The assets produced by a Dart build and the dependencies of those assets. +/// +/// If any of the dependencies change, then the Dart build should be performed +/// again. +final class DartBuildResult { + const DartBuildResult({required this.codeAssets, required this.dependencies}); + const DartBuildResult.empty() + : codeAssets = const [], + dependencies = const []; + + final List codeAssets; + final List dependencies; +} + +/// Invokes the build of all transitive Dart packages and prepares code assets +/// to be included in the native build. +Future<(DartBuildResult, Uri)> runFlutterSpecificDartBuild({ + required Map environmentDefines, + required FlutterNativeAssetsBuildRunner buildRunner, + required build_info.TargetPlatform targetPlatform, + required Uri projectUri, + Uri? nativeAssetsYamlUri, + required FileSystem fileSystem, +}) async { + final OS targetOS = _getNativeOSFromTargetPlatfrorm(targetPlatform); + final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); + final Directory buildDir = fileSystem.directory(buildUri); + + final bool flutterTester = targetPlatform == build_info.TargetPlatform.tester; + + if (nativeAssetsYamlUri == null) { + // Only `flutter test` uses the + // `build/native_assets//native_assets.yaml` file which uses absolute + // paths to the shared libraries. + // + // testCompilerBuildNativeAssets() passes `null` + assert(flutterTester); + nativeAssetsYamlUri ??= buildUri.resolve('native_assets.yaml'); + } + + if (!await buildDir.exists()) { + // Ensure the folder exists so the native build system can copy it even + // if there's no native assets. + await buildDir.create(recursive: true); + } + + if (!await _nativeBuildRequired(buildRunner)) { + await writeNativeAssetsYaml( + KernelAssets(), nativeAssetsYamlUri, fileSystem); + return (const DartBuildResult.empty(), nativeAssetsYamlUri); + } + + final build_info.BuildMode buildMode; + if (flutterTester) { + buildMode = build_info.BuildMode.debug; + } else { + final String? environmentBuildMode = + environmentDefines[build_info.kBuildMode]; + if (environmentBuildMode == null) { + throw MissingDefineException(build_info.kBuildMode, 'native_assets'); + } + buildMode = build_info.BuildMode.fromCliName(environmentBuildMode); + } + final List targets = flutterTester + ? [Target.current] + : _targetsForOS(targetPlatform, targetOS, environmentDefines); + final DartBuildResult result = targets.isEmpty + ? const DartBuildResult.empty() + : await _runDartBuild( + environmentDefines: environmentDefines, + buildRunner: buildRunner, + targets: targets, + projectUri: projectUri, + buildMode: _nativeAssetsBuildMode(buildMode), + fileSystem: fileSystem, + targetOS: targetOS); + + final String? codesignIdentity = + environmentDefines[build_info.kCodesignIdentity]; + + final Map assetTargetLocations = + _assetTargetLocationsForOS( + targetOS, result.codeAssets, flutterTester, buildUri); + await _copyNativeCodeAssetsForOS(targetOS, buildUri, buildMode, fileSystem, + assetTargetLocations, codesignIdentity, flutterTester); + final KernelAssets vmAssetMapping = + KernelAssets(assetTargetLocations.values.toList()); + await writeNativeAssetsYaml(vmAssetMapping, nativeAssetsYamlUri, fileSystem); + return (result, nativeAssetsYamlUri); +} + +Future runFlutterSpecificDartDryRunOnPlatforms({ + required Uri projectUri, + required FileSystem fileSystem, + required FlutterNativeAssetsBuildRunner buildRunner, + required List targetPlatforms, +}) async { + if (!await _nativeBuildRequired(buildRunner)) { + return null; + } + + final Map assetTargetLocations = + {}; + for (final build_info.TargetPlatform targetPlatform in targetPlatforms) { + // This dry-run functionality is only used in the `flutter run` + // implementation (not in `flutter build` or `flutter test`). + // + // Though we can end up with `flutterTester == true` if someone uses the + // `flutter-tester` device via `flutter run -d flutter-tester` (which mainly + // happens in tests) + final bool flutterTester = + targetPlatform == build_info.TargetPlatform.tester; + + final OSImpl targetOS = _getNativeOSFromTargetPlatfrorm(targetPlatform); + if (targetOS != OS.macOS && + targetOS != OS.windows && + targetOS != OS.linux && + targetOS != OS.android && + targetOS != OS.iOS) { + await ensureNoNativeAssetsOrOsIsSupported( + projectUri, + targetPlatform.toString(), + fileSystem, + buildRunner, + ); + } + + final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); + final DartBuildResult result = await _runDartDryRunBuild( + buildRunner: buildRunner, + projectUri: projectUri, + fileSystem: fileSystem, + targetOS: targetOS); + assetTargetLocations.addAll(_assetTargetLocationsForOS( + targetOS, result.codeAssets, flutterTester, buildUri)); + } + + final Uri buildUri = targetPlatforms.length == 1 + ? nativeAssetsBuildUri( + projectUri, _getNativeOSFromTargetPlatfrorm(targetPlatforms.single)) + : _buildUriMultiple(projectUri); + final Uri nativeAssetsYamlUri = buildUri.resolve('native_assets.yaml'); + await writeNativeAssetsYaml( + KernelAssets(assetTargetLocations.values.toList()), + nativeAssetsYamlUri, + fileSystem, + ); + return nativeAssetsYamlUri; +} + /// 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 { +abstract interface class FlutterNativeAssetsBuildRunner { /// Whether the project has a `.dart_tools/package_config.json`. /// /// If there is no package config, [packagesWithNativeAssets], [build], and @@ -67,15 +216,6 @@ abstract class NativeAssetsBuildRunner { required bool linkingEnabled, }); - /// Runs all [packagesWithNativeAssets] `link.dart` in dry run. - Future linkDryRun({ - required bool includeParentEnvironment, - required LinkModePreferenceImpl linkModePreference, - required OSImpl targetOS, - required Uri workingDirectory, - required BuildDryRunResult buildDryRunResult, - }); - /// Runs all [packagesWithNativeAssets] `link.dart`. Future link({ required bool includeParentEnvironment, @@ -99,8 +239,8 @@ abstract class NativeAssetsBuildRunner { } /// Uses `package:native_assets_builder` for its implementation. -class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { - NativeAssetsBuildRunnerImpl( +class FlutterNativeAssetsBuildRunnerImpl implements FlutterNativeAssetsBuildRunner { + FlutterNativeAssetsBuildRunnerImpl( this.projectUri, this.packageConfigPath, this.packageConfig, @@ -131,7 +271,7 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { late final Uri _dartExecutable = fileSystem.directory(Cache.flutterRoot).uri.resolve('bin/dart'); - late final native_assets_builder.NativeAssetsBuildRunner _buildRunner = native_assets_builder.NativeAssetsBuildRunner( + late final NativeAssetsBuildRunner _buildRunner = NativeAssetsBuildRunner( logger: _logger, dartExecutable: _dartExecutable, ); @@ -208,29 +348,6 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { ); } - - @override - Future linkDryRun({ - required bool includeParentEnvironment, - required LinkModePreferenceImpl linkModePreference, - required OSImpl targetOS, - required Uri workingDirectory, - required BuildDryRunResult buildDryRunResult, - }) { - final PackageLayout packageLayout = PackageLayout.fromPackageConfig( - packageConfig, - Uri.file(packageConfigPath), - ); - return _buildRunner.linkDryRun( - includeParentEnvironment: includeParentEnvironment, - linkModePreference: linkModePreference, - targetOS: targetOS, - workingDirectory: workingDirectory, - packageLayout: packageLayout, - buildDryRunResult: buildDryRunResult, - ); - } - @override Future link({ required bool includeParentEnvironment, @@ -277,7 +394,7 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { return cCompilerConfigWindows(); } if (globals.platform.isAndroid) { - throwToolExit('Should use ndkCCompilerConfigImpl for Android.'); + throwToolExit('Should use ndkCCompilerConfig for Android.'); } throwToolExit('Unknown target OS.'); }(); @@ -288,26 +405,25 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { }(); } -/// Write [assets] to `native_assets.yaml` in [yamlParentDirectory]. Future writeNativeAssetsYaml( KernelAssets assets, - Uri yamlParentDirectory, + Uri nativeAssetsYamlUri, FileSystem fileSystem, ) async { - globals.logger.printTrace('Writing native_assets.yaml.'); + globals.logger.printTrace('Writing native assets yaml to $nativeAssetsYamlUri.'); final String nativeAssetsDartContents = assets.toNativeAssetsFile(); - final Directory parentDirectory = fileSystem.directory(yamlParentDirectory); + final File nativeAssetsFile = fileSystem.file(nativeAssetsYamlUri); + final Directory parentDirectory = nativeAssetsFile.parent; 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. -BuildModeImpl nativeAssetsBuildMode(build_info.BuildMode buildMode) { +BuildModeImpl _nativeAssetsBuildMode(build_info.BuildMode buildMode) { switch (buildMode) { case build_info.BuildMode.debug: return BuildModeImpl.debug; @@ -324,7 +440,7 @@ BuildModeImpl nativeAssetsBuildMode(build_info.BuildMode buildMode) { /// /// Native asset builds cannot be run without a package config. If there is /// no package config, leave a logging trace about that. -Future _hasNoPackageConfig(NativeAssetsBuildRunner buildRunner) async { +Future _hasNoPackageConfig(FlutterNativeAssetsBuildRunner buildRunner) async { final bool packageConfigExists = await buildRunner.hasPackageConfig(); if (!packageConfigExists) { globals.logger.printTrace('No package config found. Skipping native assets compilation.'); @@ -332,7 +448,8 @@ Future _hasNoPackageConfig(NativeAssetsBuildRunner buildRunner) async { return !packageConfigExists; } -Future nativeBuildRequired(NativeAssetsBuildRunner buildRunner) async { + +Future _nativeBuildRequired(FlutterNativeAssetsBuildRunner buildRunner) async { if (await _hasNoPackageConfig(buildRunner)) { return false; } @@ -362,7 +479,7 @@ Future ensureNoNativeAssetsOrOsIsSupported( Uri workingDirectory, String os, FileSystem fileSystem, - NativeAssetsBuildRunner buildRunner, + FlutterNativeAssetsBuildRunner buildRunner, ) async { if (await _hasNoPackageConfig(buildRunner)) { return; @@ -389,6 +506,11 @@ Uri nativeAssetsBuildUri(Uri projectUri, OS os) { 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. class HotRunnerNativeAssetsBuilderImpl implements HotRunnerNativeAssetsBuilder { const HotRunnerNativeAssetsBuilderImpl(); @@ -401,351 +523,44 @@ class HotRunnerNativeAssetsBuilderImpl implements HotRunnerNativeAssetsBuilder { required PackageConfig packageConfig, required Logger logger, }) async { - final NativeAssetsBuildRunner buildRunner = NativeAssetsBuildRunnerImpl( + final FlutterNativeAssetsBuildRunner buildRunner = + FlutterNativeAssetsBuildRunnerImpl( projectUri, packageConfigPath, packageConfig, fileSystem, globals.logger, ); - return dryRunNativeAssets( + + // If `flutter run -d all` is used then we may have multiple OSes. + final List targetPlatforms = flutterDevices + .map((FlutterDevice d) => d.targetPlatform) + .nonNulls + .toList(); + + return runFlutterSpecificDartDryRunOnPlatforms( projectUri: projectUri, fileSystem: fileSystem, buildRunner: buildRunner, - flutterDevices: flutterDevices, + targetPlatforms: targetPlatforms, ); } } -/// 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 dryRunNativeAssets({ - required Uri projectUri, - required FileSystem fileSystem, - required NativeAssetsBuildRunner buildRunner, - required List flutterDevices, -}) async { - if (flutterDevices.length != 1) { - return dryRunNativeAssetsMultipleOSes( - 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_arm64: - 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: - nativeAssetsYaml = await dryRunNativeAssetsAndroid( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - 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 dryRunNativeAssetsMultipleOSes({ - required NativeAssetsBuildRunner buildRunner, - required Uri projectUri, - required FileSystem fileSystem, - required Iterable targetPlatforms, -}) async { - if (await nativeBuildRequired(buildRunner)) { - return null; - } - - final Uri buildUri = buildUriMultiple(projectUri); - final Iterable nativeAssetPaths = [ - if (targetPlatforms.contains(build_info.TargetPlatform.darwin) || - (targetPlatforms.contains(build_info.TargetPlatform.tester) && - OSImpl.current == OSImpl.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) && - OSImpl.current == OSImpl.linux)) - ...await dryRunNativeAssetsLinuxInternal( - fileSystem, - projectUri, - false, - buildRunner, - ), - if (targetPlatforms.contains(build_info.TargetPlatform.windows_arm64) || - targetPlatforms.contains(build_info.TargetPlatform.windows_x64) || - (targetPlatforms.contains(build_info.TargetPlatform.tester) && - OSImpl.current == OSImpl.windows)) - ...await dryRunNativeAssetsWindowsInternal( - fileSystem, - projectUri, - false, - buildRunner, - ), - if (targetPlatforms.contains(build_info.TargetPlatform.ios)) - ...await dryRunNativeAssetsIOSInternal( - fileSystem, - projectUri, - buildRunner, - ), - if (targetPlatforms.contains(build_info.TargetPlatform.android) || - targetPlatforms.contains(build_info.TargetPlatform.android_arm) || - targetPlatforms.contains(build_info.TargetPlatform.android_arm64) || - targetPlatforms.contains(build_info.TargetPlatform.android_x64) || - targetPlatforms.contains(build_info.TargetPlatform.android_x86)) - ...await dryRunNativeAssetsAndroidInternal( - fileSystem, - projectUri, - buildRunner, - ), - ]; - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(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) { +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 dryRunNativeAssetsSingleArchitecture({ - required NativeAssetsBuildRunner buildRunner, - required Uri projectUri, - bool flutterTester = false, - required FileSystem fileSystem, - required OSImpl os, -}) async { - if (!await nativeBuildRequired(buildRunner)) { - return null; - } - - final Uri buildUri = nativeAssetsBuildUri(projectUri, os); - final Iterable nativeAssetPaths = await dryRunNativeAssetsSingleArchitectureInternal( - fileSystem, - projectUri, - flutterTester, - buildRunner, - os, - ); - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(nativeAssetPaths.toList()), - buildUri, - fileSystem, - ); - return nativeAssetsUri; -} - -Future> dryRunNativeAssetsSingleArchitectureInternal( - FileSystem fileSystem, - Uri projectUri, - bool flutterTester, - NativeAssetsBuildRunner buildRunner, - OSImpl targetOS, -) async { - final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); - - globals.logger.printTrace('Dry running native assets for $targetOS.'); - - final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( - linkModePreference: LinkModePreferenceImpl.dynamic, - targetOS: targetOS, - workingDirectory: projectUri, - includeParentEnvironment: true, - ); - ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); - // No link hooks in JIT mode. - final List nativeAssets = buildDryRunResult.assets; - globals.logger.printTrace('Dry running native assets for $targetOS done.'); - final Uri? absolutePath = flutterTester ? buildUri : null; - final Map assetTargetLocations = - _assetTargetLocationsSingleArchitecture( - nativeAssets, - absolutePath, - ); - return assetTargetLocations.values; -} - -/// 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 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 OSImpl 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( - KernelAssets(), - yamlParentDirectory ?? buildUri, - fileSystem, - ); - return (nativeAssetsYaml, []); - } - - final BuildModeImpl buildModeCli = nativeAssetsBuildMode(buildMode); - final bool linkingEnabled = buildModeCli == BuildModeImpl.release; - - globals.logger.printTrace('Building native assets for $target $buildModeCli.'); - final BuildResult buildResult = await buildRunner.build( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.cCompilerConfig, - linkingEnabled: linkingEnabled, - ); - ensureNativeAssetsBuildSucceed(buildResult); - late final LinkResult linkResult; - if (linkingEnabled) { - linkResult = await buildRunner.link( - linkModePreference: LinkModePreferenceImpl.dynamic, - target: target, - buildMode: buildModeCli, - workingDirectory: projectUri, - includeParentEnvironment: true, - cCompilerConfig: await buildRunner.cCompilerConfig, - buildResult: buildResult, - ); - ensureNativeAssetsLinkSucceed(linkResult); - } - final List nativeAssets = [ - ...buildResult.assets, - if (linkingEnabled) ...linkResult.assets, - ]; - final Set dependencies = { - ...buildResult.dependencies, - if (linkingEnabled) ...linkResult.dependencies, - }; - globals.logger.printTrace('Building native assets for $target done.'); - final Uri? absolutePath = flutterTester ? buildUri : null; - final Map assetTargetLocations = - _assetTargetLocationsSingleArchitecture(nativeAssets, absolutePath); - await _copyNativeAssetsSingleArchitecture( - buildUri, - assetTargetLocations, - buildMode, - fileSystem, - ); - final Uri nativeAssetsUri = await writeNativeAssetsYaml( - KernelAssets(assetTargetLocations.values.toList()), - yamlParentDirectory ?? buildUri, - fileSystem, - ); - return (nativeAssetsUri, dependencies.toList()); -} - -Map _assetTargetLocationsSingleArchitecture( - List nativeAssets, +Map _assetTargetLocationsWindowsLinux( + List assets, Uri? absolutePath, ) { - return { - for (final AssetImpl asset in nativeAssets) + return { + for (final NativeCodeAssetImpl asset in assets) asset: _targetLocationSingleArchitecture( asset, absolutePath, @@ -754,22 +569,17 @@ Map _assetTargetLocationsSingleArchitecture( } KernelAsset _targetLocationSingleArchitecture( - AssetImpl asset, Uri? absolutePath) { - if (asset is! NativeCodeAssetImpl) { - throw Exception( - 'Unsupported asset type ${asset.runtimeType}', - ); - } - final LinkModeImpl linkMode = asset.linkMode; + NativeCodeAssetImpl asset, Uri? absolutePath) { + final LinkMode linkMode = asset.linkMode; final KernelAssetPath kernelAssetPath; switch (linkMode) { - case DynamicLoadingSystemImpl _: + case DynamicLoadingSystem _: kernelAssetPath = KernelAssetSystemPath(linkMode.uri); - case LookupInExecutableImpl _: + case LookupInExecutable _: kernelAssetPath = KernelAssetInExecutable(); - case LookupInProcessImpl _: + case LookupInProcess _: kernelAssetPath = KernelAssetInProcess(); - case DynamicLoadingBundledImpl _: + case DynamicLoadingBundled _: final String fileName = asset.file!.pathSegments.last; Uri uri; if (absolutePath != null) { @@ -794,10 +604,240 @@ KernelAsset _targetLocationSingleArchitecture( ); } -/// Extract the [Target] from a [TargetPlatform]. +Map _assetTargetLocationsForOS(OS targetOS, + List codeAssets, bool flutterTester, Uri buildUri) { + switch (targetOS) { + case OS.windows: + case OS.linux: + final Uri? absolutePath = flutterTester ? buildUri : null; + return _assetTargetLocationsWindowsLinux(codeAssets, absolutePath); + case OS.macOS: + final Uri? absolutePath = flutterTester ? buildUri : null; + return assetTargetLocationsMacOS(codeAssets, absolutePath); + case OS.iOS: + return assetTargetLocationsIOS(codeAssets); + case OS.android: + return assetTargetLocationsAndroid(codeAssets); + default: + throw UnimplementedError('This should be unreachable.'); + } +} + +Future _copyNativeCodeAssetsForOS( + OS targetOS, + Uri buildUri, + build_info.BuildMode buildMode, + FileSystem fileSystem, + Map assetTargetLocations, + String? codesignIdentity, + bool flutterTester) async { + final List codeAssets = + assetTargetLocations.keys.toList(); + switch (targetOS) { + case OS.windows: + case OS.linux: + assert(codesignIdentity == null); + await _copyNativeCodeAssetsToBundleOnWindowsLinux( + buildUri, + assetTargetLocations, + buildMode, + fileSystem, + ); + case OS.macOS: + if (flutterTester) { + await copyNativeCodeAssetsMacOSFlutterTester( + buildUri, + fatAssetTargetLocationsMacOS(codeAssets, buildUri), + codesignIdentity, + buildMode, + fileSystem, + ); + } else { + await copyNativeCodeAssetsMacOS( + buildUri, + fatAssetTargetLocationsMacOS(codeAssets, null), + codesignIdentity, + buildMode, + fileSystem, + ); + } + case OS.iOS: + await copyNativeCodeAssetsIOS( + buildUri, + fatAssetTargetLocationsIOS(codeAssets), + codesignIdentity, + buildMode, + fileSystem, + ); + case OS.android: + assert(codesignIdentity == null); + await copyNativeCodeAssetsAndroid( + buildUri, assetTargetLocations, fileSystem); + default: + throw StateError('This should be unreachable.'); + } +} + +/// Invokes the build of all transitive Dart packages. /// -/// Does not cover MacOS, iOS, and Android as these pass the architecture -/// in other enums. +/// This will invoke `hook/build.dart` and `hook/link.dart` (if applicable) for +/// all transitive dart packages that define such hooks. +Future _runDartBuild({ + required Map environmentDefines, + required FlutterNativeAssetsBuildRunner buildRunner, + required List targets, + required Uri projectUri, + required BuildModeImpl buildMode, + required FileSystem fileSystem, + required OS targetOS, +}) async { + final bool linkingEnabled = buildMode == BuildMode.release; + final String targetString = targets.length == 1 + ? targets.single.toString() + : targets.toList().toString(); + globals.logger + .printTrace('Building native assets for $targetString $buildMode.'); + final List assets = []; + final Set dependencies = {}; + final build_info.EnvironmentType? environmentType; + if (targetOS == OS.iOS) { + final String? sdkRoot = environmentDefines[build_info.kSdkRoot]; + if (sdkRoot == null) { + throw MissingDefineException(build_info.kSdkRoot, 'native_assets'); + } + environmentType = xcode.environmentTypeFromSdkroot(sdkRoot, fileSystem); + } else { + environmentType = null; + } + + final CCompilerConfigImpl cCompilerConfig = targetOS == OS.android + ? await buildRunner.ndkCCompilerConfigImpl + : await buildRunner.cCompilerConfig; + + final String? codesignIdentity = environmentDefines[build_info.kCodesignIdentity]; + assert(codesignIdentity == null || targetOS == OS.iOS || targetOS == OS.macOS); + + final int? androidNdkApi = targetOS == OS.android ? targetAndroidNdkApi(environmentDefines) : null; + final int? iOSVersion = targetOS == OS.iOS ? targetIOSVersion : null; + final int? macOSVersion = targetOS == OS.macOS ? targetMacOSVersion : null; + final IOSSdkImpl? iOSSdkImpl = targetOS == OS.iOS ? getIOSSdk(environmentType!) : null; + + for (final Target target in targets) { + final BuildResult buildResult = await buildRunner.build( + linkModePreference: LinkModePreferenceImpl.dynamic, + target: target, + buildMode: buildMode, + workingDirectory: projectUri, + includeParentEnvironment: true, + linkingEnabled: linkingEnabled, + cCompilerConfig: cCompilerConfig, + targetAndroidNdkApi: androidNdkApi, + targetIOSVersion: iOSVersion, + targetMacOSVersion: macOSVersion, + targetIOSSdkImpl: iOSSdkImpl, + ); + if (!buildResult.success) { + _throwNativeAssetsBuildFailed(); + } + assets.addAll(buildResult.assets); + dependencies.addAll(buildResult.dependencies); + if (linkingEnabled) { + final LinkResult linkResult = await buildRunner.link( + linkModePreference: LinkModePreferenceImpl.dynamic, + target: target, + buildMode: buildMode, + workingDirectory: projectUri, + includeParentEnvironment: true, + buildResult: buildResult, + cCompilerConfig: cCompilerConfig, + targetAndroidNdkApi: androidNdkApi, + targetIOSVersion: iOSVersion, + targetMacOSVersion: macOSVersion, + targetIOSSdkImpl: iOSSdkImpl, + ); + if (!linkResult.success) { + _throwNativeAssetsLinkFailed(); + } + assets.addAll(linkResult.assets); + dependencies.addAll(linkResult.dependencies); + } + } + + final List codeAssets = + assets.whereType().toList(); + globals.logger + .printTrace('Building native assets for $targetString $buildMode done.'); + return DartBuildResult(codeAssets: codeAssets, dependencies: dependencies.toList()); +} + +Future _runDartDryRunBuild({ + required FlutterNativeAssetsBuildRunner buildRunner, + required Uri projectUri, + required FileSystem fileSystem, + required OSImpl targetOS, +}) async { + globals.logger.printTrace('Dry running native assets for $targetOS.'); + final List assets = []; + final Set dependencies = {}; + + final BuildDryRunResult buildResult = await buildRunner.buildDryRun( + linkModePreference: LinkModePreferenceImpl.dynamic, + targetOS: targetOS, + workingDirectory: projectUri, + includeParentEnvironment: true, + ); + if (!buildResult.success) { + _throwNativeAssetsBuildDryRunFailed(); + } + assets.addAll(buildResult.assets); + + final List codeAssets = + assets.whereType().toList(); + globals.logger.printTrace('Dry running native assets for $targetOS done.'); + return DartBuildResult(codeAssets: codeAssets, dependencies: dependencies.toList()); +} + +List _targetsForOS(build_info.TargetPlatform targetPlatform, + OS targetOS, Map environmentDefines) { + switch (targetOS) { + case OS.linux: + return [_getNativeTarget(targetPlatform)]; + case OS.windows: + return [_getNativeTarget(targetPlatform)]; + case OS.macOS: + final List darwinArchs = + _emptyToNull(environmentDefines[build_info.kDarwinArchs]) + ?.split(' ') + .map(build_info.getDarwinArchForName) + .toList() ?? + [ + build_info.DarwinArch.x86_64, + build_info.DarwinArch.arm64 + ]; + return darwinArchs.map(getNativeMacOSTarget).toList(); + case OS.android: + final String? androidArchsEnvironment = + environmentDefines[build_info.kAndroidArchs]; + final List androidArchs = _androidArchs( + targetPlatform, + androidArchsEnvironment, + ); + return androidArchs.map(getNativeAndroidTarget).toList(); + case OS.iOS: + final List iosArchs = + _emptyToNull(environmentDefines[build_info.kIosArchs]) + ?.split(' ') + .map(build_info.getIOSArchForName) + .toList() ?? + [build_info.DarwinArch.arm64]; + return iosArchs.map(getNativeIOSTarget).toList(); + default: + // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757 + // Write the file we claim to have in the [outputs]. + return []; + } +} + Target _getNativeTarget(build_info.TargetPlatform targetPlatform) { switch (targetPlatform) { case build_info.TargetPlatform.linux_x64: @@ -823,57 +863,124 @@ Target _getNativeTarget(build_info.TargetPlatform targetPlatform) { } } -Future _copyNativeAssetsSingleArchitecture( +Future _copyNativeCodeAssetsToBundleOnWindowsLinux( Uri buildUri, - Map assetTargetLocations, + Map assetTargetLocations, build_info.BuildMode buildMode, FileSystem fileSystem, ) async { + globals.logger.printTrace('copyNativeCodeAssetsToBundleOnWindowsLinux()'); 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 assetMapping in assetTargetLocations.entries) { + for (final MapEntry assetMapping in assetTargetLocations.entries) { final Uri source = assetMapping.key.file!; final Uri target = (assetMapping.value.path as KernelAssetAbsolutePath).uri; final Uri targetUri = buildUri.resolveUri(target); final String targetFullPath = targetUri.toFilePath(); await fileSystem.file(source).copy(targetFullPath); + globals.logger.printTrace('copyNativeCodeAssetsToBundleOnWindowsLinux(): copied $source to $targetFullPath'); } globals.logger.printTrace('Copying native assets done.'); } } -void ensureNativeAssetsBuildDryRunSucceed(BuildDryRunResult result) { - if (!result.success) { - throwToolExit( - 'Building (dry run) native assets failed. See the logs for more details.', - ); +Never _throwNativeAssetsBuildDryRunFailed() { + throwToolExit( + 'Building (dry run) native assets failed. See the logs for more details.', + ); +} + +Never _throwNativeAssetsBuildFailed() { + throwToolExit( + 'Building native assets failed. See the logs for more details.', + ); +} + +Never _throwNativeAssetsLinkFailed() { + throwToolExit( + 'Linking native assets failed. See the logs for more details.', + ); +} + + +OSImpl _getNativeOSFromTargetPlatfrorm(build_info.TargetPlatform platform) { + switch (platform) { + case build_info.TargetPlatform.ios: + return OSImpl.iOS; + case build_info.TargetPlatform.darwin: + return OSImpl.macOS; + case build_info.TargetPlatform.linux_x64: + case build_info.TargetPlatform.linux_arm64: + return OSImpl.linux; + case build_info.TargetPlatform.windows_x64: + case build_info.TargetPlatform.windows_arm64: + return OSImpl.windows; + case build_info.TargetPlatform.fuchsia_arm64: + case build_info.TargetPlatform.fuchsia_x64: + return OSImpl.fuchsia; + case build_info.TargetPlatform.android: + case build_info.TargetPlatform.android_arm: + case build_info.TargetPlatform.android_arm64: + case build_info.TargetPlatform.android_x64: + case build_info.TargetPlatform.android_x86: + return OSImpl.android; + case build_info.TargetPlatform.tester: + if (const LocalPlatform().isMacOS) { + return OSImpl.macOS; + } else if (const LocalPlatform().isLinux) { + return OSImpl.linux; + } else if (const LocalPlatform().isWindows) { + return OSImpl.windows; + } else { + throw StateError('Unknown operating system'); + } + case build_info.TargetPlatform.web_javascript: + throw StateError('No dart builds for web yet.'); } } -void ensureNativeAssetsBuildSucceed(BuildResult result) { - if (!result.success) { - throwToolExit( - 'Building native assets failed. See the logs for more details.', - ); +List _androidArchs( + build_info.TargetPlatform targetPlatform, + String? androidArchsEnvironment, +) { + switch (targetPlatform) { + case build_info.TargetPlatform.android_arm: + return [build_info.AndroidArch.armeabi_v7a]; + case build_info.TargetPlatform.android_arm64: + return [build_info.AndroidArch.arm64_v8a]; + case build_info.TargetPlatform.android_x64: + return [build_info.AndroidArch.x86_64]; + case build_info.TargetPlatform.android_x86: + return [build_info.AndroidArch.x86]; + case build_info.TargetPlatform.android: + if (androidArchsEnvironment == null) { + throw MissingDefineException(build_info.kAndroidArchs, 'native_assets'); + } + return androidArchsEnvironment + .split(' ') + .map(build_info.getAndroidArchForName) + .toList(); + case build_info.TargetPlatform.darwin: + case build_info.TargetPlatform.fuchsia_arm64: + case build_info.TargetPlatform.fuchsia_x64: + case build_info.TargetPlatform.ios: + case build_info.TargetPlatform.linux_arm64: + case build_info.TargetPlatform.linux_x64: + case build_info.TargetPlatform.tester: + case build_info.TargetPlatform.web_javascript: + case build_info.TargetPlatform.windows_x64: + case build_info.TargetPlatform.windows_arm64: + throwToolExit('Unsupported Android target platform: $targetPlatform.'); } } -void ensureNativeAssetsLinkDryRunSucceed(LinkDryRunResult result) { - if (!result.success) { - throwToolExit( - 'Linking (dry run) native assets failed. See the logs for more details.', - ); - } -} - -void ensureNativeAssetsLinkSucceed(LinkResult result) { - if (!result.success) { - throwToolExit( - 'Linking native assets failed. See the logs for more details.', - ); +String? _emptyToNull(String? input) { + if (input == null || input.isEmpty) { + return null; } + return input; } diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart index d62ca7c76c3..423c297670b 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart @@ -6,16 +6,12 @@ import 'package:native_assets_cli/native_assets_cli.dart'; -import '../../../base/os.dart'; import '../../../base/platform.dart'; import '../../../build_info.dart'; import '../../../globals.dart' as globals; import '../../../native_assets.dart'; import '../../../project.dart'; -import '../linux/native_assets.dart'; -import '../macos/native_assets.dart'; import '../native_assets.dart'; -import '../windows/native_assets.dart'; class TestCompilerNativeAssetsBuilderImpl implements TestCompilerNativeAssetsBuilder { @@ -31,57 +27,36 @@ class TestCompilerNativeAssetsBuilderImpl } Future testCompilerBuildNativeAssets(BuildInfo buildInfo) async { - Uri? nativeAssetsYaml; if (!buildInfo.buildNativeAssets) { - nativeAssetsYaml = null; - } else { - final Uri projectUri = FlutterProject.current().directory.uri; - final NativeAssetsBuildRunner buildRunner = NativeAssetsBuildRunnerImpl( - projectUri, - buildInfo.packageConfigPath, - buildInfo.packageConfig, - globals.fs, - globals.logger, - ); - if (globals.platform.isMacOS) { - (nativeAssetsYaml, _) = await buildNativeAssetsMacOS( - buildMode: buildInfo.mode, - projectUri: projectUri, - flutterTester: true, - fileSystem: globals.fs, - buildRunner: buildRunner, - ); - } else if (globals.platform.isLinux) { - (nativeAssetsYaml, _) = await buildNativeAssetsLinux( - buildMode: buildInfo.mode, - projectUri: projectUri, - flutterTester: true, - fileSystem: globals.fs, - buildRunner: buildRunner, - ); - } else if (globals.platform.isWindows) { - final TargetPlatform targetPlatform; - if (globals.os.hostPlatform == HostPlatform.windows_x64) { - targetPlatform = TargetPlatform.windows_x64; - } else { - targetPlatform = TargetPlatform.windows_arm64; - } - (nativeAssetsYaml, _) = await buildNativeAssetsWindows( - buildMode: buildInfo.mode, - targetPlatform: targetPlatform, - projectUri: projectUri, - flutterTester: true, - fileSystem: globals.fs, - buildRunner: buildRunner, - ); - } else { - await ensureNoNativeAssetsOrOsIsSupported( - projectUri, - const LocalPlatform().operatingSystem, - globals.fs, - buildRunner, - ); - } + return null; } + final Uri projectUri = FlutterProject.current().directory.uri; + final FlutterNativeAssetsBuildRunner buildRunner = FlutterNativeAssetsBuildRunnerImpl( + projectUri, + buildInfo.packageConfigPath, + buildInfo.packageConfig, + globals.fs, + globals.logger, + ); + + if (!globals.platform.isMacOS && + !globals.platform.isLinux && + !globals.platform.isWindows) { + await ensureNoNativeAssetsOrOsIsSupported( + projectUri, + const LocalPlatform().operatingSystem, + globals.fs, + buildRunner, + ); + return null; + } + final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: buildInfo.mode.cliName, + }, + buildRunner: buildRunner, + targetPlatform: TargetPlatform.tester, + projectUri: projectUri, + fileSystem: globals.fs); return nativeAssetsYaml; } diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/windows/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/windows/native_assets.dart index 11703c4f288..f61b1bc76d3 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/windows/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/windows/native_assets.dart @@ -2,70 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:native_assets_builder/native_assets_builder.dart' - hide NativeAssetsBuildRunner; import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import '../../../base/file_system.dart'; -import '../../../build_info.dart'; import '../../../globals.dart' as globals; import '../../../windows/visual_studio.dart'; -import '../native_assets.dart'; - -/// 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 dryRunNativeAssetsWindows({ - required NativeAssetsBuildRunner buildRunner, - required Uri projectUri, - bool flutterTester = false, - required FileSystem fileSystem, -}) { - return dryRunNativeAssetsSingleArchitecture( - buildRunner: buildRunner, - projectUri: projectUri, - flutterTester: flutterTester, - fileSystem: fileSystem, - os: OSImpl.windows, - ); -} - -Future> dryRunNativeAssetsWindowsInternal( - FileSystem fileSystem, - Uri projectUri, - bool flutterTester, - NativeAssetsBuildRunner buildRunner, -) { - return dryRunNativeAssetsSingleArchitectureInternal( - fileSystem, - projectUri, - flutterTester, - buildRunner, - OSImpl.windows, - ); -} - -Future<(Uri? nativeAssetsYaml, List dependencies)> - buildNativeAssetsWindows({ - required NativeAssetsBuildRunner buildRunner, - TargetPlatform? targetPlatform, - required Uri projectUri, - required BuildMode buildMode, - bool flutterTester = false, - Uri? yamlParentDirectory, - required FileSystem fileSystem, -}) { - return buildNativeAssetsSingleArchitecture( - buildRunner: buildRunner, - targetPlatform: targetPlatform, - projectUri: projectUri, - buildMode: buildMode, - flutterTester: flutterTester, - yamlParentDirectory: yamlParentDirectory, - fileSystem: fileSystem, - ); -} Future cCompilerConfigWindows() async { final VisualStudio visualStudio = VisualStudio( diff --git a/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart index 3d0a03251d0..587a2a47a1f 100644 --- a/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart @@ -5,6 +5,7 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:file_testing/file_testing.dart'; +import 'package:flutter_tools/src/android/gradle_utils.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -14,7 +15,7 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals.dart' as globals; -import 'package:flutter_tools/src/isolated/native_assets/android/native_assets.dart'; +import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:package_config/package_config_types.dart'; @@ -48,173 +49,6 @@ void main() { projectUri = environment.projectDir.uri; }); - testUsingContext('dry run with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - expect( - await dryRunNativeAssetsAndroid( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ), - null, - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('build with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await buildNativeAssetsAndroid( - androidArchs: [AndroidArch.arm64_v8a], - targetAndroidNdkApi: 21, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('dry run with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsAndroid( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('dry run with assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.so'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.so'), - ), - ], - ), - ); - final Uri? nativeAssetsYaml = await dryRunNativeAssetsAndroid( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - expect( - (globals.logger as BufferLogger).traceText, - stringContainsInOrder([ - 'Dry running native assets for android.', - 'Dry running native assets for android done.', - ]), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/android/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - contains('package:bar/bar.dart'), - ); - expect(buildRunner.buildDryRunInvocations, 1); - expect(buildRunner.linkDryRunInvocations, 0); - }); - - testUsingContext('build with assets but not enabled', () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsAndroid( - androidArchs: [AndroidArch.arm64_v8a], - targetAndroidNdkApi: 21, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('build no assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - await buildNativeAssetsAndroid( - androidArchs: [AndroidArch.arm64_v8a], - targetAndroidNdkApi: 21, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ); - expect( - environment.buildDir.childFile('native_assets.yaml'), - exists, - ); - }); - for (final BuildMode buildMode in [ BuildMode.debug, BuildMode.release, @@ -227,17 +61,17 @@ void main() { }, () async { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; await packageConfig.parent.create(); await packageConfig.create(); final File dylibAfterCompiling = fileSystem.file('libbar.so'); // The mock doesn't create the file, so create it here. await dylibAfterCompiling.create(); - final FakeNativeAssetsBuildRunner buildRunner = - FakeNativeAssetsBuildRunner( + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [ Package('bar', projectUri), ], - buildResult: FakeNativeAssetsBuilderResult( + buildResult: FakeFlutterNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -249,22 +83,25 @@ void main() { ], ), ); - await buildNativeAssetsAndroid( - androidArchs: [AndroidArch.arm64_v8a], - targetAndroidNdkApi: 21, + await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: buildMode.cliName, + kMinSdkVersion: minSdkVersion, + }, + targetPlatform: TargetPlatform.android_arm64, projectUri: projectUri, - buildMode: buildMode, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, stringContainsInOrder([ - 'Building native assets for [android_arm64] $buildMode.', - 'Building native assets for [android_arm64] done.', + 'Building native assets for android_arm64 $buildMode.', + 'Building native assets for android_arm64 $buildMode done.', ]), ); + expect( environment.buildDir.childFile('native_assets.yaml'), exists, @@ -287,12 +124,16 @@ void main() { }, () async { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; await packageConfig.create(recursive: true); - await buildNativeAssetsAndroid( - androidArchs: [AndroidArch.x86_64], - targetAndroidNdkApi: 21, + await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: BuildMode.debug.cliName, + kMinSdkVersion: minSdkVersion, + }, + targetPlatform: TargetPlatform.android_x64, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, projectUri: projectUri, - buildMode: BuildMode.debug, fileSystem: fileSystem, buildRunner: _BuildRunnerWithoutNdk(), ); @@ -308,16 +149,19 @@ void main() { }, () async { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; await packageConfig.parent.create(); await packageConfig.create(); expect( - () => buildNativeAssetsAndroid( - androidArchs: [AndroidArch.arm64_v8a], - targetAndroidNdkApi: 21, + () => runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: BuildMode.debug.cliName, + kMinSdkVersion: minSdkVersion, + }, + targetPlatform: TargetPlatform.android_arm64, projectUri: projectUri, - buildMode: BuildMode.debug, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, buildRunner: _BuildRunnerWithoutNdk( packagesWithNativeAssetsResult: [ Package('bar', projectUri), @@ -330,73 +174,9 @@ void main() { ); }); - testUsingContext('Native assets dry run error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsAndroid( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building (dry run) native assets failed. See the logs for more details.', - ), - ); - }); - - testUsingContext('Native assets build error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - for (final String hook in ['Building', 'Linking']) { - expect( - () => buildNativeAssetsAndroid( - androidArchs: [AndroidArch.arm64_v8a], - targetAndroidNdkApi: 21, - projectUri: projectUri, - buildMode: BuildMode.release, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: FakeNativeAssetsBuilderResult( - success: hook != 'Building', - ), - linkResult: FakeNativeAssetsBuilderResult( - success: hook != 'Linking', - ), - ), - ), - throwsToolExit( - message: - '$hook native assets failed. See the logs for more details.', - ), - ); - } - }); } -class _BuildRunnerWithoutNdk extends FakeNativeAssetsBuildRunner { +class _BuildRunnerWithoutNdk extends FakeFlutterNativeAssetsBuildRunner { _BuildRunnerWithoutNdk({ super.packagesWithNativeAssetsResult = const [], }); diff --git a/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart index c8210fe7a9e..fc687348226 100644 --- a/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart @@ -76,21 +76,29 @@ void main() { iosEnvironment.defines.remove(kIosArchs); - final NativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(); + final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(); await NativeAssets(buildRunner: buildRunner).build(iosEnvironment); final File nativeAssetsYaml = iosEnvironment.buildDir.childFile('native_assets.yaml'); + final File depsFile = iosEnvironment.buildDir.childFile('native_assets.d'); expect(depsFile, exists); expect(nativeAssetsYaml, exists); }); - testUsingContext('NativeAssets throws error if missing sdk root', () async { + testUsingContext('NativeAssets throws error if missing sdk root', overrides: { + FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), + }, () async { await createPackageConfig(iosEnvironment); + final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('foo', iosEnvironment.projectDir.uri), + ]); + iosEnvironment.defines.remove(kSdkRoot); - expect(const NativeAssets().build(iosEnvironment), throwsA(isA())); + expect(NativeAssets(buildRunner: buildRunner).build(iosEnvironment), throwsA(isA())); }); // The NativeAssets Target should _always_ be creating a yaml an d file. @@ -109,7 +117,7 @@ void main() { () async { await createPackageConfig(iosEnvironment); - final NativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(); + final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(); await NativeAssets(buildRunner: buildRunner).build(iosEnvironment); expect(iosEnvironment.buildDir.childFile('native_assets.d'), exists); @@ -182,9 +190,9 @@ void main() { () async { await createPackageConfig(iosEnvironment); - final NativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + final FlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [Package('foo', iosEnvironment.buildDir.uri)], - buildResult: FakeNativeAssetsBuilderResult( + buildResult: FakeFlutterNativeAssetsBuilderResult( assets: [ native_assets_cli.NativeCodeAssetImpl( id: 'package:foo/foo.dart', @@ -243,11 +251,11 @@ void main() { await createPackageConfig(androidEnvironment); await fileSystem.file('libfoo.so').create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [ Package('foo', androidEnvironment.buildDir.uri) ], - buildResult: FakeNativeAssetsBuilderResult( + buildResult: FakeFlutterNativeAssetsBuilderResult( assets: [ if (hasAssets) native_assets_cli.NativeCodeAssetImpl( diff --git a/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart b/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart index 20bf8d1e1ce..339f6a7faa8 100644 --- a/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart +++ b/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart @@ -4,6 +4,7 @@ import 'package:file/file.dart'; import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'; import 'package:flutter_tools/src/resident_runner.dart'; import 'package:flutter_tools/src/run_hot.dart'; @@ -14,15 +15,15 @@ import 'package:package_config/package_config_types.dart'; /// Mocks all logic instead of using `package:native_assets_builder`, which /// relies on doing process calls to `pub` and the local file system. -class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { - FakeNativeAssetsBuildRunner({ +class FakeFlutterNativeAssetsBuildRunner implements FlutterNativeAssetsBuildRunner { + FakeFlutterNativeAssetsBuildRunner({ this.hasPackageConfigResult = true, this.packagesWithNativeAssetsResult = const [], this.onBuild, - this.buildDryRunResult = const FakeNativeAssetsBuilderResult(), - this.buildResult = const FakeNativeAssetsBuilderResult(), - this.linkResult = const FakeNativeAssetsBuilderResult(), - this.linkDryRunResult = const FakeNativeAssetsBuilderResult(), + this.buildDryRunResult = const FakeFlutterNativeAssetsBuilderResult(), + this.buildResult = const FakeFlutterNativeAssetsBuilderResult(), + this.linkResult = const FakeFlutterNativeAssetsBuilderResult(), + this.linkDryRunResult = const FakeFlutterNativeAssetsBuilderResult(), CCompilerConfigImpl? cCompilerConfigResult, CCompilerConfigImpl? ndkCCompilerConfigImplResult, }) : cCompilerConfigResult = cCompilerConfigResult ?? CCompilerConfigImpl(), @@ -96,18 +97,6 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { return buildDryRunResult; } - @override - Future linkDryRun({ - required bool includeParentEnvironment, - required LinkModePreferenceImpl linkModePreference, - required OSImpl targetOS, - required Uri workingDirectory, - required native_assets_builder.BuildDryRunResult buildDryRunResult, - }) async { - linkDryRunInvocations++; - return linkDryRunResult; - } - @override Future hasPackageConfig() async { hasPackageConfigInvocations++; @@ -129,13 +118,13 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { cCompilerConfigResult; } -final class FakeNativeAssetsBuilderResult +final class FakeFlutterNativeAssetsBuilderResult implements native_assets_builder.BuildResult, native_assets_builder.BuildDryRunResult, native_assets_builder.LinkResult, native_assets_builder.LinkDryRunResult { - const FakeNativeAssetsBuilderResult({ + const FakeFlutterNativeAssetsBuilderResult({ this.assets = const [], this.assetsForLinking = const >{}, this.dependencies = const [], @@ -158,7 +147,7 @@ final class FakeNativeAssetsBuilderResult class FakeHotRunnerNativeAssetsBuilder implements HotRunnerNativeAssetsBuilder { FakeHotRunnerNativeAssetsBuilder(this.buildRunner); - final NativeAssetsBuildRunner buildRunner; + final FlutterNativeAssetsBuildRunner buildRunner; @override Future dryRun({ @@ -169,11 +158,15 @@ class FakeHotRunnerNativeAssetsBuilder implements HotRunnerNativeAssetsBuilder { required PackageConfig packageConfig, required Logger logger, }) { - return dryRunNativeAssets( + final List targetPlatforms = flutterDevices + .map((FlutterDevice d) => d.targetPlatform) + .nonNulls + .toList(); + return runFlutterSpecificDartDryRunOnPlatforms( projectUri: projectUri, fileSystem: fileSystem, buildRunner: buildRunner, - flutterDevices: flutterDevices, + targetPlatforms: targetPlatforms, ); } } diff --git a/packages/flutter_tools/test/general.shard/isolated/hot_test.dart b/packages/flutter_tools/test/general.shard/isolated/hot_test.dart index ff10468eec6..64aa9fbf787 100644 --- a/packages/flutter_tools/test/general.shard/isolated/hot_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/hot_test.dart @@ -58,11 +58,11 @@ void main() { (fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri'); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [ Package('bar', fileSystem.currentDirectory.uri), ], - buildDryRunResult: FakeNativeAssetsBuilderResult( + buildDryRunResult: FakeFlutterNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -127,11 +127,11 @@ void main() { (fakeFlutterDevice.devFS! as FakeDevFs).baseUri = Uri.parse('file:///base_uri'); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [ Package('bar', fileSystem.currentDirectory.uri), ], - buildDryRunResult: FakeNativeAssetsBuilderResult( + buildDryRunResult: FakeFlutterNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', diff --git a/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart index 21bd6e6944b..da875d8d917 100644 --- a/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart @@ -13,9 +13,8 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals.dart' as globals; -import 'package:flutter_tools/src/isolated/native_assets/ios/native_assets.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart' - hide Target; +import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart' hide Target; import 'package:native_assets_cli/native_assets_cli_internal.dart' as native_assets_cli; import 'package:package_config/package_config_types.dart'; @@ -50,174 +49,6 @@ void main() { projectUri = environment.projectDir.uri; }); - testUsingContext('dry run with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - expect( - await dryRunNativeAssetsIOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ), - null, - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('build with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await buildNativeAssetsIOS( - darwinArchs: [DarwinArch.arm64], - environmentType: EnvironmentType.simulator, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('dry run with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsIOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('dry run with assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.dylib'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.dylib'), - ), - ], - ), - ); - final Uri? nativeAssetsYaml = await dryRunNativeAssetsIOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - expect( - (globals.logger as BufferLogger).traceText, - stringContainsInOrder([ - 'Dry running native assets for ios.', - 'Dry running native assets for ios done.', - ]), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/ios/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - contains('package:bar/bar.dart'), - ); - expect(buildRunner.buildDryRunInvocations, 1); - expect(buildRunner.linkDryRunInvocations, 0); - }); - - testUsingContext('build with assets but not enabled', () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsIOS( - darwinArchs: [DarwinArch.arm64], - environmentType: EnvironmentType.simulator, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('build no assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - await buildNativeAssetsIOS( - darwinArchs: [DarwinArch.arm64], - environmentType: EnvironmentType.simulator, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ); - expect( - environment.buildDir.childFile('native_assets.yaml'), - exists, - ); - }); - - for (final BuildMode buildMode in [ BuildMode.debug, BuildMode.release, @@ -330,15 +161,15 @@ void main() { } final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; await packageConfig.parent.create(); await packageConfig.create(); - final FakeNativeAssetsBuildRunner buildRunner = - FakeNativeAssetsBuildRunner( + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [ Package('bar', projectUri), ], onBuild: (native_assets_cli.Target target) => - FakeNativeAssetsBuilderResult( + FakeFlutterNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -355,22 +186,25 @@ void main() { file: Uri.file('${target.architecture}/libbuz.dylib'), ), ], - ), + ), ); - await buildNativeAssetsIOS( - darwinArchs: [DarwinArch.arm64, DarwinArch.x86_64], - environmentType: EnvironmentType.simulator, + await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: buildMode.cliName, + kSdkRoot: '.../iPhone Simulator', + kIosArchs: 'arm64 x86_64', + }, + targetPlatform: TargetPlatform.ios, projectUri: projectUri, - buildMode: buildMode, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, stringContainsInOrder([ 'Building native assets for [ios_arm64, ios_x64] $buildMode.', - 'Building native assets for [ios_arm64, ios_x64] done.', + 'Building native assets for [ios_arm64, ios_x64] $buildMode done.', ]), ); expect( @@ -385,69 +219,4 @@ void main() { ); }); } - - testUsingContext('Native assets dry run error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsIOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building (dry run) native assets failed. See the logs for more details.', - ), - ); - }); - - testUsingContext('Native assets build error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - for (final String hook in ['Building', 'Linking']) { - expect( - () => buildNativeAssetsIOS( - darwinArchs: [DarwinArch.arm64], - environmentType: EnvironmentType.simulator, - projectUri: projectUri, - buildMode: BuildMode.release, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: FakeNativeAssetsBuilderResult( - success: hook != 'Building', - ), - linkResult: FakeNativeAssetsBuilderResult( - success: hook != 'Linking', - ), - ), - ), - throwsToolExit( - message: - '$hook native assets failed. See the logs for more details.', - ), - ); - } - }); } diff --git a/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart index 793df6f7ec8..978e733c477 100644 --- a/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart @@ -4,7 +4,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -15,7 +14,6 @@ import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/dart/package_map.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals.dart' as globals; -import 'package:flutter_tools/src/isolated/native_assets/linux/native_assets.dart'; import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart' hide Target; @@ -51,51 +49,21 @@ void main() { projectUri = environment.projectDir.uri; }); - testUsingContext('dry run with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - expect( - await dryRunNativeAssetsLinux( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ), - null, - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('build with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await buildNativeAssetsLinux( - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - testUsingContext('does not throw if clang not present but no native assets present', overrides: { FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), ProcessManager: () => FakeProcessManager.empty(), }, () async { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; await packageConfig.create(recursive: true); - await buildNativeAssetsLinux( + + await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: BuildMode.debug.cliName, + }, + targetPlatform: TargetPlatform.linux_x64, projectUri: projectUri, - buildMode: BuildMode.debug, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, fileSystem: fileSystem, buildRunner: _BuildRunnerWithoutClang(), ); @@ -105,300 +73,6 @@ void main() { ); }); - testUsingContext('dry run for multiple OSes with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await dryRunNativeAssetsMultipleOSes( - projectUri: projectUri, - fileSystem: fileSystem, - targetPlatforms: [ - TargetPlatform.darwin, - TargetPlatform.ios, - ], - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('dry run with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsLinux( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('dry run with assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.linux, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.so'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.linux, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.so'), - ), - ], - ), - ); - final Uri? nativeAssetsYaml = await dryRunNativeAssetsLinux( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - expect( - (globals.logger as BufferLogger).traceText, - stringContainsInOrder([ - 'Dry running native assets for linux.', - 'Dry running native assets for linux done.', - ]), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/linux/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - contains('package:bar/bar.dart'), - ); - expect(buildRunner.buildDryRunInvocations, 1); - expect(buildRunner.linkDryRunInvocations, 0); - }); - - testUsingContext('build with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsLinux( - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('build no assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux( - targetPlatform: TargetPlatform.linux_x64, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/linux/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - isNot(contains('package:bar/bar.dart')), - ); - expect( - environment.projectDir - .childDirectory('build') - .childDirectory('native_assets') - .childDirectory('linux'), - exists, - ); - }); - - for (final bool flutterTester in [false, true]) { - String testName = ''; - if (flutterTester) { - testName += ' flutter tester'; - } - for (final BuildMode buildMode in [ - BuildMode.debug, - BuildMode.release, - ]) { - testUsingContext('build with assets $buildMode$testName', - overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir - .childDirectory('.dart_tool') - .childFile('package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final File dylibAfterCompiling = fileSystem.file('libbar.so'); - // The mock doesn't create the file, so create it here. - await dylibAfterCompiling.create(); - final FakeNativeAssetsBuildRunner buildRunner = - FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.linux, - architecture: ArchitectureImpl.x64, - file: dylibAfterCompiling.uri, - ), - ], - ), - ); - final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux( - targetPlatform: TargetPlatform.linux_x64, - projectUri: projectUri, - buildMode: buildMode, - fileSystem: fileSystem, - flutterTester: flutterTester, - buildRunner: buildRunner, - ); - expect( - (globals.logger as BufferLogger).traceText, - stringContainsInOrder([ - 'Building native assets for linux_x64 $buildMode.', - 'Building native assets for linux_x64 done.', - ]), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/linux/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - stringContainsInOrder([ - 'package:bar/bar.dart', - if (flutterTester) - // Tests run on host system, so the have the full path on the system. - '- ${projectUri.resolve('build/native_assets/linux/libbar.so').toFilePath()}' - else - // Apps are a bundle with the dylibs on their dlopen path. - '- libbar.so', - ]), - ); - expect(buildRunner.buildInvocations, 1); - expect( - buildRunner.linkInvocations, - buildMode == BuildMode.release ? 1 : 0, - ); - }); - } - } - - testUsingContext('Native assets dry run error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsLinux( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building (dry run) native assets failed. See the logs for more details.', - ), - ); - }); - - testUsingContext('Native assets build error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsLinux( - targetPlatform: TargetPlatform.linux_x64, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); - }); - // This logic is mocked in the other tests to avoid having test order // randomization causing issues with what processes are invoked. // Exercise the parsing of the process output in this separate test. @@ -436,14 +110,14 @@ void main() { packageConfigFile, logger: environment.logger, ); - final NativeAssetsBuildRunner runner = - NativeAssetsBuildRunnerImpl(projectUri, packageConfigFile.path, packageConfig, fileSystem, logger); + final FlutterNativeAssetsBuildRunner runner = + FlutterNativeAssetsBuildRunnerImpl(projectUri, packageConfigFile.path, packageConfig, fileSystem, logger); final CCompilerConfigImpl result = await runner.cCompilerConfig; expect(result.compiler, Uri.file('/some/path/to/clang')); }); } -class _BuildRunnerWithoutClang extends FakeNativeAssetsBuildRunner { +class _BuildRunnerWithoutClang extends FakeFlutterNativeAssetsBuildRunner { @override Future get cCompilerConfig async => throwToolExit('Failed to find clang++ on the PATH.'); diff --git a/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart index 3d23fb8b66b..5a2ae7fad05 100644 --- a/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart @@ -13,10 +13,8 @@ import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/dart/package_map.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals.dart' as globals; -import 'package:flutter_tools/src/isolated/native_assets/macos/native_assets.dart'; import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart' - hide Target; +import 'package:native_assets_cli/native_assets_cli_internal.dart' hide Target; import 'package:native_assets_cli/native_assets_cli_internal.dart' as native_assets_cli; import 'package:package_config/package_config_types.dart'; @@ -51,207 +49,9 @@ void main() { projectUri = environment.projectDir.uri; }); - testUsingContext('dry run with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - expect( - await dryRunNativeAssetsMacOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ), - null, - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('build with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await buildNativeAssetsMacOS( - darwinArchs: [DarwinArch.arm64], - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('dry run for multiple OSes with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await dryRunNativeAssetsMultipleOSes( - projectUri: projectUri, - fileSystem: fileSystem, - targetPlatforms: [ - TargetPlatform.darwin, - TargetPlatform.ios, - ], - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('dry run with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsMacOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('dry run with assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.dylib'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.dylib'), - ), - ], - ), - ); - final Uri? nativeAssetsYaml = await dryRunNativeAssetsMacOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - expect( - (globals.logger as BufferLogger).traceText, - stringContainsInOrder([ - 'Dry running native assets for macos.', - 'Dry running native assets for macos done.', - ]), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/macos/native_assets.yaml'), - ); - final String nativeAssetsYamlContents = - await fileSystem.file(nativeAssetsYaml).readAsString(); - expect( - nativeAssetsYamlContents, - contains('package:bar/bar.dart'), - ); - expect(buildRunner.buildDryRunInvocations, 1); - expect(buildRunner.linkDryRunInvocations, 0); - // Check that the framework uri is identical for both archs. - final String pathSeparator = const LocalPlatform().pathSeparator; - expect( - nativeAssetsYamlContents, - stringContainsInOrder( - [ - 'bar.framework${pathSeparator}bar', - 'bar.framework${pathSeparator}bar', - ], - ), - ); - }); - - testUsingContext('build with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsMacOS( - darwinArchs: [DarwinArch.arm64], - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('build no assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsMacOS( - darwinArchs: [DarwinArch.arm64], - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/macos/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - isNot(contains('package:bar/bar.dart')), - ); - }); - for (final bool flutterTester in [false, true]) { + final bool isArm64 = native_assets_cli.ArchitectureImpl.current == ArchitectureImpl.arm64; + String testName = ''; if (flutterTester) { testName += ' flutter tester'; @@ -276,7 +76,7 @@ void main() { } for (final BuildMode buildMode in [ BuildMode.debug, - BuildMode.release, + if (!flutterTester) BuildMode.release, ]) { testUsingContext('build with assets $buildMode$testName', overrides: { FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), @@ -289,8 +89,7 @@ void main() { '-create', '-output', dylibPathBar, - 'arm64/libbar.dylib', - 'x64/libbar.dylib', + '${isArm64 ? 'arm64' : 'x64'}/libbar.dylib', ], ), FakeCommand( @@ -312,8 +111,7 @@ void main() { '-create', '-output', dylibPathBuz, - 'arm64/libbuz.dylib', - 'x64/libbuz.dylib', + '${isArm64 ? 'arm64' : 'x64'}/libbuz.dylib', ], ), FakeCommand( @@ -323,9 +121,7 @@ void main() { dylibPathBuz, ], stdout: [ - '$dylibPathBuz (architecture x86_64):', - '@rpath/libbuz.dylib', - '$dylibPathBuz (architecture arm64):', + '$dylibPathBuz (architecture ${isArm64 ? 'arm64' : 'x86_64'}):', '@rpath/libbuz.dylib', ].join('\n'), ), @@ -349,8 +145,7 @@ void main() { '--force', '--sign', '-', - if (buildMode == BuildMode.debug) - '--timestamp=none', + if (buildMode == BuildMode.debug) '--timestamp=none', signPathBar, ], ), @@ -361,7 +156,7 @@ void main() { dylibPathBuz, '-change', '@rpath/libbar.dylib', - dylibPathBar, + dylibPathBar, '-change', '@rpath/libbuz.dylib', signPathBuz, @@ -374,8 +169,7 @@ void main() { '--force', '--sign', '-', - if (buildMode == BuildMode.debug) - '--timestamp=none', + if (buildMode == BuildMode.debug) '--timestamp=none', signPathBuz, ], ), @@ -461,15 +255,27 @@ void main() { if (const LocalPlatform().isWindows) { return; // Backslashes in commands, but we will never run these commands on Windows. } + if (flutterTester && !const LocalPlatform().isMacOS) { + // The [runFlutterSpecificDartBuild] will - when given + // `TargetPlatform.tester` - enable `flutter test` mode. That means if + // this test is run on linux, it's going to do a linux build. + // Though this test is mac-specific, so we skip that. + // + // Running the test in `!flutterTester` mode still works on linux as + // we explicitly tell it to do a mac build (instead of letting it + // choose the local build). + return; + } final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; await packageConfig.parent.create(); await packageConfig.create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [ Package('bar', projectUri), ], onBuild: (native_assets_cli.Target target) => - FakeNativeAssetsBuilderResult( + FakeFlutterNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -486,26 +292,34 @@ void main() { file: Uri.file('${target.architecture}/libbuz.dylib'), ), ], - ), + ), ); - final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsMacOS( - darwinArchs: [DarwinArch.arm64, DarwinArch.x86_64], + final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: buildMode.cliName, + kDarwinArchs: 'arm64 x86_64', + }, + targetPlatform: flutterTester ? TargetPlatform.tester : TargetPlatform.darwin, projectUri: projectUri, - buildMode: buildMode, + nativeAssetsYamlUri: flutterTester ? null : nonFlutterTesterAssetUri, fileSystem: fileSystem, - flutterTester: flutterTester, buildRunner: buildRunner, ); + final String expectedArchsBeingBuilt = flutterTester + ? (isArm64 ? 'macos_arm64' : 'macos_x64') + : '[macos_arm64, macos_x64]'; expect( (globals.logger as BufferLogger).traceText, stringContainsInOrder([ - 'Building native assets for [macos_arm64, macos_x64] $buildMode.', - 'Building native assets for [macos_arm64, macos_x64] done.', + 'Building native assets for $expectedArchsBeingBuilt $buildMode.', + 'Building native assets for $expectedArchsBeingBuilt $buildMode done.', ]), ); expect( nativeAssetsYaml, - projectUri.resolve('build/native_assets/macos/native_assets.yaml'), + flutterTester + ? projectUri.resolve('build/native_assets/macos/native_assets.yaml') + : nonFlutterTesterAssetUri ); expect( await fileSystem.file(nativeAssetsYaml).readAsString(), @@ -513,10 +327,10 @@ void main() { 'package:bar/bar.dart', if (flutterTester) // Tests run on host system, so the have the full path on the system. - '- ${projectUri.resolve('build/native_assets/macos/libbar.dylib').toFilePath()}' + projectUri.resolve('build/native_assets/macos/libbar.dylib').toFilePath() else // Apps are a bundle with the dylibs on their dlopen path. - '- bar.framework/bar', + 'bar.framework/bar', ]), ); expect( @@ -525,14 +339,14 @@ void main() { 'package:buz/buz.dart', if (flutterTester) // Tests run on host system, so the have the full path on the system. - '- ${projectUri.resolve('build/native_assets/macos/libbuz.dylib').toFilePath()}' + projectUri.resolve('build/native_assets/macos/libbuz.dylib').toFilePath() else // Apps are a bundle with the dylibs on their dlopen path. - '- buz.framework/buz', + 'buz.framework/buz', ]), ); // Multi arch. - expect(buildRunner.buildInvocations, 2); + expect(buildRunner.buildInvocations, flutterTester ? 1 : 2); expect( buildRunner.linkInvocations, buildMode == BuildMode.release ? 2 : 0, @@ -541,82 +355,24 @@ void main() { } } - testUsingContext('Native assets dry run error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsMacOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building (dry run) native assets failed. See the logs for more details.', - ), - ); - }); - - testUsingContext('Native assets build error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsMacOS( - darwinArchs: [DarwinArch.arm64], - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); - }); - // This logic is mocked in the other tests to avoid having test order // randomization causing issues with what processes are invoked. // Exercise the parsing of the process output in this separate test. - testUsingContext('NativeAssetsBuildRunnerImpl.cCompilerConfig', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.list( - [ - const FakeCommand( - command: ['xcrun', 'clang', '--version'], - stdout: ''' + testUsingContext('NativeAssetsBuildRunnerImpl.cCompilerConfig', + overrides: { + FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), + ProcessManager: () => FakeProcessManager.list( + [ + const FakeCommand( + command: ['xcrun', 'clang', '--version'], + stdout: ''' Apple clang version 14.0.0 (clang-1400.0.29.202) Target: arm64-apple-darwin22.6.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin''', - ) - ], - ), + ) + ], + ), }, () async { if (!const LocalPlatform().isMacOS) { return; @@ -632,7 +388,7 @@ InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault packageConfigFile, logger: environment.logger, ); - final NativeAssetsBuildRunner runner = NativeAssetsBuildRunnerImpl( + final FlutterNativeAssetsBuildRunner runner = FlutterNativeAssetsBuildRunnerImpl( projectUri, packageConfigFile.path, packageConfig, diff --git a/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart new file mode 100644 index 00000000000..5710e1da105 --- /dev/null +++ b/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart @@ -0,0 +1,308 @@ +// 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. + +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:file_testing/file_testing.dart'; +import 'package:flutter_tools/src/artifacts.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/build_info.dart'; +import 'package:flutter_tools/src/build_system/build_system.dart'; +import 'package:flutter_tools/src/features.dart'; +import 'package:flutter_tools/src/globals.dart' as globals; +import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:package_config/package_config_types.dart'; + +import '../../src/common.dart'; +import '../../src/context.dart'; +import '../../src/fakes.dart'; +import 'fake_native_assets_build_runner.dart'; + +void main() { + late FakeProcessManager processManager; + late Environment environment; + late Artifacts artifacts; + late FileSystem fileSystem; + late BufferLogger logger; + late Uri projectUri; + + setUp(() { + processManager = FakeProcessManager.empty(); + logger = BufferLogger.test(); + artifacts = Artifacts.test(); + fileSystem = MemoryFileSystem.test(); + environment = Environment.test( + fileSystem.currentDirectory, + inputs: {}, + artifacts: artifacts, + processManager: processManager, + fileSystem: fileSystem, + logger: logger, + ); + environment.buildDir.createSync(recursive: true); + projectUri = environment.projectDir.uri; + }); + + testUsingContext('dry run with no package config', overrides: { + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + expect( + await runFlutterSpecificDartDryRunOnPlatforms( + projectUri: projectUri, + fileSystem: fileSystem, + targetPlatforms: [TargetPlatform.windows_x64], + buildRunner: FakeFlutterNativeAssetsBuildRunner( + hasPackageConfigResult: false, + ), + ), + null, + ); + expect( + (globals.logger as BufferLogger).traceText, + contains('No package config found. Skipping native assets compilation.'), + ); + }); + + testUsingContext('build with no package config', overrides: { + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; + await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: BuildMode.debug.cliName, + }, + targetPlatform: TargetPlatform.windows_x64, + projectUri: projectUri, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, + fileSystem: fileSystem, + buildRunner: FakeFlutterNativeAssetsBuildRunner( + hasPackageConfigResult: false, + ), + ); + expect( + (globals.logger as BufferLogger).traceText, + contains('No package config found. Skipping native assets compilation.'), + ); + }); + + testUsingContext('dry run for multiple OSes with no package config', overrides: { + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + await runFlutterSpecificDartDryRunOnPlatforms( + projectUri: projectUri, + fileSystem: fileSystem, + targetPlatforms: [ + TargetPlatform.windows_x64, + TargetPlatform.darwin, + TargetPlatform.ios, + ], + buildRunner: FakeFlutterNativeAssetsBuildRunner( + hasPackageConfigResult: false, + ), + ); + expect( + (globals.logger as BufferLogger).traceText, + contains('No package config found. Skipping native assets compilation.'), + ); + }); + + testUsingContext('dry run with assets but not enabled', overrides: { + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + await packageConfig.parent.create(); + await packageConfig.create(); + expect( + () => runFlutterSpecificDartDryRunOnPlatforms( + projectUri: projectUri, + fileSystem: fileSystem, + targetPlatforms: [TargetPlatform.windows_x64], + buildRunner: FakeFlutterNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + ), + ), + throwsToolExit( + message: 'Package(s) bar require the native assets feature to be enabled. ' + 'Enable using `flutter config --enable-native-assets`.', + ), + ); + }); + + testUsingContext('dry run with assets', overrides: { + FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + await packageConfig.parent.create(); + await packageConfig.create(); + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeFlutterNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.windows, + architecture: ArchitectureImpl.x64, + file: Uri.file('bar.dll'), + ), + ], + ), + ); + final Uri? nativeAssetsYaml = await runFlutterSpecificDartDryRunOnPlatforms( + projectUri: projectUri, + fileSystem: fileSystem, + targetPlatforms: [TargetPlatform.windows_x64], + buildRunner: buildRunner, + ); + expect( + (globals.logger as BufferLogger).traceText, + stringContainsInOrder([ + 'Dry running native assets for windows.', + 'Dry running native assets for windows done.', + ]), + ); + expect( + nativeAssetsYaml, + projectUri.resolve('build/native_assets/windows/native_assets.yaml'), + ); + expect( + await fileSystem.file(nativeAssetsYaml).readAsString(), + contains('package:bar/bar.dart'), + ); + expect(buildRunner.buildDryRunInvocations, 1); + }); + + testUsingContext('build with assets but not enabled', overrides: { + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; + await packageConfig.parent.create(); + await packageConfig.create(); + expect( + () => runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: BuildMode.debug.cliName, + }, + targetPlatform: TargetPlatform.windows_x64, + projectUri: projectUri, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, + fileSystem: fileSystem, + buildRunner: FakeFlutterNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + ), + ), + throwsToolExit( + message: 'Package(s) bar require the native assets feature to be enabled. ' + 'Enable using `flutter config --enable-native-assets`.', + ), + ); + }); + + testUsingContext('build no assets', overrides: { + FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; + await packageConfig.parent.create(); + await packageConfig.create(); + final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: BuildMode.debug.cliName, + }, + targetPlatform: TargetPlatform.windows_x64, + projectUri: projectUri, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, + fileSystem: fileSystem, + buildRunner: FakeFlutterNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + ), + ); + expect(nativeAssetsYaml, nonFlutterTesterAssetUri); + expect( + await fileSystem.file(nativeAssetsYaml).readAsString(), + isNot(contains('package:bar/bar.dart')), + ); + expect( + environment.projectDir.childDirectory('build').childDirectory('native_assets').childDirectory('windows'), + exists, + ); + }); + + testUsingContext('Native assets dry run error', overrides: { + FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + final File packageConfig = + environment.projectDir.childFile('.dart_tool/package_config.json'); + await packageConfig.parent.create(); + await packageConfig.create(); + expect( + () => runFlutterSpecificDartDryRunOnPlatforms( + projectUri: projectUri, + fileSystem: fileSystem, + targetPlatforms: [TargetPlatform.windows_x64], + buildRunner: FakeFlutterNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: const FakeFlutterNativeAssetsBuilderResult( + success: false, + ), + ), + ), + throwsToolExit( + message: + 'Building (dry run) native assets failed. See the logs for more details.', + ), + ); + }); + + testUsingContext('Native assets build error', overrides: { + FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), + ProcessManager: () => FakeProcessManager.empty(), + }, () async { + final File packageConfig = + environment.projectDir.childFile('.dart_tool/package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; + await packageConfig.parent.create(); + await packageConfig.create(); + expect( + () => runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: BuildMode.debug.cliName, + }, + targetPlatform: TargetPlatform.linux_x64, + projectUri: projectUri, + nativeAssetsYamlUri: nonFlutterTesterAssetUri, + fileSystem: fileSystem, + buildRunner: FakeFlutterNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: const FakeFlutterNativeAssetsBuilderResult( + success: false, + ), + ), + ), + throwsToolExit( + message: + 'Building native assets failed. See the logs for more details.', + ), + ); + }); + +} diff --git a/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart index 362af175090..abf01d853ac 100644 --- a/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart @@ -56,7 +56,7 @@ void main() { globals.fs .file(globals.fs.path.join('lib', 'main.dart')) .createSync(recursive: true); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner(); + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(); final HotRunner residentRunner = HotRunner( [ flutterDevice, diff --git a/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart index 45c35d8a147..2eb7bf9b76f 100644 --- a/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart @@ -4,7 +4,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; @@ -15,9 +14,8 @@ import 'package:flutter_tools/src/dart/package_map.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'; -import 'package:flutter_tools/src/isolated/native_assets/windows/native_assets.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart' - hide Target; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart' as native_assets_cli; import 'package:package_config/package_config_types.dart'; import '../../../src/common.dart'; @@ -50,187 +48,6 @@ void main() { projectUri = environment.projectDir.uri; }); - testUsingContext('dry run with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - expect( - await dryRunNativeAssetsWindows( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ), - null, - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('build with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await buildNativeAssetsWindows( - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('dry run for multiple OSes with no package config', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - await dryRunNativeAssetsMultipleOSes( - projectUri: projectUri, - fileSystem: fileSystem, - targetPlatforms: [ - TargetPlatform.windows_x64, - ], - buildRunner: FakeNativeAssetsBuildRunner( - hasPackageConfigResult: false, - ), - ); - expect( - (globals.logger as BufferLogger).traceText, - contains('No package config found. Skipping native assets compilation.'), - ); - }); - - testUsingContext('dry run with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsWindows( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('dry run with assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.windows, - architecture: ArchitectureImpl.x64, - file: Uri.file('bar.dll'), - ), - ], - ), - ); - final Uri? nativeAssetsYaml = await dryRunNativeAssetsWindows( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: buildRunner, - ); - expect( - (globals.logger as BufferLogger).traceText, - stringContainsInOrder([ - 'Dry running native assets for windows.', - 'Dry running native assets for windows done.', - ]), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/windows/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - contains('package:bar/bar.dart'), - ); - expect(buildRunner.buildDryRunInvocations, 1); - expect(buildRunner.linkDryRunInvocations, 0); - }); - - testUsingContext('build with assets but not enabled', overrides: { - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsWindows( - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ), - throwsToolExit( - message: 'Package(s) bar require the native assets feature to be enabled. ' - 'Enable using `flutter config --enable-native-assets`.', - ), - ); - }); - - testUsingContext('build no assets', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsWindows( - targetPlatform: TargetPlatform.windows_x64, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - ), - ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/windows/native_assets.yaml'), - ); - expect( - await fileSystem.file(nativeAssetsYaml).readAsString(), - isNot(contains('package:bar/bar.dart')), - ); - expect( - environment.projectDir.childDirectory('build').childDirectory('native_assets').childDirectory('windows'), - exists, - ); - }); - for (final bool flutterTester in [false, true]) { String testName = ''; if (flutterTester) { @@ -238,23 +55,33 @@ void main() { } for (final BuildMode buildMode in [ BuildMode.debug, - BuildMode.release, + if (!flutterTester) BuildMode.release, ]) { + if (flutterTester && !const LocalPlatform().isWindows) { + // When calling [runFlutterSpecificDartBuild] with the flutter tester + // target platform, it will perform a build for the local machine. That + // means e.g. running this test on MacOS will cause it to run a MacOS + // build - which in return requires a special [ProcessManager] that can + // simulate output of `otool` invocations. + continue; + } + testUsingContext('build with assets $buildMode$testName', overrides: { FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), ProcessManager: () => FakeProcessManager.empty(), }, () async { final File packageConfig = environment.projectDir.childDirectory('.dart_tool').childFile('package_config.json'); + final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri; await packageConfig.parent.create(); await packageConfig.create(); final File dylibAfterCompiling = fileSystem.file('bar.dll'); // The mock doesn't create the file, so create it here. await dylibAfterCompiling.create(); - final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner( packagesWithNativeAssetsResult: [ Package('bar', projectUri), ], - buildResult: FakeNativeAssetsBuilderResult( + buildResult: FakeFlutterNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -266,24 +93,35 @@ void main() { ], ), ); - final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsWindows( - targetPlatform: TargetPlatform.windows_x64, + final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild( + environmentDefines: { + kBuildMode: buildMode.cliName, + }, + targetPlatform: flutterTester + ? TargetPlatform.tester + : TargetPlatform.windows_x64, projectUri: projectUri, - buildMode: buildMode, + nativeAssetsYamlUri: flutterTester ? null : nonFlutterTesterAssetUri, fileSystem: fileSystem, - flutterTester: flutterTester, buildRunner: buildRunner, ); + final String expectedOS = flutterTester + ? native_assets_cli.Target.current.toString() + : 'windows_x64'; expect( (globals.logger as BufferLogger).traceText, stringContainsInOrder([ - 'Building native assets for windows_x64 $buildMode.', - 'Building native assets for windows_x64 done.', + 'Building native assets for $expectedOS $buildMode.', + 'Building native assets for $expectedOS $buildMode done.', ]), ); - expect( - nativeAssetsYaml, - projectUri.resolve('build/native_assets/windows/native_assets.yaml'), + final String expectedDirectory = flutterTester + ? native_assets_cli.OSImpl.current.toString() + : 'windows'; + expect(nativeAssetsYaml, + flutterTester + ? projectUri.resolve('build/native_assets/$expectedDirectory/native_assets.yaml') + : nonFlutterTesterAssetUri ); expect( await fileSystem.file(nativeAssetsYaml).readAsString(), @@ -291,80 +129,21 @@ void main() { 'package:bar/bar.dart', if (flutterTester) // Tests run on host system, so the have the full path on the system. - '- ${projectUri.resolve('build/native_assets/windows/bar.dll').toFilePath()}' + projectUri.resolve('build/native_assets/$expectedDirectory/bar.dll').toFilePath() else // Apps are a bundle with the dylibs on their dlopen path. - '- bar.dll', + 'bar.dll', ]), ); expect(buildRunner.buildInvocations, 1); expect( buildRunner.linkInvocations, - buildMode == BuildMode.release ? 1 :0, + buildMode == BuildMode.release ? 1 : 0, ); }); } } - testUsingContext('Native assets dry run error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => dryRunNativeAssetsWindows( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildDryRunResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building (dry run) native assets failed. See the logs for more details.', - ), - ); - }); - - testUsingContext('Native assets build error', overrides: { - FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), - ProcessManager: () => FakeProcessManager.empty(), - }, () async { - final File packageConfig = - environment.projectDir.childFile('.dart_tool/package_config.json'); - await packageConfig.parent.create(); - await packageConfig.create(); - expect( - () => buildNativeAssetsWindows( - targetPlatform: TargetPlatform.windows_x64, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, - ), - ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); - }); - // This logic is mocked in the other tests to avoid having test order // randomization causing issues with what processes are invoked. // Exercise the parsing of the process output in this separate test. @@ -453,17 +232,14 @@ void main() { fileSystem.directory(r'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\bin\Hostx64\x64'); await msvcBinDir.create(recursive: true); - final File packageConfigFile = fileSystem - .directory(projectUri) - .childDirectory('.dart_tool') - .childFile('package_config.json'); + final File packageConfigFile = fileSystem.directory(projectUri).childDirectory('.dart_tool').childFile('package_config.json'); await packageConfigFile.parent.create(); await packageConfigFile.create(); final PackageConfig packageConfig = await loadPackageConfigWithLogging( packageConfigFile, logger: environment.logger, ); - final NativeAssetsBuildRunner runner = NativeAssetsBuildRunnerImpl( + final FlutterNativeAssetsBuildRunner runner = FlutterNativeAssetsBuildRunnerImpl( projectUri, packageConfigFile.path, packageConfig,