macOS: Copy macOS framwork dSYM into build outputs (#153975)

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:
* https://github.com/flutter/engine/pull/54696

This copies the FlutterMacOS.framework.dSYM bundle from the tools cache to the build outputs produced by `flutter build macOS`.

Fixes: https://github.com/flutter/flutter/issues/153879
This commit is contained in:
Chris Bracken 2024-08-23 17:14:15 -07:00 committed by GitHub
parent a09941dcf1
commit 11ab37ee81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 333 additions and 65 deletions

View File

@ -26,6 +26,7 @@ enum Artifact {
flutterXcframework, flutterXcframework,
/// The framework directory of the macOS desktop. /// The framework directory of the macOS desktop.
flutterMacOSFramework, flutterMacOSFramework,
flutterMacOSFrameworkDsym,
flutterMacOSXcframework, flutterMacOSXcframework,
vmSnapshotData, vmSnapshotData,
isolateSnapshotData, isolateSnapshotData,
@ -182,12 +183,14 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return 'flutter_tester$exe'; return 'flutter_tester$exe';
case Artifact.flutterFramework: case Artifact.flutterFramework:
return 'Flutter.framework'; return 'Flutter.framework';
case Artifact.flutterFrameworkDsym: case Artifact.flutterFrameworkDsym:
return 'Flutter.framework.dSYM'; return 'Flutter.framework.dSYM';
case Artifact.flutterXcframework: case Artifact.flutterXcframework:
return 'Flutter.xcframework'; return 'Flutter.xcframework';
case Artifact.flutterMacOSFramework: case Artifact.flutterMacOSFramework:
return 'FlutterMacOS.framework'; return 'FlutterMacOS.framework';
case Artifact.flutterMacOSFrameworkDsym:
return 'FlutterMacOS.framework.dSYM';
case Artifact.flutterMacOSXcframework: case Artifact.flutterMacOSXcframework:
return 'FlutterMacOS.xcframework'; return 'FlutterMacOS.xcframework';
case Artifact.vmSnapshotData: case Artifact.vmSnapshotData:
@ -600,15 +603,44 @@ class CachedArtifacts implements Artifacts {
String _getDesktopArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode? mode) { String _getDesktopArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode? mode) {
// When platform is null, a generic host platform artifact is being requested // When platform is null, a generic host platform artifact is being requested
// and not the gen_snapshot for darwin as a target platform. // and not the gen_snapshot for darwin as a target platform.
if (artifact == Artifact.genSnapshot) { final String engineDir = _getEngineArtifactsPath(platform, mode)!;
final String engineDir = _getEngineArtifactsPath(platform, mode)!; switch (artifact) {
return _fileSystem.path.join(engineDir, _artifactToFileName(artifact, _platform)); case Artifact.genSnapshot:
return _fileSystem.path.join(engineDir, _artifactToFileName(artifact, _platform));
case Artifact.engineDartSdkPath:
case Artifact.engineDartBinary:
case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder:
case Artifact.flutterFramework:
case Artifact.flutterFrameworkDsym:
case Artifact.flutterMacOSFramework:
return _getMacOSFrameworkPath(engineDir, _fileSystem, _platform);
case Artifact.flutterMacOSFrameworkDsym:
return _getMacOSFrameworkDsymPath(engineDir, _fileSystem, _platform);
case Artifact.flutterMacOSXcframework:
case Artifact.flutterPatchedSdkPath:
case Artifact.flutterTester:
case Artifact.flutterXcframework:
case Artifact.fontSubset:
case Artifact.fuchsiaFlutterRunner:
case Artifact.fuchsiaKernelCompiler:
case Artifact.icuData:
case Artifact.isolateSnapshotData:
case Artifact.linuxDesktopPath:
case Artifact.linuxHeaders:
case Artifact.platformKernelDill:
case Artifact.platformLibrariesJson:
case Artifact.skyEnginePath:
case Artifact.vmSnapshotData:
case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath:
case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
return _getHostArtifactPath(artifact, platform, mode);
} }
if (artifact == Artifact.flutterMacOSFramework) {
final String engineDir = _getEngineArtifactsPath(platform, mode)!;
return _getMacOSEngineArtifactPath(engineDir, _fileSystem, _platform);
}
return _getHostArtifactPath(artifact, platform, mode);
} }
String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) { String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
@ -628,6 +660,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.flutterFramework: case Artifact.flutterFramework:
case Artifact.flutterFrameworkDsym: case Artifact.flutterFrameworkDsym:
case Artifact.flutterMacOSFramework: case Artifact.flutterMacOSFramework:
case Artifact.flutterMacOSFrameworkDsym:
case Artifact.flutterMacOSXcframework: case Artifact.flutterMacOSXcframework:
case Artifact.flutterPatchedSdkPath: case Artifact.flutterPatchedSdkPath:
case Artifact.flutterTester: case Artifact.flutterTester:
@ -672,6 +705,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.frontendServerSnapshotForEngineDartSdk: case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder: case Artifact.constFinder:
case Artifact.flutterMacOSFramework: case Artifact.flutterMacOSFramework:
case Artifact.flutterMacOSFrameworkDsym:
case Artifact.flutterMacOSXcframework: case Artifact.flutterMacOSXcframework:
case Artifact.flutterPatchedSdkPath: case Artifact.flutterPatchedSdkPath:
case Artifact.flutterTester: case Artifact.flutterTester:
@ -722,6 +756,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.flutterFramework: case Artifact.flutterFramework:
case Artifact.flutterFrameworkDsym: case Artifact.flutterFrameworkDsym:
case Artifact.flutterMacOSFramework: case Artifact.flutterMacOSFramework:
case Artifact.flutterMacOSFrameworkDsym:
case Artifact.flutterMacOSXcframework: case Artifact.flutterMacOSXcframework:
case Artifact.flutterTester: case Artifact.flutterTester:
case Artifact.flutterXcframework: case Artifact.flutterXcframework:
@ -794,7 +829,14 @@ class CachedArtifacts implements Artifacts {
platformDirName = '$platformDirName-${mode!.cliName}'; platformDirName = '$platformDirName-${mode!.cliName}';
} }
final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path; final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
return _getMacOSEngineArtifactPath(_fileSystem.path.join(engineArtifactsPath, platformDirName), _fileSystem, _platform); return _getMacOSFrameworkPath(_fileSystem.path.join(engineArtifactsPath, platformDirName), _fileSystem, _platform);
case Artifact.flutterMacOSFrameworkDsym:
String platformDirName = _enginePlatformDirectoryName(platform);
if (mode == BuildMode.profile || mode == BuildMode.release) {
platformDirName = '$platformDirName-${mode!.cliName}';
}
final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
return _getMacOSFrameworkDsymPath(_fileSystem.path.join(engineArtifactsPath, platformDirName), _fileSystem, _platform);
case Artifact.flutterMacOSXcframework: case Artifact.flutterMacOSXcframework:
case Artifact.linuxDesktopPath: case Artifact.linuxDesktopPath:
case Artifact.windowsDesktopPath: case Artifact.windowsDesktopPath:
@ -957,7 +999,13 @@ String _getIosFrameworkDsymPath(
.path; .path;
} }
String _getMacOSEngineArtifactPath( /// Returns the Flutter.xcframework platform directory for the specified environment type.
///
/// `FlutterMacOS.xcframework` contains target environment/architecture-specific
/// subdirectories containing the appropriate `FlutterMacOS.framework` and
/// `FlutterMacOS.framework.dSYM` bundles for that target architecture. At present,
/// there is only one such directory: `macos-arm64_x86_64`.
Directory _getMacOSFrameworkPlatformDirectory(
String engineDirectory, String engineDirectory,
FileSystem fileSystem, FileSystem fileSystem,
Platform hostPlatform, Platform hostPlatform,
@ -969,21 +1017,43 @@ String _getMacOSEngineArtifactPath(
if (!xcframeworkDirectory.existsSync()) { if (!xcframeworkDirectory.existsSync()) {
throwToolExit('No xcframework found at ${xcframeworkDirectory.path}. Try running "flutter precache --macos".'); throwToolExit('No xcframework found at ${xcframeworkDirectory.path}. Try running "flutter precache --macos".');
} }
final Directory? flutterFrameworkSource = xcframeworkDirectory final Directory? platformDirectory = xcframeworkDirectory
.listSync() .listSync()
.whereType<Directory>() .whereType<Directory>()
.where((Directory platformDirectory) => .where((Directory platformDirectory) =>
platformDirectory.basename.startsWith('macos-')) platformDirectory.basename.startsWith('macos-'))
.firstOrNull; .firstOrNull;
if (flutterFrameworkSource == null) { if (platformDirectory == null) {
throwToolExit('No macOS frameworks found in ${xcframeworkDirectory.path}'); throwToolExit('No macOS frameworks found in ${xcframeworkDirectory.path}');
} }
return platformDirectory;
}
return flutterFrameworkSource /// Returns the path to `FlutterMacOS.framework`.
String _getMacOSFrameworkPath(
String engineDirectory,
FileSystem fileSystem,
Platform hostPlatform,
) {
final Directory platformDirectory = _getMacOSFrameworkPlatformDirectory(engineDirectory, fileSystem, hostPlatform);
return platformDirectory
.childDirectory(_artifactToFileName(Artifact.flutterMacOSFramework, hostPlatform)!) .childDirectory(_artifactToFileName(Artifact.flutterMacOSFramework, hostPlatform)!)
.path; .path;
} }
/// Returns the path to `FlutterMacOS.framework`.
String _getMacOSFrameworkDsymPath(
String engineDirectory,
FileSystem fileSystem,
Platform hostPlatform,
) {
final Directory platformDirectory = _getMacOSFrameworkPlatformDirectory(engineDirectory, fileSystem, hostPlatform);
return platformDirectory
.childDirectory('dSYMs')
.childDirectory(_artifactToFileName(Artifact.flutterMacOSFrameworkDsym, hostPlatform)!)
.path;
}
/// Manages the artifacts of a locally built engine. /// Manages the artifacts of a locally built engine.
class CachedLocalEngineArtifacts implements Artifacts { class CachedLocalEngineArtifacts implements Artifacts {
CachedLocalEngineArtifacts( CachedLocalEngineArtifacts(
@ -1158,7 +1228,10 @@ class CachedLocalEngineArtifacts implements Artifacts {
return _getIosFrameworkDsymPath( return _getIosFrameworkDsymPath(
localEngineInfo.targetOutPath, environmentType, _fileSystem, _platform); localEngineInfo.targetOutPath, environmentType, _fileSystem, _platform);
case Artifact.flutterMacOSFramework: case Artifact.flutterMacOSFramework:
return _getMacOSEngineArtifactPath( return _getMacOSFrameworkPath(
localEngineInfo.targetOutPath, _fileSystem, _platform);
case Artifact.flutterMacOSFrameworkDsym:
return _getMacOSFrameworkDsymPath(
localEngineInfo.targetOutPath, _fileSystem, _platform); localEngineInfo.targetOutPath, _fileSystem, _platform);
case Artifact.flutterPatchedSdkPath: case Artifact.flutterPatchedSdkPath:
// When using local engine always use [BuildMode.debug] regardless of // When using local engine always use [BuildMode.debug] regardless of
@ -1341,6 +1414,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case Artifact.flutterFrameworkDsym: case Artifact.flutterFrameworkDsym:
case Artifact.flutterXcframework: case Artifact.flutterXcframework:
case Artifact.flutterMacOSFramework: case Artifact.flutterMacOSFramework:
case Artifact.flutterMacOSFrameworkDsym:
case Artifact.flutterMacOSXcframework: case Artifact.flutterMacOSXcframework:
case Artifact.vmSnapshotData: case Artifact.vmSnapshotData:
case Artifact.isolateSnapshotData: case Artifact.isolateSnapshotData:

View File

@ -51,9 +51,10 @@ abstract class UnpackMacOS extends Target {
if (buildModeEnvironment == null) { if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'unpack_macos'); throw MissingDefineException(kBuildMode, 'unpack_macos');
} }
// Copy Flutter framework.
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment); final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final String basePath = environment.artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: buildMode); final String basePath = environment.artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: buildMode);
final ProcessResult result = environment.processManager.runSync(<String>[ final ProcessResult result = environment.processManager.runSync(<String>[
'rsync', 'rsync',
'-av', '-av',
@ -82,6 +83,7 @@ abstract class UnpackMacOS extends Target {
if (!frameworkBinary.existsSync()) { if (!frameworkBinary.existsSync()) {
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin'); throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
} }
await _thinFramework(environment, frameworkBinaryPath); await _thinFramework(environment, frameworkBinaryPath);
} }
@ -156,10 +158,51 @@ class ReleaseUnpackMacOS extends UnpackMacOS {
String get name => 'release_unpack_macos'; String get name => 'release_unpack_macos';
@override @override
List<Source> get inputs => <Source>[ List<Source> get outputs => super.outputs + const <Source>[
...super.inputs, Source.pattern('{OUTPUT_DIR}/FlutterMacOS.framework.dSYM/Contents/Resources/DWARF/FlutterMacOS'),
const Source.artifact(Artifact.flutterMacOSXcframework, mode: BuildMode.release),
]; ];
@override
List<Source> get inputs => super.inputs + const <Source>[
Source.artifact(Artifact.flutterMacOSXcframework, mode: BuildMode.release),
];
@override
Future<void> build(Environment environment) async {
await super.build(environment);
// Copy Flutter framework dSYM (debug symbol) bundle, if present.
final String? buildModeEnvironment = environment.defines[kBuildMode];
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'unpack_macos');
}
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Directory frameworkDsym = environment.fileSystem.directory(
environment.artifacts.getArtifactPath(
Artifact.flutterMacOSFrameworkDsym,
platform: TargetPlatform.darwin,
mode: buildMode,
)
);
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}',
);
}
}
}
} }
/// Unpack the profile prebuilt engine framework. /// Unpack the profile prebuilt engine framework.

View File

@ -25,11 +25,15 @@ void main() {
late Artifacts artifacts; late Artifacts artifacts;
late FakeProcessManager processManager; late FakeProcessManager processManager;
late File binary; late File binary;
late File frameworkDsym;
late BufferLogger logger; late BufferLogger logger;
late FakeCommand copyFrameworkCommand; late FakeCommand copyFrameworkCommand;
late FakeCommand releaseCopyFrameworkCommand;
late FakeCommand copyFrameworkDsymCommand;
late FakeCommand lipoInfoNonFatCommand; late FakeCommand lipoInfoNonFatCommand;
late FakeCommand lipoInfoFatCommand; late FakeCommand lipoInfoFatCommand;
late FakeCommand lipoVerifyX86_64Command; late FakeCommand lipoVerifyX86_64Command;
late FakeCommand lipoExtractX86_64Command;
late TestUsage usage; late TestUsage usage;
late FakeAnalytics fakeAnalytics; late FakeAnalytics fakeAnalytics;
@ -79,6 +83,51 @@ void main() {
], ],
); );
releaseCopyFrameworkCommand = FakeCommand(
command: <String>[
'rsync',
'-av',
'--delete',
'--filter',
'- .DS_Store/',
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
'Artifact.flutterMacOSFramework.release',
environment.outputDir.path,
],
);
frameworkDsym = fileSystem.directory(
artifacts.getArtifactPath(
Artifact.flutterMacOSFrameworkDsym,
platform: TargetPlatform.darwin,
mode: BuildMode.release,
),
)
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('FlutterMacOS');
environment.outputDir
.childDirectory('FlutterMacOS.framework.dSYM')
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('FlutterMacOS');
copyFrameworkDsymCommand = FakeCommand(
command: <String>[
'rsync',
'-av',
'--delete',
'--filter',
'- .DS_Store/',
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
'Artifact.flutterMacOSFrameworkDsym.TargetPlatform.darwin.release',
environment.outputDir.path,
],
);
lipoInfoNonFatCommand = FakeCommand(command: <String>[ lipoInfoNonFatCommand = FakeCommand(command: <String>[
'lipo', 'lipo',
'-info', '-info',
@ -97,6 +146,15 @@ void main() {
'-verify_arch', '-verify_arch',
'x86_64', 'x86_64',
]); ]);
lipoExtractX86_64Command = FakeCommand(command: <String>[
'lipo',
'-output',
binary.path,
'-extract',
'x86_64',
binary.path,
]);
}); });
testUsingContext('Copies files to correct cache directory', () async { testUsingContext('Copies files to correct cache directory', () async {
@ -223,14 +281,7 @@ void main() {
copyFrameworkCommand, copyFrameworkCommand,
lipoInfoFatCommand, lipoInfoFatCommand,
lipoVerifyX86_64Command, lipoVerifyX86_64Command,
FakeCommand(command: <String>[ lipoExtractX86_64Command,
'lipo',
'-output',
binary.path,
'-extract',
'x86_64',
binary.path,
]),
]); ]);
await const DebugUnpackMacOS().build(environment); await const DebugUnpackMacOS().build(environment);
@ -238,6 +289,76 @@ void main() {
expect(processManager, hasNoRemainingExpectations); expect(processManager, hasNoRemainingExpectations);
}); });
testUsingContext('Copies files to correct cache directory when no dSYM available in xcframework', () async {
binary.createSync(recursive: true);
processManager.addCommands(<FakeCommand>[
releaseCopyFrameworkCommand,
lipoInfoNonFatCommand,
lipoVerifyX86_64Command,
]);
await const ReleaseUnpackMacOS().build(environment..defines[kBuildMode] = 'release');
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
testUsingContext('Copies files to correct cache directory when dSYM available in xcframework', () async {
binary.createSync(recursive: true);
frameworkDsym.createSync(recursive: true);
processManager.addCommands(<FakeCommand>[
releaseCopyFrameworkCommand,
lipoInfoNonFatCommand,
lipoVerifyX86_64Command,
copyFrameworkDsymCommand,
]);
await const ReleaseUnpackMacOS().build(environment..defines[kBuildMode] = 'release');
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
testUsingContext('Fails if framework dSYM found within framework but copy fails', () async {
binary.createSync(recursive: true);
frameworkDsym.createSync(recursive: true);
final FakeCommand failedCopyFrameworkDsymCommand = FakeCommand(
command: <String>[
'rsync',
'-av',
'--delete',
'--filter',
'- .DS_Store/',
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
'Artifact.flutterMacOSFrameworkDsym.TargetPlatform.darwin.release',
environment.outputDir.path,
], exitCode: 1,
);
processManager.addCommands(<FakeCommand>[
releaseCopyFrameworkCommand,
lipoInfoFatCommand,
lipoVerifyX86_64Command,
lipoExtractX86_64Command,
failedCopyFrameworkDsymCommand,
]);
await expectLater(
const ReleaseUnpackMacOS().build(environment..defines[kBuildMode] = 'release'),
throwsA(isException.having(
(Exception exception) => exception.toString(),
'description',
contains('Failed to copy framework dSYM'),
)),
);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
testUsingContext('debug macOS application fails if App.framework missing', () async { testUsingContext('debug macOS application fails if App.framework missing', () async {
fileSystem.directory( fileSystem.directory(
artifacts.getArtifactPath( artifacts.getArtifactPath(

View File

@ -24,48 +24,68 @@ void main() {
]); ]);
}); });
test('verify FlutterMacOS.xcframework artifact', () { for (final String buildMode in <String>['Debug', 'Release']) {
final String flutterRoot = getFlutterRoot(); test('verify $buildMode FlutterMacOS.xcframework artifact', () {
final String flutterRoot = getFlutterRoot();
final Directory xcframeworkArtifact = fileSystem.directory( final String artifactDir = (buildMode == 'Debug') ? 'darwin-x64' : 'darwin-x64-release';
fileSystem.path.join( final Directory xcframeworkArtifact = fileSystem.directory(
flutterRoot, fileSystem.path.join(
'bin', flutterRoot,
'cache', 'bin',
'artifacts', 'cache',
'engine', 'artifacts',
'darwin-x64', 'engine',
'FlutterMacOS.xcframework', artifactDir,
), 'FlutterMacOS.xcframework',
); ),
);
final Directory tempDir = createResolvedTempDirectorySync('macos_content_validation.'); final Directory tempDir = createResolvedTempDirectorySync('macos_content_validation.');
// Pre-cache macOS engine FlutterMacOS.xcframework artifacts. // Pre-cache macOS engine FlutterMacOS.xcframework artifacts.
final ProcessResult result = processManager.runSync( final ProcessResult result = processManager.runSync(
<String>[ <String>[
flutterBin, flutterBin,
...getLocalEngineArguments(), ...getLocalEngineArguments(),
'precache', 'precache',
'--macos', '--macos',
], ],
workingDirectory: tempDir.path, workingDirectory: tempDir.path,
); );
expect(result, const ProcessResultMatcher()); expect(result, const ProcessResultMatcher());
expect(xcframeworkArtifact.existsSync(), isTrue); expect(xcframeworkArtifact.existsSync(), isTrue);
final Directory frameworkArtifact = fileSystem.directory( final Directory frameworkArtifact = fileSystem.directory(
fileSystem.path.joinAll(<String>[ fileSystem.path.joinAll(<String>[
xcframeworkArtifact.path, xcframeworkArtifact.path,
'macos-arm64_x86_64', 'macos-arm64_x86_64',
'FlutterMacOS.framework', 'FlutterMacOS.framework',
]), ]),
); );
// Check read/write permissions are set correctly in the framework engine artifact. // Check read/write permissions are set correctly in the framework engine artifact.
final String artifactStat = frameworkArtifact.statSync().mode.toRadixString(8); final String artifactStat = frameworkArtifact.statSync().mode.toRadixString(8);
expect(artifactStat, '40755'); expect(artifactStat, '40755');
});
if (buildMode == 'Release') {
final Directory dsymArtifact = fileSystem.directory(
fileSystem.path.joinAll(<String>[
xcframeworkArtifact.path,
'macos-arm64_x86_64',
'dSYMs',
'FlutterMacOS.framework.dSYM',
]),
);
// Verify dSYM is present.
expect(dsymArtifact.existsSync(), isTrue);
// Check read/write permissions are set correctly in the framework engine artifact.
final String artifactStat = dsymArtifact.statSync().mode.toRadixString(8);
expect(artifactStat, '40755');
}
});
}
for (final String buildMode in <String>['Debug', 'Release']) { for (final String buildMode in <String>['Debug', 'Release']) {
final String buildModeLower = buildMode.toLowerCase(); final String buildModeLower = buildMode.toLowerCase();
@ -129,6 +149,9 @@ void main() {
'App.framework', 'App.framework',
)); ));
final File frameworkDsymBinary =
buildPath.childFile('FlutterMacOS.framework.dSYM/Contents/Resources/DWARF/FlutterMacOS');
final File libBinary = outputAppFramework.childFile('App'); final File libBinary = outputAppFramework.childFile('App');
final File libDsymBinary = final File libDsymBinary =
buildPath.childFile('App.framework.dSYM/Contents/Resources/DWARF/App'); buildPath.childFile('App.framework.dSYM/Contents/Resources/DWARF/App');
@ -138,10 +161,17 @@ void main() {
final List<String> libSymbols = AppleTestUtils.getExportedSymbols(libBinary.path); final List<String> libSymbols = AppleTestUtils.getExportedSymbols(libBinary.path);
if (buildMode == 'Debug') { if (buildMode == 'Debug') {
// Framework dSYM is not copied for debug builds.
expect(frameworkDsymBinary.existsSync(), isFalse);
// dSYM is not created for a debug build. // dSYM is not created for a debug build.
expect(libDsymBinary.existsSync(), isFalse); expect(libDsymBinary.existsSync(), isFalse);
expect(libSymbols, isEmpty); expect(libSymbols, isEmpty);
} else { } else {
// Check framework dSYM file copied.
_checkFatBinary(frameworkDsymBinary, buildModeLower, 'dSYM companion file');
// Check extracted dSYM file.
_checkFatBinary(libDsymBinary, buildModeLower, 'dSYM companion file'); _checkFatBinary(libDsymBinary, buildModeLower, 'dSYM companion file');
expect(libSymbols, equals(AppleTestUtils.requiredSymbols)); expect(libSymbols, equals(AppleTestUtils.requiredSymbols));
final List<String> dSymSymbols = final List<String> dSymSymbols =