Unverified Commit fb69a393 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Add bitcode and architectures to App.framework build ios framework command (#46130)

parent e98acc7d
...@@ -79,18 +79,33 @@ Future<void> main() async { ...@@ -79,18 +79,33 @@ Future<void> main() async {
'App', 'App',
)); ));
final String aotSymbols = await dylibSymbols(path.join( final String appFrameworkPath = path.join(
outputPath, outputPath,
'Debug', 'Debug',
'App.framework', 'App.framework',
'App', 'App',
)); );
final String aotSymbols = await dylibSymbols(appFrameworkPath);
if (aotSymbols.contains('architecture') || if (aotSymbols.contains('architecture') ||
aotSymbols.contains('_kDartVmSnapshot')) { aotSymbols.contains('_kDartVmSnapshot')) {
throw TaskResult.failure('Debug App.framework contains AOT'); 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'); section('Check profile, release builds has Dart AOT dylib');
for (String mode in <String>['Profile', 'Release']) { for (String mode in <String>['Profile', 'Release']) {
...@@ -116,6 +131,10 @@ Future<void> main() async { ...@@ -116,6 +131,10 @@ Future<void> main() async {
throw TaskResult.failure('$mode App.framework arm64 architecture missing'); 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')) { if (!aotSymbols.contains('_kDartVmSnapshot')) {
throw TaskResult.failure('$mode App.framework missing Dart AOT'); throw TaskResult.failure('$mode App.framework missing Dart AOT');
} }
......
...@@ -53,3 +53,7 @@ Future<Map<String, dynamic>> measureIosCpuGpu({ ...@@ -53,3 +53,7 @@ Future<Map<String, dynamic>> measureIosCpuGpu({
Future<String> dylibSymbols(String pathToDylib) { Future<String> dylibSymbols(String pathToDylib) {
return eval('nm', <String>['-g', pathToDylib]); return eval('nm', <String>['-g', pathToDylib]);
} }
Future<String> fileType(String pathToDylib) {
return eval('file', <String>[pathToDylib]);
}
...@@ -244,7 +244,7 @@ class AOTSnapshotter { ...@@ -244,7 +244,7 @@ class AOTSnapshotter {
final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o'); final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o');
List<String> isysrootArgs; List<String> isysrootArgs;
if (isIOS) { if (isIOS) {
final String iPhoneSDKLocation = await xcode.iPhoneSdkLocation(); final String iPhoneSDKLocation = await xcode.sdkLocation(SdkType.iPhone);
if (iPhoneSDKLocation != null) { if (iPhoneSDKLocation != null) {
isysrootArgs = <String>['-isysroot', iPhoneSDKLocation]; isysrootArgs = <String>['-isysroot', iPhoneSDKLocation];
} }
......
...@@ -157,17 +157,11 @@ class AotAssemblyProfile extends AotAssemblyBase { ...@@ -157,17 +157,11 @@ class AotAssemblyProfile extends AotAssemblyBase {
/// This framework needs to exist for the Xcode project to link/bundle, /// 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 /// but it isn't actually executed. To generate something valid, we compile a trivial
/// constant. /// constant.
Future<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async { Future<RunResult> createStubAppFramework(File outputFile, SdkType sdk) async {
File outputFile;
try { try {
if (!appFrameworkDirectory.existsSync()) {
appFrameworkDirectory.createSync(recursive: true);
}
outputFile = appFrameworkDirectory.childFile('App');
outputFile.createSync(recursive: true); outputFile.createSync(recursive: true);
} catch (e) { } 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.'); final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_stub_source.');
...@@ -177,14 +171,32 @@ Future<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async ...@@ -177,14 +171,32 @@ Future<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async
static const int Moo = 88; 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>[ return await xcode.clang(<String>[
'-x', '-x',
'c', 'c',
...archFlags,
stubSource.path, stubSource.path,
'-dynamiclib', '-dynamiclib',
'-fembed-bitcode-marker',
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks', '-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks', '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
'-install_name', '@rpath/App.framework/App', '-install_name', '@rpath/App.framework/App',
'-isysroot', await xcode.sdkLocation(sdk),
'-o', outputFile.path, '-o', outputFile.path,
]); ]);
} finally { } finally {
...@@ -192,6 +204,8 @@ Future<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async ...@@ -192,6 +204,8 @@ Future<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async
tempDir.deleteSync(recursive: true); tempDir.deleteSync(recursive: true);
} on FileSystemException catch (_) { } on FileSystemException catch (_) {
// Best effort. Sometimes we can't delete things from system temp. // Best effort. Sometimes we can't delete things from system temp.
} catch (e) {
throwToolExit('Failed to create App.framework stub at ${outputFile.path}');
} }
} }
} }
...@@ -166,7 +166,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { ...@@ -166,7 +166,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
await _produceFlutterFramework(outputDirectory, mode, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory); await _produceFlutterFramework(outputDirectory, mode, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory);
// Build aot, create module.framework and copy. // Build aot, create module.framework and copy.
await _produceAppFramework(mode, iPhoneBuildOutput, modeDirectory); await _produceAppFramework(mode, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory);
// Build and copy plugins. // Build and copy plugins.
await processPodsIfNeeded(_project.ios, getIosBuildDirectory(), mode); await processPodsIfNeeded(_project.ios, getIosBuildDirectory(), mode);
...@@ -254,13 +254,14 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { ...@@ -254,13 +254,14 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
status.stop(); 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'; const String appFrameworkName = 'App.framework';
final Directory destinationAppFrameworkDirectory = modeDirectory.childDirectory(appFrameworkName); final Directory destinationAppFrameworkDirectory = modeDirectory.childDirectory(appFrameworkName);
destinationAppFrameworkDirectory.createSync(recursive: true);
if (mode == BuildMode.debug) { if (mode == BuildMode.debug) {
final Status status = logger.startProgress(' ├─Add placeholder App.framework for debug...', timeout: timeoutConfiguration.fastOperation); 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(); status.stop();
} else { } else {
await _produceAotAppFrameworkIfNeeded(mode, iPhoneBuildOutput, destinationAppFrameworkDirectory); await _produceAotAppFrameworkIfNeeded(mode, iPhoneBuildOutput, destinationAppFrameworkDirectory);
...@@ -283,6 +284,37 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { ...@@ -283,6 +284,37 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
status.stop(); 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 { Future<void> _produceAotAppFrameworkIfNeeded(BuildMode mode, Directory iPhoneBuildOutput, Directory destinationAppFrameworkDirectory) async {
if (mode == BuildMode.debug) { if (mode == BuildMode.debug) {
return; return;
...@@ -295,6 +327,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { ...@@ -295,6 +327,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
// Relative paths show noise in the compiler https://github.com/dart-lang/sdk/issues/37978. // Relative paths show noise in the compiler https://github.com/dart-lang/sdk/issues/37978.
mainDartFile: fs.path.absolute(targetFile), mainDartFile: fs.path.absolute(targetFile),
quiet: true, quiet: true,
bitcode: true,
reportTimings: false, reportTimings: false,
iosBuildArchs: <DarwinArch>[DarwinArch.armv7, DarwinArch.arm64], iosBuildArchs: <DarwinArch>[DarwinArch.armv7, DarwinArch.arm64],
dartDefines: dartDefines, dartDefines: dartDefines,
......
...@@ -17,6 +17,31 @@ const int kXcodeRequiredVersionMinor = 2; ...@@ -17,6 +17,31 @@ const int kXcodeRequiredVersionMinor = 2;
Xcode get xcode => context.get<Xcode>(); 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 { class Xcode {
bool get isInstalledAndMeetsVersionCheck => platform.isMacOS && isInstalled && isVersionSatisfactory; bool get isInstalledAndMeetsVersionCheck => platform.isMacOS && isInstalled && isVersionSatisfactory;
...@@ -117,9 +142,10 @@ class Xcode { ...@@ -117,9 +142,10 @@ class Xcode {
); );
} }
Future<String> iPhoneSdkLocation() async { Future<String> sdkLocation(SdkType sdk) async {
assert(sdk != null);
final RunResult runResult = await processUtils.run( final RunResult runResult = await processUtils.run(
<String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'], <String>['xcrun', '--sdk', getNameForSdk(sdk), '--show-sdk-path'],
throwOnError: true, throwOnError: true,
); );
if (runResult.exitCode != 0) { if (runResult.exitCode != 0) {
......
...@@ -244,7 +244,7 @@ void main() { ...@@ -244,7 +244,7 @@ void main() {
mockAndroidSdk = MockAndroidSdk(); mockAndroidSdk = MockAndroidSdk();
mockArtifacts = MockArtifacts(); mockArtifacts = MockArtifacts();
mockXcode = MockXcode(); mockXcode = MockXcode();
when(mockXcode.iPhoneSdkLocation()).thenAnswer((_) => Future<String>.value(kSDKPath)); when(mockXcode.sdkLocation(any)).thenAnswer((_) => Future<String>.value(kSDKPath));
bufferLogger = BufferLogger(); bufferLogger = BufferLogger();
for (BuildMode mode in BuildMode.values) { for (BuildMode mode in BuildMode.values) {
......
...@@ -190,5 +190,11 @@ void main() { ...@@ -190,5 +190,11 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
testUsingContext('SDK name', () {
expect(getNameForSdk(SdkType.iPhone), 'iphoneos');
expect(getNameForSdk(SdkType.iPhoneSimulator), 'iphonesimulator');
expect(getNameForSdk(SdkType.macOS), 'macosx');
});
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment