diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index e99f1975211..fa7ffb45912 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -161,11 +161,37 @@ class AOTSnapshotter { } } - final String assembly = _fileSystem.path.join(outputDir.path, 'snapshot_assembly.S'); + String aotSharedLibrary = _fileSystem.path.join(outputDir.path, 'app.so'); + String? frameworkPath; if (targetingApplePlatform) { - genSnapshotArgs.addAll(['--snapshot_kind=app-aot-assembly', '--assembly=$assembly']); + // On iOS and macOS, we use Xcode to compile the snapshot into a dynamic + // library that the end-developer can link into their app. + const frameworkName = 'App.framework'; + if (!quiet) { + final String targetArch = darwinArch!.name; + _logger.printStatus('Building $frameworkName for $targetArch...'); + } + frameworkPath = _fileSystem.path.join(outputPath, frameworkName); + _fileSystem.directory(frameworkPath).createSync(recursive: true); + + const frameworkSnapshotName = 'App'; + aotSharedLibrary = _fileSystem.path.join(frameworkPath, frameworkSnapshotName); + final String relocatableObject = _fileSystem.path.join(outputPath, 'app.o'); + // When the minimum version is updated, remember to update + // template MinimumOSVersion. + // https://github.com/flutter/flutter/pull/62902 + final minOSVersion = platform == TargetPlatform.ios + ? FlutterDarwinPlatform.ios.deploymentTarget().toString() + : FlutterDarwinPlatform.macos.deploymentTarget().toString(); + genSnapshotArgs.addAll([ + '--snapshot_kind=app-aot-macho-dylib', + '--macho=$aotSharedLibrary', + '--macho-object=$relocatableObject', + '--macho-min-os-version=$minOSVersion', + '--macho-rpath=@executable_path/Frameworks,@loader_path/Frameworks', + '--macho-install-name=@rpath/$frameworkName/$frameworkSnapshotName', + ]); } else { - final String aotSharedLibrary = _fileSystem.path.join(outputDir.path, 'app.so'); genSnapshotArgs.addAll(['--snapshot_kind=app-aot-elf', '--elf=$aotSharedLibrary']); } @@ -230,123 +256,38 @@ class AOTSnapshotter { return genSnapshotExitCode; } - // On iOS and macOS, we use Xcode to compile the snapshot into a dynamic library that the - // end-developer can link into their app. if (targetingApplePlatform) { - return _buildFramework( - appleArch: darwinArch!, - isIOS: platform == TargetPlatform.ios, - sdkRoot: sdkRoot, - assemblyPath: assembly, - outputPath: outputDir.path, - quiet: quiet, - stripAfterBuild: stripAfterBuild, - extractAppleDebugSymbols: extractAppleDebugSymbols, - ); - } else { - return 0; - } - } - - /// Builds an iOS or macOS framework at [outputPath]/App.framework from the assembly - /// source at [assemblyPath]. - Future _buildFramework({ - required DarwinArch appleArch, - required bool isIOS, - String? sdkRoot, - required String assemblyPath, - required String outputPath, - required bool quiet, - required bool stripAfterBuild, - required bool extractAppleDebugSymbols, - }) async { - final String targetArch = appleArch.name; - if (!quiet) { - _logger.printStatus('Building App.framework for $targetArch...'); - } - - final commonBuildOptions = [ - '-arch', - targetArch, - if (isIOS) - // When the minimum version is updated, remember to update - // template MinimumOSVersion. - // https://github.com/flutter/flutter/pull/62902 - '-miphoneos-version-min=${FlutterDarwinPlatform.ios.deploymentTarget()}', - if (sdkRoot != null) ...['-isysroot', sdkRoot], - ]; - - final String assemblyO = _fileSystem.path.join(outputPath, 'snapshot_assembly.o'); - - final RunResult compileResult = await _xcode.cc([ - ...commonBuildOptions, - '-c', - assemblyPath, - '-o', - assemblyO, - ]); - if (compileResult.exitCode != 0) { - _logger.printError( - 'Failed to compile AOT snapshot. Compiler terminated with exit code ${compileResult.exitCode}', - ); - return compileResult.exitCode; - } - - final String frameworkDir = _fileSystem.path.join(outputPath, 'App.framework'); - _fileSystem.directory(frameworkDir).createSync(recursive: true); - final String appLib = _fileSystem.path.join(frameworkDir, 'App'); - final linkArgs = [ - ...commonBuildOptions, - '-dynamiclib', - '-Xlinker', - '-rpath', - '-Xlinker', - '@executable_path/Frameworks', - '-Xlinker', - '-rpath', - '-Xlinker', - '@loader_path/Frameworks', - '-fapplication-extension', - '-install_name', - '@rpath/App.framework/App', - '-o', - appLib, - assemblyO, - ]; - - final RunResult linkResult = await _xcode.clang(linkArgs); - if (linkResult.exitCode != 0) { - _logger.printError( - 'Failed to link AOT snapshot. Linker terminated with exit code ${linkResult.exitCode}', - ); - return linkResult.exitCode; - } - - if (extractAppleDebugSymbols) { - final RunResult dsymResult = await _xcode.dsymutil([ - '-o', - '$frameworkDir.dSYM', - appLib, - ]); - if (dsymResult.exitCode != 0) { - _logger.printError( - 'Failed to generate dSYM - dsymutil terminated with exit code ${dsymResult.exitCode}', - ); - return dsymResult.exitCode; - } - - if (stripAfterBuild) { - // See https://www.unix.com/man-page/osx/1/strip/ for arguments - final RunResult stripResult = await _xcode.strip(['-x', appLib, '-o', appLib]); - if (stripResult.exitCode != 0) { + if (extractAppleDebugSymbols) { + final RunResult dsymResult = await _xcode.dsymutil([ + '-o', + '$frameworkPath.dSYM', + aotSharedLibrary, + ]); + if (dsymResult.exitCode != 0) { _logger.printError( - 'Failed to strip debugging symbols from the generated AOT snapshot - strip terminated with exit code ${stripResult.exitCode}', + 'Failed to generate dSYM - dsymutil terminated with exit code ${dsymResult.exitCode}', ); - return stripResult.exitCode; + return dsymResult.exitCode; } + + if (stripAfterBuild) { + // See https://www.unix.com/man-page/osx/1/strip/ for arguments + final RunResult stripResult = await _xcode.strip([ + '-x', + aotSharedLibrary, + '-o', + aotSharedLibrary, + ]); + if (stripResult.exitCode != 0) { + _logger.printError( + 'Failed to strip debugging symbols from the generated AOT snapshot - strip terminated with exit code ${stripResult.exitCode}', + ); + return stripResult.exitCode; + } + } + } else { + assert(!stripAfterBuild); } - } else { - assert(!stripAfterBuild); } return 0; diff --git a/packages/flutter_tools/test/general.shard/base/build_test.dart b/packages/flutter_tools/test/general.shard/base/build_test.dart index 81e72f98c8f..577a1fea13f 100644 --- a/packages/flutter_tools/test/general.shard/base/build_test.dart +++ b/packages/flutter_tools/test/general.shard/base/build_test.dart @@ -16,27 +16,6 @@ const kWhichSysctlCommand = FakeCommand(command: ['which', 'sysctl']); const kARMCheckCommand = FakeCommand(command: ['sysctl', 'hw.optional.arm64'], exitCode: 1); -const kDefaultClang = [ - '-miphoneos-version-min=13.0', - '-isysroot', - 'path/to/sdk', - '-dynamiclib', - '-Xlinker', - '-rpath', - '-Xlinker', - '@executable_path/Frameworks', - '-Xlinker', - '-rpath', - '-Xlinker', - '@loader_path/Frameworks', - '-fapplication-extension', - '-install_name', - '@rpath/App.framework/App', - '-o', - 'build/foo/App.framework/App', - 'build/foo/snapshot_assembly.o', -]; - void main() { group('GenSnapshot', () { late GenSnapshot genSnapshot; @@ -191,7 +170,6 @@ void main() { testWithoutContext('builds iOS snapshot with dwarfStackTraces', () async { final String outputPath = fileSystem.path.join('build', 'foo'); - final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S'); final String debugPath = fileSystem.path.join('foo', 'app.ios-arm64.symbols'); final String genSnapshotPath = artifacts.getArtifactPath( Artifact.genSnapshotArm64, @@ -203,8 +181,12 @@ void main() { command: [ genSnapshotPath, '--deterministic', - '--snapshot_kind=app-aot-assembly', - '--assembly=$assembly', + '--snapshot_kind=app-aot-macho-dylib', + '--macho=$outputPath/App.framework/App', + '--macho-object=$outputPath/app.o', + '--macho-min-os-version=13.0', + '--macho-rpath=@executable_path/Frameworks,@loader_path/Frameworks', + '--macho-install-name=@rpath/App.framework/App', '--dwarf-stack-traces', '--resolve-dwarf-paths', '--save-debugging-info=$debugPath', @@ -213,39 +195,23 @@ void main() { ), kWhichSysctlCommand, kARMCheckCommand, - const FakeCommand( - command: [ - 'xcrun', - 'cc', - '-arch', - 'arm64', - '-miphoneos-version-min=13.0', - '-isysroot', - 'path/to/sdk', - '-c', - 'build/foo/snapshot_assembly.S', - '-o', - 'build/foo/snapshot_assembly.o', - ], - ), - const FakeCommand(command: ['xcrun', 'clang', '-arch', 'arm64', ...kDefaultClang]), - const FakeCommand( + FakeCommand( command: [ 'xcrun', 'dsymutil', '-o', - 'build/foo/App.framework.dSYM', - 'build/foo/App.framework/App', + '$outputPath/App.framework.dSYM', + '$outputPath/App.framework/App', ], ), - const FakeCommand( + FakeCommand( command: [ 'xcrun', 'strip', '-x', - 'build/foo/App.framework/App', + '$outputPath/App.framework/App', '-o', - 'build/foo/App.framework/App', + '$outputPath/App.framework/App', ], ), ]); @@ -267,7 +233,6 @@ void main() { testWithoutContext('builds iOS snapshot with obfuscate', () async { final String outputPath = fileSystem.path.join('build', 'foo'); - final String assembly = fileSystem.path.join(outputPath, 'snapshot_assembly.S'); final String genSnapshotPath = artifacts.getArtifactPath( Artifact.genSnapshotArm64, platform: TargetPlatform.ios, @@ -278,47 +243,35 @@ void main() { command: [ genSnapshotPath, '--deterministic', - '--snapshot_kind=app-aot-assembly', - '--assembly=$assembly', + '--snapshot_kind=app-aot-macho-dylib', + '--macho=$outputPath/App.framework/App', + '--macho-object=$outputPath/app.o', + '--macho-min-os-version=13.0', + '--macho-rpath=@executable_path/Frameworks,@loader_path/Frameworks', + '--macho-install-name=@rpath/App.framework/App', '--obfuscate', 'main.dill', ], ), kWhichSysctlCommand, kARMCheckCommand, - const FakeCommand( - command: [ - 'xcrun', - 'cc', - '-arch', - 'arm64', - '-miphoneos-version-min=13.0', - '-isysroot', - 'path/to/sdk', - '-c', - 'build/foo/snapshot_assembly.S', - '-o', - 'build/foo/snapshot_assembly.o', - ], - ), - const FakeCommand(command: ['xcrun', 'clang', '-arch', 'arm64', ...kDefaultClang]), - const FakeCommand( + FakeCommand( command: [ 'xcrun', 'dsymutil', '-o', - 'build/foo/App.framework.dSYM', - 'build/foo/App.framework/App', + '$outputPath/App.framework.dSYM', + '$outputPath/App.framework/App', ], ), - const FakeCommand( + FakeCommand( command: [ 'xcrun', 'strip', '-x', - 'build/foo/App.framework/App', + '$outputPath/App.framework/App', '-o', - 'build/foo/App.framework/App', + '$outputPath/App.framework/App', ], ), ]); @@ -349,46 +302,34 @@ void main() { command: [ genSnapshotPath, '--deterministic', - '--snapshot_kind=app-aot-assembly', - '--assembly=${fileSystem.path.join(outputPath, 'snapshot_assembly.S')}', + '--snapshot_kind=app-aot-macho-dylib', + '--macho=$outputPath/App.framework/App', + '--macho-object=$outputPath/app.o', + '--macho-min-os-version=13.0', + '--macho-rpath=@executable_path/Frameworks,@loader_path/Frameworks', + '--macho-install-name=@rpath/App.framework/App', 'main.dill', ], ), kWhichSysctlCommand, kARMCheckCommand, - const FakeCommand( - command: [ - 'xcrun', - 'cc', - '-arch', - 'arm64', - '-miphoneos-version-min=13.0', - '-isysroot', - 'path/to/sdk', - '-c', - 'build/foo/snapshot_assembly.S', - '-o', - 'build/foo/snapshot_assembly.o', - ], - ), - const FakeCommand(command: ['xcrun', 'clang', '-arch', 'arm64', ...kDefaultClang]), - const FakeCommand( + FakeCommand( command: [ 'xcrun', 'dsymutil', '-o', - 'build/foo/App.framework.dSYM', - 'build/foo/App.framework/App', + '$outputPath/App.framework.dSYM', + '$outputPath/App.framework/App', ], ), - const FakeCommand( + FakeCommand( command: [ 'xcrun', 'strip', '-x', - 'build/foo/App.framework/App', + '$outputPath/App.framework/App', '-o', - 'build/foo/App.framework/App', + '$outputPath/App.framework/App', ], ), ]); 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 e206d88db4b..bf4d4b1bdcb 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 @@ -23,7 +23,7 @@ import '../../../src/fake_process_manager.dart'; const kBoundaryKey = '4d2d9609-c662-4571-afde-31410f96caa6'; const kElfAot = '--snapshot_kind=app-aot-elf'; -const kAssemblyAot = '--snapshot_kind=app-aot-assembly'; +const kMachoDylibAot = '--snapshot_kind=app-aot-macho-dylib'; final Platform macPlatform = FakePlatform( operatingSystem: 'macos', @@ -803,52 +803,15 @@ void main() { '--deterministic', '--write-v8-snapshot-profile-to=code_size_1/snapshot.arm64.json', '--trace-precompiler-to=code_size_1/trace.arm64.json', - kAssemblyAot, - '--assembly=$build/arm64/snapshot_assembly.S', + kMachoDylibAot, + '--macho=$build/arm64/App.framework/App', + '--macho-object=$build/arm64/app.o', + '--macho-min-os-version=13.0', + '--macho-rpath=@executable_path/Frameworks,@loader_path/Frameworks', + '--macho-install-name=@rpath/App.framework/App', '$build/app.dill', ], ), - FakeCommand( - command: [ - 'xcrun', - 'cc', - '-arch', - 'arm64', - '-miphoneos-version-min=13.0', - '-isysroot', - 'path/to/iPhoneOS.sdk', - '-c', - '$build/arm64/snapshot_assembly.S', - '-o', - '$build/arm64/snapshot_assembly.o', - ], - ), - FakeCommand( - command: [ - 'xcrun', - 'clang', - '-arch', - 'arm64', - '-miphoneos-version-min=13.0', - '-isysroot', - 'path/to/iPhoneOS.sdk', - '-dynamiclib', - '-Xlinker', - '-rpath', - '-Xlinker', - '@executable_path/Frameworks', - '-Xlinker', - '-rpath', - '-Xlinker', - '@loader_path/Frameworks', - '-fapplication-extension', - '-install_name', - '@rpath/App.framework/App', - '-o', - '$build/arm64/App.framework/App', - '$build/arm64/snapshot_assembly.o', - ], - ), FakeCommand( command: [ 'xcrun', diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart index b01b86b46d4..e780e14f7a3 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart @@ -817,8 +817,12 @@ void main() { command: [ 'Artifact.genSnapshotArm64.TargetPlatform.darwin.release', '--deterministic', - '--snapshot_kind=app-aot-assembly', - '--assembly=${environment.buildDir.childFile('arm64/snapshot_assembly.S').path}', + '--snapshot_kind=app-aot-macho-dylib', + '--macho=${environment.buildDir.childFile('arm64/App.framework/App').path}', + '--macho-object=${environment.buildDir.childFile('arm64/app.o').path}', + '--macho-min-os-version=10.15', + '--macho-rpath=@executable_path/Frameworks,@loader_path/Frameworks', + '--macho-install-name=@rpath/App.framework/App', environment.buildDir.childFile('app.dill').path, ], ), @@ -826,81 +830,15 @@ void main() { command: [ 'Artifact.genSnapshotX64.TargetPlatform.darwin.release', '--deterministic', - '--snapshot_kind=app-aot-assembly', - '--assembly=${environment.buildDir.childFile('x86_64/snapshot_assembly.S').path}', + '--snapshot_kind=app-aot-macho-dylib', + '--macho=${environment.buildDir.childFile('x86_64/App.framework/App').path}', + '--macho-object=${environment.buildDir.childFile('x86_64/app.o').path}', + '--macho-min-os-version=10.15', + '--macho-rpath=@executable_path/Frameworks,@loader_path/Frameworks', + '--macho-install-name=@rpath/App.framework/App', environment.buildDir.childFile('app.dill').path, ], ), - FakeCommand( - command: [ - 'xcrun', - 'cc', - '-arch', - 'arm64', - '-c', - environment.buildDir.childFile('arm64/snapshot_assembly.S').path, - '-o', - environment.buildDir.childFile('arm64/snapshot_assembly.o').path, - ], - ), - FakeCommand( - command: [ - 'xcrun', - 'cc', - '-arch', - 'x86_64', - '-c', - environment.buildDir.childFile('x86_64/snapshot_assembly.S').path, - '-o', - environment.buildDir.childFile('x86_64/snapshot_assembly.o').path, - ], - ), - FakeCommand( - command: [ - 'xcrun', - 'clang', - '-arch', - 'arm64', - '-dynamiclib', - '-Xlinker', - '-rpath', - '-Xlinker', - '@executable_path/Frameworks', - '-Xlinker', - '-rpath', - '-Xlinker', - '@loader_path/Frameworks', - '-fapplication-extension', - '-install_name', - '@rpath/App.framework/App', - '-o', - environment.buildDir.childFile('arm64/App.framework/App').path, - environment.buildDir.childFile('arm64/snapshot_assembly.o').path, - ], - ), - FakeCommand( - command: [ - 'xcrun', - 'clang', - '-arch', - 'x86_64', - '-dynamiclib', - '-Xlinker', - '-rpath', - '-Xlinker', - '@executable_path/Frameworks', - '-Xlinker', - '-rpath', - '-Xlinker', - '@loader_path/Frameworks', - '-fapplication-extension', - '-install_name', - '@rpath/App.framework/App', - '-o', - environment.buildDir.childFile('x86_64/App.framework/App').path, - environment.buildDir.childFile('x86_64/snapshot_assembly.o').path, - ], - ), FakeCommand( command: [ 'xcrun',