mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Bitcode strip Flutter.framework in assemble build target (#77329)
This commit is contained in:
parent
565e48777a
commit
7e02cc34f9
@ -42,7 +42,7 @@ Future<bool> containsBitcode(String pathToBinary) async {
|
||||
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
|
||||
final String emptyBitcodeMarker = lines
|
||||
.skip(index - 1)
|
||||
.take(3)
|
||||
.take(4)
|
||||
.firstWhere(
|
||||
(String line) => line.contains(' size 0x0000000000000001'),
|
||||
orElse: () => null,
|
||||
|
||||
@ -137,9 +137,8 @@ is set to release or run \"flutter build ios --release\", then re-run Archive fr
|
||||
local_engine_flag="--local-engine=${LOCAL_ENGINE}"
|
||||
flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.xcframework"
|
||||
fi
|
||||
|
||||
local bitcode_flag=""
|
||||
if [[ "$ENABLE_BITCODE" == "YES" ]]; then
|
||||
if [[ "$ENABLE_BITCODE" == "YES" && "$ACTION" == "install" ]]; then
|
||||
bitcode_flag="true"
|
||||
fi
|
||||
|
||||
@ -218,10 +217,6 @@ EmbedFlutterFrameworks() {
|
||||
|
||||
# Copy Xcode behavior and don't copy over headers or modules.
|
||||
RunCommand rsync -av --delete --filter "- .DS_Store" --filter "- Headers" --filter "- Modules" "${BUILT_PRODUCTS_DIR}/Flutter.framework" "${xcode_frameworks_dir}/"
|
||||
if [[ "$ACTION" != "install" || "$ENABLE_BITCODE" == "NO" ]]; then
|
||||
# Strip bitcode from the destination unless archiving, or if bitcode is disabled entirely.
|
||||
RunCommand "${DT_TOOLCHAIN_DIR}"/usr/bin/bitcode_strip "${BUILT_PRODUCTS_DIR}/Flutter.framework/Flutter" -r -o "${xcode_frameworks_dir}/Flutter.framework/Flutter"
|
||||
fi
|
||||
|
||||
# Sign the binaries we moved.
|
||||
if [[ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ]]; then
|
||||
|
||||
@ -280,8 +280,18 @@ abstract class UnpackIOS extends Target {
|
||||
if (environment.defines[kIosArchs] == null) {
|
||||
throw MissingDefineException(kIosArchs, name);
|
||||
}
|
||||
if (environment.defines[kBitcodeFlag] == null) {
|
||||
throw MissingDefineException(kBitcodeFlag, name);
|
||||
}
|
||||
await _copyFramework(environment);
|
||||
await _thinFramework(environment);
|
||||
|
||||
final File frameworkBinary = environment.outputDir.childDirectory('Flutter.framework').childFile('Flutter');
|
||||
final String frameworkBinaryPath = frameworkBinary.path;
|
||||
if (!frameworkBinary.existsSync()) {
|
||||
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
|
||||
}
|
||||
await _thinFramework(environment, frameworkBinaryPath);
|
||||
await _bitcodeStripFramework(environment, frameworkBinaryPath);
|
||||
}
|
||||
|
||||
Future<void> _copyFramework(Environment environment) async {
|
||||
@ -312,37 +322,30 @@ abstract class UnpackIOS extends Target {
|
||||
}
|
||||
|
||||
/// Destructively thin Flutter.framework to include only the specified architectures.
|
||||
Future<void> _thinFramework(Environment environment) async {
|
||||
final Directory frameworkDirectory = environment.outputDir;
|
||||
|
||||
final File flutterFramework = frameworkDirectory.childDirectory('Flutter.framework').childFile('Flutter');
|
||||
final String binaryPath = flutterFramework.path;
|
||||
if (!flutterFramework.existsSync()) {
|
||||
throw Exception('Binary $binaryPath does not exist, cannot thin');
|
||||
}
|
||||
Future<void> _thinFramework(Environment environment, String frameworkBinaryPath) async {
|
||||
final String archs = environment.defines[kIosArchs];
|
||||
final List<String> archList = archs.split(' ').toList();
|
||||
final ProcessResult infoResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
binaryPath,
|
||||
frameworkBinaryPath,
|
||||
]);
|
||||
final String lipoInfo = infoResult.stdout as String;
|
||||
|
||||
final ProcessResult verifyResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
binaryPath,
|
||||
frameworkBinaryPath,
|
||||
'-verify_arch',
|
||||
...archList
|
||||
]);
|
||||
|
||||
if (verifyResult.exitCode != 0) {
|
||||
throw Exception('Binary $binaryPath does not contain $archs. Running lipo -info:\n$lipoInfo');
|
||||
throw Exception('Binary $frameworkBinaryPath does not contain $archs. Running lipo -info:\n$lipoInfo');
|
||||
}
|
||||
|
||||
// Skip thinning for non-fat executables.
|
||||
if (lipoInfo.startsWith('Non-fat file:')) {
|
||||
environment.logger.printTrace('Skipping lipo for non-fat file $binaryPath');
|
||||
environment.logger.printTrace('Skipping lipo for non-fat file $frameworkBinaryPath');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -350,17 +353,36 @@ abstract class UnpackIOS extends Target {
|
||||
final ProcessResult extractResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
'-output',
|
||||
binaryPath,
|
||||
frameworkBinaryPath,
|
||||
for (final String arch in archList)
|
||||
...<String>[
|
||||
'-extract',
|
||||
arch,
|
||||
],
|
||||
...<String>[binaryPath],
|
||||
...<String>[frameworkBinaryPath],
|
||||
]);
|
||||
|
||||
if (extractResult.exitCode != 0) {
|
||||
throw Exception('Failed to extract $archs for $binaryPath.\n${extractResult.stderr}\nRunning lipo -info:\n$lipoInfo');
|
||||
throw Exception('Failed to extract $archs for $frameworkBinaryPath.\n${extractResult.stderr}\nRunning lipo -info:\n$lipoInfo');
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructively strip bitcode from the framework, if needed.
|
||||
Future<void> _bitcodeStripFramework(Environment environment, String frameworkBinaryPath) async {
|
||||
if (environment.defines[kBitcodeFlag] == 'true') {
|
||||
return;
|
||||
}
|
||||
final ProcessResult stripResult = environment.processManager.runSync(<String>[
|
||||
'xcrun',
|
||||
'bitcode_strip',
|
||||
frameworkBinaryPath,
|
||||
'-m', // leave the bitcode marker.
|
||||
'-o',
|
||||
frameworkBinaryPath,
|
||||
]);
|
||||
|
||||
if (stripResult.exitCode != 0) {
|
||||
throw Exception('Failed to strip bitcode for $frameworkBinaryPath.\n${stripResult.stderr}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,7 +239,7 @@ void main() {
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
group('copy and thin engine Flutter.framework', () {
|
||||
group('copy, thin, and bitcode strip engine Flutter.framework', () {
|
||||
Directory outputDir;
|
||||
FakeCommand copyPhysicalFrameworkCommand;
|
||||
|
||||
@ -269,6 +269,7 @@ void main() {
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'x86_64',
|
||||
kSdkRoot: 'path/to/iPhoneSimulator.sdk',
|
||||
kBitcodeFlag: 'true',
|
||||
},
|
||||
);
|
||||
|
||||
@ -308,7 +309,7 @@ void main() {
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('thinning fails when frameworks missing', () async {
|
||||
testWithoutContext('fails when frameworks missing', () async {
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
processManager: processManager,
|
||||
@ -319,10 +320,11 @@ void main() {
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
kBitcodeFlag: '',
|
||||
},
|
||||
);
|
||||
processManager.addCommand(copyPhysicalFrameworkCommand);
|
||||
expect(
|
||||
await expectLater(
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
@ -331,7 +333,7 @@ void main() {
|
||||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('thinning fails when requested archs missing from framework', () async {
|
||||
testWithoutContext('fails when requested archs missing from framework', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
@ -344,6 +346,7 @@ void main() {
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64 armv7',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
kBitcodeFlag: '',
|
||||
},
|
||||
);
|
||||
|
||||
@ -366,7 +369,7 @@ void main() {
|
||||
], exitCode: 1),
|
||||
);
|
||||
|
||||
expect(
|
||||
await expectLater(
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
@ -375,7 +378,7 @@ void main() {
|
||||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('thinning fails when lipo extract fails', () async {
|
||||
testWithoutContext('fails when lipo extract fails', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
@ -388,6 +391,7 @@ void main() {
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64 armv7',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
kBitcodeFlag: '',
|
||||
},
|
||||
);
|
||||
|
||||
@ -424,7 +428,7 @@ void main() {
|
||||
stderr: 'lipo error'),
|
||||
);
|
||||
|
||||
expect(
|
||||
await expectLater(
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
@ -433,7 +437,7 @@ void main() {
|
||||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('skips thin frameworks', () async {
|
||||
testWithoutContext('skips thin framework', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
@ -446,6 +450,7 @@ void main() {
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
kBitcodeFlag: 'true',
|
||||
},
|
||||
);
|
||||
|
||||
@ -473,7 +478,7 @@ void main() {
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('thins fat frameworks', () async {
|
||||
testWithoutContext('thins fat framework', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
@ -486,6 +491,7 @@ void main() {
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64 armv7',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
kBitcodeFlag: 'true',
|
||||
},
|
||||
);
|
||||
|
||||
@ -524,5 +530,100 @@ void main() {
|
||||
await const DebugUnpackIOS().build(environment);
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('fails when bitcode strip fails', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
processManager: processManager,
|
||||
artifacts: artifacts,
|
||||
logger: logger,
|
||||
fileSystem: fileSystem,
|
||||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
kBitcodeFlag: '',
|
||||
},
|
||||
);
|
||||
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyPhysicalFrameworkCommand,
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
binary.path,
|
||||
], stdout: 'Non-fat file:'),
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
binary.path,
|
||||
'-verify_arch',
|
||||
'arm64',
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun',
|
||||
'bitcode_strip',
|
||||
binary.path,
|
||||
'-m',
|
||||
'-o',
|
||||
binary.path,
|
||||
], exitCode: 1, stderr: 'bitcode_strip error'),
|
||||
]);
|
||||
|
||||
await expectLater(
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
'description',
|
||||
contains('Failed to strip bitcode for output/Flutter.framework/Flutter.\nbitcode_strip error'),
|
||||
)));
|
||||
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('strips framework', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
processManager: processManager,
|
||||
artifacts: artifacts,
|
||||
logger: logger,
|
||||
fileSystem: fileSystem,
|
||||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
kBitcodeFlag: '',
|
||||
},
|
||||
);
|
||||
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyPhysicalFrameworkCommand,
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
binary.path,
|
||||
], stdout: 'Non-fat file:'),
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
binary.path,
|
||||
'-verify_arch',
|
||||
'arm64',
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun',
|
||||
'bitcode_strip',
|
||||
binary.path,
|
||||
'-m',
|
||||
'-o',
|
||||
binary.path,
|
||||
]),
|
||||
]);
|
||||
await const DebugUnpackIOS().build(environment);
|
||||
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ bool containsBitcode(String pathToBinary, ProcessManager processManager) {
|
||||
lines.asMap().forEach((int index, String line) {
|
||||
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
|
||||
final String emptyBitcodeMarker =
|
||||
lines.skip(index - 1).take(3).firstWhere(
|
||||
lines.skip(index - 1).take(4).firstWhere(
|
||||
(String line) => line.contains(' size 0x0000000000000001'),
|
||||
orElse: () => null,
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user