Commit 1328562e authored by Devon Carew's avatar Devon Carew

Merge pull request #1656 from devoncarew/gen_refactoring

General flutter_tools refactoring
parents 01a5b837 9e6d45cb
......@@ -2,16 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library flutter_tools;
import 'dart:async';
import 'package:archive/archive.dart';
import 'src/flx.dart' as flx;
/// Assembles a Flutter .flx file from a pre-existing manifest descriptor
/// and a pre-compiled snapshot.
/// Assembles a Flutter .flx file from a pre-existing manifest descriptor and a
/// pre-compiled snapshot.
Future<int> assembleFlx({
Map manifestDescriptor: const {},
ArchiveFile snapshotFile: null,
......
......@@ -19,7 +19,7 @@ class Adb {
final String adbPath;
Map<String, String> _idToNameCache = <String, String>{};
final Map<String, String> _idToNameCache = <String, String>{};
bool exists() {
try {
......
......@@ -35,36 +35,16 @@ class AndroidDeviceDiscovery extends DeviceDiscovery {
}
class AndroidDevice extends Device {
static final String defaultDeviceID = 'default_android_device';
String productID;
String modelID;
String deviceCodeName;
bool _connected;
String _adbPath;
String get adbPath => _adbPath;
bool _hasAdb = false;
bool _hasValidAndroid = false;
factory AndroidDevice({
String id: null,
String productID: null,
String modelID: null,
String deviceCodeName: null,
AndroidDevice(
String id, {
this.productID,
this.modelID,
this.deviceCodeName,
bool connected
}) {
AndroidDevice device = Device.unique(id ?? defaultDeviceID, (String id) => new AndroidDevice.fromId(id));
device.productID = productID;
device.modelID = modelID;
device.deviceCodeName = deviceCodeName;
}) : super(id) {
if (connected != null)
device._connected = connected;
return device;
}
_connected = connected;
/// This constructor is intended as protected access; prefer [AndroidDevice].
AndroidDevice.fromId(id) : super.fromId(id) {
_adbPath = getAdbPath();
_hasAdb = _checkForAdb();
......@@ -78,6 +58,16 @@ class AndroidDevice extends Device {
}
}
final String productID;
final String modelID;
final String deviceCodeName;
bool _connected;
String _adbPath;
String get adbPath => _adbPath;
bool _hasAdb = false;
bool _hasValidAndroid = false;
static String getAndroidSdkPath() {
if (Platform.environment.containsKey('ANDROID_HOME')) {
String androidHomeDir = Platform.environment['ANDROID_HOME'];
......@@ -98,12 +88,7 @@ class AndroidDevice extends Device {
}
List<String> adbCommandForDevice(List<String> args) {
List<String> result = <String>[adbPath];
if (id != defaultDeviceID) {
result.addAll(['-s', id]);
}
result.addAll(args);
return result;
return <String>[adbPath, '-s', id]..addAll(args);
}
bool _isValidAdbVersion(String adbVersion) {
......@@ -132,12 +117,12 @@ class AndroidDevice extends Device {
bool _checkForAdb() {
try {
String adbVersion = runCheckedSync([adbPath, 'version']);
String adbVersion = runCheckedSync(<String>[adbPath, 'version']);
if (_isValidAdbVersion(adbVersion)) {
return true;
}
String locatedAdbPath = runCheckedSync(['which', 'adb']);
String locatedAdbPath = runCheckedSync(<String>['which', 'adb']);
printError('"$locatedAdbPath" is too old. '
'Please install version 1.0.32 or later.\n'
'Try setting ANDROID_HOME to the path to your Android SDK install. '
......@@ -157,18 +142,18 @@ class AndroidDevice extends Device {
// output lines like this, which we want to ignore:
// adb server is out of date. killing..
// * daemon started successfully *
runCheckedSync(adbCommandForDevice(['start-server']));
runCheckedSync(adbCommandForDevice(<String>['start-server']));
String ready = runSync(adbCommandForDevice(['shell', 'echo', 'ready']));
String ready = runSync(adbCommandForDevice(<String>['shell', 'echo', 'ready']));
if (ready.trim() != 'ready') {
printTrace('Android device not found.');
return false;
}
// Sample output: '22'
String sdkVersion =
runCheckedSync(adbCommandForDevice(['shell', 'getprop', 'ro.build.version.sdk']))
.trimRight();
String sdkVersion = runCheckedSync(
adbCommandForDevice(<String>['shell', 'getprop', 'ro.build.version.sdk'])
).trimRight();
int sdkVersionParsed =
int.parse(sdkVersion, onError: (String source) => null);
......@@ -194,7 +179,7 @@ class AndroidDevice extends Device {
}
String _getDeviceApkSha1(ApplicationPackage app) {
return runCheckedSync(adbCommandForDevice(['shell', 'cat', _getDeviceSha1Path(app)]));
return runCheckedSync(adbCommandForDevice(<String>['shell', 'cat', _getDeviceSha1Path(app)]));
}
String _getSourceSha1(ApplicationPackage app) {
......@@ -208,10 +193,10 @@ class AndroidDevice extends Device {
@override
bool isAppInstalled(ApplicationPackage app) {
if (!isConnected()) {
if (!isConnected())
return false;
}
if (runCheckedSync(adbCommandForDevice(['shell', 'pm', 'path', app.id])) == '') {
if (runCheckedSync(adbCommandForDevice(<String>['shell', 'pm', 'path', app.id])) == '') {
printTrace('TODO(iansf): move this log to the caller. ${app.name} is not on the device. Installing now...');
return false;
}
......@@ -284,21 +269,21 @@ class AndroidDevice extends Device {
this.clearLogs();
String deviceTmpPath = '/data/local/tmp/dev.flx';
runCheckedSync(adbCommandForDevice(['push', bundlePath, deviceTmpPath]));
List<String> cmd = adbCommandForDevice([
runCheckedSync(adbCommandForDevice(<String>['push', bundlePath, deviceTmpPath]));
List<String> cmd = adbCommandForDevice(<String>[
'shell', 'am', 'start',
'-a', 'android.intent.action.RUN',
'-d', deviceTmpPath,
'-f', '0x20000000', // FLAG_ACTIVITY_SINGLE_TOP
]);
if (checked)
cmd.addAll(['--ez', 'enable-checked-mode', 'true']);
cmd.addAll(<String>['--ez', 'enable-checked-mode', 'true']);
if (traceStartup)
cmd.addAll(['--ez', 'trace-startup', 'true']);
cmd.addAll(<String>['--ez', 'trace-startup', 'true']);
if (startPaused)
cmd.addAll(['--ez', 'start-paused', 'true']);
cmd.addAll(<String>['--ez', 'start-paused', 'true']);
if (route != null)
cmd.addAll(['--es', 'route', route]);
cmd.addAll(<String>['--es', 'route', route]);
cmd.add(apk.launchActivity);
runCheckedSync(cmd);
return true;
......@@ -345,7 +330,7 @@ class AndroidDevice extends Device {
Future<bool> stopApp(ApplicationPackage app) async {
final AndroidApk apk = app;
runSync(adbCommandForDevice(['shell', 'am', 'force-stop', apk.id]));
runSync(adbCommandForDevice(<String>['shell', 'am', 'force-stop', apk.id]));
return true;
}
......@@ -353,13 +338,13 @@ class AndroidDevice extends Device {
TargetPlatform get platform => TargetPlatform.android;
void clearLogs() {
runSync(adbCommandForDevice(['logcat', '-c']));
runSync(adbCommandForDevice(<String>['logcat', '-c']));
}
DeviceLogReader createLogReader() => new _AdbLogReader(this);
void startTracing(AndroidApk apk) {
runCheckedSync(adbCommandForDevice([
runCheckedSync(adbCommandForDevice(<String>[
'shell',
'am',
'broadcast',
......@@ -371,18 +356,18 @@ class AndroidDevice extends Device {
// Return the most recent timestamp in the Android log. The format can be
// passed to logcat's -T option.
String lastLogcatTimestamp() {
String output = runCheckedSync(adbCommandForDevice(['logcat', '-v', 'time', '-t', '1']));
String output = runCheckedSync(adbCommandForDevice(<String>['logcat', '-v', 'time', '-t', '1']));
RegExp timeRegExp = new RegExp(r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}', multiLine: true);
Match timeMatch = timeRegExp.firstMatch(output);
return timeMatch[0];
}
Future<String> stopTracing(AndroidApk apk, { String outPath: null }) async {
Future<String> stopTracing(AndroidApk apk, { String outPath }) async {
// Workaround for logcat -c not always working:
// http://stackoverflow.com/questions/25645012/logcat-on-android-l-not-clearing-after-unplugging-and-reconnecting
String beforeStop = lastLogcatTimestamp();
runCheckedSync(adbCommandForDevice([
runCheckedSync(adbCommandForDevice(<String>[
'shell',
'am',
'broadcast',
......@@ -396,7 +381,7 @@ class AndroidDevice extends Device {
String tracePath = null;
bool isComplete = false;
while (!isComplete) {
String logs = runCheckedSync(adbCommandForDevice(['logcat', '-d', '-T', beforeStop]));
String logs = runCheckedSync(adbCommandForDevice(<String>['logcat', '-d', '-T', beforeStop]));
Match fileMatch = traceRegExp.firstMatch(logs);
if (fileMatch != null && fileMatch[1] != null) {
tracePath = fileMatch[1];
......@@ -430,7 +415,7 @@ class AndroidDevice extends Device {
return null;
}
bool isConnected() => _connected != null ? _connected : _hasValidAndroid;
bool isConnected() => _connected ?? _hasValidAndroid;
void setConnected(bool value) {
_connected = value;
......@@ -444,13 +429,13 @@ List<AndroidDevice> getAdbDevices([AndroidDevice mockAndroid]) {
String adbPath = (mockAndroid != null) ? mockAndroid.adbPath : getAdbPath();
try {
runCheckedSync([adbPath, 'version']);
runCheckedSync(<String>[adbPath, 'version']);
} catch (e) {
printError('Unable to find adb. Is "adb" in your path?');
return devices;
}
List<String> output = runSync([adbPath, 'devices', '-l']).trim().split('\n');
List<String> output = runSync(<String>[adbPath, 'devices', '-l']).trim().split('\n');
// 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper
RegExp deviceRegex1 = new RegExp(
......@@ -461,7 +446,7 @@ List<AndroidDevice> getAdbDevices([AndroidDevice mockAndroid]) {
RegExp unauthorizedRegex = new RegExp(r'^(\S+)\s+unauthorized\s+\S+$');
RegExp offlineRegex = new RegExp(r'^(\S+)\s+offline\s+\S+$');
// Skip first line, which is always 'List of devices attached'.
// Skip the first line, which is always 'List of devices attached'.
for (String line in output.skip(1)) {
// Skip lines like:
// * daemon not running. starting it now on port 5037 *
......@@ -484,15 +469,16 @@ List<AndroidDevice> getAdbDevices([AndroidDevice mockAndroid]) {
modelID = modelID.replaceAll('_', ' ');
devices.add(new AndroidDevice(
id: deviceID,
deviceID,
productID: productID,
modelID: modelID,
deviceCodeName: deviceCodeName
deviceCodeName: deviceCodeName,
connected: true
));
} else if (deviceRegex2.hasMatch(line)) {
Match match = deviceRegex2.firstMatch(line);
String deviceID = match[1];
devices.add(new AndroidDevice(id: deviceID));
devices.add(new AndroidDevice(deviceID, connected: true));
} else if (unauthorizedRegex.hasMatch(line)) {
Match match = unauthorizedRegex.firstMatch(line);
String deviceID = match[1];
......
......@@ -366,7 +366,7 @@ int _buildApk(
ensureDirectoryExists(finalApk.path);
builder.align(unalignedApk, finalApk);
printStatus('APK generated: ${finalApk.path}');
printStatus('Generated APK to ${finalApk.path}.');
return 0;
} finally {
......@@ -520,7 +520,7 @@ Future<ApplicationPackageStore> buildAll(
// TODO(mpcomplete): Temporary hack. We only support the apk builder atm.
if (package == applicationPackages.android) {
if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) {
printStatus('Using pre-built SkyShell.apk');
printStatus('Using pre-built SkyShell.apk.');
continue;
}
......
......@@ -362,7 +362,7 @@ class AndroidDeviceDiscovery {
if (androidDevice == null) {
// device added
androidDevice = new AndroidDevice(
id: device.id,
device.id,
productID: device.productID,
modelID: device.modelID,
deviceCodeName: device.deviceCodeName,
......@@ -385,11 +385,6 @@ class AndroidDeviceDiscovery {
for (AndroidDevice device in currentDevices) {
_devices.remove(device.id);
// I don't know the purpose of this cache or if it's a good idea. We should
// probably have a DeviceManager singleton class to coordinate known devices
// and different device discovery mechanisms.
Device.removeFromCache(device.id);
removedController.add(device);
}
}
......@@ -424,7 +419,7 @@ class IOSSimulatorDeviceDiscovery {
if (androidDevice == null) {
// device added
androidDevice = new IOSSimulator(id: device.udid, name: device.name);
androidDevice = new IOSSimulator(device.udid, name: device.name);
_devices[androidDevice.id] = androidDevice;
addedController.add(androidDevice);
} else {
......@@ -436,8 +431,6 @@ class IOSSimulatorDeviceDiscovery {
for (IOSSimulator device in currentDevices) {
_devices.remove(device.id);
Device.removeFromCache(device.id);
removedController.add(device);
}
}
......
......@@ -66,18 +66,9 @@ abstract class DeviceDiscovery {
}
abstract class Device {
final String id;
static Map<String, Device> _deviceCache = {};
static Device unique(String id, Device constructor(String id)) {
return _deviceCache.putIfAbsent(id, () => constructor(id));
}
static void removeFromCache(String id) {
_deviceCache.remove(id);
}
Device(this.id);
Device.fromId(this.id);
final String id;
String get name;
......@@ -133,6 +124,12 @@ abstract class DeviceLogReader {
// TODO(devoncarew): Unify this with [DeviceManager].
class DeviceStore {
DeviceStore({
this.android,
this.iOS,
this.iOSSimulator
});
final AndroidDevice android;
final IOSDevice iOS;
final IOSSimulator iOSSimulator;
......@@ -148,12 +145,6 @@ class DeviceStore {
return result;
}
DeviceStore({
this.android,
this.iOS,
this.iOSSimulator
});
static Device _deviceForConfig(BuildConfiguration config, List<Device> devices) {
Device device = null;
......@@ -194,10 +185,6 @@ class DeviceStore {
case TargetPlatform.iOSSimulator:
assert(iOSSimulator == null);
iOSSimulator = _deviceForConfig(config, IOSSimulator.getAttachedDevices());
if (iOSSimulator == null) {
// Creates a simulator with the default identifier
iOSSimulator = new IOSSimulator();
}
break;
case TargetPlatform.mac:
case TargetPlatform.linux:
......
......@@ -33,8 +33,8 @@ Map<String, double> _kIconDensities = {
'xxhdpi' : 3.0,
'xxxhdpi' : 4.0
};
const List<String> _kThemes = const ['white', 'black'];
const List<int> _kSizes = const [18, 24, 36, 48];
const List<String> _kThemes = const <String>['white', 'black'];
const List<int> _kSizes = const <int>[18, 24, 36, 48];
class _Asset {
final String source;
......
......@@ -47,7 +47,19 @@ class IOSSimulatorDiscovery extends DeviceDiscovery {
}
class IOSDevice extends Device {
static final String defaultDeviceID = 'default_ios_id';
IOSDevice(String id, { this.name }) : super(id) {
_installerPath = _checkForCommand('ideviceinstaller');
_listerPath = _checkForCommand('idevice_id');
_informerPath = _checkForCommand('ideviceinfo');
_debuggerPath = _checkForCommand('idevicedebug');
_loggerPath = _checkForCommand('idevicesyslog');
_pusherPath = _checkForCommand(
'ios-deploy',
'To copy files to iOS devices, please install ios-deploy. '
'You can do this using homebrew as follows:\n'
'\$ brew tap flutter/flutter\n'
'\$ brew install ios-deploy');
}
String _installerPath;
String get installerPath => _installerPath;
......@@ -67,50 +79,25 @@ class IOSDevice extends Device {
String _pusherPath;
String get pusherPath => _pusherPath;
String _name;
String get name => _name;
factory IOSDevice({String id, String name}) {
IOSDevice device = Device.unique(id ?? defaultDeviceID, (String id) => new IOSDevice.fromId(id));
device._name = name;
return device;
}
IOSDevice.fromId(String id) : super.fromId(id) {
_installerPath = _checkForCommand('ideviceinstaller');
_listerPath = _checkForCommand('idevice_id');
_informerPath = _checkForCommand('ideviceinfo');
_debuggerPath = _checkForCommand('idevicedebug');
_loggerPath = _checkForCommand('idevicesyslog');
_pusherPath = _checkForCommand(
'ios-deploy',
'To copy files to iOS devices, please install ios-deploy. '
'You can do this using homebrew as follows:\n'
'\$ brew tap flutter/flutter\n'
'\$ brew install ios-deploy');
}
final String name;
static List<IOSDevice> getAttachedDevices([IOSDevice mockIOS]) {
List<IOSDevice> devices = [];
for (String id in _getAttachedDeviceIDs(mockIOS)) {
String name = _getDeviceName(id, mockIOS);
devices.add(new IOSDevice(id: id, name: name));
devices.add(new IOSDevice(id, name: name));
}
return devices;
}
static Iterable<String> _getAttachedDeviceIDs([IOSDevice mockIOS]) {
String listerPath =
(mockIOS != null) ? mockIOS.listerPath : _checkForCommand('idevice_id');
String output;
String listerPath = (mockIOS != null) ? mockIOS.listerPath : _checkForCommand('idevice_id');
try {
output = runSync([listerPath, '-l']);
String output = runSync([listerPath, '-l']);
return output.trim().split('\n').where((String s) => s != null && s.isNotEmpty);
} catch (e) {
return [];
return <String>[];
}
return output.trim()
.split('\n')
.where((String s) => s != null && s.length > 0);
}
static String _getDeviceName(String deviceID, [IOSDevice mockIOS]) {
......@@ -142,11 +129,7 @@ class IOSDevice extends Device {
@override
bool installApp(ApplicationPackage app) {
try {
if (id == defaultDeviceID) {
runCheckedSync([installerPath, '-i', app.localPath]);
} else {
runCheckedSync([installerPath, '-u', id, '-i', app.localPath]);
}
return true;
} catch (e) {
return false;
......@@ -155,15 +138,7 @@ class IOSDevice extends Device {
}
@override
bool isConnected() {
Iterable<String> ids = _getAttachedDeviceIDs();
for (String id in ids) {
if (id == this.id || this.id == defaultDeviceID) {
return true;
}
}
return false;
}
bool isConnected() => _getAttachedDeviceIDs().contains(id);
@override
bool isAppInstalled(ApplicationPackage app) {
......@@ -238,7 +213,7 @@ class IOSDevice extends Device {
Future<bool> pushFile(ApplicationPackage app, String localFile, String targetFile) async {
if (Platform.isMacOS) {
runSync([
runSync(<String>[
pusherPath,
'-t',
'1',
......@@ -263,22 +238,15 @@ class IOSDevice extends Device {
}
class IOSSimulator extends Device {
factory IOSSimulator({String id, String name}) {
IOSSimulator device = Device.unique(id, (String id) => new IOSSimulator.fromId(id));
device._name = name;
return device;
}
IOSSimulator(String id, { this.name }) : super(id);
static List<IOSSimulator> getAttachedDevices() {
return SimControl.getConnectedDevices().map((SimDevice device) {
return new IOSSimulator(id: device.udid, name: device.name);
return new IOSSimulator(device.udid, name: device.name);
}).toList();
}
IOSSimulator.fromId(String id) : super.fromId(id);
String _name;
String get name => _name;
final String name;
String get xcrunPath => path.join('/usr', 'bin', 'xcrun');
......@@ -378,7 +346,7 @@ class IOSSimulator extends Device {
ApplicationPackage app, String localFile, String targetFile) async {
if (Platform.isMacOS) {
String simulatorHomeDirectory = _getSimulatorAppHomeDirectory(app);
runCheckedSync(['cp', localFile, path.join(simulatorHomeDirectory, targetFile)]);
runCheckedSync(<String>['cp', localFile, path.join(simulatorHomeDirectory, targetFile)]);
return true;
}
return false;
......@@ -413,7 +381,7 @@ class _IOSDeviceLogReader extends DeviceLogReader {
return 2;
return await runCommandAndStreamOutput(
[device.loggerPath],
<String>[device.loggerPath],
prefix: '[$name] ',
filter: new RegExp(r'(FlutterRunner|flutter.runner.Runner)')
);
......@@ -512,7 +480,7 @@ bool _checkXcodeVersion() {
if (!Platform.isMacOS)
return false;
try {
String version = runCheckedSync(['xcodebuild', '-version']);
String version = runCheckedSync(<String>['xcodebuild', '-version']);
Match match = _xcodeVersionRegExp.firstMatch(version);
if (int.parse(match[1]) < 7) {
printError('Found "${match[0]}". $_xcodeRequirement');
......@@ -534,12 +502,12 @@ Future<bool> _buildIOSXcodeProject(ApplicationPackage app, bool isDevice) async
if (!_checkXcodeVersion())
return false;
List<String> commands = [
List<String> commands = <String>[
'/usr/bin/env', 'xcrun', 'xcodebuild', '-target', 'Runner', '-configuration', 'Release'
];
if (!isDevice) {
commands.addAll(['-sdk', 'iphonesimulator']);
commands.addAll(<String>['-sdk', 'iphonesimulator']);
}
try {
......
......@@ -9,22 +9,10 @@ main() => defineTests();
defineTests() {
group('android_device', () {
test('uses the correct default ID', () {
AndroidDevice android = new AndroidDevice();
expect(android.id, equals(AndroidDevice.defaultDeviceID));
});
test('stores the requested id', () {
String deviceId = '1234';
AndroidDevice android = new AndroidDevice(id: deviceId);
expect(android.id, equals(deviceId));
});
test('correctly creates only one of each requested device id', () {
String deviceID = '1234';
AndroidDevice a1 = new AndroidDevice(id: deviceID);
AndroidDevice a2 = new AndroidDevice(id: deviceID);
expect(a1, equals(a2));
AndroidDevice device = new AndroidDevice(deviceId);
expect(device.id, equals(deviceId));
});
});
}
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