Commit 6a63af40 authored by John McCutchan's avatar John McCutchan Committed by GitHub

Rebuild Android apk when Dart source is modified (#7345)

- [x] Wire up dependency checker and plumb flags down to the right place

Fixes #7014
parent 68425979
...@@ -269,7 +269,8 @@ class AndroidDevice extends Device { ...@@ -269,7 +269,8 @@ class AndroidDevice extends Device {
String route, String route,
DebuggingOptions debuggingOptions, DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs, Map<String, dynamic> platformArgs,
bool prebuiltApplication: false bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
}) async { }) async {
if (!_checkForSupportedAdbVersion() || !_checkForSupportedAndroidVersion()) if (!_checkForSupportedAdbVersion() || !_checkForSupportedAndroidVersion())
return new LaunchResult.failed(); return new LaunchResult.failed();
...@@ -281,7 +282,8 @@ class AndroidDevice extends Device { ...@@ -281,7 +282,8 @@ class AndroidDevice extends Device {
printTrace('Building APK'); printTrace('Building APK');
await buildApk(platform, await buildApk(platform,
target: mainPath, target: mainPath,
buildMode: debuggingOptions.buildMode buildMode: debuggingOptions.buildMode,
applicationNeedsRebuild: applicationNeedsRebuild
); );
} }
......
...@@ -27,6 +27,8 @@ abstract class ApplicationPackage { ...@@ -27,6 +27,8 @@ abstract class ApplicationPackage {
String get displayName => name; String get displayName => name;
String get packagePath => null;
@override @override
String toString() => displayName; String toString() => displayName;
} }
...@@ -119,6 +121,9 @@ class AndroidApk extends ApplicationPackage { ...@@ -119,6 +121,9 @@ class AndroidApk extends ApplicationPackage {
); );
} }
@override
String get packagePath => apkPath;
@override @override
String get name => path.basename(apkPath); String get name => path.basename(apkPath);
} }
......
...@@ -483,7 +483,8 @@ Future<Null> buildAndroid( ...@@ -483,7 +483,8 @@ Future<Null> buildAndroid(
String target, String target,
String flxPath, String flxPath,
String aotPath, String aotPath,
ApkKeystoreInfo keystore ApkKeystoreInfo keystore,
bool applicationNeedsRebuild: false
}) async { }) async {
outputFile ??= _defaultOutputPath; outputFile ??= _defaultOutputPath;
...@@ -508,12 +509,14 @@ Future<Null> buildAndroid( ...@@ -508,12 +509,14 @@ Future<Null> buildAndroid(
} }
} }
final bool needRebuild =
applicationNeedsRebuild ||
_needsRebuild(outputFile, manifest, platform, buildMode, extraFiles);
// In debug (JIT) mode, the snapshot lives in the FLX, and we can skip the APK // In debug (JIT) mode, the snapshot lives in the FLX, and we can skip the APK
// rebuild if none of the resources in the APK are stale. // rebuild if none of the resources in the APK are stale.
// In AOT modes, the snapshot lives in the APK, so the APK must be rebuilt. // In AOT modes, the snapshot lives in the APK, so the APK must be rebuilt.
if (!isAotBuildMode(buildMode) && if (!isAotBuildMode(buildMode) && !force && !needRebuild) {
!force &&
!_needsRebuild(outputFile, manifest, platform, buildMode, extraFiles)) {
printTrace('APK up to date; skipping build step.'); printTrace('APK up to date; skipping build step.');
return; return;
} }
...@@ -608,7 +611,8 @@ Future<Null> buildAndroidWithGradle( ...@@ -608,7 +611,8 @@ Future<Null> buildAndroidWithGradle(
Future<Null> buildApk( Future<Null> buildApk(
TargetPlatform platform, { TargetPlatform platform, {
String target, String target,
BuildMode buildMode: BuildMode.debug BuildMode buildMode: BuildMode.debug,
bool applicationNeedsRebuild: false,
}) async { }) async {
if (isProjectUsingGradle()) { if (isProjectUsingGradle()) {
return await buildAndroidWithGradle( return await buildAndroidWithGradle(
...@@ -625,7 +629,8 @@ Future<Null> buildApk( ...@@ -625,7 +629,8 @@ Future<Null> buildApk(
platform, platform,
buildMode, buildMode,
force: false, force: false,
target: target target: target,
applicationNeedsRebuild: applicationNeedsRebuild,
); );
} }
} }
......
...@@ -61,6 +61,7 @@ class DependencyChecker { ...@@ -61,6 +61,7 @@ class DependencyChecker {
return true; return true;
} }
} }
printTrace('DependencyChecker: nothing is modified after $threshold.');
return false; return false;
} }
} }
...@@ -205,7 +205,8 @@ abstract class Device { ...@@ -205,7 +205,8 @@ abstract class Device {
String route, String route,
DebuggingOptions debuggingOptions, DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs, Map<String, dynamic> platformArgs,
bool prebuiltApplication: false bool prebuiltApplication: false,
bool applicationNeedsRebuild: false
}); });
/// Does this device implement support for hot reloading / restarting? /// Does this device implement support for hot reloading / restarting?
......
...@@ -9,13 +9,12 @@ import 'package:path/path.dart' as path; ...@@ -9,13 +9,12 @@ import 'package:path/path.dart' as path;
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'asset.dart';
import 'base/context.dart'; import 'base/context.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'dart/package_map.dart';
import 'dart/dependencies.dart'; import 'dart/dependencies.dart';
import 'devfs.dart'; import 'devfs.dart';
import 'device.dart'; import 'device.dart';
...@@ -50,26 +49,16 @@ class HotRunner extends ResidentRunner { ...@@ -50,26 +49,16 @@ class HotRunner extends ResidentRunner {
}) : super(device, }) : super(device,
target: target, target: target,
debuggingOptions: debuggingOptions, debuggingOptions: debuggingOptions,
usesTerminalUI: usesTerminalUI) { usesTerminalUI: usesTerminalUI,
_projectRootPath = projectRootPath ?? fs.currentDirectory.path; projectRootPath: projectRootPath,
_packagesFilePath = packagesFilePath: packagesFilePath,
packagesFilePath ?? path.absolute(PackageMap.globalPackagesPath); projectAssets: projectAssets);
if (projectAssets != null)
_bundle = new AssetBundle.fixed(_projectRootPath, projectAssets);
else
_bundle = new AssetBundle();
}
ApplicationPackage _package;
String _mainPath;
String _projectRootPath;
String _packagesFilePath;
final String applicationBinary; final String applicationBinary;
bool get prebuiltMode => applicationBinary != null; bool get prebuiltMode => applicationBinary != null;
Set<String> _dartDependencies; Set<String> _dartDependencies;
Uri _observatoryUri; Uri _observatoryUri;
AssetBundle _bundle;
AssetBundle get bundle => _bundle;
final bool benchmarkMode; final bool benchmarkMode;
final Map<String, int> benchmarkData = new Map<String, int>(); final Map<String, int> benchmarkData = new Map<String, int>();
// The initial launch is from a snapshot. // The initial launch is from a snapshot.
...@@ -83,7 +72,6 @@ class HotRunner extends ResidentRunner { ...@@ -83,7 +72,6 @@ class HotRunner extends ResidentRunner {
bool shouldBuild: true bool shouldBuild: true
}) { }) {
// Don't let uncaught errors kill the process. // Don't let uncaught errors kill the process.
assert(shouldBuild == !prebuiltMode);
return Chain.capture(() { return Chain.capture(() {
return _run( return _run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
...@@ -107,7 +95,7 @@ class HotRunner extends ResidentRunner { ...@@ -107,7 +95,7 @@ class HotRunner extends ResidentRunner {
} }
DartDependencySetBuilder dartDependencySetBuilder = DartDependencySetBuilder dartDependencySetBuilder =
new DartDependencySetBuilder( new DartDependencySetBuilder(
_mainPath, _projectRootPath, _packagesFilePath); mainPath, projectRootPath, packagesFilePath);
try { try {
Set<String> dependencies = dartDependencySetBuilder.build(); Set<String> dependencies = dartDependencySetBuilder.build();
_dartDependencies = new Set<String>(); _dartDependencies = new Set<String>();
...@@ -135,18 +123,17 @@ class HotRunner extends ResidentRunner { ...@@ -135,18 +123,17 @@ class HotRunner extends ResidentRunner {
String route, String route,
bool shouldBuild: true bool shouldBuild: true
}) async { }) async {
_mainPath = findMainDartFile(target); if (!fs.isFileSync(mainPath)) {
if (!fs.isFileSync(_mainPath)) { String message = 'Tried to run $mainPath, but that file does not exist.';
String message = 'Tried to run $_mainPath, but that file does not exist.';
if (target == null) if (target == null)
message += '\nConsider using the -t option to specify the Dart file to start.'; message += '\nConsider using the -t option to specify the Dart file to start.';
printError(message); printError(message);
return 1; return 1;
} }
_package = getApplicationPackageForPlatform(device.platform, applicationBinary: applicationBinary); package = getApplicationPackageForPlatform(device.platform, applicationBinary: applicationBinary);
if (_package == null) { if (package == null) {
String message = 'No application found for ${device.platform}.'; String message = 'No application found for ${device.platform}.';
String hint = getMissingPackageHintForPlatform(device.platform); String hint = getMissingPackageHintForPlatform(device.platform);
if (hint != null) if (hint != null)
...@@ -163,20 +150,21 @@ class HotRunner extends ResidentRunner { ...@@ -163,20 +150,21 @@ class HotRunner extends ResidentRunner {
Map<String, dynamic> platformArgs = new Map<String, dynamic>(); Map<String, dynamic> platformArgs = new Map<String, dynamic>();
await startEchoingDeviceLog(_package); await startEchoingDeviceLog(package);
String modeName = getModeName(debuggingOptions.buildMode); String modeName = getModeName(debuggingOptions.buildMode);
printStatus('Launching ${getDisplayPath(_mainPath)} on ${device.name} in $modeName mode...'); printStatus('Launching ${getDisplayPath(mainPath)} on ${device.name} in $modeName mode...');
// Start the application. // Start the application.
Future<LaunchResult> futureResult = device.startApp( Future<LaunchResult> futureResult = device.startApp(
_package, package,
debuggingOptions.buildMode, debuggingOptions.buildMode,
mainPath: _mainPath, mainPath: mainPath,
debuggingOptions: debuggingOptions, debuggingOptions: debuggingOptions,
platformArgs: platformArgs, platformArgs: platformArgs,
route: route, route: route,
prebuiltApplication: prebuiltMode prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies()
); );
LaunchResult result = await futureResult; LaunchResult result = await futureResult;
...@@ -264,11 +252,11 @@ class HotRunner extends ResidentRunner { ...@@ -264,11 +252,11 @@ class HotRunner extends ResidentRunner {
DevFS _devFS; DevFS _devFS;
Future<Uri> _initDevFS() { Future<Uri> _initDevFS() {
String fsName = path.basename(_projectRootPath); String fsName = path.basename(projectRootPath);
_devFS = new DevFS(vmService, _devFS = new DevFS(vmService,
fsName, fsName,
fs.directory(_projectRootPath), fs.directory(projectRootPath),
packagesFilePath: _packagesFilePath); packagesFilePath: packagesFilePath);
return _devFS.create(); return _devFS.create();
} }
...@@ -277,16 +265,16 @@ class HotRunner extends ResidentRunner { ...@@ -277,16 +265,16 @@ class HotRunner extends ResidentRunner {
// Did not update DevFS because of a Dart source error. // Did not update DevFS because of a Dart source error.
return false; return false;
} }
final bool rebuildBundle = bundle.needsBuild(); final bool rebuildBundle = assetBundle.needsBuild();
if (rebuildBundle) { if (rebuildBundle) {
printTrace('Updating assets'); printTrace('Updating assets');
int result = await bundle.build(); int result = await assetBundle.build();
if (result != 0) if (result != 0)
return false; return false;
} }
Status devFSStatus = logger.startProgress('Syncing files to device...'); Status devFSStatus = logger.startProgress('Syncing files to device...');
await _devFS.update(progressReporter: progressReporter, await _devFS.update(progressReporter: progressReporter,
bundle: bundle, bundle: assetBundle,
bundleDirty: rebuildBundle, bundleDirty: rebuildBundle,
fileFilter: _dartDependencies); fileFilter: _dartDependencies);
devFSStatus.stop(); devFSStatus.stop();
...@@ -329,7 +317,7 @@ class HotRunner extends ResidentRunner { ...@@ -329,7 +317,7 @@ class HotRunner extends ResidentRunner {
Future<Null> _launchFromDevFS(ApplicationPackage package, Future<Null> _launchFromDevFS(ApplicationPackage package,
String mainScript) async { String mainScript) async {
String entryPath = path.relative(mainScript, from: _projectRootPath); String entryPath = path.relative(mainScript, from: projectRootPath);
String deviceEntryPath = String deviceEntryPath =
_devFS.baseUri.resolve(entryPath).toFilePath(); _devFS.baseUri.resolve(entryPath).toFilePath();
String devicePackagesPath = String devicePackagesPath =
...@@ -347,7 +335,7 @@ class HotRunner extends ResidentRunner { ...@@ -347,7 +335,7 @@ class HotRunner extends ResidentRunner {
bool updatedDevFS = await _updateDevFS(); bool updatedDevFS = await _updateDevFS();
if (!updatedDevFS) if (!updatedDevFS)
return new OperationResult(1, 'Dart Source Error'); return new OperationResult(1, 'Dart Source Error');
await _launchFromDevFS(_package, _mainPath); await _launchFromDevFS(package, mainPath);
restartTimer.stop(); restartTimer.stop();
printTrace('Restart performed in ' printTrace('Restart performed in '
'${getElapsedAsMilliseconds(restartTimer.elapsed)}.'); '${getElapsedAsMilliseconds(restartTimer.elapsed)}.');
...@@ -440,7 +428,7 @@ class HotRunner extends ResidentRunner { ...@@ -440,7 +428,7 @@ class HotRunner extends ResidentRunner {
return new OperationResult(1, 'Dart Source Error'); return new OperationResult(1, 'Dart Source Error');
String reloadMessage; String reloadMessage;
try { try {
String entryPath = path.relative(_mainPath, from: _projectRootPath); String entryPath = path.relative(mainPath, from: projectRootPath);
String deviceEntryPath = String deviceEntryPath =
_devFS.baseUri.resolve(entryPath).toFilePath(); _devFS.baseUri.resolve(entryPath).toFilePath();
String devicePackagesPath = String devicePackagesPath =
......
...@@ -186,7 +186,8 @@ class IOSDevice extends Device { ...@@ -186,7 +186,8 @@ class IOSDevice extends Device {
String route, String route,
DebuggingOptions debuggingOptions, DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs, Map<String, dynamic> platformArgs,
bool prebuiltApplication: false bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
}) async { }) async {
if (!prebuiltApplication) { if (!prebuiltApplication) {
// TODO(chinmaygarde): Use checked, mainPath, route. // TODO(chinmaygarde): Use checked, mainPath, route.
......
...@@ -416,7 +416,8 @@ class IOSSimulator extends Device { ...@@ -416,7 +416,8 @@ class IOSSimulator extends Device {
String route, String route,
DebuggingOptions debuggingOptions, DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs, Map<String, dynamic> platformArgs,
bool prebuiltApplication: false bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
}) async { }) async {
if (!prebuiltApplication) { if (!prebuiltApplication) {
printTrace('Building ${app.name} for $id.'); printTrace('Building ${app.name} for $id.');
......
...@@ -7,11 +7,19 @@ import 'dart:async'; ...@@ -7,11 +7,19 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'application_package.dart'; import 'application_package.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/io.dart'; import 'base/io.dart';
import 'asset.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'dart/dependencies.dart';
import 'dart/package_map.dart';
import 'dependency_checker.dart';
import 'device.dart'; import 'device.dart';
import 'globals.dart'; import 'globals.dart';
import 'vmservice.dart'; import 'vmservice.dart';
...@@ -21,14 +29,35 @@ abstract class ResidentRunner { ...@@ -21,14 +29,35 @@ abstract class ResidentRunner {
ResidentRunner(this.device, { ResidentRunner(this.device, {
this.target, this.target,
this.debuggingOptions, this.debuggingOptions,
this.usesTerminalUI: true this.usesTerminalUI: true,
}); String projectRootPath,
String packagesFilePath,
String projectAssets,
}) {
_mainPath = findMainDartFile(target);
_projectRootPath = projectRootPath ?? fs.currentDirectory;
_packagesFilePath =
packagesFilePath ?? path.absolute(PackageMap.globalPackagesPath);
if (projectAssets != null)
_assetBundle = new AssetBundle.fixed(_projectRootPath, projectAssets);
else
_assetBundle = new AssetBundle();
}
final Device device; final Device device;
final String target; final String target;
final DebuggingOptions debuggingOptions; final DebuggingOptions debuggingOptions;
final bool usesTerminalUI; final bool usesTerminalUI;
final Completer<int> _finished = new Completer<int>(); final Completer<int> _finished = new Completer<int>();
String _packagesFilePath;
String get packagesFilePath => _packagesFilePath;
String _projectRootPath;
String get projectRootPath => _projectRootPath;
String _mainPath;
String get mainPath => _mainPath;
AssetBundle _assetBundle;
AssetBundle get assetBundle => _assetBundle;
ApplicationPackage package;
bool get isRunningDebug => debuggingOptions.buildMode == BuildMode.debug; bool get isRunningDebug => debuggingOptions.buildMode == BuildMode.debug;
bool get isRunningProfile => debuggingOptions.buildMode == BuildMode.profile; bool get isRunningProfile => debuggingOptions.buildMode == BuildMode.profile;
...@@ -233,6 +262,27 @@ abstract class ResidentRunner { ...@@ -233,6 +262,27 @@ abstract class ResidentRunner {
return exitCode; return exitCode;
} }
bool hasDirtyDependencies() {
DartDependencySetBuilder dartDependencySetBuilder =
new DartDependencySetBuilder(
mainPath, projectRootPath, packagesFilePath);
DependencyChecker dependencyChecker =
new DependencyChecker(dartDependencySetBuilder, assetBundle);
String path = package.packagePath;
if (path == null) {
return true;
}
final FileStat stat = fs.file(path).statSync();
if (stat.type != FileSystemEntityType.FILE) {
return true;
}
if (!fs.file(path).existsSync()) {
return true;
}
final DateTime lastBuildTime = stat.modified;
return dependencyChecker.check(lastBuildTime);
}
Future<Null> preStop() async { } Future<Null> preStop() async { }
Future<Null> stopApp() async { Future<Null> stopApp() async {
......
...@@ -29,8 +29,6 @@ class RunAndStayResident extends ResidentRunner { ...@@ -29,8 +29,6 @@ class RunAndStayResident extends ResidentRunner {
debuggingOptions: debuggingOptions, debuggingOptions: debuggingOptions,
usesTerminalUI: usesTerminalUI); usesTerminalUI: usesTerminalUI);
ApplicationPackage _package;
String _mainPath;
LaunchResult _result; LaunchResult _result;
final bool traceStartup; final bool traceStartup;
final String applicationBinary; final String applicationBinary;
...@@ -46,7 +44,6 @@ class RunAndStayResident extends ResidentRunner { ...@@ -46,7 +44,6 @@ class RunAndStayResident extends ResidentRunner {
}) { }) {
// Don't let uncaught errors kill the process. // Don't let uncaught errors kill the process.
return Chain.capture(() { return Chain.capture(() {
assert(shouldBuild == !prebuiltMode);
return _run( return _run(
traceStartup: traceStartup, traceStartup: traceStartup,
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
...@@ -67,9 +64,8 @@ class RunAndStayResident extends ResidentRunner { ...@@ -67,9 +64,8 @@ class RunAndStayResident extends ResidentRunner {
bool shouldBuild: true bool shouldBuild: true
}) async { }) async {
if (!prebuiltMode) { if (!prebuiltMode) {
_mainPath = findMainDartFile(target); if (!fs.isFileSync(mainPath)) {
if (!fs.isFileSync(_mainPath)) { String message = 'Tried to run $mainPath, but that file does not exist.';
String message = 'Tried to run $_mainPath, but that file does not exist.';
if (target == null) if (target == null)
message += '\nConsider using the -t option to specify the Dart file to start.'; message += '\nConsider using the -t option to specify the Dart file to start.';
printError(message); printError(message);
...@@ -77,9 +73,9 @@ class RunAndStayResident extends ResidentRunner { ...@@ -77,9 +73,9 @@ class RunAndStayResident extends ResidentRunner {
} }
} }
_package = getApplicationPackageForPlatform(device.platform, applicationBinary: applicationBinary); package = getApplicationPackageForPlatform(device.platform, applicationBinary: applicationBinary);
if (_package == null) { if (package == null) {
String message = 'No application found for ${device.platform}.'; String message = 'No application found for ${device.platform}.';
String hint = getMissingPackageHintForPlatform(device.platform); String hint = getMissingPackageHintForPlatform(device.platform);
if (hint != null) if (hint != null)
...@@ -94,24 +90,25 @@ class RunAndStayResident extends ResidentRunner { ...@@ -94,24 +90,25 @@ class RunAndStayResident extends ResidentRunner {
if (traceStartup != null) if (traceStartup != null)
platformArgs = <String, dynamic>{ 'trace-startup': traceStartup }; platformArgs = <String, dynamic>{ 'trace-startup': traceStartup };
await startEchoingDeviceLog(_package); await startEchoingDeviceLog(package);
String modeName = getModeName(debuggingOptions.buildMode); String modeName = getModeName(debuggingOptions.buildMode);
if (_mainPath == null) { if (mainPath == null) {
assert(prebuiltMode); assert(prebuiltMode);
printStatus('Launching ${_package.displayName} on ${device.name} in $modeName mode...'); printStatus('Launching ${package.displayName} on ${device.name} in $modeName mode...');
} else { } else {
printStatus('Launching ${getDisplayPath(_mainPath)} on ${device.name} in $modeName mode...'); printStatus('Launching ${getDisplayPath(mainPath)} on ${device.name} in $modeName mode...');
} }
_result = await device.startApp( _result = await device.startApp(
_package, package,
debuggingOptions.buildMode, debuggingOptions.buildMode,
mainPath: _mainPath, mainPath: mainPath,
debuggingOptions: debuggingOptions, debuggingOptions: debuggingOptions,
platformArgs: platformArgs, platformArgs: platformArgs,
route: route, route: route,
prebuiltApplication: prebuiltMode prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies()
); );
if (!_result.started) { if (!_result.started) {
...@@ -195,6 +192,6 @@ class RunAndStayResident extends ResidentRunner { ...@@ -195,6 +192,6 @@ class RunAndStayResident extends ResidentRunner {
Future<Null> preStop() async { Future<Null> preStop() async {
// If we're running in release mode, stop the app using the device logic. // If we're running in release mode, stop the app using the device logic.
if (vmService == null) if (vmService == null)
await device.stopApp(_package); await device.stopApp(package);
} }
} }
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