diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh index 58224a4e73a..e86fb3f17f0 100755 --- a/packages/flutter_tools/bin/xcode_backend.sh +++ b/packages/flutter_tools/bin/xcode_backend.sh @@ -244,7 +244,7 @@ LipoExecutable() { all_executables+=("${output}") else echo "Failed to extract ${arch} for ${executable}. Running lipo -info:" - lipo -info "${executable}" + RunCommand lipo -info "${executable}" exit 1 fi fi @@ -254,10 +254,10 @@ LipoExecutable() { # Skip this step for non-fat executables. if [[ ${#all_executables[@]} > 0 ]]; then local merged="${executable}_merged" - lipo -output "${merged}" -create "${all_executables[@]}" + RunCommand lipo -output "${merged}" -create "${all_executables[@]}" - cp -f -- "${merged}" "${executable}" > /dev/null - rm -f -- "${merged}" "${all_executables[@]}" + RunCommand cp -f -- "${merged}" "${executable}" > /dev/null + RunCommand rm -f -- "${merged}" "${all_executables[@]}" fi } diff --git a/packages/flutter_tools/lib/src/build_system/targets/ios.dart b/packages/flutter_tools/lib/src/build_system/targets/ios.dart index 50c409a8fd0..639c12a6af9 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart @@ -10,7 +10,6 @@ import '../../base/io.dart'; import '../../base/process.dart'; import '../../build_info.dart'; import '../../globals.dart' as globals hide fs, logger, processManager, artifacts; -import '../../macos/xcode.dart'; import '../../project.dart'; import '../build_system.dart'; import '../depfile.dart'; @@ -209,45 +208,19 @@ class DebugUniversalFramework extends Target { @override Future build(Environment environment) async { // Generate a trivial App.framework. - final Set iosArchs = environment.defines[kIosArchs] + final Set iosArchNames = environment.defines[kIosArchs] ?.split(' ') - ?.map(getIOSArchForName) - ?.toSet() - ?? {DarwinArch.arm64}; - final File iphoneFile = environment.buildDir.childFile('iphone_framework'); - final File simulatorFile = environment.buildDir.childFile('simulator_framework'); - final File lipoOutputFile = environment.buildDir + ?.toSet(); + final File output = environment.buildDir .childDirectory('App.framework') .childFile('App'); - lipoOutputFile.parent.createSync(recursive: true); - final RunResult iphoneResult = await createStubAppFramework( - iphoneFile, - SdkType.iPhone, - // Only include 32bit if it is contained in the active architectures. - include32Bit: iosArchs.contains(DarwinArch.armv7) + environment.buildDir.createSync(recursive: true); + final RunResult createFrameworkResult = await createStubAppFramework( + output, + environment.defines[kSdkRoot], + iosArchNames, ); - final RunResult simulatorResult = await createStubAppFramework( - simulatorFile, - SdkType.iPhoneSimulator, - ); - if (iphoneResult.exitCode != 0 || simulatorResult.exitCode != 0) { - throw Exception('Failed to create App.framework.'); - } - final List lipoCommand = [ - ...globals.xcode.xcrunCommand(), - 'lipo', - '-create', - iphoneFile.path, - simulatorFile.path, - '-output', - lipoOutputFile.path, - ]; - - final RunResult lipoResult = await globals.processUtils.run( - lipoCommand, - ); - - if (lipoResult.exitCode != 0) { + if (createFrameworkResult.exitCode != 0) { throw Exception('Failed to create App.framework.'); } } @@ -410,7 +383,8 @@ class ReleaseIosApplicationBundle extends IosAssetBundle { /// This framework needs to exist for the Xcode project to link/bundle, /// but it isn't actually executed. To generate something valid, we compile a trivial /// constant. -Future createStubAppFramework(File outputFile, SdkType sdk, { bool include32Bit = true }) async { +Future createStubAppFramework(File outputFile, String sdkRoot, + Set iosArchNames) async { try { outputFile.createSync(recursive: true); } on Exception catch (e) { @@ -425,32 +399,17 @@ Future createStubAppFramework(File outputFile, SdkType sdk, { bool in static const int Moo = 88; '''); - List archFlags; - if (sdk == SdkType.iPhone) { - archFlags = [ - if (include32Bit) - ...['-arch', getNameForDarwinArch(DarwinArch.armv7)], - '-arch', - getNameForDarwinArch(DarwinArch.arm64), - ]; - } else { - archFlags = [ - '-arch', - getNameForDarwinArch(DarwinArch.x86_64), - ]; - } - return await globals.xcode.clang([ '-x', 'c', - ...archFlags, + for (String arch in iosArchNames) ...['-arch', arch], stubSource.path, '-dynamiclib', '-fembed-bitcode-marker', '-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks', '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks', '-install_name', '@rpath/App.framework/App', - '-isysroot', await globals.xcode.sdkLocation(sdk), + '-isysroot', sdkRoot, '-o', outputFile.path, ]); } finally { diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart index 13785abbb8d..a00a5e732c5 100644 --- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart @@ -203,12 +203,15 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { } // Build aot, create module.framework and copy. - await _produceAppFramework(buildInfo, modeDirectory); + final Directory iPhoneBuildOutput = + modeDirectory.childDirectory('iphoneos'); + final Directory simulatorBuildOutput = + modeDirectory.childDirectory('iphonesimulator'); + await _produceAppFramework( + buildInfo, modeDirectory, iPhoneBuildOutput, simulatorBuildOutput); // Build and copy plugins. await processPodsIfNeeded(_project.ios, getIosBuildDirectory(), buildInfo.mode); - final Directory iPhoneBuildOutput = modeDirectory.childDirectory('iphoneos'); - final Directory simulatorBuildOutput = modeDirectory.childDirectory('iphonesimulator'); if (hasPlugins(_project)) { await _producePlugins(buildInfo.mode, xcodeBuildConfiguration, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory, outputDirectory); } @@ -348,64 +351,81 @@ end await _produceXCFrameworkFromUniversal(buildInfo, fatFlutterFrameworkCopy); } - Future _produceAppFramework(BuildInfo buildInfo, Directory modeDirectory) async { + Future _produceAppFramework( + BuildInfo buildInfo, + Directory outputDirectory, + Directory iPhoneBuildOutput, + Directory simulatorBuildOutput, + ) async { const String appFrameworkName = 'App.framework'; final Status status = globals.logger.startProgress( ' ├─Building App.framework...', ); - try { - Target target; - if (buildInfo.isDebug) { - target = const DebugIosApplicationBundle(); - } else if (buildInfo.isProfile) { - target = const ProfileIosApplicationBundle(); - } else { - target = const ReleaseIosApplicationBundle(); - } + final List sdkTypes = [SdkType.iPhone]; + final List frameworks = []; + Target target; + if (buildInfo.isDebug) { + sdkTypes.add(SdkType.iPhoneSimulator); + target = const DebugIosApplicationBundle(); + } else if (buildInfo.isProfile) { + target = const ProfileIosApplicationBundle(); + } else { + target = const ReleaseIosApplicationBundle(); + } - final Environment environment = Environment( - projectDir: globals.fs.currentDirectory, - outputDir: modeDirectory, - buildDir: _project.dartTool.childDirectory('flutter_build'), - cacheDir: null, - flutterRootDir: globals.fs.directory(Cache.flutterRoot), - defines: { - kTargetFile: targetFile, - kBuildMode: getNameForBuildMode(buildInfo.mode), - kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios), - kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(), - kDartDefines: jsonEncode(buildInfo.dartDefines), - kBitcodeFlag: 'true', - if (buildInfo?.extraGenSnapshotOptions?.isNotEmpty ?? false) - kExtraGenSnapshotOptions: buildInfo.extraGenSnapshotOptions.join(','), - if (buildInfo?.extraFrontEndOptions?.isNotEmpty ?? false) - kExtraFrontEndOptions: buildInfo.extraFrontEndOptions.join(','), - kIosArchs: [DarwinArch.armv7, DarwinArch.arm64] - .map(getNameForDarwinArch).join(' '), - kSdkRoot: await globals.xcode.sdkLocation(SdkType.iPhone), - }, - artifacts: globals.artifacts, - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - engineVersion: globals.artifacts.isLocalEngine - ? null - : globals.flutterVersion.engineRevision, - ); - final BuildResult result = await buildSystem.build(target, environment); - if (!result.success) { - for (final ExceptionMeasurement measurement in result.exceptions.values) { - globals.printError(measurement.exception.toString()); + try { + for (final SdkType sdkType in sdkTypes) { + final Directory outputBuildDirectory = sdkType == SdkType.iPhone + ? iPhoneBuildOutput + : simulatorBuildOutput; + frameworks.add(outputBuildDirectory.childDirectory(appFrameworkName)); + final Environment environment = Environment( + projectDir: globals.fs.currentDirectory, + outputDir: outputBuildDirectory, + buildDir: _project.dartTool.childDirectory('flutter_build'), + cacheDir: null, + flutterRootDir: globals.fs.directory(Cache.flutterRoot), + defines: { + kTargetFile: targetFile, + kBuildMode: getNameForBuildMode(buildInfo.mode), + kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios), + kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(), + kDartDefines: jsonEncode(buildInfo.dartDefines), + kBitcodeFlag: 'true', + if (buildInfo?.extraGenSnapshotOptions?.isNotEmpty ?? false) + kExtraGenSnapshotOptions: + buildInfo.extraGenSnapshotOptions.join(','), + if (buildInfo?.extraFrontEndOptions?.isNotEmpty ?? false) + kExtraFrontEndOptions: buildInfo.extraFrontEndOptions.join(','), + kIosArchs: defaultIOSArchsForSdk(sdkType) + .map(getNameForDarwinArch) + .join(' '), + kSdkRoot: await globals.xcode.sdkLocation(sdkType), + }, + artifacts: globals.artifacts, + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + engineVersion: globals.artifacts.isLocalEngine + ? null + : globals.flutterVersion.engineRevision, + ); + final BuildResult result = await buildSystem.build(target, environment); + if (!result.success) { + for (final ExceptionMeasurement measurement + in result.exceptions.values) { + globals.printError(measurement.exception.toString()); + } + throwToolExit('The App.framework build failed.'); } - throwToolExit('The App.framework build failed.'); } } finally { status.stop(); } - final Directory destinationAppFrameworkDirectory = modeDirectory.childDirectory(appFrameworkName); - await _produceXCFrameworkFromUniversal(buildInfo, destinationAppFrameworkDirectory); + await _produceUniversalFramework(frameworks, 'App', outputDirectory); + await _produceXCFramework(frameworks, 'App', outputDirectory); } Future _producePlugins( @@ -437,7 +457,6 @@ end 'iphoneos', '-configuration', xcodeBuildConfiguration, - '-destination generic/platform=iOS', 'SYMROOT=${iPhoneBuildOutput.path}', 'BITCODE_GENERATION_MODE=$bitcodeGenerationMode', 'ONLY_ACTIVE_ARCH=NO', // No device targeted, so build all valid architectures. @@ -463,7 +482,6 @@ end 'iphonesimulator', '-configuration', xcodeBuildConfiguration, - '-destination generic/platform=iOS', 'SYMROOT=${simulatorBuildOutput.path}', 'ARCHS=x86_64', 'ONLY_ACTIVE_ARCH=NO', // No device targeted, so build all valid architectures. diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart index 5b4f1dc3e12..1cc62091729 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart @@ -35,6 +35,7 @@ const List _kSharedConfig = [ '-install_name', '@rpath/App.framework/App', '-isysroot', + 'path/to/sdk', ]; void main() { @@ -70,49 +71,26 @@ void main() { testUsingContext('DebugUniveralFramework creates expected binary with arm64 only arch', () async { environment.defines[kIosArchs] = 'arm64'; - processManager.addCommands([ - const FakeCommand(command: ['xcrun', '--sdk', 'iphoneos', '--show-sdk-path']), + environment.defines[kSdkRoot] = 'path/to/sdk'; + processManager.addCommand( FakeCommand(command: [ 'xcrun', 'clang', '-x', 'c', - // iphone only gets 64 bit arch based on kIosArchs + // iphone only gets 64 bit arch based on kIosArchs '-arch', 'arm64', - fileSystem.path.absolute(fileSystem.path.join('.tmp_rand0', 'flutter_tools_stub_source.rand0', 'debug_app.cc')), + fileSystem.path.absolute(fileSystem.path.join( + '.tmp_rand0', 'flutter_tools_stub_source.rand0', 'debug_app.cc')), ..._kSharedConfig, - '', '-o', - environment.buildDir.childFile('iphone_framework').path + environment.buildDir + .childDirectory('App.framework') + .childFile('App') + .path, ]), - // Create simulator stub. - const FakeCommand(command: ['xcrun', '--sdk', 'iphonesimulator', '--show-sdk-path']), - FakeCommand(command: [ - 'xcrun', - 'clang', - '-x', - 'c', - // Simulator only as x86_64 arch - '-arch', - 'x86_64', - fileSystem.path.absolute(fileSystem.path.join('.tmp_rand0', 'flutter_tools_stub_source.rand0', 'debug_app.cc')), - ..._kSharedConfig, - '', - '-o', - environment.buildDir.childFile('simulator_framework').path - ]), - // Lipo stubs together. - FakeCommand(command: [ - 'xcrun', - 'lipo', - '-create', - environment.buildDir.childFile('iphone_framework').path, - environment.buildDir.childFile('simulator_framework').path, - '-output', - environment.buildDir.childDirectory('App.framework').childFile('App').path, - ]), - ]); + ); await const DebugUniversalFramework().build(environment); }, overrides: {