Revert "Separate copying dsym into its own target (#178261)" (#178545)

This reverts commit b1d5f03351590a0954d19c7584186f0722323169.

Turns out this isn't needed for
https://github.com/flutter/flutter/issues/166489 after all. It appeared
that SwiftPM wasn't copying the dSYM, but I now think I had my build
modes mixed up (since dSYMs are only relevant to release mode).

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
Victoria Ashworth 2025-11-17 09:48:50 -06:00 committed by GitHub
parent ce7d9b9882
commit ae455b9fca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 118 additions and 245 deletions

View File

@ -5,7 +5,6 @@
import 'package:meta/meta.dart';
import '../../artifacts.dart';
import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../build_info.dart';
import '../../globals.dart' as globals show stdio;
@ -114,61 +113,6 @@ abstract class UnpackDarwin extends Target {
}
}
abstract class ReleaseUnpackDarwinDsym extends Target {
const ReleaseUnpackDarwinDsym();
BuildMode get buildMode => BuildMode.release;
@visibleForOverriding
TargetPlatform get targetPlatform;
Artifact get dsymArtifact;
@override
List<Source> get inputs => <Source>[
const Source.pattern(
'{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/darwin.dart',
),
Source.artifact(dsymArtifact, platform: targetPlatform, mode: buildMode),
];
@override
List<Target> get dependencies => [];
Future<void> copyFrameworkDsym(
Environment environment, {
EnvironmentType? environmentType,
}) async {
// Copy Flutter framework dSYM (debug symbol) bundle, if present.
final Directory frameworkDsym = environment.fileSystem.directory(
environment.artifacts.getArtifactPath(
dsymArtifact,
platform: targetPlatform,
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}',
);
}
}
}
}
/// Log warning message to the Xcode build logs. Log will show as yellow with an icon.
///
/// If the issue occurs in a specific file, include the [filePath] as an absolute path.

View File

@ -285,6 +285,7 @@ abstract class UnpackIOS extends UnpackDarwin {
targetPlatform: TargetPlatform.ios,
buildMode: buildMode,
);
await _copyFrameworkDysm(environment, sdkRoot: sdkRoot, environmentType: environmentType);
final File frameworkBinary = environment.outputDir
.childDirectory(FlutterDarwinPlatform.ios.frameworkName)
@ -296,6 +297,40 @@ abstract class UnpackIOS extends UnpackDarwin {
await thinFramework(environment, frameworkBinaryPath, archs);
await _signFramework(environment, frameworkBinary, buildMode);
}
Future<void> _copyFrameworkDysm(
Environment environment, {
required String sdkRoot,
EnvironmentType? environmentType,
}) async {
// 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}',
);
}
}
}
}
/// Unpack the release prebuilt engine framework.
@ -307,9 +342,6 @@ class ReleaseUnpackIOS extends UnpackIOS {
@override
BuildMode get buildMode => BuildMode.release;
@override
List<Target> get dependencies => [...super.dependencies, const ReleaseUnpackIOSDsym()];
}
/// Unpack the profile prebuilt engine framework.
@ -334,45 +366,6 @@ class DebugUnpackIOS extends UnpackIOS {
BuildMode get buildMode => BuildMode.debug;
}
class ReleaseUnpackIOSDsym extends ReleaseUnpackDarwinDsym {
const ReleaseUnpackIOSDsym();
@override
String get name => 'release_unpack_ios_dsym';
@override
TargetPlatform get targetPlatform => TargetPlatform.ios;
@override
Artifact get dsymArtifact => Artifact.flutterFrameworkDsym;
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.pattern(
'{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/ios.dart',
),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/Flutter.framework.dSYM/Contents/Resources/DWARF/Flutter'),
];
@override
Future<void> build(Environment environment) async {
final String? sdkRoot = environment.defines[kSdkRoot];
if (sdkRoot == null) {
throw MissingDefineException(kSdkRoot, name);
}
final EnvironmentType? environmentType = environmentTypeFromSdkroot(
sdkRoot,
environment.fileSystem,
);
await copyFrameworkDsym(environment, environmentType: environmentType);
}
}
// TODO(gaaclarke): Remove this after a reasonable amount of time where the
// UISceneDelegate migration being on stable. This incurs a minor build time
// cost.

View File

@ -7,6 +7,7 @@ import 'package:unified_analytics/unified_analytics.dart';
import '../../artifacts.dart';
import '../../base/build.dart';
import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../base/process.dart';
import '../../build_info.dart';
import '../../darwin/darwin.dart';
@ -107,13 +108,56 @@ class ReleaseUnpackMacOS extends UnpackMacOS {
@override
String get name => 'release_unpack_macos';
@override
List<Source> get outputs =>
super.outputs +
const <Source>[
Source.pattern(
'{OUTPUT_DIR}/FlutterMacOS.framework.dSYM/Contents/Resources/DWARF/FlutterMacOS',
),
];
@override
List<Source> get inputs =>
super.inputs +
const <Source>[Source.artifact(Artifact.flutterMacOSXcframework, mode: BuildMode.release)];
@override
List<Target> get dependencies => [...super.dependencies, const ReleaseUnpackMacOSDsym()];
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.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.
@ -144,39 +188,6 @@ class DebugUnpackMacOS extends UnpackMacOS {
];
}
class ReleaseUnpackMacOSDsym extends ReleaseUnpackDarwinDsym {
const ReleaseUnpackMacOSDsym();
@override
String get name => 'release_unpack_macos_dsym';
@override
TargetPlatform get targetPlatform => TargetPlatform.darwin;
@override
Artifact get dsymArtifact => Artifact.flutterMacOSFrameworkDsym;
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.pattern(
'{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart',
),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern(
'{OUTPUT_DIR}/FlutterMacOS.framework.dSYM/Contents/Resources/DWARF/FlutterMacOS',
),
];
@override
Future<void> build(Environment environment) async {
await copyFrameworkDsym(environment);
}
}
/// Create an App.framework for debug macOS targets.
///
/// This framework needs to exist for the Xcode project to link/bundle,

View File

@ -694,8 +694,7 @@ void main() {
group('copies Flutter.framework', () {
late Directory outputDir;
late File binary;
late FakeCommand copyPhysicalDebugFrameworkCommand;
late FakeCommand copyPhysicalReleaseFrameworkCommand;
late FakeCommand copyPhysicalFrameworkCommand;
late FakeCommand copyPhysicalFrameworkDsymCommand;
late FakeCommand copyPhysicalFrameworkDsymCommandFailure;
late FakeCommand lipoCommandNonFatResult;
@ -708,7 +707,7 @@ void main() {
outputDir = fileSystem.directory('output');
binary = outputDir.childDirectory('Flutter.framework').childFile('Flutter');
copyPhysicalDebugFrameworkCommand = FakeCommand(
copyPhysicalFrameworkCommand = FakeCommand(
command: <String>[
'rsync',
'-av',
@ -720,18 +719,6 @@ void main() {
outputDir.path,
],
);
copyPhysicalReleaseFrameworkCommand = FakeCommand(
command: <String>[
'rsync',
'-av',
'--delete',
'--filter',
'- .DS_Store/',
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
'Artifact.flutterFramework.TargetPlatform.ios.release.EnvironmentType.physical',
outputDir.path,
],
);
copyPhysicalFrameworkDsymCommand = FakeCommand(
command: <String>[
@ -741,7 +728,7 @@ void main() {
'--filter',
'- .DS_Store/',
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
'Artifact.flutterFrameworkDsym.TargetPlatform.ios.release.EnvironmentType.physical',
'Artifact.flutterFrameworkDsym.TargetPlatform.ios.debug.EnvironmentType.physical',
outputDir.path,
],
);
@ -754,7 +741,7 @@ void main() {
'--filter',
'- .DS_Store/',
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
'Artifact.flutterFrameworkDsym.TargetPlatform.ios.release.EnvironmentType.physical',
'Artifact.flutterFrameworkDsym.TargetPlatform.ios.debug.EnvironmentType.physical',
outputDir.path,
],
exitCode: 1,
@ -827,7 +814,7 @@ void main() {
outputDir: outputDir,
defines: <String, String>{kIosArchs: 'arm64', kSdkRoot: 'path/to/iPhoneOS.sdk'},
);
processManager.addCommand(copyPhysicalDebugFrameworkCommand);
processManager.addCommand(copyPhysicalFrameworkCommand);
await expectLater(
const DebugUnpackIOS().build(environment),
throwsA(
@ -846,7 +833,7 @@ void main() {
artifacts.getArtifactPath(
Artifact.flutterFrameworkDsym,
platform: TargetPlatform.ios,
mode: BuildMode.release,
mode: BuildMode.debug,
environmentType: EnvironmentType.physical,
),
);
@ -861,9 +848,12 @@ void main() {
outputDir: outputDir,
defines: <String, String>{kIosArchs: 'arm64', kSdkRoot: 'path/to/iPhoneOS.sdk'},
);
processManager.addCommands(<FakeCommand>[copyPhysicalFrameworkDsymCommandFailure]);
processManager.addCommands(<FakeCommand>[
copyPhysicalFrameworkCommand,
copyPhysicalFrameworkDsymCommandFailure,
]);
await expectLater(
const ReleaseUnpackIOSDsym().build(environment),
const DebugUnpackIOS().build(environment),
throwsA(
isException.having(
(Exception exception) => exception.toString(),
@ -888,7 +878,7 @@ void main() {
);
processManager.addCommands(<FakeCommand>[
copyPhysicalDebugFrameworkCommand,
copyPhysicalFrameworkCommand,
FakeCommand(
command: <String>['lipo', '-info', binary.path],
stdout: 'Architectures in the fat file:',
@ -928,7 +918,7 @@ void main() {
);
processManager.addCommands(<FakeCommand>[
copyPhysicalDebugFrameworkCommand,
copyPhysicalFrameworkCommand,
FakeCommand(
command: <String>['lipo', '-info', binary.path],
stdout: 'Architectures in the fat file:',
@ -1062,7 +1052,7 @@ void main() {
);
processManager.addCommands(<FakeCommand>[
copyPhysicalDebugFrameworkCommand,
copyPhysicalFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
xattrCommand,
@ -1092,7 +1082,7 @@ void main() {
);
processManager.addCommands(<FakeCommand>[
copyPhysicalDebugFrameworkCommand,
copyPhysicalFrameworkCommand,
FakeCommand(
command: <String>['lipo', '-info', binary.path],
stdout: 'Architectures in the fat file:',
@ -1132,7 +1122,7 @@ void main() {
);
processManager.addCommands(<FakeCommand>[
copyPhysicalDebugFrameworkCommand,
copyPhysicalFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
xattrCommand,
@ -1161,7 +1151,7 @@ void main() {
);
processManager.addCommands(<FakeCommand>[
copyPhysicalDebugFrameworkCommand,
copyPhysicalFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
xattrCommand,
@ -1196,13 +1186,13 @@ void main() {
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('codesigns framework in release mode', () async {
testWithoutContext('codesigns framework', () async {
binary.createSync(recursive: true);
final Directory dSYM = fileSystem.directory(
artifacts.getArtifactPath(
Artifact.flutterFrameworkDsym,
platform: TargetPlatform.ios,
mode: BuildMode.release,
mode: BuildMode.debug,
environmentType: EnvironmentType.physical,
),
);
@ -1223,41 +1213,8 @@ void main() {
);
processManager.addCommands(<FakeCommand>[
copyPhysicalFrameworkCommand,
copyPhysicalFrameworkDsymCommand,
copyPhysicalReleaseFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
xattrCommand,
FakeCommand(command: <String>['codesign', '--force', '--sign', 'ABC123', binary.path]),
]);
const Target target = ReleaseUnpackIOS();
for (final Target dep in target.dependencies) {
await dep.build(environment);
}
await target.build(environment);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('codesigns framework in debug mode', () async {
binary.createSync(recursive: true);
final 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',
kCodesignIdentity: 'ABC123',
},
);
processManager.addCommands(<FakeCommand>[
copyPhysicalDebugFrameworkCommand,
lipoCommandNonFatResult,
lipoVerifyArm64Command,
xattrCommand,
@ -1272,11 +1229,7 @@ void main() {
],
),
]);
const Target target = DebugUnpackIOS();
for (final Target dep in target.dependencies) {
await dep.build(environment);
}
await target.build(environment);
await const DebugUnpackIOS().build(environment);
expect(processManager, hasNoRemainingExpectations);
});

View File

@ -329,17 +329,13 @@ void main() {
binary.createSync(recursive: true);
frameworkDsym.createSync(recursive: true);
processManager.addCommands(<FakeCommand>[
copyFrameworkDsymCommand,
releaseCopyFrameworkCommand,
lipoInfoNonFatCommand,
lipoVerifyX86_64Command,
copyFrameworkDsymCommand,
]);
const Target target = ReleaseUnpackMacOS();
for (final Target dep in target.dependencies) {
await dep.build(environment..defines[kBuildMode] = 'release');
}
await target.build(environment..defines[kBuildMode] = 'release');
await const ReleaseUnpackMacOS().build(environment..defines[kBuildMode] = 'release');
expect(processManager, hasNoRemainingExpectations);
},
@ -367,10 +363,16 @@ void main() {
],
exitCode: 1,
);
processManager.addCommands(<FakeCommand>[failedCopyFrameworkDsymCommand]);
processManager.addCommands(<FakeCommand>[
releaseCopyFrameworkCommand,
lipoInfoFatCommand,
lipoVerifyX86_64Command,
lipoExtractX86_64Command,
failedCopyFrameworkDsymCommand,
]);
await expectLater(
const ReleaseUnpackMacOSDsym().build(environment..defines[kBuildMode] = 'release'),
const ReleaseUnpackMacOS().build(environment..defines[kBuildMode] = 'release'),
throwsA(
isException.having(
(Exception exception) => exception.toString(),

View File

@ -88,8 +88,6 @@ void main() {
late Directory buildPath;
late Directory buildAppFrameworkDsym;
late File buildAppFrameworkDsymBinary;
late Directory flutterFrameworkDsym;
late File flutterFrameworkDsymBinary;
late ProcessResult buildResult;
setUpAll(() {
@ -139,11 +137,6 @@ void main() {
buildAppFrameworkDsymBinary = buildAppFrameworkDsym.childFile(
'Contents/Resources/DWARF/App',
);
flutterFrameworkDsym = buildPath.childDirectory('Flutter.framework.dSYM');
flutterFrameworkDsymBinary = flutterFrameworkDsym.childFile(
'Contents/Resources/DWARF/Flutter',
);
});
testWithoutContext('flutter build ios builds a valid app', () {
@ -171,7 +164,6 @@ void main() {
expect(outputAppFramework.childFile('Info.plist'), exists);
expect(buildAppFrameworkDsymBinary.existsSync(), buildMode != BuildMode.debug);
expect(flutterFrameworkDsymBinary.existsSync(), buildMode != BuildMode.debug);
final File vmSnapshot = fileSystem.file(
fileSystem.path.join(outputAppFramework.path, 'flutter_assets', 'vm_snapshot_data'),
@ -216,36 +208,22 @@ void main() {
if (buildMode == BuildMode.debug) {
expect(symbols, isEmpty);
} else {
expect(symbols, equals(AppleTestUtils.requiredAppSymbols));
expect(symbols, equals(AppleTestUtils.requiredSymbols));
}
final List<String> flutterSymbols = AppleTestUtils.getExportedSymbols(
outputFlutterFrameworkBinary.path,
);
expect(flutterSymbols, containsAll(AppleTestUtils.expectedFlutterSymbols));
});
testWithoutContext('check symbols in dSYM', () {
if (buildMode == BuildMode.debug) {
// dSYM is not created for a debug build.
expect(buildAppFrameworkDsymBinary.existsSync(), isFalse);
expect(flutterFrameworkDsymBinary.existsSync(), isFalse);
} else {
final List<String> appSymbols = AppleTestUtils.getExportedSymbols(
final List<String> symbols = AppleTestUtils.getExportedSymbols(
buildAppFrameworkDsymBinary.path,
);
expect(appSymbols, containsAll(AppleTestUtils.requiredAppSymbols));
expect(symbols, containsAll(AppleTestUtils.requiredSymbols));
// The actual number of symbols is going to vary but there should
// be "many" in the dSYM. At the time of writing, it was 7656.
expect(appSymbols.length, greaterThanOrEqualTo(5000));
final List<String> flutterSymbols = AppleTestUtils.getExportedSymbols(
flutterFrameworkDsymBinary.path,
);
expect(flutterSymbols, containsAll(AppleTestUtils.expectedFlutterSymbols));
// The actual number of symbols is going to vary but there should
// be "many" in the dSYM. At the time of writing, it was 35940.
expect(flutterSymbols.length, greaterThanOrEqualTo(35000));
expect(symbols.length, greaterThanOrEqualTo(5000));
}
});

View File

@ -203,14 +203,12 @@ void main() {
} else {
// Check framework dSYM file copied.
_checkFatBinary(frameworkDsymBinary, buildModeLower, 'dSYM companion file');
final List<String> symbols = AppleTestUtils.getExportedSymbols(frameworkDsymBinary.path);
expect(symbols, containsAll(AppleTestUtils.expectedFlutterSymbols));
// Check extracted dSYM file.
_checkFatBinary(libDsymBinary, buildModeLower, 'dSYM companion file');
expect(libSymbols, equals(AppleTestUtils.requiredAppSymbols));
expect(libSymbols, equals(AppleTestUtils.requiredSymbols));
final List<String> dSymSymbols = AppleTestUtils.getExportedSymbols(libDsymBinary.path);
expect(dSymSymbols, containsAll(AppleTestUtils.requiredAppSymbols));
expect(dSymSymbols, containsAll(AppleTestUtils.requiredSymbols));
// The actual number of symbols is going to vary but there should
// be "many" in the dSYM. At the time of writing, it was 19195.
expect(dSymSymbols.length, greaterThanOrEqualTo(15000));

View File

@ -101,19 +101,13 @@ Future<void> pollForServiceExtensionValue<T>({
}
abstract final class AppleTestUtils {
static const requiredAppSymbols = <String>[
static const requiredSymbols = <String>[
'_kDartIsolateSnapshotData',
'_kDartIsolateSnapshotInstructions',
'_kDartVmSnapshotData',
'_kDartVmSnapshotInstructions',
];
/// A small subset of expected symbols for the Flutter/FlutterMacOS framework
static const expectedFlutterSymbols = <String>[
r'_OBJC_CLASS_$_FlutterViewController',
r'_OBJC_CLASS_$_FlutterEngine',
];
static List<String> getExportedSymbols(String dwarfPath) {
final ProcessResult nm = processManager.runSync(<String>[
'nm',