diff --git a/packages/flutter_tools/lib/src/build_system/targets/common.dart b/packages/flutter_tools/lib/src/build_system/targets/common.dart index ba03270c526..258eaa86185 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/common.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/common.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:package_config/package_config.dart'; +import 'package:yaml/yaml.dart'; import '../../artifacts.dart'; import '../../base/build.dart'; @@ -121,15 +122,17 @@ class ReleaseCopyFlutterBundle extends CopyFlutterBundle { /// even though it is not listed as an input. Pub inserts a timestamp into /// the file which causes unnecessary rebuilds, so instead a subset of the contents /// are used an input instead. -class KernelSnapshot extends Target { - const KernelSnapshot(); +/// +/// This kernel snapshot is concatenated with the [KernelSnapshotNativeAssets] +/// inside [KernelSnapshot] byte-wise to create the combined kernel snapshot. +class KernelSnapshotProgram extends Target { + const KernelSnapshotProgram(); @override - String get name => 'kernel_snapshot'; + String get name => 'kernel_snapshot_program'; @override List get inputs => const [ - Source.pattern('{BUILD_DIR}/native_assets.yaml'), Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'), Source.artifact(Artifact.platformKernelDill), @@ -139,7 +142,9 @@ class KernelSnapshot extends Target { ]; @override - List get outputs => const []; + List get outputs => const [ + // TODO(mosuem): Should output resources.json. https://github.com/flutter/flutter/issues/146263 + ]; @override List get depfiles => [ @@ -148,11 +153,12 @@ class KernelSnapshot extends Target { @override List get dependencies => const [ - NativeAssets(), GenerateLocalizationsTarget(), DartPluginRegistrantTarget(), ]; + static const String dillName = 'program.dill'; + @override Future build(Environment environment) async { final KernelCompiler compiler = KernelCompiler( @@ -186,13 +192,6 @@ class KernelSnapshot extends Target { final List? fileSystemRoots = environment.defines[kFileSystemRoots]?.split(','); final String? fileSystemScheme = environment.defines[kFileSystemScheme]; - final File nativeAssetsFile = environment.buildDir.childFile('native_assets.yaml'); - final String nativeAssets = nativeAssetsFile.path; - if (!await nativeAssetsFile.exists()) { - throwToolExit("$nativeAssets doesn't exist."); - } - environment.logger.printTrace('Embedding native assets mapping $nativeAssets in kernel.'); - TargetModel targetModel = TargetModel.flutter; if (targetPlatform == TargetPlatform.fuchsia_x64 || targetPlatform == TargetPlatform.fuchsia_arm64) { @@ -242,6 +241,8 @@ class KernelSnapshot extends Target { logger: environment.logger, ); + final String dillPath = environment.buildDir.childFile(dillName).path; + final CompilerOutput? output = await compiler.compile( sdkRoot: environment.artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -252,9 +253,8 @@ class KernelSnapshot extends Target { buildMode: buildMode, trackWidgetCreation: trackWidgetCreation && buildMode != BuildMode.release, targetModel: targetModel, - outputFilePath: environment.buildDir.childFile('app.dill').path, - initializeFromDill: buildMode.isPrecompiled ? null : - environment.buildDir.childFile('app.dill').path, + outputFilePath: dillPath, + initializeFromDill: buildMode.isPrecompiled ? null : dillPath, packagesPath: packagesFile.path, linkPlatformKernelIn: forceLinkPlatform || buildMode.isPrecompiled, mainPath: targetFileAbsolute, @@ -268,6 +268,109 @@ class KernelSnapshot extends Target { buildDir: environment.buildDir, targetOS: targetOS, checkDartPluginRegistry: environment.generateDartPluginRegistry, + ); + if (output == null || output.errorCount != 0) { + throw Exception(); + } + } +} + +/// Generate a kernel snapshot of the native assets mapping for resolving +/// `@Native` assets at runtime. +/// +/// This kernel snapshot is concatenated to the [KernelSnapshotProgram] +/// inside [KernelSnapshot] to create the combined kernel snapshot. +class KernelSnapshotNativeAssets extends Target { + const KernelSnapshotNativeAssets(); + + @override + String get name => 'kernel_snapshot_native_assets'; + + @override + List get inputs => [ + const Source.pattern('{BUILD_DIR}/native_assets.yaml'), + ...const KernelSnapshotProgram().inputs, + ]; + + @override + List get outputs => const []; + + @override + List get depfiles => const []; + + @override + List get dependencies => [ + const NativeAssets(), + ]; + + static const String dillName = 'native_assets.dill'; + + @override + Future build(Environment environment) async { + final File nativeAssetsFile = environment.buildDir.childFile('native_assets.yaml'); + final File dillFile = environment.buildDir.childFile(dillName); + + final YamlNode nativeAssetContents = loadYamlNode(await nativeAssetsFile.readAsString()); + final Object? nativeAssetsInYaml = (nativeAssetContents as Map)['native-assets']; + if (nativeAssetsInYaml is! Map || nativeAssetsInYaml.isEmpty) { + // Write an empty file to make concatenation a no-op. + // Write the file out to disk for caching. + await dillFile.writeAsBytes([]); + return; + } + + final KernelCompiler compiler = KernelCompiler( + fileSystem: environment.fileSystem, + logger: environment.logger, + processManager: environment.processManager, + artifacts: environment.artifacts, + fileSystemRoots: [], + ); + final String? buildModeEnvironment = environment.defines[kBuildMode]; + if (buildModeEnvironment == null) { + throw MissingDefineException(kBuildMode, 'kernel_snapshot'); + } + final String? targetPlatformEnvironment = environment.defines[kTargetPlatform]; + if (targetPlatformEnvironment == null) { + throw MissingDefineException(kTargetPlatform, 'kernel_snapshot'); + } + final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment); + final File packagesFile = environment.projectDir + .childDirectory('.dart_tool') + .childFile('package_config.json'); + + final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment); + + final String? frontendServerStarterPath = environment.defines[kFrontendServerStarterPath]; + + final String nativeAssets = nativeAssetsFile.path; + if (!await nativeAssetsFile.exists()) { + throwToolExit("$nativeAssets doesn't exist."); + } + environment.logger.printTrace('Embedding native assets mapping $nativeAssets in kernel.'); + + final PackageConfig packageConfig = await loadPackageConfigWithLogging( + packagesFile, + logger: environment.logger, + ); + + final String dillPath = dillFile.path; + + final CompilerOutput? output = await compiler.compile( + sdkRoot: environment.artifacts.getArtifactPath( + Artifact.flutterPatchedSdkPath, + platform: targetPlatform, + mode: buildMode, + ), + aot: buildMode.isPrecompiled, + buildMode: buildMode, + trackWidgetCreation: false, + outputFilePath: dillPath, + packagesPath: packagesFile.path, + frontendServerStarterPath: frontendServerStarterPath, + packageConfig: packageConfig, + buildDir: environment.buildDir, + dartDefines: [], nativeAssets: nativeAssets, ); if (output == null || output.errorCount != 0) { @@ -276,6 +379,43 @@ class KernelSnapshot extends Target { } } +class KernelSnapshot extends Target { + const KernelSnapshot(); + + @override + String get name => 'kernel_snapshot'; + + @override + List get dependencies => const [ + KernelSnapshotProgram(), + KernelSnapshotNativeAssets(), + ]; + + @override + List get inputs => []; + + @override + List get outputs => []; + + static const String dillName = 'app.dill'; + + @override + Future build(Environment environment) async { + final File programDill = environment.buildDir.childFile( + KernelSnapshotProgram.dillName, + ); + final File nativeAssetsDill = environment.buildDir.childFile( + KernelSnapshotNativeAssets.dillName, + ); + final File dill = environment.buildDir.childFile(dillName); + await programDill.copy(dill.path); + await dill.writeAsBytes( + await nativeAssetsDill.readAsBytes(), + mode: FileMode.append, + ); + } +} + /// Supports compiling a dart kernel file to an ELF binary. abstract class AotElfBase extends Target { const AotElfBase(); 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 e2ab35de109..4775a262d69 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 @@ -371,13 +371,17 @@ class NativeAssets extends Target { ]; @override - List get dependencies => []; + List get dependencies => const [ + // In AOT, depends on tree-shaking information (resources.json) from compiling dart. + KernelSnapshotProgram(), + ]; @override List get inputs => const [ Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart'), // If different packages are resolved, different native assets might need to be built. Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'), + // TODO(mosuem): Should consume resources.json. https://github.com/flutter/flutter/issues/146263 ]; @override diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index 2fe3b8ba3e9..f1ce205dbfc 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -247,12 +247,14 @@ class KernelCompiler { sdkRoot = '$sdkRoot/'; } String? mainUri; - final File mainFile = _fileSystem.file(mainPath); - final Uri mainFileUri = mainFile.uri; - if (packagesPath != null) { - mainUri = packageConfig.toPackageUri(mainFileUri)?.toString(); + if (mainPath != null) { + final File mainFile = _fileSystem.file(mainPath); + final Uri mainFileUri = mainFile.uri; + if (packagesPath != null) { + mainUri = packageConfig.toPackageUri(mainFileUri)?.toString(); + } + mainUri ??= toMultiRootPath(mainFileUri, _fileSystemScheme, _fileSystemRoots, _fileSystem.path.separator == r'\'); } - mainUri ??= toMultiRootPath(mainFileUri, _fileSystemScheme, _fileSystemRoots, _fileSystem.path.separator == r'\'); if (outputFilePath != null && !_fileSystem.isFileSync(outputFilePath)) { _fileSystem.file(outputFilePath).createSync(recursive: true); } @@ -359,7 +361,8 @@ class KernelCompiler { // See: https://github.com/flutter/flutter/issues/103994 '--verbosity=error', ...?extraFrontEndOptions, - mainUri, + if (mainUri != null) mainUri + else '--native-assets-only', ]; _logger.printTrace(command.join(' ')); diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart index 0eab969458e..79a778e0675 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart @@ -64,10 +64,10 @@ void main() { iosEnvironment.buildDir.createSync(recursive: true); }); - testWithoutContext('KernelSnapshot throws error if missing build mode', () async { + testWithoutContext('KernelSnapshotProgram throws error if missing build mode', () async { androidEnvironment.defines.remove(kBuildMode); expect( - const KernelSnapshot().build(androidEnvironment), + const KernelSnapshotProgram().build(androidEnvironment), throwsA(isA())); }); @@ -79,13 +79,22 @@ format-version: native-assets: {} '''; - testWithoutContext('KernelSnapshot handles null result from kernel compilation', () async { + const String nonEmptyNativeAssets = ''' +format-version: + - 1 + - 0 + - 0 +native-assets: + macos_arm64: + package:my_package/my_package_bindings_generated.dart: + - absolute + - my_package.framework/my_package +'''; + + testWithoutContext('KernelSnapshotProgram handles null result from kernel compilation', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -110,27 +119,22 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', ], exitCode: 1), ]); - await expectLater(() => const KernelSnapshot().build(androidEnvironment), throwsException); + await expectLater(() => const KernelSnapshotProgram().build(androidEnvironment), throwsException); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot does use track widget creation on profile builds', () async { + testWithoutContext('KernelSnapshotProgram does use track widget creation on profile builds', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -155,28 +159,23 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot().build(androidEnvironment); + await const KernelSnapshotProgram().build(androidEnvironment); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot correctly handles an empty string in ExtraFrontEndOptions', () async { + testWithoutContext('KernelSnapshotProgram correctly handles an empty string in ExtraFrontEndOptions', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -201,29 +200,24 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot() + await const KernelSnapshotProgram() .build(androidEnvironment..defines[kExtraFrontEndOptions] = ''); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot correctly forwards FrontendServerStarterPath', () async { + testWithoutContext('KernelSnapshotProgram correctly forwards FrontendServerStarterPath', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -248,29 +242,24 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot() + await const KernelSnapshotProgram() .build(androidEnvironment..defines[kFrontendServerStarterPath] = 'path/to/frontend_server_starter.dart'); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot correctly forwards ExtraFrontEndOptions', () async { + testWithoutContext('KernelSnapshotProgram correctly forwards ExtraFrontEndOptions', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -295,31 +284,27 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'foo', 'bar', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot() + await const KernelSnapshotProgram() .build(androidEnvironment..defines[kExtraFrontEndOptions] = 'foo,bar'); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot can disable track-widget-creation on debug builds', () async { + testWithoutContext('KernelSnapshotProgram can disable track-widget-creation on debug builds', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); + final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -340,33 +325,28 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', '--incremental', '--initialize-from-dill', - '$build/app.dill', - '--native-assets', - '$build/native_assets.yaml', + '$build/program.dill', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot().build(androidEnvironment + await const KernelSnapshotProgram().build(androidEnvironment ..defines[kBuildMode] = BuildMode.debug.cliName ..defines[kTrackWidgetCreation] = 'false'); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot forces platform linking on debug for darwin target platforms', () async { + testWithoutContext('KernelSnapshotProgram forces platform linking on debug for darwin target platforms', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -386,20 +366,18 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', '--incremental', '--initialize-from-dill', - '$build/app.dill', - '--native-assets', - '$build/native_assets.yaml', + '$build/program.dill', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot().build(androidEnvironment + await const KernelSnapshotProgram().build(androidEnvironment ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin) ..defines[kBuildMode] = BuildMode.debug.cliName ..defines[kTrackWidgetCreation] = 'false' @@ -408,7 +386,7 @@ native-assets: {} expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot does use track widget creation on debug builds', () async { + testWithoutContext('KernelSnapshotProgram does use track widget creation on debug builds', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); @@ -423,9 +401,6 @@ native-assets: {} fileSystem: fileSystem, logger: logger, ); - testEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = testEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -447,24 +422,72 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', '--incremental', '--initialize-from-dill', - '$build/app.dill', - '--native-assets', - '$build/native_assets.yaml', + '$build/program.dill', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/program.dill 0\n'), ]); - await const KernelSnapshot().build(testEnvironment); + await const KernelSnapshotProgram().build(testEnvironment); expect(processManager, hasNoRemainingExpectations); }); + for (final BuildMode buildMode in [BuildMode.debug, BuildMode.release]) { + for (final bool empty in [true, false]) { + final String testName = empty ? 'empty' : 'non empty'; + testWithoutContext('KernelSnapshotNativeAssets ${buildMode.name} $testName', () async { + fileSystem.file('.dart_tool/package_config.json') + ..createSync(recursive: true) + ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); + androidEnvironment.buildDir.childFile('native_assets.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(empty ? emptyNativeAssets : nonEmptyNativeAssets); + final String build = androidEnvironment.buildDir.path; + final String flutterPatchedSdkPath = artifacts.getArtifactPath( + Artifact.flutterPatchedSdkPath, + platform: TargetPlatform.darwin, + mode: buildMode, + ); + processManager.addCommands([ + if (!empty) + FakeCommand(command: [ + artifacts.getArtifactPath(Artifact.engineDartAotRuntime), + '--disable-dart-dev', + artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk), + '--sdk-root', + '$flutterPatchedSdkPath/', + '--target=flutter', + '--no-print-incremental-dependencies', + ...buildModeOptions(buildMode, []), + '--no-link-platform', + if (buildMode == BuildMode.release) ...['--aot', '--tfa'], + '--packages', + '/.dart_tool/package_config.json', + '--output-dill', + '$build/native_assets.dill', + '--native-assets', + '$build/native_assets.yaml', + '--verbosity=error', + '--native-assets-only', + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ]); + + await const KernelSnapshotNativeAssets().build(androidEnvironment + ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin) + ..defines[kBuildMode] = buildMode.cliName + ); + + expect(processManager, hasNoRemainingExpectations); + }); + } + } + testUsingContext('AotElfProfile Produces correct output directory', () async { final String build = androidEnvironment.buildDir.path; processManager.addCommands([