mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
Move iOS Flutter.framework thinning into copy assemble build target (#77007)
This commit is contained in:
parent
dac968a439
commit
3969e5b47b
|
@ -204,19 +204,6 @@ is set to release or run \"flutter build ios --release\", then re-run Archive fr
|
|||
return 0
|
||||
}
|
||||
|
||||
# Destructively thins the Flutter and App frameworks to include only the specified
|
||||
# architectures.
|
||||
ThinAppFrameworks() {
|
||||
RunCommand "${FLUTTER_ROOT}/bin/flutter" \
|
||||
${verbose_flag} \
|
||||
assemble \
|
||||
--no-version-check \
|
||||
--output="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" \
|
||||
-dTargetPlatform=ios \
|
||||
-dIosArchs="${ARCHS}" \
|
||||
"thin_ios_application_frameworks"
|
||||
}
|
||||
|
||||
# Adds the App.framework as an embedded binary and the flutter_assets as
|
||||
# resources.
|
||||
EmbedFlutterFrameworks() {
|
||||
|
@ -273,11 +260,6 @@ AddObservatoryBonjourService() {
|
|||
fi
|
||||
}
|
||||
|
||||
EmbedAndThinFrameworks() {
|
||||
EmbedFlutterFrameworks
|
||||
ThinAppFrameworks
|
||||
}
|
||||
|
||||
# Main entry point.
|
||||
if [[ $# == 0 ]]; then
|
||||
# Named entry points were introduced in Flutter v0.0.7.
|
||||
|
@ -288,11 +270,13 @@ else
|
|||
"build")
|
||||
BuildApp ;;
|
||||
"thin")
|
||||
ThinAppFrameworks ;;
|
||||
# No-op, thinning is handled during the bundle asset assemble build target.
|
||||
;;
|
||||
"embed")
|
||||
EmbedFlutterFrameworks ;;
|
||||
"embed_and_thin")
|
||||
EmbedAndThinFrameworks ;;
|
||||
# Thinning is handled during the bundle asset assemble build target, so just embed.
|
||||
EmbedFlutterFrameworks ;;
|
||||
"test_observatory_bonjour_service")
|
||||
# Exposed for integration testing only.
|
||||
AddObservatoryBonjourService ;;
|
||||
|
|
|
@ -277,6 +277,14 @@ abstract class UnpackIOS extends Target {
|
|||
if (environment.defines[kSdkRoot] == null) {
|
||||
throw MissingDefineException(kSdkRoot, name);
|
||||
}
|
||||
if (environment.defines[kIosArchs] == null) {
|
||||
throw MissingDefineException(kIosArchs, name);
|
||||
}
|
||||
await _copyFramework(environment);
|
||||
await _thinFramework(environment);
|
||||
}
|
||||
|
||||
Future<void> _copyFramework(Environment environment) async {
|
||||
final Directory sdkRoot = environment.fileSystem.directory(environment.defines[kSdkRoot]);
|
||||
final EnvironmentType environmentType = environmentTypeFromSdkroot(sdkRoot);
|
||||
final String basePath = environment.artifacts.getArtifactPath(
|
||||
|
@ -302,6 +310,59 @@ 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');
|
||||
}
|
||||
final String archs = environment.defines[kIosArchs];
|
||||
final List<String> archList = archs.split(' ').toList();
|
||||
final ProcessResult infoResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
binaryPath,
|
||||
]);
|
||||
final String lipoInfo = infoResult.stdout as String;
|
||||
|
||||
final ProcessResult verifyResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
binaryPath,
|
||||
'-verify_arch',
|
||||
...archList
|
||||
]);
|
||||
|
||||
if (verifyResult.exitCode != 0) {
|
||||
throw Exception('Binary $binaryPath 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');
|
||||
return;
|
||||
}
|
||||
|
||||
// Thin in-place.
|
||||
final ProcessResult extractResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
'-output',
|
||||
binaryPath,
|
||||
for (final String arch in archList)
|
||||
...<String>[
|
||||
'-extract',
|
||||
arch,
|
||||
],
|
||||
...<String>[binaryPath],
|
||||
]);
|
||||
|
||||
if (extractResult.exitCode != 0) {
|
||||
throw Exception('Failed to extract $archs for $binaryPath.\n${extractResult.stderr}\nRunning lipo -info:\n$lipoInfo');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unpack the release prebuilt engine framework.
|
||||
|
@ -534,78 +595,3 @@ Future<RunResult> createStubAppFramework(File outputFile, String sdkRoot,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructively thins the Flutter.framework to include only the specified architectures.
|
||||
///
|
||||
/// This target is not fingerprinted and will always run.
|
||||
class ThinIosApplicationFrameworks extends Target {
|
||||
const ThinIosApplicationFrameworks();
|
||||
|
||||
@override
|
||||
String get name => 'thin_ios_application_frameworks';
|
||||
|
||||
@override
|
||||
List<Target> get dependencies => const <Target>[];
|
||||
|
||||
@override
|
||||
List<Source> get inputs => const <Source>[];
|
||||
|
||||
@override
|
||||
List<Source> get outputs => const <Source>[];
|
||||
|
||||
@override
|
||||
Future<void> build(Environment environment) async {
|
||||
if (environment.defines[kIosArchs] == null) {
|
||||
throw MissingDefineException(kIosArchs, 'thin_ios_application_frameworks');
|
||||
}
|
||||
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');
|
||||
}
|
||||
final String archs = environment.defines[kIosArchs];
|
||||
final List<String> archList = archs.split(' ').toList();
|
||||
final ProcessResult infoResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
binaryPath,
|
||||
]);
|
||||
final String lipoInfo = infoResult.stdout as String;
|
||||
|
||||
final ProcessResult verifyResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
binaryPath,
|
||||
'-verify_arch',
|
||||
...archList
|
||||
]);
|
||||
|
||||
if (verifyResult.exitCode != 0) {
|
||||
throw Exception('Binary $binaryPath does not contain $archs. Running lipo -info:\n$lipoInfo');
|
||||
}
|
||||
|
||||
// Skip this step for non-fat executables.
|
||||
if (lipoInfo.startsWith('Non-fat file:')) {
|
||||
environment.logger.printTrace('Skipping lipo for non-fat file $binaryPath');
|
||||
return;
|
||||
}
|
||||
|
||||
// Thin in-place.
|
||||
final ProcessResult extractResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
'-output',
|
||||
binaryPath,
|
||||
for (final String arch in archList)
|
||||
...<String>[
|
||||
'-extract',
|
||||
arch,
|
||||
],
|
||||
...<String>[binaryPath],
|
||||
]);
|
||||
|
||||
if (extractResult.exitCode != 0) {
|
||||
throw Exception('Failed to extract $archs for $binaryPath.\n${extractResult.stderr}\nRunning lipo -info:\n$lipoInfo');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,6 @@ const List<Target> _kDefaultTargets = <Target>[
|
|||
DebugIosApplicationBundle(),
|
||||
ProfileIosApplicationBundle(),
|
||||
ReleaseIosApplicationBundle(),
|
||||
ThinIosApplicationFrameworks(),
|
||||
// Windows targets
|
||||
UnpackWindows(),
|
||||
DebugBundleWindowsAssets(),
|
||||
|
|
|
@ -239,10 +239,26 @@ void main() {
|
|||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
group('copy engine Flutter.framework', () {
|
||||
testWithoutContext('iphonesimulator', () async {
|
||||
group('copy and thin engine Flutter.framework', () {
|
||||
Directory outputDir;
|
||||
FakeCommand copyPhysicalFrameworkCommand;
|
||||
|
||||
setUp(() {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Directory outputDir = fileSystem.directory('output');
|
||||
outputDir = fileSystem.directory('output');
|
||||
copyPhysicalFrameworkCommand = FakeCommand(command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'--filter',
|
||||
'- .DS_Store/',
|
||||
'Artifact.flutterFramework.TargetPlatform.ios.debug.EnvironmentType.physical',
|
||||
outputDir.path,
|
||||
]);
|
||||
});
|
||||
|
||||
testWithoutContext('iphonesimulator', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter');
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
processManager: processManager,
|
||||
|
@ -251,6 +267,7 @@ void main() {
|
|||
fileSystem: fileSystem,
|
||||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'x86_64',
|
||||
kSdkRoot: 'path/to/iPhoneSimulator.sdk',
|
||||
},
|
||||
);
|
||||
|
@ -264,47 +281,34 @@ void main() {
|
|||
'- .DS_Store/',
|
||||
'Artifact.flutterFramework.TargetPlatform.ios.debug.EnvironmentType.simulator',
|
||||
outputDir.path,
|
||||
]),
|
||||
);
|
||||
|
||||
await const DebugUnpackIOS().build(environment);
|
||||
});
|
||||
|
||||
testWithoutContext('iphoneos', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Directory outputDir = fileSystem.directory('output');
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
processManager: processManager,
|
||||
artifacts: artifacts,
|
||||
logger: logger,
|
||||
fileSystem: fileSystem,
|
||||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
},
|
||||
],
|
||||
onRun: () => binary.createSync(recursive: true),
|
||||
),
|
||||
);
|
||||
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'--filter',
|
||||
'- .DS_Store/',
|
||||
'Artifact.flutterFramework.TargetPlatform.ios.debug.EnvironmentType.physical',
|
||||
outputDir.path,
|
||||
]),
|
||||
'lipo',
|
||||
'-info',
|
||||
binary.path,
|
||||
], stdout: 'Non-fat file:'),
|
||||
);
|
||||
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
binary.path,
|
||||
'-verify_arch',
|
||||
'x86_64',
|
||||
]),
|
||||
);
|
||||
await const DebugUnpackIOS().build(environment);
|
||||
});
|
||||
});
|
||||
|
||||
group('thin frameworks', () {
|
||||
testWithoutContext('fails when frameworks missing', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Directory outputDir = fileSystem.directory('Runner.app').childDirectory('Frameworks');
|
||||
expect(logger.traceText, contains('Skipping lipo for non-fat file output/Flutter.framework/Flutter'));
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('thinning fails when frameworks missing', () async {
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
processManager: processManager,
|
||||
|
@ -314,10 +318,12 @@ void main() {
|
|||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
},
|
||||
);
|
||||
processManager.addCommand(copyPhysicalFrameworkCommand);
|
||||
expect(
|
||||
const ThinIosApplicationFrameworks().build(environment),
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
'description',
|
||||
|
@ -325,9 +331,7 @@ void main() {
|
|||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('fails when requested archs missing from framework', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Directory outputDir = fileSystem.directory('Runner.app').childDirectory('Frameworks')..createSync(recursive: true);
|
||||
testWithoutContext('thinning 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(
|
||||
|
@ -339,9 +343,11 @@ void main() {
|
|||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64 armv7',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
},
|
||||
);
|
||||
|
||||
processManager.addCommand(copyPhysicalFrameworkCommand);
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
|
@ -361,7 +367,7 @@ void main() {
|
|||
);
|
||||
|
||||
expect(
|
||||
const ThinIosApplicationFrameworks().build(environment),
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
'description',
|
||||
|
@ -369,9 +375,7 @@ void main() {
|
|||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('fails when lipo extract fails', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Directory outputDir = fileSystem.directory('Runner.app').childDirectory('Frameworks')..createSync(recursive: true);
|
||||
testWithoutContext('thinning fails when lipo extract fails', () async {
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
|
@ -383,9 +387,11 @@ void main() {
|
|||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64 armv7',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
},
|
||||
);
|
||||
|
||||
processManager.addCommand(copyPhysicalFrameworkCommand);
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
|
@ -419,17 +425,15 @@ void main() {
|
|||
);
|
||||
|
||||
expect(
|
||||
const ThinIosApplicationFrameworks().build(environment),
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
'description',
|
||||
contains('Failed to extract arm64 armv7 for Runner.app/Frameworks/Flutter.framework/Flutter.\nlipo error\nRunning lipo -info:\nArchitectures in the fat file:'),
|
||||
contains('Failed to extract arm64 armv7 for output/Flutter.framework/Flutter.\nlipo error\nRunning lipo -info:\nArchitectures in the fat file:'),
|
||||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('skips thin frameworks', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Directory outputDir = fileSystem.directory('Runner.app').childDirectory('Frameworks')..createSync(recursive: true);
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
|
@ -441,9 +445,11 @@ void main() {
|
|||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
},
|
||||
);
|
||||
|
||||
processManager.addCommand(copyPhysicalFrameworkCommand);
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
|
@ -460,16 +466,14 @@ void main() {
|
|||
'arm64',
|
||||
]),
|
||||
);
|
||||
await const ThinIosApplicationFrameworks().build(environment);
|
||||
await const DebugUnpackIOS().build(environment);
|
||||
|
||||
expect(logger.traceText, contains('Skipping lipo for non-fat file Runner.app/Frameworks/Flutter.framework/Flutter'));
|
||||
expect(logger.traceText, contains('Skipping lipo for non-fat file output/Flutter.framework/Flutter'));
|
||||
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('thins fat frameworks', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Directory outputDir = fileSystem.directory('Runner.app').childDirectory('Frameworks')..createSync(recursive: true);
|
||||
final File binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter')..createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
|
@ -481,9 +485,11 @@ void main() {
|
|||
outputDir: outputDir,
|
||||
defines: <String, String>{
|
||||
kIosArchs: 'arm64 armv7',
|
||||
kSdkRoot: 'path/to/iPhoneOS.sdk',
|
||||
},
|
||||
);
|
||||
|
||||
processManager.addCommand(copyPhysicalFrameworkCommand);
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
|
@ -515,7 +521,7 @@ void main() {
|
|||
]),
|
||||
);
|
||||
|
||||
await const ThinIosApplicationFrameworks().build(environment);
|
||||
await const DebugUnpackIOS().build(environment);
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -23,6 +23,7 @@ void main() {
|
|||
|
||||
Directory buildPath;
|
||||
Directory outputApp;
|
||||
Directory frameworkDirectory;
|
||||
Directory outputFlutterFramework;
|
||||
File outputFlutterFrameworkBinary;
|
||||
Directory outputAppFramework;
|
||||
|
@ -70,22 +71,11 @@ void main() {
|
|||
|
||||
outputApp = buildPath.childDirectory('Runner.app');
|
||||
|
||||
outputFlutterFramework = fileSystem.directory(
|
||||
fileSystem.path.join(
|
||||
outputApp.path,
|
||||
'Frameworks',
|
||||
'Flutter.framework',
|
||||
),
|
||||
);
|
||||
|
||||
frameworkDirectory = outputApp.childDirectory('Frameworks');
|
||||
outputFlutterFramework = frameworkDirectory.childDirectory('Flutter.framework');
|
||||
outputFlutterFrameworkBinary = outputFlutterFramework.childFile('Flutter');
|
||||
|
||||
outputAppFramework = fileSystem.directory(fileSystem.path.join(
|
||||
outputApp.path,
|
||||
'Frameworks',
|
||||
'App.framework',
|
||||
));
|
||||
|
||||
outputAppFramework = frameworkDirectory.childDirectory('App.framework');
|
||||
outputAppFrameworkBinary = outputAppFramework.childFile('App');
|
||||
});
|
||||
|
||||
|
@ -94,6 +84,8 @@ void main() {
|
|||
});
|
||||
|
||||
testWithoutContext('flutter build ios builds a valid app', () {
|
||||
// Should only contain Flutter.framework and App.framework.
|
||||
expect(frameworkDirectory.listSync().length, 2);
|
||||
expect(outputAppFramework.childFile('App'), exists);
|
||||
|
||||
final File vmSnapshot = fileSystem.file(fileSystem.path.join(
|
||||
|
@ -195,8 +187,6 @@ void main() {
|
|||
'VERBOSE_SCRIPT_LOGGING': '1',
|
||||
'FLUTTER_BUILD_MODE': 'release',
|
||||
'ACTION': 'install',
|
||||
'ARCHS': 'arm64 armv7',
|
||||
'FLUTTER_ROOT': flutterRoot,
|
||||
// Skip bitcode stripping since we just checked that above.
|
||||
},
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue