Commit 7c47837c authored by Devon Carew's avatar Devon Carew

show build progress; print app sizes (#4263)

* show build progress; print app sizes

* add todo

* review comments

* remove unused import
parent 26a4d7bb
...@@ -164,6 +164,8 @@ class RunResult { ...@@ -164,6 +164,8 @@ class RunResult {
final ProcessResult processResult; final ProcessResult processResult;
int get exitCode => processResult.exitCode; int get exitCode => processResult.exitCode;
String get stdout => processResult.stdout;
String get stderr => processResult.stderr;
@override @override
String toString() { String toString() {
......
...@@ -71,6 +71,11 @@ String toPrettyJson(Object jsonable) { ...@@ -71,6 +71,11 @@ String toPrettyJson(Object jsonable) {
return new JsonEncoder.withIndent(' ').convert(jsonable) + '\n'; return new JsonEncoder.withIndent(' ').convert(jsonable) + '\n';
} }
/// Return a String - with units - for the size in MB of the given number of bytes.
String getSizeAsMB(int bytesLength) {
return '${(bytesLength / (1024 * 1024)).toStringAsFixed(1)}MB';
}
/// A class to maintain a list of items, fire events when items are added or /// A class to maintain a list of items, fire events when items are added or
/// removed, and calculate a diff of changes when a new list of items is /// removed, and calculate a diff of changes when a new list of items is
/// available. /// available.
......
...@@ -7,6 +7,7 @@ import 'dart:io'; ...@@ -7,6 +7,7 @@ import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../base/logger.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
...@@ -50,17 +51,22 @@ class BuildAotCommand extends FlutterCommand { ...@@ -50,17 +51,22 @@ class BuildAotCommand extends FlutterCommand {
printError('Unknown platform: $targetPlatform'); printError('Unknown platform: $targetPlatform');
return 1; return 1;
} }
String outputPath = buildAotSnapshot(
String typeName = path.basename(tools.getEngineArtifactsDirectory(platform, getBuildMode()).path);
Status status = logger.startProgress('Building AOT snapshot in ${getModeName(getBuildMode())} mode ($typeName)...');
String outputPath = await buildAotSnapshot(
findMainDartFile(argResults['target']), findMainDartFile(argResults['target']),
platform, platform,
getBuildMode(), getBuildMode(),
outputPath: argResults['output-dir'], outputPath: argResults['output-dir'],
interpreter: argResults['interpreter'] interpreter: argResults['interpreter']
); );
status.stop(showElapsedTime: true);
if (outputPath == null) if (outputPath == null)
return 1; return 1;
printStatus('Built $outputPath.'); printStatus('Built to $outputPath${Platform.pathSeparator}.');
return 0; return 0;
} }
} }
...@@ -72,13 +78,13 @@ String _getSdkExtensionPath(String packagesPath, String package) { ...@@ -72,13 +78,13 @@ String _getSdkExtensionPath(String packagesPath, String package) {
/// Build an AOT snapshot. Return `null` (and log to `printError`) if the method /// Build an AOT snapshot. Return `null` (and log to `printError`) if the method
/// fails. /// fails.
String buildAotSnapshot( Future<String> buildAotSnapshot(
String mainPath, String mainPath,
TargetPlatform platform, TargetPlatform platform,
BuildMode buildMode, { BuildMode buildMode, {
String outputPath: _kDefaultAotOutputDir, String outputPath: _kDefaultAotOutputDir,
bool interpreter: false bool interpreter: false
}) { }) async {
try { try {
return _buildAotSnapshot( return _buildAotSnapshot(
mainPath, mainPath,
...@@ -94,13 +100,13 @@ String buildAotSnapshot( ...@@ -94,13 +100,13 @@ String buildAotSnapshot(
} }
} }
String _buildAotSnapshot( Future<String> _buildAotSnapshot(
String mainPath, String mainPath,
TargetPlatform platform, TargetPlatform platform,
BuildMode buildMode, { BuildMode buildMode, {
String outputPath: _kDefaultAotOutputDir, String outputPath: _kDefaultAotOutputDir,
bool interpreter: false bool interpreter: false
}) { }) async {
if (!isAotBuildMode(buildMode)) { if (!isAotBuildMode(buildMode)) {
printError('${toTitleCase(getModeName(buildMode))} mode does not support AOT compilation.'); printError('${toTitleCase(getModeName(buildMode))} mode does not support AOT compilation.');
return null; return null;
...@@ -253,10 +259,11 @@ String _buildAotSnapshot( ...@@ -253,10 +259,11 @@ String _buildAotSnapshot(
genSnapshotCmd.add(mainPath); genSnapshotCmd.add(mainPath);
String typeName = path.basename(tools.getEngineArtifactsDirectory(platform, buildMode).path); RunResult results = await runAsync(genSnapshotCmd);
printStatus('Building snapshot in ${getModeName(buildMode)} mode ($typeName)...'); if (results.exitCode != 0) {
printStatus(results.toString());
runCheckedSync(genSnapshotCmd, truncateCommand: true); return null;
}
// On iOS, we use Xcode to compile the snapshot into a dynamic library that the // On iOS, 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.
......
...@@ -11,6 +11,7 @@ import 'package:path/path.dart' as path; ...@@ -11,6 +11,7 @@ import 'package:path/path.dart' as path;
import '../android/android_sdk.dart'; import '../android/android_sdk.dart';
import '../base/file_system.dart' show ensureDirectoryExists; import '../base/file_system.dart' show ensureDirectoryExists;
import '../base/os.dart'; import '../base/os.dart';
import '../base/logger.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
...@@ -347,9 +348,6 @@ int _buildApk( ...@@ -347,9 +348,6 @@ int _buildApk(
File apkShaFile = new File('$outputFile.sha1'); File apkShaFile = new File('$outputFile.sha1');
apkShaFile.writeAsStringSync(calculateSha(finalApk)); apkShaFile.writeAsStringSync(calculateSha(finalApk));
double size = finalApk.lengthSync() / (1024 * 1024);
printStatus('Built ${finalApk.path} (${size.toStringAsFixed(1)}MB).');
return 0; return 0;
} finally { } finally {
tempDir.deleteSync(recursive: true); tempDir.deleteSync(recursive: true);
...@@ -492,7 +490,7 @@ Future<int> buildAndroid( ...@@ -492,7 +490,7 @@ Future<int> buildAndroid(
} }
String typeName = path.basename(tools.getEngineArtifactsDirectory(platform, buildMode).path); String typeName = path.basename(tools.getEngineArtifactsDirectory(platform, buildMode).path);
printStatus('Building APK in ${getModeName(buildMode)} mode ($typeName)...'); Status status = logger.startProgress('Building APK in ${getModeName(buildMode)} mode ($typeName)...');
if (flxPath != null && flxPath.isNotEmpty) { if (flxPath != null && flxPath.isNotEmpty) {
if (!FileSystemEntity.isFileSync(flxPath)) { if (!FileSystemEntity.isFileSync(flxPath)) {
...@@ -513,7 +511,7 @@ Future<int> buildAndroid( ...@@ -513,7 +511,7 @@ Future<int> buildAndroid(
// Build an AOT snapshot if needed. // Build an AOT snapshot if needed.
if (isAotBuildMode(buildMode) && aotPath == null) { if (isAotBuildMode(buildMode) && aotPath == null) {
aotPath = buildAotSnapshot(findMainDartFile(target), platform, buildMode); aotPath = await buildAotSnapshot(findMainDartFile(target), platform, buildMode);
if (aotPath == null) { if (aotPath == null) {
printError('Failed to build AOT snapshot'); printError('Failed to build AOT snapshot');
return 1; return 1;
...@@ -540,13 +538,19 @@ Future<int> buildAndroid( ...@@ -540,13 +538,19 @@ Future<int> buildAndroid(
} }
int result = _buildApk(platform, buildMode, components, flxPath, keystore, outputFile); int result = _buildApk(platform, buildMode, components, flxPath, keystore, outputFile);
status.stop(showElapsedTime: true);
if (result == 0) { if (result == 0) {
File apkFile = new File(outputFile);
printStatus('Built $outputFile (${getSizeAsMB(apkFile.lengthSync())}).');
_writeBuildMetaEntry( _writeBuildMetaEntry(
path.dirname(outputFile), path.dirname(outputFile),
'targetBuildType', 'targetBuildType',
_getTargetBuildTypeToken(platform, buildMode, new File(outputFile)) _getTargetBuildTypeToken(platform, buildMode, new File(outputFile))
); );
} }
return result; return result;
} }
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
import 'dart:async'; import 'dart:async';
import 'package:path/path.dart' as path;
import '../application_package.dart'; import '../application_package.dart';
import '../base/logger.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../globals.dart'; import '../globals.dart';
import '../ios/mac.dart'; import '../ios/mac.dart';
...@@ -42,24 +45,26 @@ class BuildIOSCommand extends FlutterCommand { ...@@ -42,24 +45,26 @@ class BuildIOSCommand extends FlutterCommand {
bool shouldCodesign = argResults['codesign']; bool shouldCodesign = argResults['codesign'];
if (!forSimulator && !shouldCodesign) { if (!forSimulator && !shouldCodesign) {
printStatus('Warning: Building for device with codesigning disabled.'); printStatus('Warning: Building for device with codesigning disabled. You will '
printStatus('You will have to manually codesign before deploying to device.'); 'have to manually codesign before deploying to device.');
} }
String logTarget = forSimulator ? 'simulator' : 'device'; String logTarget = forSimulator ? 'simulator' : 'device';
printStatus('Building $app for $logTarget...'); String typeName = path.basename(tools.getEngineArtifactsDirectory(TargetPlatform.ios, getBuildMode()).path);
Status status = logger.startProgress('Building $app for $logTarget ($typeName)...');
bool result = await buildIOSXcodeProject(app, getBuildMode(), XcodeBuildResult result = await buildXcodeProject(app, getBuildMode(),
buildForDevice: !forSimulator, buildForDevice: !forSimulator,
codesign: shouldCodesign); codesign: shouldCodesign);
status.stop(showElapsedTime: true);
if (!result) { if (!result.success) {
printError('Encountered error while building for $logTarget.'); printError('Encountered error while building for $logTarget.');
return 1; return 1;
} }
printStatus('Built in ios/.generated.'); if (result.output != null)
printStatus('Built ${result.output}.');
return 0; return 0;
} }
......
...@@ -180,8 +180,8 @@ class IOSDevice extends Device { ...@@ -180,8 +180,8 @@ class IOSDevice extends Device {
printTrace('Building ${app.name} for $id'); printTrace('Building ${app.name} for $id');
// Step 1: Install the precompiled/DBC application if necessary. // Step 1: Install the precompiled/DBC application if necessary.
bool buildResult = await buildIOSXcodeProject(app, mode, buildForDevice: true); XcodeBuildResult buildResult = await buildXcodeProject(app, mode, buildForDevice: true);
if (!buildResult) { if (!buildResult.success) {
printError('Could not build the precompiled application for the device.'); printError('Could not build the precompiled application for the device.');
return new LaunchResult.failed(); return new LaunchResult.failed();
} }
......
...@@ -97,7 +97,7 @@ bool _xcodeVersionCheckValid(int major, int minor) { ...@@ -97,7 +97,7 @@ bool _xcodeVersionCheckValid(int major, int minor) {
return false; return false;
} }
Future<bool> buildIOSXcodeProject(ApplicationPackage app, BuildMode mode, Future<XcodeBuildResult> buildXcodeProject(ApplicationPackage app, BuildMode mode,
{ bool buildForDevice, bool codesign: true }) async { { bool buildForDevice, bool codesign: true }) async {
String flutterProjectPath = Directory.current.path; String flutterProjectPath = Directory.current.path;
...@@ -105,17 +105,17 @@ Future<bool> buildIOSXcodeProject(ApplicationPackage app, BuildMode mode, ...@@ -105,17 +105,17 @@ Future<bool> buildIOSXcodeProject(ApplicationPackage app, BuildMode mode,
printTrace('Initializing the Xcode project.'); printTrace('Initializing the Xcode project.');
if ((await setupXcodeProjectHarness(flutterProjectPath, mode)) != 0) { if ((await setupXcodeProjectHarness(flutterProjectPath, mode)) != 0) {
printError('Could not initialize the Xcode project.'); printError('Could not initialize the Xcode project.');
return false; return new XcodeBuildResult(false);
} }
} else { } else {
updateXcodeLocalProperties(flutterProjectPath); updateXcodeLocalProperties(flutterProjectPath);
} }
if (!_validateEngineRevision(app)) if (!_validateEngineRevision(app))
return false; return new XcodeBuildResult(false);
if (!_checkXcodeVersion()) if (!_checkXcodeVersion())
return false; return new XcodeBuildResult(false);
// Before the build, all service definitions must be updated and the dylibs // Before the build, all service definitions must be updated and the dylibs
// copied over to a location that is suitable for Xcodebuild to find them. // copied over to a location that is suitable for Xcodebuild to find them.
...@@ -148,20 +148,30 @@ Future<bool> buildIOSXcodeProject(ApplicationPackage app, BuildMode mode, ...@@ -148,20 +148,30 @@ Future<bool> buildIOSXcodeProject(ApplicationPackage app, BuildMode mode,
commands.addAll(<String>['-sdk', 'iphonesimulator', '-arch', 'x86_64']); commands.addAll(<String>['-sdk', 'iphonesimulator', '-arch', 'x86_64']);
} }
printTrace(commands.join(' ')); RunResult result = await runAsync(commands, workingDirectory: app.rootPath);
ProcessResult result = Process.runSync(
commands.first, commands.sublist(1), workingDirectory: app.rootPath
);
if (result.exitCode != 0) { if (result.exitCode != 0) {
if (result.stderr.isNotEmpty) if (result.stderr.isNotEmpty)
printStatus(result.stderr); printStatus(result.stderr);
if (result.stdout.isNotEmpty) if (result.stdout.isNotEmpty)
printStatus(result.stdout); printStatus(result.stdout);
return new XcodeBuildResult(false);
} else {
// Look for 'clean build/Release-iphoneos/Runner.app'.
RegExp regexp = new RegExp(r' clean (\S*\.app)$', multiLine: true);
Match match = regexp.firstMatch(result.stdout);
String outputDir;
if (match != null)
outputDir = path.join(app.rootPath, match.group(1));
return new XcodeBuildResult(true, outputDir);
} }
}
class XcodeBuildResult {
XcodeBuildResult(this.success, [this.output]);
return result.exitCode == 0; final bool success;
final String output;
} }
final RegExp _xcodeVersionRegExp = new RegExp(r'Xcode (\d+)\..*'); final RegExp _xcodeVersionRegExp = new RegExp(r'Xcode (\d+)\..*');
......
...@@ -548,8 +548,8 @@ class IOSSimulator extends Device { ...@@ -548,8 +548,8 @@ class IOSSimulator extends Device {
Future<bool> _buildAndInstallApplicationBundle(ApplicationPackage app) async { Future<bool> _buildAndInstallApplicationBundle(ApplicationPackage app) async {
// Step 1: Build the Xcode project. // Step 1: Build the Xcode project.
// The build mode for the simulator is always debug. // The build mode for the simulator is always debug.
bool buildResult = await buildIOSXcodeProject(app, BuildMode.debug, buildForDevice: false); XcodeBuildResult buildResult = await buildXcodeProject(app, BuildMode.debug, buildForDevice: false);
if (!buildResult) { if (!buildResult.success) {
printError('Could not build the application for the simulator.'); printError('Could not build the application for the simulator.');
return false; return false;
} }
......
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