Unverified Commit 723b82e4 authored by Ivan Dlugos's avatar Ivan Dlugos Committed by GitHub

Feat: dSYM debug info for iOS & macOS builds (#101586)

parent 96345a4b
...@@ -189,6 +189,23 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals ...@@ -189,6 +189,23 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
'vm_snapshot_data', 'vm_snapshot_data',
)); ));
final String appFrameworkDsymPath = path.join(
outputPath,
mode,
'App.xcframework',
'ios-arm64',
'dSYMs',
'App.framework.dSYM'
);
checkDirectoryExists(appFrameworkDsymPath);
await _checkDsym(path.join(
appFrameworkDsymPath,
'Contents',
'Resources',
'DWARF',
'App',
));
checkFileExists(path.join( checkFileExists(path.join(
outputPath, outputPath,
mode, mode,
...@@ -404,6 +421,25 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals ...@@ -404,6 +421,25 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
'App', 'App',
)); ));
if (mode != 'Debug') {
final String appFrameworkDsymPath = path.join(
cocoapodsOutputPath,
mode,
'App.xcframework',
'ios-arm64',
'dSYMs',
'App.framework.dSYM'
);
checkDirectoryExists(appFrameworkDsymPath);
await _checkDsym(path.join(
appFrameworkDsymPath,
'Contents',
'Resources',
'DWARF',
'App',
));
}
if (Directory(path.join( if (Directory(path.join(
cocoapodsOutputPath, cocoapodsOutputPath,
mode, mode,
...@@ -582,6 +618,23 @@ Future<void> _testBuildMacOSFramework(Directory projectDir) async { ...@@ -582,6 +618,23 @@ Future<void> _testBuildMacOSFramework(Directory projectDir) async {
'Resources', 'Resources',
'Info.plist', 'Info.plist',
)); ));
final String appFrameworkDsymPath = path.join(
outputPath,
mode,
'App.xcframework',
'macos-arm64_x86_64',
'dSYMs',
'App.framework.dSYM'
);
checkDirectoryExists(appFrameworkDsymPath);
await _checkDsym(path.join(
appFrameworkDsymPath,
'Contents',
'Resources',
'DWARF',
'App',
));
} }
section("Check all modes' engine dylib"); section("Check all modes' engine dylib");
...@@ -712,6 +765,25 @@ Future<void> _testBuildMacOSFramework(Directory projectDir) async { ...@@ -712,6 +765,25 @@ Future<void> _testBuildMacOSFramework(Directory projectDir) async {
'App', 'App',
)); ));
if (mode != 'Debug') {
final String appFrameworkDsymPath = path.join(
cocoapodsOutputPath,
mode,
'App.xcframework',
'macos-arm64_x86_64',
'dSYMs',
'App.framework.dSYM'
);
checkDirectoryExists(appFrameworkDsymPath);
await _checkDsym(path.join(
appFrameworkDsymPath,
'Contents',
'Resources',
'DWARF',
'App',
));
}
await _checkStatic(path.join( await _checkStatic(path.join(
cocoapodsOutputPath, cocoapodsOutputPath,
mode, mode,
...@@ -750,6 +822,13 @@ Future<void> _checkDylib(String pathToLibrary) async { ...@@ -750,6 +822,13 @@ Future<void> _checkDylib(String pathToLibrary) async {
} }
} }
Future<void> _checkDsym(String pathToSymbolFile) async {
final String binaryFileType = await fileType(pathToSymbolFile);
if (!binaryFileType.contains('dSYM companion file')) {
throw TaskResult.failure('$pathToSymbolFile is not a dSYM, found: $binaryFileType');
}
}
Future<void> _checkStatic(String pathToLibrary) async { Future<void> _checkStatic(String pathToLibrary) async {
final String binaryFileType = await fileType(pathToLibrary); final String binaryFileType = await fileType(pathToLibrary);
if (!binaryFileType.contains('current ar archive random library')) { if (!binaryFileType.contains('current ar archive random library')) {
......
...@@ -440,6 +440,16 @@ end ...@@ -440,6 +440,16 @@ end
'Frameworks', 'Frameworks',
'url_launcher_ios.framework', 'url_launcher_ios.framework',
)); ));
checkFileExists(path.join(
'${objectiveCBuildArchiveDirectory.path}.xcarchive',
'dSYMs',
'App.framework.dSYM',
'Contents',
'Resources',
'DWARF',
'App'
));
}); });
section('Run platform unit tests'); section('Run platform unit tests');
......
...@@ -139,10 +139,17 @@ class AOTSnapshotter { ...@@ -139,10 +139,17 @@ class AOTSnapshotter {
'--deterministic', '--deterministic',
]; ];
final bool targetingApplePlatform =
platform == TargetPlatform.ios || platform == TargetPlatform.darwin;
_logger.printTrace('targetingApplePlatform = $targetingApplePlatform');
final bool extractAppleDebugSymbols =
buildMode == BuildMode.profile || buildMode == BuildMode.release;
_logger.printTrace('extractAppleDebugSymbols = $extractAppleDebugSymbols');
// We strip snapshot by default, but allow to suppress this behavior // We strip snapshot by default, but allow to suppress this behavior
// by supplying --no-strip in extraGenSnapshotOptions. // by supplying --no-strip in extraGenSnapshotOptions.
bool shouldStrip = true; bool shouldStrip = true;
if (extraGenSnapshotOptions != null && extraGenSnapshotOptions.isNotEmpty) { if (extraGenSnapshotOptions != null && extraGenSnapshotOptions.isNotEmpty) {
_logger.printTrace('Extra gen_snapshot options: $extraGenSnapshotOptions'); _logger.printTrace('Extra gen_snapshot options: $extraGenSnapshotOptions');
for (final String option in extraGenSnapshotOptions) { for (final String option in extraGenSnapshotOptions) {
...@@ -168,8 +175,20 @@ class AOTSnapshotter { ...@@ -168,8 +175,20 @@ class AOTSnapshotter {
]); ]);
} }
if (shouldStrip) { // When buiding for iOS and splitting out debug info, we want to strip
genSnapshotArgs.add('--strip'); // manually after the dSYM export, instead of in the `gen_snapshot`.
final bool stripAfterBuild;
if (targetingApplePlatform) {
stripAfterBuild = shouldStrip;
if (stripAfterBuild) {
_logger.printTrace('Will strip AOT snapshot manual after build and dSYM generation.');
}
} else {
stripAfterBuild = false;
if (shouldStrip) {
genSnapshotArgs.add('--strip');
_logger.printTrace('Will strip AOT snapshot during build.');
}
} }
if (platform == TargetPlatform.android_arm) { if (platform == TargetPlatform.android_arm) {
...@@ -218,8 +237,8 @@ class AOTSnapshotter { ...@@ -218,8 +237,8 @@ class AOTSnapshotter {
// On iOS and macOS, we use Xcode to compile the snapshot into a dynamic library that the // On iOS and macOS, we use Xcode to compile the snapshot into a dynamic library that the
// end-developer can link into their app. // end-developer can link into their app.
if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin) { if (targetingApplePlatform) {
final RunResult result = await _buildFramework( return _buildFramework(
appleArch: darwinArch!, appleArch: darwinArch!,
isIOS: platform == TargetPlatform.ios, isIOS: platform == TargetPlatform.ios,
sdkRoot: sdkRoot, sdkRoot: sdkRoot,
...@@ -227,24 +246,26 @@ class AOTSnapshotter { ...@@ -227,24 +246,26 @@ class AOTSnapshotter {
outputPath: outputDir.path, outputPath: outputDir.path,
bitcode: bitcode, bitcode: bitcode,
quiet: quiet, quiet: quiet,
stripAfterBuild: stripAfterBuild,
extractAppleDebugSymbols: extractAppleDebugSymbols
); );
if (result.exitCode != 0) { } else {
return result.exitCode; return 0;
}
} }
return 0;
} }
/// Builds an iOS or macOS framework at [outputPath]/App.framework from the assembly /// Builds an iOS or macOS framework at [outputPath]/App.framework from the assembly
/// source at [assemblyPath]. /// source at [assemblyPath].
Future<RunResult> _buildFramework({ Future<int> _buildFramework({
required DarwinArch appleArch, required DarwinArch appleArch,
required bool isIOS, required bool isIOS,
String? sdkRoot, String? sdkRoot,
required String assemblyPath, required String assemblyPath,
required String outputPath, required String outputPath,
required bool bitcode, required bool bitcode,
required bool quiet required bool quiet,
required bool stripAfterBuild,
required bool extractAppleDebugSymbols
}) async { }) async {
final String targetArch = getNameForDarwinArch(appleArch); final String targetArch = getNameForDarwinArch(appleArch);
if (!quiet) { if (!quiet) {
...@@ -278,7 +299,7 @@ class AOTSnapshotter { ...@@ -278,7 +299,7 @@ class AOTSnapshotter {
]); ]);
if (compileResult.exitCode != 0) { if (compileResult.exitCode != 0) {
_logger.printError('Failed to compile AOT snapshot. Compiler terminated with exit code ${compileResult.exitCode}'); _logger.printError('Failed to compile AOT snapshot. Compiler terminated with exit code ${compileResult.exitCode}');
return compileResult; return compileResult.exitCode;
} }
final String frameworkDir = _fileSystem.path.join(outputPath, 'App.framework'); final String frameworkDir = _fileSystem.path.join(outputPath, 'App.framework');
...@@ -294,11 +315,33 @@ class AOTSnapshotter { ...@@ -294,11 +315,33 @@ class AOTSnapshotter {
'-o', appLib, '-o', appLib,
assemblyO, assemblyO,
]; ];
final RunResult linkResult = await _xcode.clang(linkArgs); final RunResult linkResult = await _xcode.clang(linkArgs);
if (linkResult.exitCode != 0) { if (linkResult.exitCode != 0) {
_logger.printError('Failed to link AOT snapshot. Linker terminated with exit code ${compileResult.exitCode}'); _logger.printError('Failed to link AOT snapshot. Linker terminated with exit code ${linkResult.exitCode}');
return linkResult.exitCode;
}
if (extractAppleDebugSymbols) {
final RunResult dsymResult = await _xcode.dsymutil(<String>['-o', '$frameworkDir.dSYM', appLib]);
if (dsymResult.exitCode != 0) {
_logger.printError('Failed to generate dSYM - dsymutil terminated with exit code ${dsymResult.exitCode}');
return dsymResult.exitCode;
}
if (stripAfterBuild) {
// See https://www.unix.com/man-page/osx/1/strip/ for arguments
final RunResult stripResult = await _xcode.strip(<String>['-S', appLib, '-o', appLib]);
if (stripResult.exitCode != 0) {
_logger.printError('Failed to strip debugging symbols from the generated AOT snapshot - strip terminated with exit code ${stripResult.exitCode}');
return stripResult.exitCode;
}
}
} else {
assert(stripAfterBuild == false);
} }
return linkResult;
return 0;
} }
bool _isValidAotPlatform(TargetPlatform platform, BuildMode buildMode) { bool _isValidAotPlatform(TargetPlatform platform, BuildMode buildMode) {
......
...@@ -7,6 +7,7 @@ import 'package:package_config/package_config.dart'; ...@@ -7,6 +7,7 @@ import 'package:package_config/package_config.dart';
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../base/build.dart'; import '../../base/build.dart';
import '../../base/file_system.dart'; import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../build_info.dart'; import '../../build_info.dart';
import '../../compile.dart'; import '../../compile.dart';
import '../../dart/package_map.dart'; import '../../dart/package_map.dart';
...@@ -394,3 +395,48 @@ abstract class CopyFlutterAotBundle extends Target { ...@@ -394,3 +395,48 @@ abstract class CopyFlutterAotBundle extends Target {
environment.buildDir.childFile('app.so').copySync(outputFile.path); environment.buildDir.childFile('app.so').copySync(outputFile.path);
} }
} }
/// Lipo CLI tool wrapper shared by iOS and macOS builds.
class Lipo {
/// Static only.
Lipo._();
/// Create a "fat" binary by combining multiple architecture-specific ones.
/// `skipMissingInputs` can be changed to `true` to first check whether
/// the expected input paths exist and ignore the command if they don't.
/// Otherwise, `lipo` would fail if the given paths didn't exist.
static Future<void> create(
Environment environment,
List<DarwinArch> darwinArchs, {
required String relativePath,
required String inputDir,
bool skipMissingInputs = false,
}) async {
final String resultPath = environment.fileSystem.path.join(environment.buildDir.path, relativePath);
environment.fileSystem.directory(resultPath).parent.createSync(recursive: true);
Iterable<String> inputPaths = darwinArchs.map(
(DarwinArch iosArch) => environment.fileSystem.path.join(inputDir, getNameForDarwinArch(iosArch), relativePath)
);
if (skipMissingInputs) {
inputPaths = inputPaths.where(environment.fileSystem.isFileSync);
if (inputPaths.isEmpty) {
return;
}
}
final List<String> lipoArgs = <String>[
'lipo',
...inputPaths,
'-create',
'-output',
resultPath,
];
final ProcessResult result = await environment.processManager.run(lipoArgs);
if (result.exitCode != 0) {
throw Exception('lipo exited with code ${result.exitCode}.\n${result.stderr}');
}
}
}
...@@ -112,19 +112,24 @@ abstract class AotAssemblyBase extends Target { ...@@ -112,19 +112,24 @@ abstract class AotAssemblyBase extends Target {
if (results.any((int result) => result != 0)) { if (results.any((int result) => result != 0)) {
throw Exception('AOT snapshotter exited with code ${results.join()}'); throw Exception('AOT snapshotter exited with code ${results.join()}');
} }
final String resultPath = environment.fileSystem.path.join(environment.buildDir.path, 'App.framework', 'App');
environment.fileSystem.directory(resultPath).parent.createSync(recursive: true); // Combine the app lib into a fat framework.
final ProcessResult result = await environment.processManager.run(<String>[ await Lipo.create(
'lipo', environment,
...darwinArchs.map((DarwinArch iosArch) => darwinArchs,
environment.fileSystem.path.join(buildOutputPath, getNameForDarwinArch(iosArch), 'App.framework', 'App')), relativePath: 'App.framework/App',
'-create', inputDir: buildOutputPath,
'-output', );
resultPath,
]); // And combine the dSYM for each architecture too, if it was created.
if (result.exitCode != 0) { await Lipo.create(
throw Exception('lipo exited with code ${result.exitCode}.\n${result.stderr}'); environment,
} darwinArchs,
relativePath: 'App.framework.dSYM/Contents/Resources/DWARF/App',
inputDir: buildOutputPath,
// Don't fail if the dSYM wasn't created (i.e. during a debug build).
skipMissingInputs: true,
);
} }
} }
...@@ -489,6 +494,26 @@ abstract class IosAssetBundle extends Target { ...@@ -489,6 +494,26 @@ abstract class IosAssetBundle extends Target {
.copySync(frameworkBinaryPath); .copySync(frameworkBinaryPath);
} }
// Copy the dSYM
if (environment.buildDir.childDirectory('App.framework.dSYM').existsSync()) {
final File dsymOutputBinary = environment
.outputDir
.childDirectory('App.framework.dSYM')
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('App');
dsymOutputBinary.parent.createSync(recursive: true);
environment
.buildDir
.childDirectory('App.framework.dSYM')
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('App')
.copySync(dsymOutputBinary.path);
}
// Copy the assets. // Copy the assets.
final Depfile assetDepfile = await copyAssets( final Depfile assetDepfile = await copyAssets(
environment, environment,
...@@ -547,8 +572,25 @@ class DebugIosApplicationBundle extends IosAssetBundle { ...@@ -547,8 +572,25 @@ class DebugIosApplicationBundle extends IosAssetBundle {
]; ];
} }
/// IosAssetBundle with debug symbols, used for Profile and Release builds.
abstract class _IosAssetBundleWithDSYM extends IosAssetBundle {
const _IosAssetBundleWithDSYM();
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.pattern('{BUILD_DIR}/App.framework.dSYM/Contents/Resources/DWARF/App'),
];
@override
List<Source> get outputs => <Source>[
...super.outputs,
const Source.pattern('{OUTPUT_DIR}/App.framework.dSYM/Contents/Resources/DWARF/App'),
];
}
/// Build a profile iOS application bundle. /// Build a profile iOS application bundle.
class ProfileIosApplicationBundle extends IosAssetBundle { class ProfileIosApplicationBundle extends _IosAssetBundleWithDSYM {
const ProfileIosApplicationBundle(); const ProfileIosApplicationBundle();
@override @override
...@@ -561,7 +603,7 @@ class ProfileIosApplicationBundle extends IosAssetBundle { ...@@ -561,7 +603,7 @@ class ProfileIosApplicationBundle extends IosAssetBundle {
} }
/// Build a release iOS application bundle. /// Build a release iOS application bundle.
class ReleaseIosApplicationBundle extends IosAssetBundle { class ReleaseIosApplicationBundle extends _IosAssetBundleWithDSYM {
const ReleaseIosApplicationBundle(); const ReleaseIosApplicationBundle();
@override @override
......
...@@ -302,19 +302,23 @@ class CompileMacOSFramework extends Target { ...@@ -302,19 +302,23 @@ class CompileMacOSFramework extends Target {
throw Exception('AOT snapshotter exited with code ${results.join()}'); throw Exception('AOT snapshotter exited with code ${results.join()}');
} }
final String resultPath = environment.fileSystem.path.join(environment.buildDir.path, 'App.framework', 'App'); // Combine the app lib into a fat framework.
environment.fileSystem.directory(resultPath).parent.createSync(recursive: true); await Lipo.create(
final ProcessResult result = await environment.processManager.run(<String>[ environment,
'lipo', darwinArchs,
...darwinArchs.map((DarwinArch iosArch) => relativePath: 'App.framework/App',
environment.fileSystem.path.join(buildOutputPath, getNameForDarwinArch(iosArch), 'App.framework', 'App')), inputDir: buildOutputPath,
'-create', );
'-output',
resultPath, // And combine the dSYM for each architecture too, if it was created.
]); await Lipo.create(
if (result.exitCode != 0) { environment,
throw Exception('lipo exited with code ${result.exitCode}.\n${result.stderr}'); darwinArchs,
} relativePath: 'App.framework.dSYM/Contents/Resources/DWARF/App',
inputDir: buildOutputPath,
// Don't fail if the dSYM wasn't created (i.e. during a debug build).
skipMissingInputs: true,
);
} }
@override @override
...@@ -332,6 +336,7 @@ class CompileMacOSFramework extends Target { ...@@ -332,6 +336,7 @@ class CompileMacOSFramework extends Target {
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/App.framework/App'), Source.pattern('{BUILD_DIR}/App.framework/App'),
Source.pattern('{BUILD_DIR}/App.framework.dSYM/Contents/Resources/DWARF/App'),
]; ];
} }
...@@ -382,6 +387,26 @@ abstract class MacOSBundleFlutterAssets extends Target { ...@@ -382,6 +387,26 @@ abstract class MacOSBundleFlutterAssets extends Target {
.childFile('App') .childFile('App')
.copySync(outputDirectory.childFile('App').path); .copySync(outputDirectory.childFile('App').path);
// Copy the dSYM
if (environment.buildDir.childDirectory('App.framework.dSYM').existsSync()) {
final File dsymOutputBinary = environment
.outputDir
.childDirectory('App.framework.dSYM')
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('App');
dsymOutputBinary.parent.createSync(recursive: true);
environment
.buildDir
.childDirectory('App.framework.dSYM')
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('App')
.copySync(dsymOutputBinary.path);
}
// Copy assets into asset directory. // Copy assets into asset directory.
final Directory assetDirectory = outputDirectory final Directory assetDirectory = outputDirectory
.childDirectory('Resources') .childDirectory('Resources')
...@@ -530,6 +555,18 @@ class ProfileMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets { ...@@ -530,6 +555,18 @@ class ProfileMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
CompileMacOSFramework(), CompileMacOSFramework(),
ProfileUnpackMacOS(), ProfileUnpackMacOS(),
]; ];
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.pattern('{BUILD_DIR}/App.framework.dSYM/Contents/Resources/DWARF/App'),
];
@override
List<Source> get outputs => <Source>[
...super.outputs,
const Source.pattern('{OUTPUT_DIR}/App.framework.dSYM/Contents/Resources/DWARF/App'),
];
} }
...@@ -546,6 +583,18 @@ class ReleaseMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets { ...@@ -546,6 +583,18 @@ class ReleaseMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
ReleaseUnpackMacOS(), ReleaseUnpackMacOS(),
]; ];
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.pattern('{BUILD_DIR}/App.framework.dSYM/Contents/Resources/DWARF/App'),
];
@override
List<Source> get outputs => <Source>[
...super.outputs,
const Source.pattern('{OUTPUT_DIR}/App.framework.dSYM/Contents/Resources/DWARF/App'),
];
@override @override
Future<void> build(Environment environment) async { Future<void> build(Environment environment) async {
bool buildSuccess = true; bool buildSuccess = true;
......
...@@ -68,8 +68,11 @@ class SymbolizeCommand extends FlutterCommand { ...@@ -68,8 +68,11 @@ class SymbolizeCommand extends FlutterCommand {
if (argResults?.wasParsed('debug-info') != true) { if (argResults?.wasParsed('debug-info') != true) {
throwToolExit('"--debug-info" is required to symbolize stack traces.'); throwToolExit('"--debug-info" is required to symbolize stack traces.');
} }
if (!_fileSystem.isFileSync(stringArgDeprecated('debug-info')!)) { final String debugInfoPath = stringArgDeprecated('debug-info')!;
throwToolExit('${stringArgDeprecated('debug-info')} does not exist.'); if (debugInfoPath.endsWith('.dSYM')
? !_fileSystem.isDirectorySync(debugInfoPath)
: !_fileSystem.isFileSync(debugInfoPath)) {
throwToolExit('$debugInfoPath does not exist.');
} }
if ((argResults?.wasParsed('input') ?? false) && !_fileSystem.isFileSync(stringArgDeprecated('input')!)) { if ((argResults?.wasParsed('input') ?? false) && !_fileSystem.isFileSync(stringArgDeprecated('input')!)) {
throwToolExit('${stringArgDeprecated('input')} does not exist.'); throwToolExit('${stringArgDeprecated('input')} does not exist.');
...@@ -105,7 +108,25 @@ class SymbolizeCommand extends FlutterCommand { ...@@ -105,7 +108,25 @@ class SymbolizeCommand extends FlutterCommand {
input = _stdio.stdin; input = _stdio.stdin;
} }
final Uint8List symbols = _fileSystem.file(stringArgDeprecated('debug-info')).readAsBytesSync(); String debugInfoPath = stringArgDeprecated('debug-info')!;
// If it's a dSYM container, expand the path to the actual DWARF.
if (debugInfoPath.endsWith('.dSYM')) {
final Directory debugInfoDir = _fileSystem
.directory(debugInfoPath)
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF');
final List<FileSystemEntity> dwarfFiles = debugInfoDir.listSync().whereType<File>().toList();
if (dwarfFiles.length == 1) {
debugInfoPath = dwarfFiles.first.path;
} else {
throwToolExit('Expected a single DWARF file in a dSYM container.');
}
}
final Uint8List symbols = _fileSystem.file(debugInfoPath).readAsBytesSync();
await _dwarfSymbolizationService.decode( await _dwarfSymbolizationService.decode(
input: input, input: input,
output: output, output: output,
......
...@@ -165,16 +165,17 @@ class Xcode { ...@@ -165,16 +165,17 @@ class Xcode {
/// See [XcodeProjectInterpreter.xcrunCommand]. /// See [XcodeProjectInterpreter.xcrunCommand].
List<String> xcrunCommand() => _xcodeProjectInterpreter.xcrunCommand(); List<String> xcrunCommand() => _xcodeProjectInterpreter.xcrunCommand();
Future<RunResult> cc(List<String> args) { Future<RunResult> cc(List<String> args) => _run('cc', args);
return _processUtils.run(
<String>[...xcrunCommand(), 'cc', ...args], Future<RunResult> clang(List<String> args) => _run('clang', args);
throwOnError: true,
); Future<RunResult> dsymutil(List<String> args) => _run('dsymutil', args);
}
Future<RunResult> strip(List<String> args) => _run('strip', args);
Future<RunResult> clang(List<String> args) { Future<RunResult> _run(String command, List<String> args) {
return _processUtils.run( return _processUtils.run(
<String>[...xcrunCommand(), 'clang', ...args], <String>[...xcrunCommand(), command, ...args],
throwOnError: true, throwOnError: true,
); );
} }
......
...@@ -69,7 +69,7 @@ void main() { ...@@ -69,7 +69,7 @@ void main() {
OutputPreferences: () => OutputPreferences.test(), OutputPreferences: () => OutputPreferences.test(),
}); });
testUsingContext('symbolize exits when --debug-info file is missing', () async { testUsingContext('symbolize exits when --debug-info dwarf file is missing', () async {
final SymbolizeCommand command = SymbolizeCommand( final SymbolizeCommand command = SymbolizeCommand(
stdio: stdio, stdio: stdio,
fileSystem: fileSystem, fileSystem: fileSystem,
...@@ -83,6 +83,20 @@ void main() { ...@@ -83,6 +83,20 @@ void main() {
OutputPreferences: () => OutputPreferences.test(), OutputPreferences: () => OutputPreferences.test(),
}); });
testUsingContext('symbolize exits when --debug-info dSYM is missing', () async {
final SymbolizeCommand command = SymbolizeCommand(
stdio: stdio,
fileSystem: fileSystem,
dwarfSymbolizationService: DwarfSymbolizationService.test(),
);
final Future<void> result = createTestCommandRunner(command)
.run(const <String>['symbolize', '--debug-info=app.dSYM']);
expect(result, throwsToolExit(message: 'app.dSYM does not exist.'));
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences.test(),
});
testUsingContext('symbolize exits when --input file is missing', () async { testUsingContext('symbolize exits when --input file is missing', () async {
final SymbolizeCommand command = SymbolizeCommand( final SymbolizeCommand command = SymbolizeCommand(
stdio: stdio, stdio: stdio,
......
...@@ -210,7 +210,6 @@ void main() { ...@@ -210,7 +210,6 @@ void main() {
'--deterministic', '--deterministic',
'--snapshot_kind=app-aot-assembly', '--snapshot_kind=app-aot-assembly',
'--assembly=$assembly', '--assembly=$assembly',
'--strip',
'main.dill', 'main.dill',
]), ]),
kWhichSysctlCommand, kWhichSysctlCommand,
...@@ -253,6 +252,21 @@ void main() { ...@@ -253,6 +252,21 @@ void main() {
'build/foo/App.framework/App', 'build/foo/App.framework/App',
'build/foo/snapshot_assembly.o', 'build/foo/snapshot_assembly.o',
]), ]),
const FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'build/foo/App.framework.dSYM',
'build/foo/App.framework/App',
]),
const FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
'build/foo/App.framework/App',
'-o',
'build/foo/App.framework/App',
]),
]); ]);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
...@@ -285,7 +299,6 @@ void main() { ...@@ -285,7 +299,6 @@ void main() {
'--deterministic', '--deterministic',
'--snapshot_kind=app-aot-assembly', '--snapshot_kind=app-aot-assembly',
'--assembly=$assembly', '--assembly=$assembly',
'--strip',
'--dwarf-stack-traces', '--dwarf-stack-traces',
'--save-debugging-info=$debugPath', '--save-debugging-info=$debugPath',
'main.dill', 'main.dill',
...@@ -312,6 +325,21 @@ void main() { ...@@ -312,6 +325,21 @@ void main() {
'arm64', 'arm64',
...kDefaultClang, ...kDefaultClang,
]), ]),
const FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'build/foo/App.framework.dSYM',
'build/foo/App.framework/App',
]),
const FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
'build/foo/App.framework/App',
'-o',
'build/foo/App.framework/App',
]),
]); ]);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
...@@ -344,7 +372,6 @@ void main() { ...@@ -344,7 +372,6 @@ void main() {
'--deterministic', '--deterministic',
'--snapshot_kind=app-aot-assembly', '--snapshot_kind=app-aot-assembly',
'--assembly=$assembly', '--assembly=$assembly',
'--strip',
'--obfuscate', '--obfuscate',
'main.dill', 'main.dill',
]), ]),
...@@ -370,6 +397,21 @@ void main() { ...@@ -370,6 +397,21 @@ void main() {
'arm64', 'arm64',
...kDefaultClang, ...kDefaultClang,
]), ]),
const FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'build/foo/App.framework.dSYM',
'build/foo/App.framework/App',
]),
const FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
'build/foo/App.framework/App',
'-o',
'build/foo/App.framework/App',
]),
]); ]);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
...@@ -400,7 +442,6 @@ void main() { ...@@ -400,7 +442,6 @@ void main() {
'--deterministic', '--deterministic',
'--snapshot_kind=app-aot-assembly', '--snapshot_kind=app-aot-assembly',
'--assembly=${fileSystem.path.join(outputPath, 'snapshot_assembly.S')}', '--assembly=${fileSystem.path.join(outputPath, 'snapshot_assembly.S')}',
'--strip',
'main.dill', 'main.dill',
]), ]),
kWhichSysctlCommand, kWhichSysctlCommand,
...@@ -425,6 +466,21 @@ void main() { ...@@ -425,6 +466,21 @@ void main() {
'arm64', 'arm64',
...kDefaultClang, ...kDefaultClang,
]), ]),
const FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'build/foo/App.framework.dSYM',
'build/foo/App.framework/App',
]),
const FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
'build/foo/App.framework/App',
'-o',
'build/foo/App.framework/App',
]),
]); ]);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
......
...@@ -477,7 +477,6 @@ void main() { ...@@ -477,7 +477,6 @@ void main() {
'--deterministic', '--deterministic',
kAssemblyAot, kAssemblyAot,
'--assembly=$build/arm64/snapshot_assembly.S', '--assembly=$build/arm64/snapshot_assembly.S',
'--strip',
'$build/app.dill', '$build/app.dill',
]), ]),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -520,6 +519,21 @@ void main() { ...@@ -520,6 +519,21 @@ void main() {
'$build/arm64/App.framework/App', '$build/arm64/App.framework/App',
'$build/arm64/snapshot_assembly.o', '$build/arm64/snapshot_assembly.o',
]), ]),
FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'$build/arm64/App.framework.dSYM',
'$build/arm64/App.framework/App',
]),
FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
'$build/arm64/App.framework/App',
'-o',
'$build/arm64/App.framework/App',
]),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'lipo', 'lipo',
'$build/arm64/App.framework/App', '$build/arm64/App.framework/App',
...@@ -553,7 +567,6 @@ void main() { ...@@ -553,7 +567,6 @@ void main() {
'--trace-precompiler-to=code_size_1/trace.arm64.json', '--trace-precompiler-to=code_size_1/trace.arm64.json',
kAssemblyAot, kAssemblyAot,
'--assembly=$build/arm64/snapshot_assembly.S', '--assembly=$build/arm64/snapshot_assembly.S',
'--strip',
'$build/app.dill', '$build/app.dill',
]), ]),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -596,6 +609,21 @@ void main() { ...@@ -596,6 +609,21 @@ void main() {
'$build/arm64/App.framework/App', '$build/arm64/App.framework/App',
'$build/arm64/snapshot_assembly.o', '$build/arm64/snapshot_assembly.o',
]), ]),
FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
'$build/arm64/App.framework.dSYM',
'$build/arm64/App.framework/App',
]),
FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
'$build/arm64/App.framework/App',
'-o',
'$build/arm64/App.framework/App',
]),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'lipo', 'lipo',
'$build/arm64/App.framework/App', '$build/arm64/App.framework/App',
......
...@@ -239,6 +239,14 @@ void main() { ...@@ -239,6 +239,14 @@ void main() {
.childFile('App') .childFile('App')
.createSync(recursive: true); .createSync(recursive: true);
// Input dSYM
environment.buildDir
.childDirectory('App.framework.dSYM')
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('App')
.createSync(recursive: true);
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework'); final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
final File frameworkDirectoryBinary = frameworkDirectory.childFile('App'); final File frameworkDirectoryBinary = frameworkDirectory.childFile('App');
...@@ -257,6 +265,12 @@ void main() { ...@@ -257,6 +265,12 @@ void main() {
expect(frameworkDirectoryBinary, exists); expect(frameworkDirectoryBinary, exists);
expect(frameworkDirectory.childFile('Info.plist'), exists); expect(frameworkDirectory.childFile('Info.plist'), exists);
expect(environment.outputDir
.childDirectory('App.framework.dSYM')
.childDirectory('Contents')
.childDirectory('Resources')
.childDirectory('DWARF')
.childFile('App'), exists);
final Directory assetDirectory = frameworkDirectory.childDirectory('flutter_assets'); final Directory assetDirectory = frameworkDirectory.childDirectory('flutter_assets');
expect(assetDirectory.childFile('kernel_blob.bin'), isNot(exists)); expect(assetDirectory.childFile('kernel_blob.bin'), isNot(exists));
......
...@@ -295,6 +295,28 @@ void main() { ...@@ -295,6 +295,28 @@ void main() {
ProcessManager: () => processManager, ProcessManager: () => processManager,
}); });
testUsingContext('release macOS application creates App.framework.dSYM', () async {
fileSystem.file('bin/cache/artifacts/engine/darwin-x64/vm_isolate_snapshot.bin')
.createSync(recursive: true);
fileSystem.file('bin/cache/artifacts/engine/darwin-x64/isolate_snapshot.bin')
.createSync(recursive: true);
fileSystem.file('${environment.buildDir.path}/App.framework/App')
.createSync(recursive: true);
fileSystem.file('${environment.buildDir.path}/App.framework.dSYM/Contents/Resources/DWARF/App')
.createSync(recursive: true);
await const ReleaseMacOSBundleFlutterAssets()
.build(environment..defines[kBuildMode] = 'release');
expect(fileSystem.file(
'App.framework.dSYM/Contents/Resources/DWARF/App'),
exists,
);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
testUsingContext('release/profile macOS application updates when App.framework updates', () async { testUsingContext('release/profile macOS application updates when App.framework updates', () async {
fileSystem.file('bin/cache/artifacts/engine/darwin-x64/vm_isolate_snapshot.bin') fileSystem.file('bin/cache/artifacts/engine/darwin-x64/vm_isolate_snapshot.bin')
.createSync(recursive: true); .createSync(recursive: true);
...@@ -415,13 +437,20 @@ void main() { ...@@ -415,13 +437,20 @@ void main() {
environment.defines[kDarwinArchs] = 'arm64 x86_64'; environment.defines[kDarwinArchs] = 'arm64 x86_64';
environment.defines[kBuildMode] = 'release'; environment.defines[kBuildMode] = 'release';
// Input dSYMs need to exist for `lipo` to combine them
environment.buildDir
.childFile('arm64/App.framework.dSYM/Contents/Resources/DWARF/App')
.createSync(recursive: true);
environment.buildDir
.childFile('x86_64/App.framework.dSYM/Contents/Resources/DWARF/App')
.createSync(recursive: true);
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'Artifact.genSnapshot.TargetPlatform.darwin.release_arm64', 'Artifact.genSnapshot.TargetPlatform.darwin.release_arm64',
'--deterministic', '--deterministic',
'--snapshot_kind=app-aot-assembly', '--snapshot_kind=app-aot-assembly',
'--assembly=${environment.buildDir.childFile('arm64/snapshot_assembly.S').path}', '--assembly=${environment.buildDir.childFile('arm64/snapshot_assembly.S').path}',
'--strip',
environment.buildDir.childFile('app.dill').path, environment.buildDir.childFile('app.dill').path,
]), ]),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -429,7 +458,6 @@ void main() { ...@@ -429,7 +458,6 @@ void main() {
'--deterministic', '--deterministic',
'--snapshot_kind=app-aot-assembly', '--snapshot_kind=app-aot-assembly',
'--assembly=${environment.buildDir.childFile('x86_64/snapshot_assembly.S').path}', '--assembly=${environment.buildDir.childFile('x86_64/snapshot_assembly.S').path}',
'--strip',
environment.buildDir.childFile('app.dill').path, environment.buildDir.childFile('app.dill').path,
]), ]),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -458,6 +486,36 @@ void main() { ...@@ -458,6 +486,36 @@ void main() {
'-o', environment.buildDir.childFile('x86_64/App.framework/App').path, '-o', environment.buildDir.childFile('x86_64/App.framework/App').path,
environment.buildDir.childFile('x86_64/snapshot_assembly.o').path, environment.buildDir.childFile('x86_64/snapshot_assembly.o').path,
]), ]),
FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
environment.buildDir.childFile('arm64/App.framework.dSYM').path,
environment.buildDir.childFile('arm64/App.framework/App').path,
]),
FakeCommand(command: <String>[
'xcrun',
'dsymutil',
'-o',
environment.buildDir.childFile('x86_64/App.framework.dSYM').path,
environment.buildDir.childFile('x86_64/App.framework/App').path,
]),
FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
environment.buildDir.childFile('arm64/App.framework/App').path,
'-o',
environment.buildDir.childFile('arm64/App.framework/App').path,
]),
FakeCommand(command: <String>[
'xcrun',
'strip',
'-S',
environment.buildDir.childFile('x86_64/App.framework/App').path,
'-o',
environment.buildDir.childFile('x86_64/App.framework/App').path,
]),
FakeCommand(command: <String>[ FakeCommand(command: <String>[
'lipo', 'lipo',
environment.buildDir.childFile('arm64/App.framework/App').path, environment.buildDir.childFile('arm64/App.framework/App').path,
...@@ -466,6 +524,14 @@ void main() { ...@@ -466,6 +524,14 @@ void main() {
'-output', '-output',
environment.buildDir.childFile('App.framework/App').path, environment.buildDir.childFile('App.framework/App').path,
]), ]),
FakeCommand(command: <String>[
'lipo',
environment.buildDir.childFile('arm64/App.framework.dSYM/Contents/Resources/DWARF/App').path,
environment.buildDir.childFile('x86_64/App.framework.dSYM/Contents/Resources/DWARF/App').path,
'-create',
'-output',
environment.buildDir.childFile('App.framework.dSYM/Contents/Resources/DWARF/App').path,
]),
]); ]);
await const CompileMacOSFramework().build(environment); await const CompileMacOSFramework().build(environment);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'package:file_testing/file_testing.dart'; import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/utils.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import '../integration.shard/test_utils.dart'; import '../integration.shard/test_utils.dart';
...@@ -75,7 +76,7 @@ void main() { ...@@ -75,7 +76,7 @@ void main() {
for (final BuildMode buildMode in <BuildMode>[BuildMode.debug, BuildMode.release]) { for (final BuildMode buildMode in <BuildMode>[BuildMode.debug, BuildMode.release]) {
group('build in ${buildMode.name} mode', () { group('build in ${buildMode.name} mode', () {
late Directory buildPath; late Directory outputPath;
late Directory outputApp; late Directory outputApp;
late Directory frameworkDirectory; late Directory frameworkDirectory;
late Directory outputFlutterFramework; late Directory outputFlutterFramework;
...@@ -83,6 +84,9 @@ void main() { ...@@ -83,6 +84,9 @@ void main() {
late Directory outputAppFramework; late Directory outputAppFramework;
late File outputAppFrameworkBinary; late File outputAppFrameworkBinary;
late File outputPluginFrameworkBinary; late File outputPluginFrameworkBinary;
late Directory buildPath;
late Directory buildAppFrameworkDsym;
late File buildAppFrameworkDsymBinary;
late ProcessResult buildResult; late ProcessResult buildResult;
setUpAll(() { setUpAll(() {
...@@ -98,14 +102,14 @@ void main() { ...@@ -98,14 +102,14 @@ void main() {
'--split-debug-info=foo debug info/', '--split-debug-info=foo debug info/',
], workingDirectory: projectRoot); ], workingDirectory: projectRoot);
buildPath = fileSystem.directory(fileSystem.path.join( outputPath = fileSystem.directory(fileSystem.path.join(
projectRoot, projectRoot,
'build', 'build',
'ios', 'ios',
'iphoneos', 'iphoneos',
)); ));
outputApp = buildPath.childDirectory('Runner.app'); outputApp = outputPath.childDirectory('Runner.app');
frameworkDirectory = outputApp.childDirectory('Frameworks'); frameworkDirectory = outputApp.childDirectory('Frameworks');
outputFlutterFramework = frameworkDirectory.childDirectory('Flutter.framework'); outputFlutterFramework = frameworkDirectory.childDirectory('Flutter.framework');
...@@ -115,6 +119,16 @@ void main() { ...@@ -115,6 +119,16 @@ void main() {
outputAppFrameworkBinary = outputAppFramework.childFile('App'); outputAppFrameworkBinary = outputAppFramework.childFile('App');
outputPluginFrameworkBinary = frameworkDirectory.childDirectory('hello.framework').childFile('hello'); outputPluginFrameworkBinary = frameworkDirectory.childDirectory('hello.framework').childFile('hello');
buildPath = fileSystem.directory(fileSystem.path.join(
projectRoot,
'build',
'ios',
'${sentenceCase(buildMode.name)}-iphoneos',
));
buildAppFrameworkDsym = buildPath.childDirectory('App.framework.dSYM');
buildAppFrameworkDsymBinary = buildAppFrameworkDsym.childFile('Contents/Resources/DWARF/App');
}); });
testWithoutContext('flutter build ios builds a valid app', () { testWithoutContext('flutter build ios builds a valid app', () {
...@@ -128,6 +142,8 @@ void main() { ...@@ -128,6 +142,8 @@ void main() {
expect(outputAppFrameworkBinary, exists); expect(outputAppFrameworkBinary, exists);
expect(outputAppFramework.childFile('Info.plist'), exists); expect(outputAppFramework.childFile('Info.plist'), exists);
expect(buildAppFrameworkDsymBinary.existsSync(), buildMode != BuildMode.debug);
final File vmSnapshot = fileSystem.file(fileSystem.path.join( final File vmSnapshot = fileSystem.file(fileSystem.path.join(
outputAppFramework.path, outputAppFramework.path,
'flutter_assets', 'flutter_assets',
...@@ -190,6 +206,25 @@ void main() { ...@@ -190,6 +206,25 @@ void main() {
expect(aotSymbolsFound, buildMode != BuildMode.debug); expect(aotSymbolsFound, buildMode != BuildMode.debug);
}); });
// dSYM is not created for a debug build so nothing to check.
if (buildMode != BuildMode.debug) {
testWithoutContext('check symbols in dSYM', () {
final ProcessResult nm = processManager.runSync(
<String>[
'nm',
'--debug-syms',
'--defined-only',
'--just-symbol-name',
buildAppFrameworkDsymBinary.path,
'-arch',
'arm64',
],
);
final List<String> symbols = (nm.stdout as String).split('\n');
expect(symbols, contains('_kDartVmSnapshotInstructions'));
});
}
testWithoutContext('xcode_backend embed_and_thin', () { testWithoutContext('xcode_backend embed_and_thin', () {
outputFlutterFramework.deleteSync(recursive: true); outputFlutterFramework.deleteSync(recursive: true);
outputAppFramework.deleteSync(recursive: true); outputAppFramework.deleteSync(recursive: true);
...@@ -219,7 +254,7 @@ void main() { ...@@ -219,7 +254,7 @@ void main() {
'ios', 'ios',
'Release-iphoneos', 'Release-iphoneos',
), ),
'TARGET_BUILD_DIR': buildPath.path, 'TARGET_BUILD_DIR': outputPath.path,
'FRAMEWORKS_FOLDER_PATH': 'Runner.app/Frameworks', 'FRAMEWORKS_FOLDER_PATH': 'Runner.app/Frameworks',
'VERBOSE_SCRIPT_LOGGING': '1', 'VERBOSE_SCRIPT_LOGGING': '1',
'FLUTTER_BUILD_MODE': 'release', 'FLUTTER_BUILD_MODE': 'release',
......
...@@ -67,18 +67,18 @@ void main() { ...@@ -67,18 +67,18 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
expect(result.stdout, contains('Running pod install')); expect(result.stdout, contains('Running pod install'));
expect(podfile.lastModifiedSync().isBefore(podfileLock.lastModifiedSync()), isTrue);
final Directory outputApp = fileSystem.directory(fileSystem.path.join( final Directory buildPath = fileSystem.directory(fileSystem.path.join(
workingDirectory, workingDirectory,
'build', 'build',
'macos', 'macos',
'Build', 'Build',
'Products', 'Products',
buildMode, buildMode,
'flutter_gallery.app',
)); ));
expect(podfile.lastModifiedSync().isBefore(podfileLock.lastModifiedSync()), isTrue);
final Directory outputApp = buildPath.childDirectory('flutter_gallery.app');
final Directory outputAppFramework = final Directory outputAppFramework =
fileSystem.directory(fileSystem.path.join( fileSystem.directory(fileSystem.path.join(
outputApp.path, outputApp.path,
...@@ -87,19 +87,19 @@ void main() { ...@@ -87,19 +87,19 @@ void main() {
'App.framework', 'App.framework',
)); ));
final File outputAppFrameworkBinary = outputAppFramework.childFile('App'); _checkFatBinary(
final String archs = processManager.runSync( outputAppFramework.childFile('App'),
<String>['file', outputAppFrameworkBinary.path], buildModeLower,
).stdout as String; 'dynamically linked shared library',
);
final bool containsX64 = archs.contains('Mach-O 64-bit dynamically linked shared library x86_64');
final bool containsArm = archs.contains('Mach-O 64-bit dynamically linked shared library arm64'); // dSYM is not created for a debug build so nothing to check.
if (buildModeLower == 'debug') { if (buildMode != 'Debug') {
// Only build the architecture matching the machine running this test, not both. _checkFatBinary(
expect(containsX64 ^ containsArm, isTrue, reason: 'Unexpected architecture $archs'); buildPath.childFile('App.framework.dSYM/Contents/Resources/DWARF/App'),
} else { buildModeLower,
expect(containsX64, isTrue, reason: 'Unexpected architecture $archs'); 'dSYM companion file',
expect(containsArm, isTrue, reason: 'Unexpected architecture $archs'); );
} }
expect(outputAppFramework.childLink('Resources'), exists); expect(outputAppFramework.childLink('Resources'), exists);
...@@ -172,3 +172,19 @@ void main() { ...@@ -172,3 +172,19 @@ void main() {
}, skip: !platform.isMacOS); // [intended] only makes sense for macos platform. }, skip: !platform.isMacOS); // [intended] only makes sense for macos platform.
} }
} }
void _checkFatBinary(File file, String buildModeLower, String expectedType) {
final String archs = processManager.runSync(
<String>['file', file.path],
).stdout as String;
final bool containsX64 = archs.contains('Mach-O 64-bit $expectedType x86_64');
final bool containsArm = archs.contains('Mach-O 64-bit $expectedType arm64');
if (buildModeLower == 'debug') {
// Only build the architecture matching the machine running this test, not both.
expect(containsX64 ^ containsArm, isTrue, reason: 'Unexpected architecture $archs');
} else {
expect(containsX64, isTrue, reason: 'Unexpected architecture $archs');
expect(containsArm, isTrue, reason: 'Unexpected architecture $archs');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment