Skip to content

Commit 7e02cc3

Browse files
authored
Bitcode strip Flutter.framework in assemble build target (#77329)
1 parent 565e487 commit 7e02cc3

File tree

5 files changed

+151
-33
lines changed

5 files changed

+151
-33
lines changed

dev/devicelab/lib/framework/ios.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Future<bool> containsBitcode(String pathToBinary) async {
4242
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
4343
final String emptyBitcodeMarker = lines
4444
.skip(index - 1)
45-
.take(3)
45+
.take(4)
4646
.firstWhere(
4747
(String line) => line.contains(' size 0x0000000000000001'),
4848
orElse: () => null,

packages/flutter_tools/bin/xcode_backend.sh

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,8 @@ is set to release or run \"flutter build ios --release\", then re-run Archive fr
137137
local_engine_flag="--local-engine=${LOCAL_ENGINE}"
138138
flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.xcframework"
139139
fi
140-
141140
local bitcode_flag=""
142-
if [[ "$ENABLE_BITCODE" == "YES" ]]; then
141+
if [[ "$ENABLE_BITCODE" == "YES" && "$ACTION" == "install" ]]; then
143142
bitcode_flag="true"
144143
fi
145144

@@ -218,10 +217,6 @@ EmbedFlutterFrameworks() {
218217

219218
# Copy Xcode behavior and don't copy over headers or modules.
220219
RunCommand rsync -av --delete --filter "- .DS_Store" --filter "- Headers" --filter "- Modules" "${BUILT_PRODUCTS_DIR}/Flutter.framework" "${xcode_frameworks_dir}/"
221-
if [[ "$ACTION" != "install" || "$ENABLE_BITCODE" == "NO" ]]; then
222-
# Strip bitcode from the destination unless archiving, or if bitcode is disabled entirely.
223-
RunCommand "${DT_TOOLCHAIN_DIR}"/usr/bin/bitcode_strip "${BUILT_PRODUCTS_DIR}/Flutter.framework/Flutter" -r -o "${xcode_frameworks_dir}/Flutter.framework/Flutter"
224-
fi
225220

226221
# Sign the binaries we moved.
227222
if [[ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ]]; then

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

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,18 @@ abstract class UnpackIOS extends Target {
280280
if (environment.defines[kIosArchs] == null) {
281281
throw MissingDefineException(kIosArchs, name);
282282
}
283+
if (environment.defines[kBitcodeFlag] == null) {
284+
throw MissingDefineException(kBitcodeFlag, name);
285+
}
283286
await _copyFramework(environment);
284-
await _thinFramework(environment);
287+
288+
final File frameworkBinary = environment.outputDir.childDirectory('Flutter.framework').childFile('Flutter');
289+
final String frameworkBinaryPath = frameworkBinary.path;
290+
if (!frameworkBinary.existsSync()) {
291+
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
292+
}
293+
await _thinFramework(environment, frameworkBinaryPath);
294+
await _bitcodeStripFramework(environment, frameworkBinaryPath);
285295
}
286296

287297
Future<void> _copyFramework(Environment environment) async {
@@ -312,55 +322,67 @@ abstract class UnpackIOS extends Target {
312322
}
313323

314324
/// Destructively thin Flutter.framework to include only the specified architectures.
315-
Future<void> _thinFramework(Environment environment) async {
316-
final Directory frameworkDirectory = environment.outputDir;
317-
318-
final File flutterFramework = frameworkDirectory.childDirectory('Flutter.framework').childFile('Flutter');
319-
final String binaryPath = flutterFramework.path;
320-
if (!flutterFramework.existsSync()) {
321-
throw Exception('Binary $binaryPath does not exist, cannot thin');
322-
}
325+
Future<void> _thinFramework(Environment environment, String frameworkBinaryPath) async {
323326
final String archs = environment.defines[kIosArchs];
324327
final List<String> archList = archs.split(' ').toList();
325328
final ProcessResult infoResult = environment.processManager.runSync(<String>[
326329
'lipo',
327330
'-info',
328-
binaryPath,
331+
frameworkBinaryPath,
329332
]);
330333
final String lipoInfo = infoResult.stdout as String;
331334

332335
final ProcessResult verifyResult = environment.processManager.runSync(<String>[
333336
'lipo',
334-
binaryPath,
337+
frameworkBinaryPath,
335338
'-verify_arch',
336339
...archList
337340
]);
338341

339342
if (verifyResult.exitCode != 0) {
340-
throw Exception('Binary $binaryPath does not contain $archs. Running lipo -info:\n$lipoInfo');
343+
throw Exception('Binary $frameworkBinaryPath does not contain $archs. Running lipo -info:\n$lipoInfo');
341344
}
342345

343346
// Skip thinning for non-fat executables.
344347
if (lipoInfo.startsWith('Non-fat file:')) {
345-
environment.logger.printTrace('Skipping lipo for non-fat file $binaryPath');
348+
environment.logger.printTrace('Skipping lipo for non-fat file $frameworkBinaryPath');
346349
return;
347350
}
348351

349352
// Thin in-place.
350353
final ProcessResult extractResult = environment.processManager.runSync(<String>[
351354
'lipo',
352355
'-output',
353-
binaryPath,
356+
frameworkBinaryPath,
354357
for (final String arch in archList)
355358
...<String>[
356359
'-extract',
357360
arch,
358361
],
359-
...<String>[binaryPath],
362+
...<String>[frameworkBinaryPath],
360363
]);
361364

362365
if (extractResult.exitCode != 0) {
363-
throw Exception('Failed to extract $archs for $binaryPath.\n${extractResult.stderr}\nRunning lipo -info:\n$lipoInfo');
366+
throw Exception('Failed to extract $archs for $frameworkBinaryPath.\n${extractResult.stderr}\nRunning lipo -info:\n$lipoInfo');
367+
}
368+
}
369+
370+
/// Destructively strip bitcode from the framework, if needed.
371+
Future<void> _bitcodeStripFramework(Environment environment, String frameworkBinaryPath) async {
372+
if (environment.defines[kBitcodeFlag] == 'true') {
373+
return;
374+
}
375+
final ProcessResult stripResult = environment.processManager.runSync(<String>[
376+
'xcrun',
377+
'bitcode_strip',
378+
frameworkBinaryPath,
379+
'-m', // leave the bitcode marker.
380+
'-o',
381+
frameworkBinaryPath,
382+
]);
383+
384+
if (stripResult.exitCode != 0) {
385+
throw Exception('Failed to strip bitcode for $frameworkBinaryPath.\n${stripResult.stderr}');
364386
}
365387
}
366388
}

packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ void main() {
239239
Platform: () => macPlatform,
240240
});
241241

242-
group('copy and thin engine Flutter.framework', () {
242+
group('copy, thin, and bitcode strip engine Flutter.framework', () {
243243
Directory outputDir;
244244
FakeCommand copyPhysicalFrameworkCommand;
245245

@@ -269,6 +269,7 @@ void main() {
269269
defines: <String, String>{
270270
kIosArchs: 'x86_64',
271271
kSdkRoot: 'path/to/iPhoneSimulator.sdk',
272+
kBitcodeFlag: 'true',
272273
},
273274
);
274275

@@ -308,7 +309,7 @@ void main() {
308309
expect(processManager.hasRemainingExpectations, isFalse);
309310
});
310311

311-
testWithoutContext('thinning fails when frameworks missing', () async {
312+
testWithoutContext('fails when frameworks missing', () async {
312313
final Environment environment = Environment.test(
313314
fileSystem.currentDirectory,
314315
processManager: processManager,
@@ -319,10 +320,11 @@ void main() {
319320
defines: <String, String>{
320321
kIosArchs: 'arm64',
321322
kSdkRoot: 'path/to/iPhoneOS.sdk',
323+
kBitcodeFlag: '',
322324
},
323325
);
324326
processManager.addCommand(copyPhysicalFrameworkCommand);
325-
expect(
327+
await expectLater(
326328
const DebugUnpackIOS().build(environment),
327329
throwsA(isA<Exception>().having(
328330
(Exception exception) => exception.toString(),
@@ -331,7 +333,7 @@ void main() {
331333
)));
332334
});
333335

334-
testWithoutContext('thinning fails when requested archs missing from framework', () async {
336+
testWithoutContext('fails when requested archs missing from framework', () async {
335337
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
336338

337339
final Environment environment = Environment.test(
@@ -344,6 +346,7 @@ void main() {
344346
defines: <String, String>{
345347
kIosArchs: 'arm64 armv7',
346348
kSdkRoot: 'path/to/iPhoneOS.sdk',
349+
kBitcodeFlag: '',
347350
},
348351
);
349352

@@ -366,7 +369,7 @@ void main() {
366369
], exitCode: 1),
367370
);
368371

369-
expect(
372+
await expectLater(
370373
const DebugUnpackIOS().build(environment),
371374
throwsA(isA<Exception>().having(
372375
(Exception exception) => exception.toString(),
@@ -375,7 +378,7 @@ void main() {
375378
)));
376379
});
377380

378-
testWithoutContext('thinning fails when lipo extract fails', () async {
381+
testWithoutContext('fails when lipo extract fails', () async {
379382
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
380383

381384
final Environment environment = Environment.test(
@@ -388,6 +391,7 @@ void main() {
388391
defines: <String, String>{
389392
kIosArchs: 'arm64 armv7',
390393
kSdkRoot: 'path/to/iPhoneOS.sdk',
394+
kBitcodeFlag: '',
391395
},
392396
);
393397

@@ -424,7 +428,7 @@ void main() {
424428
stderr: 'lipo error'),
425429
);
426430

427-
expect(
431+
await expectLater(
428432
const DebugUnpackIOS().build(environment),
429433
throwsA(isA<Exception>().having(
430434
(Exception exception) => exception.toString(),
@@ -433,7 +437,7 @@ void main() {
433437
)));
434438
});
435439

436-
testWithoutContext('skips thin frameworks', () async {
440+
testWithoutContext('skips thin framework', () async {
437441
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
438442

439443
final Environment environment = Environment.test(
@@ -446,6 +450,7 @@ void main() {
446450
defines: <String, String>{
447451
kIosArchs: 'arm64',
448452
kSdkRoot: 'path/to/iPhoneOS.sdk',
453+
kBitcodeFlag: 'true',
449454
},
450455
);
451456

@@ -473,7 +478,7 @@ void main() {
473478
expect(processManager.hasRemainingExpectations, isFalse);
474479
});
475480

476-
testWithoutContext('thins fat frameworks', () async {
481+
testWithoutContext('thins fat framework', () async {
477482
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
478483

479484
final Environment environment = Environment.test(
@@ -486,6 +491,7 @@ void main() {
486491
defines: <String, String>{
487492
kIosArchs: 'arm64 armv7',
488493
kSdkRoot: 'path/to/iPhoneOS.sdk',
494+
kBitcodeFlag: 'true',
489495
},
490496
);
491497

@@ -524,5 +530,100 @@ void main() {
524530
await const DebugUnpackIOS().build(environment);
525531
expect(processManager.hasRemainingExpectations, isFalse);
526532
});
533+
534+
testWithoutContext('fails when bitcode strip fails', () async {
535+
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
536+
537+
final Environment environment = Environment.test(
538+
fileSystem.currentDirectory,
539+
processManager: processManager,
540+
artifacts: artifacts,
541+
logger: logger,
542+
fileSystem: fileSystem,
543+
outputDir: outputDir,
544+
defines: <String, String>{
545+
kIosArchs: 'arm64',
546+
kSdkRoot: 'path/to/iPhoneOS.sdk',
547+
kBitcodeFlag: '',
548+
},
549+
);
550+
551+
processManager.addCommands(<FakeCommand>[
552+
copyPhysicalFrameworkCommand,
553+
FakeCommand(command: <String>[
554+
'lipo',
555+
'-info',
556+
binary.path,
557+
], stdout: 'Non-fat file:'),
558+
FakeCommand(command: <String>[
559+
'lipo',
560+
binary.path,
561+
'-verify_arch',
562+
'arm64',
563+
]),
564+
FakeCommand(command: <String>[
565+
'xcrun',
566+
'bitcode_strip',
567+
binary.path,
568+
'-m',
569+
'-o',
570+
binary.path,
571+
], exitCode: 1, stderr: 'bitcode_strip error'),
572+
]);
573+
574+
await expectLater(
575+
const DebugUnpackIOS().build(environment),
576+
throwsA(isA<Exception>().having(
577+
(Exception exception) => exception.toString(),
578+
'description',
579+
contains('Failed to strip bitcode for output/Flutter.framework/Flutter.\nbitcode_strip error'),
580+
)));
581+
582+
expect(processManager.hasRemainingExpectations, isFalse);
583+
});
584+
585+
testWithoutContext('strips framework', () async {
586+
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
587+
588+
final Environment environment = Environment.test(
589+
fileSystem.currentDirectory,
590+
processManager: processManager,
591+
artifacts: artifacts,
592+
logger: logger,
593+
fileSystem: fileSystem,
594+
outputDir: outputDir,
595+
defines: <String, String>{
596+
kIosArchs: 'arm64',
597+
kSdkRoot: 'path/to/iPhoneOS.sdk',
598+
kBitcodeFlag: '',
599+
},
600+
);
601+
602+
processManager.addCommands(<FakeCommand>[
603+
copyPhysicalFrameworkCommand,
604+
FakeCommand(command: <String>[
605+
'lipo',
606+
'-info',
607+
binary.path,
608+
], stdout: 'Non-fat file:'),
609+
FakeCommand(command: <String>[
610+
'lipo',
611+
binary.path,
612+
'-verify_arch',
613+
'arm64',
614+
]),
615+
FakeCommand(command: <String>[
616+
'xcrun',
617+
'bitcode_strip',
618+
binary.path,
619+
'-m',
620+
'-o',
621+
binary.path,
622+
]),
623+
]);
624+
await const DebugUnpackIOS().build(environment);
625+
626+
expect(processManager.hasRemainingExpectations, isFalse);
627+
});
527628
});
528629
}

packages/flutter_tools/test/src/darwin_common.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ bool containsBitcode(String pathToBinary, ProcessManager processManager) {
4040
lines.asMap().forEach((int index, String line) {
4141
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
4242
final String emptyBitcodeMarker =
43-
lines.skip(index - 1).take(3).firstWhere(
43+
lines.skip(index - 1).take(4).firstWhere(
4444
(String line) => line.contains(' size 0x0000000000000001'),
4545
orElse: () => null,
4646
);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy