Skip to content

Commit 585b02f

Browse files
committed
macOS: Copy macOS framwork dSYM into build outputs
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 FlutterMacOS.xcframework shipped in the `darwin-x64-release` tools archive as of engine patches: * flutter/engine#54696 This copies the FlutterMacOS.framework.dSYM bundle from the tools cache to the build outputs produced by `flutter build macos`. Issue: https://github.com/flutter/flutter/issue/153879
1 parent 5e19438 commit 585b02f

3 files changed

Lines changed: 222 additions & 26 deletions

File tree

packages/flutter_tools/lib/src/artifacts.dart

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum Artifact {
2626
flutterXcframework,
2727
/// The framework directory of the macOS desktop.
2828
flutterMacOSFramework,
29+
flutterMacOSFrameworkDsym,
2930
flutterMacOSXcframework,
3031
vmSnapshotData,
3132
isolateSnapshotData,
@@ -182,12 +183,14 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
182183
return 'flutter_tester$exe';
183184
case Artifact.flutterFramework:
184185
return 'Flutter.framework';
185-
case Artifact.flutterFrameworkDsym:
186-
return 'Flutter.framework.dSYM';
186+
case Artifact.flutterFrameworkDsym:
187+
return 'Flutter.framework.dSYM';
187188
case Artifact.flutterXcframework:
188189
return 'Flutter.xcframework';
189190
case Artifact.flutterMacOSFramework:
190191
return 'FlutterMacOS.framework';
192+
case Artifact.flutterMacOSFrameworkDsym:
193+
return 'FlutterMacOS.framework.dSYM';
191194
case Artifact.flutterMacOSXcframework:
192195
return 'FlutterMacOS.xcframework';
193196
case Artifact.vmSnapshotData:
@@ -600,15 +603,44 @@ class CachedArtifacts implements Artifacts {
600603
String _getDesktopArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode? mode) {
601604
// When platform is null, a generic host platform artifact is being requested
602605
// and not the gen_snapshot for darwin as a target platform.
603-
if (artifact == Artifact.genSnapshot) {
604-
final String engineDir = _getEngineArtifactsPath(platform, mode)!;
605-
return _fileSystem.path.join(engineDir, _artifactToFileName(artifact, _platform));
606-
}
607-
if (artifact == Artifact.flutterMacOSFramework) {
608-
final String engineDir = _getEngineArtifactsPath(platform, mode)!;
609-
return _getMacOSEngineArtifactPath(engineDir, _fileSystem, _platform);
606+
final String engineDir = _getEngineArtifactsPath(platform, mode)!;
607+
switch (artifact) {
608+
case Artifact.genSnapshot:
609+
return _fileSystem.path.join(engineDir, _artifactToFileName(artifact, _platform));
610+
case Artifact.engineDartSdkPath:
611+
case Artifact.engineDartBinary:
612+
case Artifact.engineDartAotRuntime:
613+
case Artifact.dart2jsSnapshot:
614+
case Artifact.dart2wasmSnapshot:
615+
case Artifact.frontendServerSnapshotForEngineDartSdk:
616+
case Artifact.constFinder:
617+
case Artifact.flutterFramework:
618+
case Artifact.flutterFrameworkDsym:
619+
case Artifact.flutterMacOSFramework:
620+
return _getMacOSFrameworkPath(engineDir, _fileSystem, _platform);
621+
case Artifact.flutterMacOSFrameworkDsym:
622+
return _getMacOSFrameworkDsymPath(engineDir, _fileSystem, _platform);
623+
case Artifact.flutterMacOSXcframework:
624+
case Artifact.flutterPatchedSdkPath:
625+
case Artifact.flutterTester:
626+
case Artifact.flutterXcframework:
627+
case Artifact.fontSubset:
628+
case Artifact.fuchsiaFlutterRunner:
629+
case Artifact.fuchsiaKernelCompiler:
630+
case Artifact.icuData:
631+
case Artifact.isolateSnapshotData:
632+
case Artifact.linuxDesktopPath:
633+
case Artifact.linuxHeaders:
634+
case Artifact.platformKernelDill:
635+
case Artifact.platformLibrariesJson:
636+
case Artifact.skyEnginePath:
637+
case Artifact.vmSnapshotData:
638+
case Artifact.windowsCppClientWrapper:
639+
case Artifact.windowsDesktopPath:
640+
case Artifact.flutterToolsFileGenerators:
641+
case Artifact.flutterPreviewDevice:
642+
return _getHostArtifactPath(artifact, platform, mode);
610643
}
611-
return _getHostArtifactPath(artifact, platform, mode);
612644
}
613645

614646
String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
@@ -628,6 +660,7 @@ class CachedArtifacts implements Artifacts {
628660
case Artifact.flutterFramework:
629661
case Artifact.flutterFrameworkDsym:
630662
case Artifact.flutterMacOSFramework:
663+
case Artifact.flutterMacOSFrameworkDsym:
631664
case Artifact.flutterMacOSXcframework:
632665
case Artifact.flutterPatchedSdkPath:
633666
case Artifact.flutterTester:
@@ -672,6 +705,7 @@ class CachedArtifacts implements Artifacts {
672705
case Artifact.frontendServerSnapshotForEngineDartSdk:
673706
case Artifact.constFinder:
674707
case Artifact.flutterMacOSFramework:
708+
case Artifact.flutterMacOSFrameworkDsym:
675709
case Artifact.flutterMacOSXcframework:
676710
case Artifact.flutterPatchedSdkPath:
677711
case Artifact.flutterTester:
@@ -722,6 +756,7 @@ class CachedArtifacts implements Artifacts {
722756
case Artifact.flutterFramework:
723757
case Artifact.flutterFrameworkDsym:
724758
case Artifact.flutterMacOSFramework:
759+
case Artifact.flutterMacOSFrameworkDsym:
725760
case Artifact.flutterMacOSXcframework:
726761
case Artifact.flutterTester:
727762
case Artifact.flutterXcframework:
@@ -794,7 +829,14 @@ class CachedArtifacts implements Artifacts {
794829
platformDirName = '$platformDirName-${mode!.cliName}';
795830
}
796831
final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
797-
return _getMacOSEngineArtifactPath(_fileSystem.path.join(engineArtifactsPath, platformDirName), _fileSystem, _platform);
832+
return _getMacOSFrameworkPath(_fileSystem.path.join(engineArtifactsPath, platformDirName), _fileSystem, _platform);
833+
case Artifact.flutterMacOSFrameworkDsym:
834+
String platformDirName = _enginePlatformDirectoryName(platform);
835+
if (mode == BuildMode.profile || mode == BuildMode.release) {
836+
platformDirName = '$platformDirName-${mode!.cliName}';
837+
}
838+
final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
839+
return _getMacOSFrameworkDsymPath(_fileSystem.path.join(engineArtifactsPath, platformDirName), _fileSystem, _platform);
798840
case Artifact.flutterMacOSXcframework:
799841
case Artifact.linuxDesktopPath:
800842
case Artifact.windowsDesktopPath:
@@ -957,7 +999,13 @@ String _getIosFrameworkDsymPath(
957999
.path;
9581000
}
9591001

960-
String _getMacOSEngineArtifactPath(
1002+
/// Returns the Flutter.xcframework platform directory for the specified environment type.
1003+
///
1004+
/// `FlutterMacOS.xcframework` contains target environment/architecture-specific
1005+
/// subdirectories containing the appropriate `FlutterMacOS.framework` and
1006+
/// `FlutterMacOS.framework.dSYM` bundles for that target architecture. At present,
1007+
/// there is only one such directory: `macos-arm64_x86_64`.
1008+
Directory _getMacOSFrameworkPlatformDirectory(
9611009
String engineDirectory,
9621010
FileSystem fileSystem,
9631011
Platform hostPlatform,
@@ -969,21 +1017,43 @@ String _getMacOSEngineArtifactPath(
9691017
if (!xcframeworkDirectory.existsSync()) {
9701018
throwToolExit('No xcframework found at ${xcframeworkDirectory.path}. Try running "flutter precache --macos".');
9711019
}
972-
final Directory? flutterFrameworkSource = xcframeworkDirectory
1020+
final Directory? platformDirectory = xcframeworkDirectory
9731021
.listSync()
9741022
.whereType<Directory>()
9751023
.where((Directory platformDirectory) =>
9761024
platformDirectory.basename.startsWith('macos-'))
9771025
.firstOrNull;
978-
if (flutterFrameworkSource == null) {
1026+
if (platformDirectory == null) {
9791027
throwToolExit('No macOS frameworks found in ${xcframeworkDirectory.path}');
9801028
}
1029+
return platformDirectory;
1030+
}
9811031

982-
return flutterFrameworkSource
1032+
/// Returns the path to `FlutterMacOS.framework`.
1033+
String _getMacOSFrameworkPath(
1034+
String engineDirectory,
1035+
FileSystem fileSystem,
1036+
Platform hostPlatform,
1037+
) {
1038+
final Directory platformDirectory = _getMacOSFrameworkPlatformDirectory(engineDirectory, fileSystem, hostPlatform);
1039+
return platformDirectory
9831040
.childDirectory(_artifactToFileName(Artifact.flutterMacOSFramework, hostPlatform)!)
9841041
.path;
9851042
}
9861043

1044+
/// Returns the path to `FlutterMacOS.framework`.
1045+
String _getMacOSFrameworkDsymPath(
1046+
String engineDirectory,
1047+
FileSystem fileSystem,
1048+
Platform hostPlatform,
1049+
) {
1050+
final Directory platformDirectory = _getMacOSFrameworkPlatformDirectory(engineDirectory, fileSystem, hostPlatform);
1051+
return platformDirectory
1052+
.childDirectory('dSYMs')
1053+
.childDirectory(_artifactToFileName(Artifact.flutterMacOSFrameworkDsym, hostPlatform)!)
1054+
.path;
1055+
}
1056+
9871057
/// Manages the artifacts of a locally built engine.
9881058
class CachedLocalEngineArtifacts implements Artifacts {
9891059
CachedLocalEngineArtifacts(
@@ -1158,7 +1228,10 @@ class CachedLocalEngineArtifacts implements Artifacts {
11581228
return _getIosFrameworkDsymPath(
11591229
localEngineInfo.targetOutPath, environmentType, _fileSystem, _platform);
11601230
case Artifact.flutterMacOSFramework:
1161-
return _getMacOSEngineArtifactPath(
1231+
return _getMacOSFrameworkPath(
1232+
localEngineInfo.targetOutPath, _fileSystem, _platform);
1233+
case Artifact.flutterMacOSFrameworkDsym:
1234+
return _getMacOSFrameworkDsymPath(
11621235
localEngineInfo.targetOutPath, _fileSystem, _platform);
11631236
case Artifact.flutterPatchedSdkPath:
11641237
// When using local engine always use [BuildMode.debug] regardless of
@@ -1341,6 +1414,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
13411414
case Artifact.flutterFrameworkDsym:
13421415
case Artifact.flutterXcframework:
13431416
case Artifact.flutterMacOSFramework:
1417+
case Artifact.flutterMacOSFrameworkDsym:
13441418
case Artifact.flutterMacOSXcframework:
13451419
case Artifact.vmSnapshotData:
13461420
case Artifact.isolateSnapshotData:

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ abstract class UnpackMacOS extends Target {
4040
@override
4141
List<Source> get outputs => const <Source>[
4242
Source.pattern('{OUTPUT_DIR}/FlutterMacOS.framework/Versions/A/FlutterMacOS'),
43+
Source.pattern('{OUTPUT_DIR}/FlutterMacOS.framework.dSYM/Contents/Resources/DWARF/FlutterMacOS'),
4344
];
4445

4546
@override
@@ -51,9 +52,10 @@ abstract class UnpackMacOS extends Target {
5152
if (buildModeEnvironment == null) {
5253
throw MissingDefineException(kBuildMode, 'unpack_macos');
5354
}
55+
56+
// Copy Flutter framework.
5457
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
5558
final String basePath = environment.artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: buildMode);
56-
5759
final ProcessResult result = environment.processManager.runSync(<String>[
5860
'rsync',
5961
'-av',
@@ -82,7 +84,35 @@ abstract class UnpackMacOS extends Target {
8284
if (!frameworkBinary.existsSync()) {
8385
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
8486
}
87+
8588
await _thinFramework(environment, frameworkBinaryPath);
89+
90+
// Copy Flutter framework dSYM (debug symbol) bundle, if present.
91+
final Directory frameworkDsym = environment.fileSystem.directory(
92+
environment.artifacts.getArtifactPath(
93+
Artifact.flutterMacOSFrameworkDsym,
94+
platform: TargetPlatform.darwin,
95+
mode: buildMode,
96+
)
97+
);
98+
if (frameworkDsym.existsSync()) {
99+
final ProcessResult result = await environment.processManager.run(<String>[
100+
'rsync',
101+
'-av',
102+
'--delete',
103+
'--filter',
104+
'- .DS_Store/',
105+
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
106+
frameworkDsym.path,
107+
environment.outputDir.path,
108+
]);
109+
if (result.exitCode != 0) {
110+
throw Exception(
111+
'Failed to copy framework dSYM (exit ${result.exitCode}:\n'
112+
'${result.stdout}\n---\n${result.stderr}',
113+
);
114+
}
115+
}
86116
}
87117

88118
static const List<String> _copyDenylist = <String>['entitlements.txt', 'without_entitlements.txt'];
@@ -159,6 +189,7 @@ class ReleaseUnpackMacOS extends UnpackMacOS {
159189
List<Source> get inputs => <Source>[
160190
...super.inputs,
161191
const Source.artifact(Artifact.flutterMacOSXcframework, mode: BuildMode.release),
192+
const Source.artifact(Artifact.flutterMacOSXcframework, mode: BuildMode.release),
162193
];
163194
}
164195

0 commit comments

Comments
 (0)