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