mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[iOS] Copy Flutter.framework.dSYM into app archive (#153215)
As of Xcode 16, App Store validation now requires that apps uploaded to the App store bundle dSYM debug information bundles for each Framework they embed. dSYM bundles are packaged in the Flutter.xcframework shipped in the `ios-release` tools archive as of engine patches: * https://github.com/flutter/engine/pull/54414 * https://github.com/flutter/engine/pull/54458 This copies the Flutter.framework.dSYM bundle from the tools cache to the app archive produced by `flutter build ipa`. Issue: https://github.com/flutter/flutter/issues/116493
This commit is contained in:
parent
306e9e46f1
commit
c375dd8d72
@ -22,6 +22,7 @@ enum Artifact {
|
||||
/// The flutter tester binary.
|
||||
flutterTester,
|
||||
flutterFramework,
|
||||
flutterFrameworkDsym,
|
||||
flutterXcframework,
|
||||
/// The framework directory of the macOS desktop.
|
||||
flutterMacOSFramework,
|
||||
@ -181,6 +182,8 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
|
||||
return 'flutter_tester$exe';
|
||||
case Artifact.flutterFramework:
|
||||
return 'Flutter.framework';
|
||||
case Artifact.flutterFrameworkDsym:
|
||||
return 'Flutter.framework.dSYM';
|
||||
case Artifact.flutterXcframework:
|
||||
return 'Flutter.xcframework';
|
||||
case Artifact.flutterMacOSFramework:
|
||||
@ -623,6 +626,7 @@ class CachedArtifacts implements Artifacts {
|
||||
case Artifact.frontendServerSnapshotForEngineDartSdk:
|
||||
case Artifact.constFinder:
|
||||
case Artifact.flutterFramework:
|
||||
case Artifact.flutterFrameworkDsym:
|
||||
case Artifact.flutterMacOSFramework:
|
||||
case Artifact.flutterMacOSXcframework:
|
||||
case Artifact.flutterPatchedSdkPath:
|
||||
@ -656,7 +660,10 @@ class CachedArtifacts implements Artifacts {
|
||||
return _fileSystem.path.join(engineDir, artifactFileName);
|
||||
case Artifact.flutterFramework:
|
||||
final String engineDir = _getEngineArtifactsPath(platform, mode)!;
|
||||
return _getIosEngineArtifactPath(engineDir, environmentType, _fileSystem, _platform);
|
||||
return _getIosFrameworkPath(engineDir, environmentType, _fileSystem, _platform);
|
||||
case Artifact.flutterFrameworkDsym:
|
||||
final String engineDir = _getEngineArtifactsPath(platform, mode)!;
|
||||
return _getIosFrameworkDsymPath(engineDir, environmentType, _fileSystem, _platform);
|
||||
case Artifact.engineDartSdkPath:
|
||||
case Artifact.engineDartBinary:
|
||||
case Artifact.engineDartAotRuntime:
|
||||
@ -713,6 +720,7 @@ class CachedArtifacts implements Artifacts {
|
||||
return _fileSystem.path.join(root, runtime, artifactFileName);
|
||||
case Artifact.constFinder:
|
||||
case Artifact.flutterFramework:
|
||||
case Artifact.flutterFrameworkDsym:
|
||||
case Artifact.flutterMacOSFramework:
|
||||
case Artifact.flutterMacOSXcframework:
|
||||
case Artifact.flutterTester:
|
||||
@ -814,6 +822,7 @@ class CachedArtifacts implements Artifacts {
|
||||
.childFile(_artifactToFileName(artifact, _platform, mode)!)
|
||||
.path;
|
||||
case Artifact.flutterFramework:
|
||||
case Artifact.flutterFrameworkDsym:
|
||||
case Artifact.flutterXcframework:
|
||||
case Artifact.fuchsiaFlutterRunner:
|
||||
case Artifact.fuchsiaKernelCompiler:
|
||||
@ -882,8 +891,16 @@ TargetPlatform _currentHostPlatform(Platform platform, OperatingSystemUtils oper
|
||||
throw UnimplementedError('Host OS not supported.');
|
||||
}
|
||||
|
||||
String _getIosEngineArtifactPath(String engineDirectory,
|
||||
EnvironmentType? environmentType, FileSystem fileSystem, Platform hostPlatform) {
|
||||
/// Returns the Flutter.xcframework platform directory for the specified environment type.
|
||||
///
|
||||
/// `Flutter.xcframework` contains target environment/architecture-specific
|
||||
/// subdirectories containing the appropriate `Flutter.framework` and
|
||||
/// `dSYMs/Flutter.framework.dSYMs` bundles for that target architecture.
|
||||
Directory _getIosFlutterFrameworkPlatformDirectory(
|
||||
String engineDirectory,
|
||||
EnvironmentType? environmentType,
|
||||
FileSystem fileSystem,
|
||||
Platform hostPlatform) {
|
||||
final Directory xcframeworkDirectory = fileSystem
|
||||
.directory(engineDirectory)
|
||||
.childDirectory(_artifactToFileName(Artifact.flutterXcframework, hostPlatform)!);
|
||||
@ -891,9 +908,7 @@ String _getIosEngineArtifactPath(String engineDirectory,
|
||||
if (!xcframeworkDirectory.existsSync()) {
|
||||
throwToolExit('No xcframework found at ${xcframeworkDirectory.path}. Try running "flutter precache --ios".');
|
||||
}
|
||||
Directory? flutterFrameworkSource;
|
||||
for (final Directory platformDirectory
|
||||
in xcframeworkDirectory.listSync().whereType<Directory>()) {
|
||||
for (final Directory platformDirectory in xcframeworkDirectory.listSync().whereType<Directory>()) {
|
||||
if (!platformDirectory.basename.startsWith('ios-')) {
|
||||
continue;
|
||||
}
|
||||
@ -901,18 +916,47 @@ String _getIosEngineArtifactPath(String engineDirectory,
|
||||
final bool simulatorDirectory = platformDirectory.basename.endsWith('-simulator');
|
||||
if ((environmentType == EnvironmentType.simulator && simulatorDirectory) ||
|
||||
(environmentType == EnvironmentType.physical && !simulatorDirectory)) {
|
||||
flutterFrameworkSource = platformDirectory;
|
||||
return platformDirectory;
|
||||
}
|
||||
}
|
||||
if (flutterFrameworkSource == null) {
|
||||
throwToolExit('No iOS frameworks found in ${xcframeworkDirectory.path}');
|
||||
}
|
||||
}
|
||||
|
||||
return flutterFrameworkSource
|
||||
/// Returns the path to Flutter.framework.
|
||||
String _getIosFrameworkPath(
|
||||
String engineDirectory,
|
||||
EnvironmentType? environmentType,
|
||||
FileSystem fileSystem,
|
||||
Platform hostPlatform) {
|
||||
final Directory platformDir = _getIosFlutterFrameworkPlatformDirectory(
|
||||
engineDirectory,
|
||||
environmentType,
|
||||
fileSystem,
|
||||
hostPlatform,
|
||||
);
|
||||
return platformDir
|
||||
.childDirectory(_artifactToFileName(Artifact.flutterFramework, hostPlatform)!)
|
||||
.path;
|
||||
}
|
||||
|
||||
/// Returns the path to Flutter.framework.dSYM.
|
||||
String _getIosFrameworkDsymPath(
|
||||
String engineDirectory,
|
||||
EnvironmentType? environmentType,
|
||||
FileSystem fileSystem,
|
||||
Platform hostPlatform) {
|
||||
final Directory platformDir = _getIosFlutterFrameworkPlatformDirectory(
|
||||
engineDirectory,
|
||||
environmentType,
|
||||
fileSystem,
|
||||
hostPlatform,
|
||||
);
|
||||
return platformDir
|
||||
.childDirectory('dSYMs')
|
||||
.childDirectory(_artifactToFileName(Artifact.flutterFrameworkDsym, hostPlatform)!)
|
||||
.path;
|
||||
}
|
||||
|
||||
String _getMacOSEngineArtifactPath(
|
||||
String engineDirectory,
|
||||
FileSystem fileSystem,
|
||||
@ -1108,7 +1152,10 @@ class CachedLocalEngineArtifacts implements Artifacts {
|
||||
case Artifact.platformLibrariesJson:
|
||||
return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), 'lib', artifactFileName);
|
||||
case Artifact.flutterFramework:
|
||||
return _getIosEngineArtifactPath(
|
||||
return _getIosFrameworkPath(
|
||||
localEngineInfo.targetOutPath, environmentType, _fileSystem, _platform);
|
||||
case Artifact.flutterFrameworkDsym:
|
||||
return _getIosFrameworkDsymPath(
|
||||
localEngineInfo.targetOutPath, environmentType, _fileSystem, _platform);
|
||||
case Artifact.flutterMacOSFramework:
|
||||
return _getMacOSEngineArtifactPath(
|
||||
@ -1291,6 +1338,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
|
||||
case Artifact.genSnapshot:
|
||||
case Artifact.flutterTester:
|
||||
case Artifact.flutterFramework:
|
||||
case Artifact.flutterFrameworkDsym:
|
||||
case Artifact.flutterXcframework:
|
||||
case Artifact.flutterMacOSFramework:
|
||||
case Artifact.flutterMacOSXcframework:
|
||||
|
@ -300,6 +300,7 @@ abstract class UnpackIOS extends Target {
|
||||
}
|
||||
|
||||
Future<void> _copyFramework(Environment environment, String sdkRoot) async {
|
||||
// Copy Flutter framework.
|
||||
final EnvironmentType? environmentType = environmentTypeFromSdkroot(sdkRoot, environment.fileSystem);
|
||||
final String basePath = environment.artifacts.getArtifactPath(
|
||||
Artifact.flutterFramework,
|
||||
@ -324,6 +325,34 @@ abstract class UnpackIOS extends Target {
|
||||
'${result.stdout}\n---\n${result.stderr}',
|
||||
);
|
||||
}
|
||||
|
||||
// Copy Flutter framework dSYM (debug symbol) bundle, if present.
|
||||
final Directory frameworkDsym = environment.fileSystem.directory(
|
||||
environment.artifacts.getArtifactPath(
|
||||
Artifact.flutterFrameworkDsym,
|
||||
platform: TargetPlatform.ios,
|
||||
mode: buildMode,
|
||||
environmentType: environmentType,
|
||||
)
|
||||
);
|
||||
if (frameworkDsym.existsSync()) {
|
||||
final ProcessResult result = await environment.processManager.run(<String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'--filter',
|
||||
'- .DS_Store/',
|
||||
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
|
||||
frameworkDsym.path,
|
||||
environment.outputDir.path,
|
||||
]);
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception(
|
||||
'Failed to copy framework dSYM (exit ${result.exitCode}:\n'
|
||||
'${result.stdout}\n---\n${result.stderr}',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructively thin Flutter.framework to include only the specified architectures.
|
||||
|
@ -526,6 +526,8 @@ void main() {
|
||||
late Directory outputDir;
|
||||
late File binary;
|
||||
late FakeCommand copyPhysicalFrameworkCommand;
|
||||
late FakeCommand copyPhysicalFrameworkDsymCommand;
|
||||
late FakeCommand copyPhysicalFrameworkDsymCommandFailure;
|
||||
late FakeCommand lipoCommandNonFatResult;
|
||||
late FakeCommand lipoVerifyArm64Command;
|
||||
late FakeCommand xattrCommand;
|
||||
@ -535,6 +537,7 @@ void main() {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
outputDir = fileSystem.directory('output');
|
||||
binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter');
|
||||
|
||||
copyPhysicalFrameworkCommand = FakeCommand(command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
@ -546,6 +549,28 @@ void main() {
|
||||
outputDir.path,
|
||||
]);
|
||||
|
||||
copyPhysicalFrameworkDsymCommand = FakeCommand(command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'--filter',
|
||||
'- .DS_Store/',
|
||||
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
|
||||
'Artifact.flutterFrameworkDsym.TargetPlatform.ios.debug.EnvironmentType.physical',
|
||||
outputDir.path,
|
||||
]);
|
||||
|
||||
copyPhysicalFrameworkDsymCommandFailure = FakeCommand(command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'--filter',
|
||||
'- .DS_Store/',
|
||||
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
|
||||
'Artifact.flutterFrameworkDsym.TargetPlatform.ios.debug.EnvironmentType.physical',
|
||||
outputDir.path,
|
||||
], exitCode: 1);
|
||||
|
||||
lipoCommandNonFatResult = FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
@ -643,6 +668,42 @@ void main() {
|
||||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('fails when framework dSYM copy fails', () async {
|
||||
binary.createSync(recursive: true);
|
||||
final Directory dSYM = fileSystem.directory(
|
||||
artifacts.getArtifactPath(Artifact.flutterFrameworkDsym,
|
||||
platform: TargetPlatform.ios,
|
||||
mode: BuildMode.debug,
|
||||
environmentType: EnvironmentType.physical,
|
||||
),
|
||||
);
|
||||
dSYM.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',
|
||||
},
|
||||
);
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyPhysicalFrameworkCommand,
|
||||
copyPhysicalFrameworkDsymCommandFailure,
|
||||
]);
|
||||
await expectLater(
|
||||
const DebugUnpackIOS().build(environment),
|
||||
throwsA(isException.having(
|
||||
(Exception exception) => exception.toString(),
|
||||
'description',
|
||||
contains('Failed to copy framework dSYM'),
|
||||
)));
|
||||
});
|
||||
|
||||
testWithoutContext('fails when requested archs missing from framework', () async {
|
||||
binary.createSync(recursive: true);
|
||||
|
||||
@ -894,6 +955,14 @@ void main() {
|
||||
|
||||
testWithoutContext('codesigns framework', () async {
|
||||
binary.createSync(recursive: true);
|
||||
final Directory dSYM = fileSystem.directory(
|
||||
artifacts.getArtifactPath(Artifact.flutterFrameworkDsym,
|
||||
platform: TargetPlatform.ios,
|
||||
mode: BuildMode.debug,
|
||||
environmentType: EnvironmentType.physical,
|
||||
),
|
||||
);
|
||||
dSYM.createSync(recursive: true);
|
||||
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
@ -911,6 +980,7 @@ void main() {
|
||||
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyPhysicalFrameworkCommand,
|
||||
copyPhysicalFrameworkDsymCommand,
|
||||
lipoCommandNonFatResult,
|
||||
lipoVerifyArm64Command,
|
||||
xattrCommand,
|
||||
|
Loading…
Reference in New Issue
Block a user