Skip to content

Commit fb69a39

Browse files
authored
Add bitcode and architectures to App.framework build ios framework command (#46130)
1 parent e98acc7 commit fb69a39

File tree

8 files changed

+119
-17
lines changed

8 files changed

+119
-17
lines changed

dev/devicelab/bin/tasks/build_ios_framework_module_test.dart

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,33 @@ Future<void> main() async {
7979
'App',
8080
));
8181

82-
final String aotSymbols = await dylibSymbols(path.join(
82+
final String appFrameworkPath = path.join(
8383
outputPath,
8484
'Debug',
8585
'App.framework',
8686
'App',
87-
));
87+
);
88+
final String aotSymbols = await dylibSymbols(appFrameworkPath);
8889

8990
if (aotSymbols.contains('architecture') ||
9091
aotSymbols.contains('_kDartVmSnapshot')) {
9192
throw TaskResult.failure('Debug App.framework contains AOT');
9293
}
9394

95+
final String debugAppArchs = await fileType(appFrameworkPath);
96+
97+
if (!debugAppArchs.contains('armv7')) {
98+
throw TaskResult.failure('Debug App.framework armv7 architecture missing');
99+
}
100+
101+
if (!debugAppArchs.contains('arm64')) {
102+
throw TaskResult.failure('Debug App.framework arm64 architecture missing');
103+
}
104+
105+
if (!debugAppArchs.contains('x86_64')) {
106+
throw TaskResult.failure('Debug App.framework x86_64 architecture missing');
107+
}
108+
94109
section('Check profile, release builds has Dart AOT dylib');
95110

96111
for (String mode in <String>['Profile', 'Release']) {
@@ -116,6 +131,10 @@ Future<void> main() async {
116131
throw TaskResult.failure('$mode App.framework arm64 architecture missing');
117132
}
118133

134+
if (aotSymbols.contains('x86_64')) {
135+
throw TaskResult.failure('$mode App.framework contains x86_64 architecture');
136+
}
137+
119138
if (!aotSymbols.contains('_kDartVmSnapshot')) {
120139
throw TaskResult.failure('$mode App.framework missing Dart AOT');
121140
}

dev/devicelab/lib/framework/ios.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ Future<Map<String, dynamic>> measureIosCpuGpu({
5353
Future<String> dylibSymbols(String pathToDylib) {
5454
return eval('nm', <String>['-g', pathToDylib]);
5555
}
56+
57+
Future<String> fileType(String pathToDylib) {
58+
return eval('file', <String>[pathToDylib]);
59+
}

packages/flutter_tools/lib/src/base/build.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ class AOTSnapshotter {
244244
final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o');
245245
List<String> isysrootArgs;
246246
if (isIOS) {
247-
final String iPhoneSDKLocation = await xcode.iPhoneSdkLocation();
247+
final String iPhoneSDKLocation = await xcode.sdkLocation(SdkType.iPhone);
248248
if (iPhoneSDKLocation != null) {
249249
isysrootArgs = <String>['-isysroot', iPhoneSDKLocation];
250250
}

packages/flutter_tools/lib/src/build_system/targets/ios.dart

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,11 @@ class AotAssemblyProfile extends AotAssemblyBase {
157157
/// This framework needs to exist for the Xcode project to link/bundle,
158158
/// but it isn't actually executed. To generate something valid, we compile a trivial
159159
/// constant.
160-
Future<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async {
161-
File outputFile;
160+
Future<RunResult> createStubAppFramework(File outputFile, SdkType sdk) async {
162161
try {
163-
if (!appFrameworkDirectory.existsSync()) {
164-
appFrameworkDirectory.createSync(recursive: true);
165-
}
166-
167-
outputFile = appFrameworkDirectory.childFile('App');
168162
outputFile.createSync(recursive: true);
169163
} catch (e) {
170-
throwToolExit('Failed to create App.framework stub at ${appFrameworkDirectory.path}');
164+
throwToolExit('Failed to create App.framework stub at ${outputFile.path}');
171165
}
172166

173167
final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_stub_source.');
@@ -177,21 +171,41 @@ Future<RunResult> createStubAppFramework(Directory appFrameworkDirectory) async
177171
static const int Moo = 88;
178172
''');
179173

174+
List<String> archFlags;
175+
if (sdk == SdkType.iPhone) {
176+
archFlags = <String>[
177+
'-arch',
178+
getNameForDarwinArch(DarwinArch.armv7),
179+
'-arch',
180+
getNameForDarwinArch(DarwinArch.arm64),
181+
];
182+
} else {
183+
archFlags = <String>[
184+
'-arch',
185+
getNameForDarwinArch(DarwinArch.x86_64),
186+
];
187+
}
188+
180189
return await xcode.clang(<String>[
181190
'-x',
182191
'c',
192+
...archFlags,
183193
stubSource.path,
184194
'-dynamiclib',
195+
'-fembed-bitcode-marker',
185196
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
186197
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
187198
'-install_name', '@rpath/App.framework/App',
199+
'-isysroot', await xcode.sdkLocation(sdk),
188200
'-o', outputFile.path,
189201
]);
190202
} finally {
191203
try {
192204
tempDir.deleteSync(recursive: true);
193205
} on FileSystemException catch (_) {
194206
// Best effort. Sometimes we can't delete things from system temp.
207+
} catch (e) {
208+
throwToolExit('Failed to create App.framework stub at ${outputFile.path}');
195209
}
196210
}
197211
}

packages/flutter_tools/lib/src/commands/build_ios_framework.dart

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
166166
await _produceFlutterFramework(outputDirectory, mode, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory);
167167

168168
// Build aot, create module.framework and copy.
169-
await _produceAppFramework(mode, iPhoneBuildOutput, modeDirectory);
169+
await _produceAppFramework(mode, iPhoneBuildOutput, simulatorBuildOutput, modeDirectory);
170170

171171
// Build and copy plugins.
172172
await processPodsIfNeeded(_project.ios, getIosBuildDirectory(), mode);
@@ -254,13 +254,14 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
254254
status.stop();
255255
}
256256

257-
Future<void> _produceAppFramework(BuildMode mode, Directory iPhoneBuildOutput, Directory modeDirectory) async {
257+
Future<void> _produceAppFramework(BuildMode mode, Directory iPhoneBuildOutput, Directory simulatorBuildOutput, Directory modeDirectory) async {
258258
const String appFrameworkName = 'App.framework';
259259
final Directory destinationAppFrameworkDirectory = modeDirectory.childDirectory(appFrameworkName);
260+
destinationAppFrameworkDirectory.createSync(recursive: true);
260261

261262
if (mode == BuildMode.debug) {
262263
final Status status = logger.startProgress(' ├─Add placeholder App.framework for debug...', timeout: timeoutConfiguration.fastOperation);
263-
await createStubAppFramework(destinationAppFrameworkDirectory);
264+
await _produceStubAppFrameworkIfNeeded(mode, iPhoneBuildOutput, simulatorBuildOutput, destinationAppFrameworkDirectory);
264265
status.stop();
265266
} else {
266267
await _produceAotAppFrameworkIfNeeded(mode, iPhoneBuildOutput, destinationAppFrameworkDirectory);
@@ -283,6 +284,37 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
283284
status.stop();
284285
}
285286

287+
Future<void> _produceStubAppFrameworkIfNeeded(BuildMode mode, Directory iPhoneBuildOutput, Directory simulatorBuildOutput, Directory destinationAppFrameworkDirectory) async {
288+
if (mode != BuildMode.debug) {
289+
return;
290+
}
291+
const String appFrameworkName = 'App.framework';
292+
const String binaryName = 'App';
293+
294+
final Directory iPhoneAppFrameworkDirectory = iPhoneBuildOutput.childDirectory(appFrameworkName);
295+
final File iPhoneAppFrameworkFile = iPhoneAppFrameworkDirectory.childFile(binaryName);
296+
await createStubAppFramework(iPhoneAppFrameworkFile, SdkType.iPhone);
297+
298+
final Directory simulatorAppFrameworkDirectory = simulatorBuildOutput.childDirectory(appFrameworkName);
299+
final File simulatorAppFrameworkFile = simulatorAppFrameworkDirectory.childFile(binaryName);
300+
await createStubAppFramework(simulatorAppFrameworkFile, SdkType.iPhoneSimulator);
301+
302+
final List<String> lipoCommand = <String>[
303+
'xcrun',
304+
'lipo',
305+
'-create',
306+
iPhoneAppFrameworkFile.path,
307+
simulatorAppFrameworkFile.path,
308+
'-output',
309+
destinationAppFrameworkDirectory.childFile(binaryName).path
310+
];
311+
312+
await processUtils.run(
313+
lipoCommand,
314+
allowReentrantFlutter: false,
315+
);
316+
}
317+
286318
Future<void> _produceAotAppFrameworkIfNeeded(BuildMode mode, Directory iPhoneBuildOutput, Directory destinationAppFrameworkDirectory) async {
287319
if (mode == BuildMode.debug) {
288320
return;
@@ -295,6 +327,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
295327
// Relative paths show noise in the compiler https://github.com/dart-lang/sdk/issues/37978.
296328
mainDartFile: fs.path.absolute(targetFile),
297329
quiet: true,
330+
bitcode: true,
298331
reportTimings: false,
299332
iosBuildArchs: <DarwinArch>[DarwinArch.armv7, DarwinArch.arm64],
300333
dartDefines: dartDefines,

packages/flutter_tools/lib/src/macos/xcode.dart

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@ const int kXcodeRequiredVersionMinor = 2;
1717

1818
Xcode get xcode => context.get<Xcode>();
1919

20+
enum SdkType {
21+
iPhone,
22+
iPhoneSimulator,
23+
macOS,
24+
}
25+
26+
/// SDK name passed to `xcrun --sdk`. Corresponds to undocumented Xcode
27+
/// SUPPORTED_PLATFORMS values.
28+
///
29+
/// Usage: xcrun [options] <tool name> ... arguments ...
30+
/// ...
31+
/// --sdk <sdk name> find the tool for the given SDK name
32+
String getNameForSdk(SdkType sdk) {
33+
switch (sdk) {
34+
case SdkType.iPhone:
35+
return 'iphoneos';
36+
case SdkType.iPhoneSimulator:
37+
return 'iphonesimulator';
38+
case SdkType.macOS:
39+
return 'macosx';
40+
}
41+
assert(false);
42+
return null;
43+
}
44+
2045
class Xcode {
2146
bool get isInstalledAndMeetsVersionCheck => platform.isMacOS && isInstalled && isVersionSatisfactory;
2247

@@ -117,9 +142,10 @@ class Xcode {
117142
);
118143
}
119144

120-
Future<String> iPhoneSdkLocation() async {
145+
Future<String> sdkLocation(SdkType sdk) async {
146+
assert(sdk != null);
121147
final RunResult runResult = await processUtils.run(
122-
<String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
148+
<String>['xcrun', '--sdk', getNameForSdk(sdk), '--show-sdk-path'],
123149
throwOnError: true,
124150
);
125151
if (runResult.exitCode != 0) {

packages/flutter_tools/test/general.shard/base/build_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ void main() {
244244
mockAndroidSdk = MockAndroidSdk();
245245
mockArtifacts = MockArtifacts();
246246
mockXcode = MockXcode();
247-
when(mockXcode.iPhoneSdkLocation()).thenAnswer((_) => Future<String>.value(kSDKPath));
247+
when(mockXcode.sdkLocation(any)).thenAnswer((_) => Future<String>.value(kSDKPath));
248248

249249
bufferLogger = BufferLogger();
250250
for (BuildMode mode in BuildMode.values) {

packages/flutter_tools/test/general.shard/macos/xcode_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,5 +190,11 @@ void main() {
190190
}, overrides: <Type, Generator>{
191191
ProcessManager: () => mockProcessManager,
192192
});
193+
194+
testUsingContext('SDK name', () {
195+
expect(getNameForSdk(SdkType.iPhone), 'iphoneos');
196+
expect(getNameForSdk(SdkType.iPhoneSimulator), 'iphonesimulator');
197+
expect(getNameForSdk(SdkType.macOS), 'macosx');
198+
});
193199
});
194200
}

0 commit comments

Comments
 (0)