diff --git a/.gitignore b/.gitignore index cf6472708ce..b7d74c8266d 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,9 @@ unlinked_spec.ds # Coverage coverage/ +# Symbols +app.*.symbols + # Exceptions to above rules. !**/ios/**/default.mode1v3 !**/ios/**/default.mode2v3 diff --git a/packages/flutter_tools/bin/macos_assemble.sh b/packages/flutter_tools/bin/macos_assemble.sh index c923cc61faf..b821c96fc14 100755 --- a/packages/flutter_tools/bin/macos_assemble.sh +++ b/packages/flutter_tools/bin/macos_assemble.sh @@ -37,6 +37,12 @@ if [[ -n "$FLUTTER_ENGINE" ]]; then flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}" fi +# Provide location to split debug info +split_debug_info_option="" +if [[ -n "$SPLIT_DEBUG_INFO" ]]; then + split_debug_info_option="-dSplitDebugInfo=${SPLIT_DEBUG_INFO}" +fi + # Set the build mode build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")" @@ -75,7 +81,8 @@ RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \ -dTargetPlatform=darwin-x64 \ -dTargetFile="${target_path}" \ -dBuildMode="${build_mode}" \ - -dFontSubset="${icon_tree_shaker_flag}" \ + "${split_debug_info_option}" \ + -dFontSubset="${icon_tree_shaker_flag}" \ --build-inputs="${build_inputs_path}" \ --build-outputs="${build_outputs_path}" \ --output="${ephemeral_dir}" \ diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle index 69a63df6005..ee764b913a1 100644 --- a/packages/flutter_tools/gradle/flutter.gradle +++ b/packages/flutter_tools/gradle/flutter.gradle @@ -603,6 +603,10 @@ class FlutterPlugin implements Plugin { if (project.hasProperty('extra-gen-snapshot-options')) { extraGenSnapshotOptionsValue = project.property('extra-gen-snapshot-options') } + String splitDebugInfoValue = null + if (project.hasProperty('split-debug-info')) { + splitDebugInfoValue = project.property('split-debug-info') + } Boolean treeShakeIconsOptionsValue = false if (project.hasProperty('tree-shake-icons')) { treeShakeIconsOptionsValue = project.property('tree-shake-icons').toBoolean() @@ -641,6 +645,7 @@ class FlutterPlugin implements Plugin { intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/") extraFrontEndOptions extraFrontEndOptionsValue extraGenSnapshotOptions extraGenSnapshotOptionsValue + splitDebugInfo splitDebugInfoValue treeShakeIcons treeShakeIconsOptionsValue } File libJar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/libs.jar") @@ -775,6 +780,8 @@ abstract class BaseFlutterTask extends DefaultTask { @Optional @Input String extraGenSnapshotOptions @Optional @Input + String splitDebugInfo + @Optional @Input Boolean treeShakeIcons @OutputFiles @@ -832,6 +839,9 @@ abstract class BaseFlutterTask extends DefaultTask { if (extraFrontEndOptions != null) { args "-dExtraFrontEndOptions=${extraFrontEndOptions}" } + if (splitDebugInfo != null) { + args "-dSplitDebugInfo=${splitDebugInfo}" + } if (treeShakeIcons == true) { args "-dTreeShakeIcons=true" } diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 74bb0070cb9..9d386d5c568 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -338,6 +338,9 @@ Future buildGradleApp({ if (androidBuildInfo.fastStart) { command.add('-Pfast-start=true'); } + if (androidBuildInfo.buildInfo.splitDebugInfoPath != null) { + command.add('-Psplit-debug-info=${androidBuildInfo.buildInfo.splitDebugInfoPath}'); + } if (androidBuildInfo.buildInfo.treeShakeIcons) { command.add('-Ptree-shake-icons=true'); } diff --git a/packages/flutter_tools/lib/src/aot.dart b/packages/flutter_tools/lib/src/aot.dart index 24d3b84bc69..c3c5d8ba088 100644 --- a/packages/flutter_tools/lib/src/aot.dart +++ b/packages/flutter_tools/lib/src/aot.dart @@ -92,6 +92,7 @@ class AotBuilder { extraGenSnapshotOptions: extraGenSnapshotOptions, bitcode: bitcode, quiet: quiet, + splitDebugInfo: null, ).then((int buildExitCode) { return buildExitCode; }); @@ -128,6 +129,7 @@ class AotBuilder { outputPath: outputPath, extraGenSnapshotOptions: extraGenSnapshotOptions, bitcode: false, + splitDebugInfo: null, ); if (snapshotExitCode != 0) { status?.cancel(); diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index 42bef53483a..370e7503c82 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -94,6 +94,7 @@ class AOTSnapshotter { DarwinArch darwinArch, List extraGenSnapshotOptions = const [], @required bool bitcode, + @required String splitDebugInfo, bool quiet = false, }) async { if (bitcode && platform != TargetPlatform.ios) { @@ -157,11 +158,25 @@ class AOTSnapshotter { genSnapshotArgs.add('--no-use-integer-division'); } + // The name of the debug file must contain additonal information about + // the architecture, since a single build command may produce + // multiple debug files. + final String archName = getNameForTargetPlatform(platform, darwinArch: darwinArch); + final String debugFilename = 'app.$archName.symbols'; + if (splitDebugInfo != null) { + globals.fs.directory(splitDebugInfo) + .createSync(recursive: true); + } + // Optimization arguments. genSnapshotArgs.addAll([ // Faster async/await '--no-causal-async-stacks', '--lazy-async-stacks', + if (splitDebugInfo != null) ...[ + '--dwarf-stack-traces', + '--save-debugging-info=${globals.fs.path.join(splitDebugInfo, debugFilename)}' + ] ]); genSnapshotArgs.add(mainPath); diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index 38889540bbb..edad71e3c82 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -21,6 +21,7 @@ class BuildInfo { this.fileSystemScheme, this.buildNumber, this.buildName, + this.splitDebugInfoPath, @required this.treeShakeIcons, }); @@ -62,6 +63,11 @@ class BuildInfo { /// On Xcode builds it is used as CFBundleShortVersionString, final String buildName; + /// An optional directory path to save debugging information from dwarf stack + /// traces. If null, stack trace information is not stripped from the + /// executable. + final String splitDebugInfoPath; + static const BuildInfo debug = BuildInfo(BuildMode.debug, null, treeShakeIcons: false); static const BuildInfo profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault); static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault); @@ -392,7 +398,7 @@ DarwinArch getIOSArchForName(String arch) { return null; } -String getNameForTargetPlatform(TargetPlatform platform) { +String getNameForTargetPlatform(TargetPlatform platform, {DarwinArch darwinArch}) { switch (platform) { case TargetPlatform.android_arm: return 'android-arm'; @@ -403,6 +409,9 @@ String getNameForTargetPlatform(TargetPlatform platform) { case TargetPlatform.android_x86: return 'android-x86'; case TargetPlatform.ios: + if (darwinArch != null) { + return 'ios-${getNameForDarwinArch(darwinArch)}'; + } return 'ios'; case TargetPlatform.darwin_x64: return 'darwin-x64'; diff --git a/packages/flutter_tools/lib/src/build_system/targets/android.dart b/packages/flutter_tools/lib/src/build_system/targets/android.dart index d2766cbac77..f7a8405cb2d 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/android.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/android.dart @@ -204,6 +204,7 @@ class AndroidAot extends AotElfBase { Future build(Environment environment) async { final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: false); final Directory output = environment.buildDir.childDirectory(_androidAbiName); + final String splitDebugInfo = environment.defines[kSplitDebugInfo]; if (environment.defines[kBuildMode] == null) { throw MissingDefineException(kBuildMode, 'aot_elf'); } @@ -221,6 +222,7 @@ class AndroidAot extends AotElfBase { outputPath: output.path, bitcode: false, extraGenSnapshotOptions: extraGenSnapshotOptions, + splitDebugInfo: splitDebugInfo, ); if (snapshotExitCode != 0) { throw Exception('AOT snapshotter exited with code $snapshotExitCode'); diff --git a/packages/flutter_tools/lib/src/build_system/targets/dart.dart b/packages/flutter_tools/lib/src/build_system/targets/dart.dart index 7e46db2d0d0..9a03afb9593 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/dart.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/dart.dart @@ -41,6 +41,9 @@ const String kExtraFrontEndOptions = 'ExtraFrontEndOptions'; /// This is expected to be a comma separated list of strings. const String kExtraGenSnapshotOptions = 'ExtraGenSnapshotOptions'; +/// Whether to strip source code information out of release builds and where to save it. +const String kSplitDebugInfo = 'SplitDebugInfo'; + /// Alternative scheme for file URIs. /// /// May be used along with [kFileSystemRoots] to support a multi-root @@ -259,6 +262,7 @@ abstract class AotElfBase extends Target { ?? const []; final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]); + final String saveDebuggingInformation = environment.defines[kSplitDebugInfo]; final int snapshotExitCode = await snapshotter.build( platform: targetPlatform, buildMode: buildMode, @@ -267,6 +271,7 @@ abstract class AotElfBase extends Target { outputPath: outputPath, bitcode: false, extraGenSnapshotOptions: extraGenSnapshotOptions, + splitDebugInfo: saveDebuggingInformation ); if (snapshotExitCode != 0) { throw Exception('AOT snapshotter exited with code $snapshotExitCode'); diff --git a/packages/flutter_tools/lib/src/build_system/targets/ios.dart b/packages/flutter_tools/lib/src/build_system/targets/ios.dart index a25b1ef4234..e71258b9697 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart @@ -38,6 +38,7 @@ abstract class AotAssemblyBase extends Target { final bool bitcode = environment.defines[kBitcodeFlag] == 'true'; final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]); + final String splitDebugInfo = environment.defines[kSplitDebugInfo]; final List iosArchs = environment.defines[kIosArchs] ?.split(' ') ?.map(getIOSArchForName) @@ -60,6 +61,7 @@ abstract class AotAssemblyBase extends Target { darwinArch: iosArch, bitcode: bitcode, quiet: true, + splitDebugInfo: splitDebugInfo, )); } final List results = await Future.wait(pending); diff --git a/packages/flutter_tools/lib/src/build_system/targets/macos.dart b/packages/flutter_tools/lib/src/build_system/targets/macos.dart index 2eeba00c591..73685123e30 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/macos.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/macos.dart @@ -200,6 +200,7 @@ class CompileMacOSFramework extends Target { if (buildMode == BuildMode.debug) { throw Exception('precompiled macOS framework only supported in release/profile builds.'); } + final String splitDebugInfo = environment.defines[kSplitDebugInfo]; final int result = await AOTSnapshotter(reportTimings: false).build( bitcode: false, buildMode: buildMode, @@ -208,6 +209,7 @@ class CompileMacOSFramework extends Target { platform: TargetPlatform.darwin_x64, darwinArch: DarwinArch.x86_64, packagesPath: environment.projectDir.childFile('.packages').path, + splitDebugInfo: splitDebugInfo, ); if (result != 0) { throw Exception('gen shapshot failed.'); diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart index 7e45e75a8b8..de01a1e3278 100644 --- a/packages/flutter_tools/lib/src/commands/build_apk.dart +++ b/packages/flutter_tools/lib/src/commands/build_apk.dart @@ -26,6 +26,7 @@ class BuildApkCommand extends BuildSubCommand { usesBuildNumberOption(); usesBuildNameOption(); addShrinkingFlag(); + addSplitDebugInfoOption(); argParser ..addFlag('split-per-abi', negatable: false, diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart index e3410f1943e..f4c857d9de9 100644 --- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart +++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart @@ -167,6 +167,11 @@ List _xcodeBuildSettingsLines({ xcodeBuildSettings.add('FLUTTER_TARGET=$targetOverride'); } + // This is an optional path to split debug info + if (buildInfo.splitDebugInfoPath != null) { + xcodeBuildSettings.add('SPLIT_DEBUG_INFO=${buildInfo.splitDebugInfoPath}'); + } + // The build outputs directory, relative to FLUTTER_APPLICATION_PATH. xcodeBuildSettings.add('FLUTTER_BUILD_DIR=${buildDirOverride ?? getBuildDirectory()}'); diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index cb03804d491..68f932ad3bb 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -107,6 +107,7 @@ class FlutterOptions { static const String kEnableExperiment = 'enable-experiment'; static const String kFileSystemRoot = 'filesystem-root'; static const String kFileSystemScheme = 'filesystem-scheme'; + static const String kSplitDebugInfoOption = 'split-debug-info'; } abstract class FlutterCommand extends Command { @@ -366,6 +367,20 @@ abstract class FlutterCommand extends Command { help: 'Build a JIT release version of your app${defaultToRelease ? ' (default mode)' : ''}.'); } + void addSplitDebugInfoOption() { + argParser.addOption(FlutterOptions.kSplitDebugInfoOption, + help: 'In a release build, this flag reduces application size by storing ' + 'Dart program symbols in a separate file on the host rather than in the ' + 'application. The value of the flag should be a directory where program ' + 'symbol files can be stored for later use. These symbol files contain ' + 'the information needed to symbolize Dart stack traces. For an app built ' + 'with this flag, the \'flutter symbolize\' command with the right program ' + 'symbol file is required to obtain a human readable stack trace. This ' + 'command is tracked by https://github.com/flutter/flutter/issues/50206', + valueHelp: '/project-name/v1.2.3/', + ); + } + void addTreeShakeIconsFlag() { argParser.addFlag('tree-shake-icons', negatable: true, @@ -499,6 +514,9 @@ abstract class FlutterCommand extends Command { buildName: argParser.options.containsKey('build-name') ? stringArg('build-name') : null, + splitDebugInfoPath: argParser.options.containsKey(FlutterOptions.kSplitDebugInfoOption) + ? stringArg(FlutterOptions.kSplitDebugInfoOption) + : null, treeShakeIcons: argParser.options.containsKey('tree-shake-icons') ? boolArg('tree-shake-icons') : kIconTreeShakerEnabledDefault, diff --git a/packages/flutter_tools/templates/app/.gitignore.tmpl b/packages/flutter_tools/templates/app/.gitignore.tmpl index ae1f1838ee7..78b3c2b3364 100644 --- a/packages/flutter_tools/templates/app/.gitignore.tmpl +++ b/packages/flutter_tools/templates/app/.gitignore.tmpl @@ -33,5 +33,8 @@ # Web related lib/generated_plugin_registrant.dart +# Symbolication related +app.*.symbols + # Exceptions to above rules. !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/flutter_tools/templates/module/common/.gitignore.tmpl b/packages/flutter_tools/templates/module/common/.gitignore.tmpl index 86f469179f5..1607ae6bebb 100644 --- a/packages/flutter_tools/templates/module/common/.gitignore.tmpl +++ b/packages/flutter_tools/templates/module/common/.gitignore.tmpl @@ -40,3 +40,6 @@ build/ .ios/ .flutter-plugins .flutter-plugins-dependencies + +# Symbolication related +app.*.symbols diff --git a/packages/flutter_tools/test/general.shard/base/build_test.dart b/packages/flutter_tools/test/general.shard/base/build_test.dart index b50555b1932..baa51eed321 100644 --- a/packages/flutter_tools/test/general.shard/base/build_test.dart +++ b/packages/flutter_tools/test/general.shard/base/build_test.dart @@ -266,6 +266,7 @@ void main() { packagesPath: '.packages', outputPath: outputPath, bitcode: false, + splitDebugInfo: null, ), isNot(equals(0))); }, overrides: contextOverrides); @@ -278,6 +279,7 @@ void main() { packagesPath: '.packages', outputPath: outputPath, bitcode: false, + splitDebugInfo: null, ), isNot(0)); }, overrides: contextOverrides); @@ -290,6 +292,7 @@ void main() { packagesPath: '.packages', outputPath: outputPath, bitcode: false, + splitDebugInfo: null, ), isNot(0)); }, overrides: contextOverrides); @@ -316,6 +319,7 @@ void main() { outputPath: outputPath, darwinArch: DarwinArch.armv7, bitcode: true, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -376,6 +380,7 @@ void main() { outputPath: outputPath, darwinArch: DarwinArch.armv7, bitcode: true, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -435,6 +440,7 @@ void main() { outputPath: outputPath, darwinArch: DarwinArch.armv7, bitcode: false, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -463,6 +469,61 @@ void main() { expect(assemblyFile.readAsStringSync().contains('.section __DWARF'), true); }, overrides: contextOverrides); + testUsingContext('builds iOS armv7 profile AOT snapshot with dwarf stack traces', () async { + globals.fs.file('main.dill').writeAsStringSync('binary magic'); + + final String outputPath = globals.fs.path.join('build', 'foo'); + globals.fs.directory(outputPath).createSync(recursive: true); + + final String assembly = globals.fs.path.join(outputPath, 'snapshot_assembly.S'); + genSnapshot.outputs = { + assembly: 'blah blah\n.section __DWARF\nblah blah\n', + }; + final String debugPath = globals.fs.path.join('foo', 'app.ios-armv7.symbols'); + + final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), ['command name', 'arguments...']); + when(mockXcode.cc(any)).thenAnswer((_) => Future.value(successResult)); + when(mockXcode.clang(any)).thenAnswer((_) => Future.value(successResult)); + + final int genSnapshotExitCode = await snapshotter.build( + platform: TargetPlatform.ios, + buildMode: BuildMode.profile, + mainPath: 'main.dill', + packagesPath: '.packages', + outputPath: outputPath, + darwinArch: DarwinArch.armv7, + bitcode: false, + splitDebugInfo: 'foo', + ); + + expect(genSnapshotExitCode, 0); + expect(genSnapshot.callCount, 1); + expect(genSnapshot.snapshotType.platform, TargetPlatform.ios); + expect(genSnapshot.snapshotType.mode, BuildMode.profile); + expect(genSnapshot.additionalArgs, [ + '--deterministic', + '--snapshot_kind=app-aot-assembly', + '--assembly=$assembly', + '--strip', + '--no-sim-use-hardfp', + '--no-use-integer-division', + '--no-causal-async-stacks', + '--lazy-async-stacks', + '--dwarf-stack-traces', + '--save-debugging-info=$debugPath', + 'main.dill', + ]); + verifyNever(mockXcode.cc(argThat(contains('-fembed-bitcode')))); + verifyNever(mockXcode.clang(argThat(contains('-fembed-bitcode')))); + + verify(mockXcode.cc(argThat(contains('-isysroot')))).called(1); + verify(mockXcode.clang(argThat(contains('-isysroot')))).called(1); + + final File assemblyFile = globals.fs.file(assembly); + expect(assemblyFile.existsSync(), true); + expect(assemblyFile.readAsStringSync().contains('.section __DWARF'), true); + }, overrides: contextOverrides); + testUsingContext('builds iOS arm64 profile AOT snapshot', () async { globals.fs.file('main.dill').writeAsStringSync('binary magic'); @@ -485,6 +546,7 @@ void main() { outputPath: outputPath, darwinArch: DarwinArch.arm64, bitcode: false, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -524,6 +586,7 @@ void main() { outputPath: outputPath, darwinArch: DarwinArch.armv7, bitcode: false, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -565,6 +628,7 @@ void main() { outputPath: outputPath, darwinArch: DarwinArch.arm64, bitcode: false, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -595,6 +659,7 @@ void main() { packagesPath: '.packages', outputPath: outputPath, bitcode: false, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -614,6 +679,42 @@ void main() { ]); }, overrides: contextOverrides); + testUsingContext('builds shared library for android-arm with dwarf stack traces', () async { + globals.fs.file('main.dill').writeAsStringSync('binary magic'); + + final String outputPath = globals.fs.path.join('build', 'foo'); + final String debugPath = globals.fs.path.join('foo', 'app.android-arm.symbols'); + globals.fs.directory(outputPath).createSync(recursive: true); + + final int genSnapshotExitCode = await snapshotter.build( + platform: TargetPlatform.android_arm, + buildMode: BuildMode.release, + mainPath: 'main.dill', + packagesPath: '.packages', + outputPath: outputPath, + bitcode: false, + splitDebugInfo: 'foo', + ); + + expect(genSnapshotExitCode, 0); + expect(genSnapshot.callCount, 1); + expect(genSnapshot.snapshotType.platform, TargetPlatform.android_arm); + expect(genSnapshot.snapshotType.mode, BuildMode.release); + expect(genSnapshot.additionalArgs, [ + '--deterministic', + '--snapshot_kind=app-aot-elf', + '--elf=build/foo/app.so', + '--strip', + '--no-sim-use-hardfp', + '--no-use-integer-division', + '--no-causal-async-stacks', + '--lazy-async-stacks', + '--dwarf-stack-traces', + '--save-debugging-info=$debugPath', + 'main.dill', + ]); + }, overrides: contextOverrides); + testUsingContext('builds shared library for android-arm64', () async { globals.fs.file('main.dill').writeAsStringSync('binary magic'); @@ -627,6 +728,7 @@ void main() { packagesPath: '.packages', outputPath: outputPath, bitcode: false, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); @@ -665,6 +767,7 @@ void main() { packagesPath: '.packages', outputPath: outputPath, bitcode: false, + splitDebugInfo: null, ); expect(genSnapshotExitCode, 0); diff --git a/packages/flutter_tools/test/general.shard/build_info_test.dart b/packages/flutter_tools/test/general.shard/build_info_test.dart index fcd28eefb6f..5db5da75735 100644 --- a/packages/flutter_tools/test/general.shard/build_info_test.dart +++ b/packages/flutter_tools/test/general.shard/build_info_test.dart @@ -78,4 +78,11 @@ void main() { expect(() => BuildMode.fromName('foo'), throwsArgumentError); }); }); + + test('getNameForTargetPlatform on Darwin arches', () { + expect(getNameForTargetPlatform(TargetPlatform.ios, darwinArch: DarwinArch.arm64), 'ios-arm64'); + expect(getNameForTargetPlatform(TargetPlatform.ios, darwinArch: DarwinArch.armv7), 'ios-armv7'); + expect(getNameForTargetPlatform(TargetPlatform.ios, darwinArch: DarwinArch.x86_64), 'ios-x86_64'); + expect(getNameForTargetPlatform(TargetPlatform.android), isNot(contains('ios'))); + }); } diff --git a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart index 31a63706771..d467790d400 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart @@ -230,6 +230,35 @@ void main() { ProcessManager: () => mockProcessManager, }); + testUsingContext('--split-debug-info is enabled when an output directory is provided', () async { + final String projectPath = await createProject(tempDir, + arguments: ['--no-pub', '--template=app']); + + await expectLater(() async { + await runBuildApkCommand(projectPath, arguments: ['--split-debug-info=${tempDir.path}']); + }, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1')); + + verify(mockProcessManager.start( + [ + gradlew, + '-q', + '-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', + '-Ptrack-widget-creation=true', + '-Pshrink=true', + '-Ptarget-platform=android-arm,android-arm64,android-x64', + '-Psplit-debug-info=${tempDir.path}', + 'assembleRelease', + ], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).called(1); + }, + overrides: { + AndroidSdk: () => mockAndroidSdk, + FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), + ProcessManager: () => mockProcessManager, + }); + testUsingContext('shrinking is disabled when --no-shrink is passed', () async { final String projectPath = await createProject(tempDir, arguments: ['--no-pub', '--template=app']);