Unverified Commit a0745e23 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] add --analyze-size flag (#62697)

parent 67e9b294
...@@ -9,6 +9,7 @@ import 'package:meta/meta.dart'; ...@@ -9,6 +9,7 @@ import 'package:meta/meta.dart';
import 'package:xml/xml.dart'; import 'package:xml/xml.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/analyze_size.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
...@@ -18,6 +19,7 @@ import '../base/terminal.dart'; ...@@ -18,6 +19,7 @@ import '../base/terminal.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../convert.dart';
import '../flutter_manifest.dart'; import '../flutter_manifest.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../project.dart'; import '../project.dart';
...@@ -499,6 +501,25 @@ Future<void> buildGradleApp({ ...@@ -499,6 +501,25 @@ Future<void> buildGradleApp({
'$successMark Built ${globals.fs.path.relative(apkFile.path)}$appSize.', '$successMark Built ${globals.fs.path.relative(apkFile.path)}$appSize.',
color: TerminalColor.green, color: TerminalColor.green,
); );
// Call size analyzer if --analyze-size flag was provided.
if (buildInfo.analyzeSize != null && !globals.platform.isWindows) {
final SizeAnalyzer sizeAnalyzer = SizeAnalyzer(
fileSystem: globals.fs,
logger: globals.logger,
processUtils: ProcessUtils.instance,
);
final Map<String, Object> output = await sizeAnalyzer.analyzeApkSizeAndAotSnapshot(
apk: apkFile,
aotSnapshot: globals.fs.file(buildInfo.analyzeSize),
);
final File outputFile = globals.fsUtils.getUniqueFile(globals.fs.currentDirectory, 'apk-analysis', 'json')
..writeAsStringSync(jsonEncode(output));
// This message is used as a sentinel in analyze_apk_size_test.dart
globals.printStatus(
'A summary of your APK analysis can be found at: ${outputFile.path}',
);
}
} }
/// Builds AAR and POM files. /// Builds AAR and POM files.
......
...@@ -33,6 +33,7 @@ class BuildInfo { ...@@ -33,6 +33,7 @@ class BuildInfo {
this.performanceMeasurementFile, this.performanceMeasurementFile,
this.packagesPath = '.packages', this.packagesPath = '.packages',
this.nullSafetyMode = NullSafetyMode.autodetect, this.nullSafetyMode = NullSafetyMode.autodetect,
this.analyzeSize,
}); });
final BuildMode mode; final BuildMode mode;
...@@ -113,6 +114,10 @@ class BuildInfo { ...@@ -113,6 +114,10 @@ class BuildInfo {
/// rerun tasks. /// rerun tasks.
final String performanceMeasurementFile; final String performanceMeasurementFile;
/// If provided, an output file where a v8-style heapsnapshot will be written for size
/// profiling.
final String analyzeSize;
static const BuildInfo debug = BuildInfo(BuildMode.debug, null, treeShakeIcons: false); static const BuildInfo debug = BuildInfo(BuildMode.debug, null, treeShakeIcons: false);
static const BuildInfo profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault); static const BuildInfo profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault); static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
......
...@@ -34,6 +34,7 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -34,6 +34,7 @@ class BuildApkCommand extends BuildSubCommand {
addEnableExperimentation(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp);
addBuildPerformanceFile(hide: !verboseHelp); addBuildPerformanceFile(hide: !verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp); addNullSafetyModeOptions(hide: !verboseHelp);
usesAnalyzeSizeFlag();
argParser argParser
..addFlag('split-per-abi', ..addFlag('split-per-abi',
negatable: false, negatable: false,
......
...@@ -112,6 +112,7 @@ class FlutterOptions { ...@@ -112,6 +112,7 @@ class FlutterOptions {
static const String kPerformanceMeasurementFile = 'performance-measurement-file'; static const String kPerformanceMeasurementFile = 'performance-measurement-file';
static const String kNullSafety = 'sound-null-safety'; static const String kNullSafety = 'sound-null-safety';
static const String kDeviceUser = 'device-user'; static const String kDeviceUser = 'device-user';
static const String kAnalyzeSize = 'analyze-size';
} }
abstract class FlutterCommand extends Command<void> { abstract class FlutterCommand extends Command<void> {
...@@ -605,6 +606,15 @@ abstract class FlutterCommand extends Command<void> { ...@@ -605,6 +606,15 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
void usesAnalyzeSizeFlag() {
argParser.addFlag(
FlutterOptions.kAnalyzeSize,
defaultsTo: false,
help: 'Whether to produce additonal profile information for artifact output size. '
'This flag is only support on release builds on macOS/Linux hosts.'
);
}
/// Compute the [BuildInfo] for the current flutter command. /// Compute the [BuildInfo] for the current flutter command.
/// Commands that build multiple build modes can pass in a [forcedBuildMode] /// Commands that build multiple build modes can pass in a [forcedBuildMode]
/// to be used instead of parsing flags. /// to be used instead of parsing flags.
...@@ -640,6 +650,15 @@ abstract class FlutterCommand extends Command<void> { ...@@ -640,6 +650,15 @@ abstract class FlutterCommand extends Command<void> {
} }
} }
String analyzeSize;
if (argParser.options.containsKey(FlutterOptions.kAnalyzeSize)
&& boolArg(FlutterOptions.kAnalyzeSize)
&& !globals.platform.isWindows) {
final File file = globals.fsUtils.getUniqueFile(globals.fs.currentDirectory, 'flutter_size', 'json');
extraGenSnapshotOptions.add('--write-v8-snapshot-profile-to=${file.path}');
analyzeSize = file.path;
}
NullSafetyMode nullSafetyMode = NullSafetyMode.unsound; NullSafetyMode nullSafetyMode = NullSafetyMode.unsound;
if (argParser.options.containsKey(FlutterOptions.kNullSafety)) { if (argParser.options.containsKey(FlutterOptions.kNullSafety)) {
final bool nullSafety = boolArg(FlutterOptions.kNullSafety); final bool nullSafety = boolArg(FlutterOptions.kNullSafety);
...@@ -671,6 +690,10 @@ abstract class FlutterCommand extends Command<void> { ...@@ -671,6 +690,10 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
final BuildMode buildMode = forcedBuildMode ?? getBuildMode(); final BuildMode buildMode = forcedBuildMode ?? getBuildMode();
if (buildMode != BuildMode.release && analyzeSize != null) {
throwToolExit('--analyze-size can only be used on release builds.');
}
final bool treeShakeIcons = argParser.options.containsKey('tree-shake-icons') final bool treeShakeIcons = argParser.options.containsKey('tree-shake-icons')
&& buildMode.isPrecompiled && buildMode.isPrecompiled
&& boolArg('tree-shake-icons'); && boolArg('tree-shake-icons');
...@@ -715,6 +738,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -715,6 +738,7 @@ abstract class FlutterCommand extends Command<void> {
performanceMeasurementFile: performanceMeasurementFile, performanceMeasurementFile: performanceMeasurementFile,
packagesPath: globalResults['packages'] as String ?? '.packages', packagesPath: globalResults['packages'] as String ?? '.packages',
nullSafetyMode: nullSafetyMode, nullSafetyMode: nullSafetyMode,
analyzeSize: analyzeSize,
); );
} }
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:process/process.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import '../src/common.dart';
const String debugMessage = 'A summary of your APK analysis can be found at: ';
void main() {
test('--analyze-size flag produces expected output on hello_world', () async {
final String flutterBin = globals.fs.path.join(getFlutterRoot(), 'bin', 'flutter');
final ProcessResult result = await const LocalProcessManager().run(<String>[
flutterBin,
'build',
'apk',
'--analyze-size',
], workingDirectory: globals.fs.path.join(getFlutterRoot(), 'examples', 'hello_world'));
print(result.stdout);
print(result.stderr);
expect(result.stdout.toString(), contains('app-release.apk (total compressed)'));
final String line = result.stdout.toString()
.split('\n')
.firstWhere((String line) => line.contains(debugMessage));
expect(globals.fs.file(globals.fs.path.join(line.split(debugMessage).last.trim())).existsSync(), true);
expect(result.exitCode, 0);
}, skip: const LocalPlatform().isWindows); // Not yet supported on Windows
}
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