Add bitcode and architectures to App.framework build ios framework command (#46130)
diff --git a/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart b/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart
index 2b11560..d23ce3c 100644
--- a/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart
+++ b/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart
@@ -79,18 +79,33 @@
'App',
));
- final String aotSymbols = await dylibSymbols(path.join(
+ final String appFrameworkPath = path.join(
outputPath,
'Debug',
'App.framework',
'App',
- ));
+ );
+ final String aotSymbols = await dylibSymbols(appFrameworkPath);
if (aotSymbols.contains('architecture') ||
aotSymbols.contains('_kDartVmSnapshot')) {
throw TaskResult.failure('Debug App.framework contains AOT');
}
+ final String debugAppArchs = await fileType(appFrameworkPath);
+
+ if (!debugAppArchs.contains('armv7')) {
+ throw TaskResult.failure('Debug App.framework armv7 architecture missing');
+ }
+
+ if (!debugAppArchs.contains('arm64')) {
+ throw TaskResult.failure('Debug App.framework arm64 architecture missing');
+ }
+
+ if (!debugAppArchs.contains('x86_64')) {
+ throw TaskResult.failure('Debug App.framework x86_64 architecture missing');
+ }
+
section('Check profile, release builds has Dart AOT dylib');
for (String mode in <String>['Profile', 'Release']) {
@@ -116,6 +131,10 @@
throw TaskResult.failure('$mode App.framework arm64 architecture missing');
}
+ if (aotSymbols.contains('x86_64')) {
+ throw TaskResult.failure('$mode App.framework contains x86_64 architecture');
+ }
+
if (!aotSymbols.contains('_kDartVmSnapshot')) {
throw TaskResult.failure('$mode App.framework missing Dart AOT');
}
diff --git a/dev/devicelab/lib/framework/ios.dart b/dev/devicelab/lib/framework/ios.dart
index 01ffd0c..c3380fe 100644
--- a/dev/devicelab/lib/framework/ios.dart
+++ b/dev/devicelab/lib/framework/ios.dart
@@ -53,3 +53,7 @@
Future<String> dylibSymbols(String pathToDylib) {
return eval('nm', <String>['-g', pathToDylib]);
}
+
+Future<String> fileType(String pathToDylib) {
+ return eval('file', <String>[pathToDylib]);
+}
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart
index cc1be54..32aa189 100644
--- a/packages/flutter_tools/lib/src/base/build.dart
+++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -244,7 +244,7 @@
final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o');
List<String> isysrootArgs;
if (isIOS) {
- final String iPhoneSDKLocation = await xcode.iPhoneSdkLocation();
+ final String iPhoneSDKLocation = await xcode.sdkLocation(SdkType.iPhone);
if (iPhoneSDKLocation != null) {
isysrootArgs = <String>['-isysroot', iPhoneSDKLocation];
}
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 3b24bb3..47b41f4 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
@@ -157,17 +157,11 @@
/// 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<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async {
- File outputFile;
+Future<RunResult> createStubAppFramework(File outputFile, SdkType sdk) async {
try {
- if (!appFrameworkDirectory.existsSync()) {
- appFrameworkDirectory.createSync(recursive: true);
- }
-
- outputFile = appFrameworkDirectory.childFile('App');
outputFile.createSync(recursive: true);
} catch (e) {
- throwToolExit('Failed to create App.framework stub at ${appFrameworkDirectory.path}');
+ throwToolExit('Failed to create App.framework stub at ${outputFile.path}');
}
final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_stub_source.');
@@ -177,14 +171,32 @@
static const int Moo = 88;
''');
+ List<String> archFlags;
+ if (sdk == SdkType.iPhone) {
+ archFlags = <String>[
+ '-arch',
+ getNameForDarwinArch(DarwinArch.armv7),
+ '-arch',
+ getNameForDarwinArch(DarwinArch.arm64),
+ ];
+ } else {
+ archFlags = <String>[
+ '-arch',
+ getNameForDarwinArch(DarwinArch.x86_64),
+ ];
+ }
+
return await xcode.clang(<String>[
'-x',
'c',
+ ...archFlags,
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 xcode.sdkLocation(sdk),
'-o', outputFile.path,
]);
} finally {
@@ -192,6 +204,8 @@
tempDir.deleteSync(recursive: true);
} on FileSystemException catch (_) {
// Best effort. Sometimes we can't delete things from system temp.
+ } catch (e) {
+ throwToolExit('Failed to create App.framework stub at ${outputFile.path}');
}
}
}
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 d8dfb32..96a5c7d 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
@@ -166,7 +166,7 @@
await _produceFlutterFramework(outputDirectory, mode, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory);
// Build aot, create module.framework and copy.
- await _produceAppFramework(mode, iPhoneBuildOutput, modeDirectory);
+ await _produceAppFramework(mode, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory);
// Build and copy plugins.
await processPodsIfNeeded(_project.ios, getIosBuildDirectory(), mode);
@@ -254,13 +254,14 @@
status.stop();
}
- Future<void> _produceAppFramework(BuildMode mode, Directory iPhoneBuildOutput, Directory modeDirectory) async {
+ Future<void> _produceAppFramework(BuildMode mode, Directory iPhoneBuildOutput, Directory simulatorBuildOutput, Directory modeDirectory) async {
const String appFrameworkName = 'App.framework';
final Directory destinationAppFrameworkDirectory = modeDirectory.childDirectory(appFrameworkName);
+ destinationAppFrameworkDirectory.createSync(recursive: true);
if (mode == BuildMode.debug) {
final Status status = logger.startProgress(' ├─Add placeholder App.framework for debug...', timeout: timeoutConfiguration.fastOperation);
- await createStubAppFramework(destinationAppFrameworkDirectory);
+ await _produceStubAppFrameworkIfNeeded(mode, iPhoneBuildOutput, simulatorBuildOutput, destinationAppFrameworkDirectory);
status.stop();
} else {
await _produceAotAppFrameworkIfNeeded(mode, iPhoneBuildOutput, destinationAppFrameworkDirectory);
@@ -283,6 +284,37 @@
status.stop();
}
+ Future<void> _produceStubAppFrameworkIfNeeded(BuildMode mode, Directory iPhoneBuildOutput, Directory simulatorBuildOutput, Directory destinationAppFrameworkDirectory) async {
+ if (mode != BuildMode.debug) {
+ return;
+ }
+ const String appFrameworkName = 'App.framework';
+ const String binaryName = 'App';
+
+ final Directory iPhoneAppFrameworkDirectory = iPhoneBuildOutput.childDirectory(appFrameworkName);
+ final File iPhoneAppFrameworkFile = iPhoneAppFrameworkDirectory.childFile(binaryName);
+ await createStubAppFramework(iPhoneAppFrameworkFile, SdkType.iPhone);
+
+ final Directory simulatorAppFrameworkDirectory = simulatorBuildOutput.childDirectory(appFrameworkName);
+ final File simulatorAppFrameworkFile = simulatorAppFrameworkDirectory.childFile(binaryName);
+ await createStubAppFramework(simulatorAppFrameworkFile, SdkType.iPhoneSimulator);
+
+ final List<String> lipoCommand = <String>[
+ 'xcrun',
+ 'lipo',
+ '-create',
+ iPhoneAppFrameworkFile.path,
+ simulatorAppFrameworkFile.path,
+ '-output',
+ destinationAppFrameworkDirectory.childFile(binaryName).path
+ ];
+
+ await processUtils.run(
+ lipoCommand,
+ allowReentrantFlutter: false,
+ );
+ }
+
Future<void> _produceAotAppFrameworkIfNeeded(BuildMode mode, Directory iPhoneBuildOutput, Directory destinationAppFrameworkDirectory) async {
if (mode == BuildMode.debug) {
return;
@@ -295,6 +327,7 @@
// Relative paths show noise in the compiler https://github.com/dart-lang/sdk/issues/37978.
mainDartFile: fs.path.absolute(targetFile),
quiet: true,
+ bitcode: true,
reportTimings: false,
iosBuildArchs: <DarwinArch>[DarwinArch.armv7, DarwinArch.arm64],
dartDefines: dartDefines,
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart
index 28a733d..6c40ce8 100644
--- a/packages/flutter_tools/lib/src/macos/xcode.dart
+++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -17,6 +17,31 @@
Xcode get xcode => context.get<Xcode>();
+enum SdkType {
+ iPhone,
+ iPhoneSimulator,
+ macOS,
+}
+
+/// SDK name passed to `xcrun --sdk`. Corresponds to undocumented Xcode
+/// SUPPORTED_PLATFORMS values.
+///
+/// Usage: xcrun [options] <tool name> ... arguments ...
+/// ...
+/// --sdk <sdk name> find the tool for the given SDK name
+String getNameForSdk(SdkType sdk) {
+ switch (sdk) {
+ case SdkType.iPhone:
+ return 'iphoneos';
+ case SdkType.iPhoneSimulator:
+ return 'iphonesimulator';
+ case SdkType.macOS:
+ return 'macosx';
+ }
+ assert(false);
+ return null;
+}
+
class Xcode {
bool get isInstalledAndMeetsVersionCheck => platform.isMacOS && isInstalled && isVersionSatisfactory;
@@ -117,9 +142,10 @@
);
}
- Future<String> iPhoneSdkLocation() async {
+ Future<String> sdkLocation(SdkType sdk) async {
+ assert(sdk != null);
final RunResult runResult = await processUtils.run(
- <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
+ <String>['xcrun', '--sdk', getNameForSdk(sdk), '--show-sdk-path'],
throwOnError: true,
);
if (runResult.exitCode != 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 07ef6a0..ecea2c3 100644
--- a/packages/flutter_tools/test/general.shard/base/build_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/build_test.dart
@@ -244,7 +244,7 @@
mockAndroidSdk = MockAndroidSdk();
mockArtifacts = MockArtifacts();
mockXcode = MockXcode();
- when(mockXcode.iPhoneSdkLocation()).thenAnswer((_) => Future<String>.value(kSDKPath));
+ when(mockXcode.sdkLocation(any)).thenAnswer((_) => Future<String>.value(kSDKPath));
bufferLogger = BufferLogger();
for (BuildMode mode in BuildMode.values) {
diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
index 8e20841..8a56721 100644
--- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
@@ -190,5 +190,11 @@
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
+
+ testUsingContext('SDK name', () {
+ expect(getNameForSdk(SdkType.iPhone), 'iphoneos');
+ expect(getNameForSdk(SdkType.iPhoneSimulator), 'iphonesimulator');
+ expect(getNameForSdk(SdkType.macOS), 'macosx');
+ });
});
}