Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
4ec96084
Commit
4ec96084
authored
Feb 21, 2016
by
Devon Carew
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2066 from devoncarew/device_polling_redux
add the ability to start and stop device polling
parents
9e56fc4e
67046f93
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
1098 additions
and
362 deletions
+1098
-362
adb.dart
packages/flutter_tools/lib/src/android/adb.dart
+1
-1
android_device.dart
packages/flutter_tools/lib/src/android/android_device.dart
+3
-9
utils.dart
packages/flutter_tools/lib/src/base/utils.dart
+48
-0
daemon.dart
packages/flutter_tools/lib/src/commands/daemon.dart
+39
-146
install.dart
packages/flutter_tools/lib/src/commands/install.dart
+1
-1
trace.dart
packages/flutter_tools/lib/src/commands/trace.dart
+1
-1
device.dart
packages/flutter_tools/lib/src/device.dart
+68
-28
devices.dart
packages/flutter_tools/lib/src/ios/devices.dart
+255
-0
mac.dart
packages/flutter_tools/lib/src/ios/mac.dart
+147
-0
simulator.dart
packages/flutter_tools/lib/src/ios/simulator.dart
+0
-173
simulators.dart
packages/flutter_tools/lib/src/ios/simulators.dart
+488
-0
android_device_test.dart
packages/flutter_tools/test/android_device_test.dart
+1
-1
base_utils_test.dart
packages/flutter_tools/test/base_utils_test.dart
+37
-0
daemon_test.dart
packages/flutter_tools/test/daemon_test.dart
+6
-0
mocks.dart
packages/flutter_tools/test/src/mocks.dart
+3
-2
No files found.
packages/flutter_tools/lib/src/android/adb.dart
View file @
4ec96084
...
...
@@ -74,7 +74,7 @@ class Adb {
).
toList
();
}
/// Listen to device activations and deactivations via the a
s
b server's
/// Listen to device activations and deactivations via the a
d
b server's
/// 'track-devices' command. Call cancel on the returned stream to stop
/// listening.
Stream
<
List
<
AdbDevice
>>
trackDevices
()
{
...
...
packages/flutter_tools/lib/src/android/
device_android
.dart
→
packages/flutter_tools/lib/src/android/
android_device
.dart
View file @
4ec96084
...
...
@@ -29,17 +29,11 @@ const String _deviceBundlePath = '/data/local/tmp/dev.flx';
// Path where the snapshot will be copied on the device.
const
String
_deviceSnapshotPath
=
'/data/local/tmp/dev_snapshot.bin'
;
class
AndroidDevice
Discovery
extends
DeviceDiscovery
{
List
<
Device
>
_devices
=
<
Device
>[]
;
class
AndroidDevice
s
extends
Polling
DeviceDiscovery
{
AndroidDevices
()
:
super
(
'AndroidDevices'
)
;
bool
get
supportsPlatform
=>
true
;
Future
init
()
{
_devices
=
getAdbDevices
();
return
new
Future
.
value
();
}
List
<
Device
>
get
devices
=>
_devices
;
List
<
Device
>
pollingGetDevices
()
=>
getAdbDevices
();
}
class
AndroidDevice
extends
Device
{
...
...
packages/flutter_tools/lib/src/base/utils.dart
0 → 100644
View file @
4ec96084
// Copyright 2016 The Chromium 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
'dart:async'
;
/// 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.
class
ItemListNotifier
<
T
>
{
ItemListNotifier
()
{
_items
=
new
Set
<
T
>();
}
ItemListNotifier
.
from
(
List
<
T
>
items
)
{
_items
=
new
Set
<
T
>.
from
(
items
);
}
Set
<
T
>
_items
;
StreamController
<
T
>
_addedController
=
new
StreamController
<
T
>.
broadcast
();
StreamController
<
T
>
_removedController
=
new
StreamController
<
T
>.
broadcast
();
Stream
<
T
>
get
onAdded
=>
_addedController
.
stream
;
Stream
<
T
>
get
onRemoved
=>
_removedController
.
stream
;
List
<
T
>
get
items
=>
_items
.
toList
();
void
updateWithNewList
(
List
<
T
>
updatedList
)
{
Set
<
T
>
updatedSet
=
new
Set
<
T
>.
from
(
updatedList
);
Set
<
T
>
addedItems
=
updatedSet
.
difference
(
_items
);
Set
<
T
>
removedItems
=
_items
.
difference
(
updatedSet
);
_items
=
updatedSet
;
for
(
T
item
in
addedItems
)
_addedController
.
add
(
item
);
for
(
T
item
in
removedItems
)
_removedController
.
add
(
item
);
}
/// Close the streams.
void
dispose
()
{
_addedController
.
close
();
_removedController
.
close
();
}
}
packages/flutter_tools/lib/src/commands/daemon.dart
View file @
4ec96084
...
...
@@ -6,15 +6,13 @@ import 'dart:async';
import
'dart:convert'
;
import
'dart:io'
;
import
'../android/adb.dart'
;
import
'../android/android_sdk.dart'
;
import
'../android/device_android.dart'
;
import
'../android/android_device.dart'
;
import
'../base/context.dart'
;
import
'../base/logger.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../ios/device
_io
s.dart'
;
import
'../ios/simulator.dart'
;
import
'../ios/devices.dart'
;
import
'../ios/simulator
s
.dart'
;
import
'../runner/flutter_command.dart'
;
import
'run.dart'
;
import
'stop.dart'
as
stop
;
...
...
@@ -126,10 +124,8 @@ class Daemon {
throw
'no domain for method:
$method
'
;
_domainMap
[
prefix
].
handleCommand
(
name
,
id
,
request
[
'params'
]);
}
catch
(
error
,
trace
)
{
}
catch
(
error
)
{
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
stderr
.
writeln
(
'error handling
$request
:
$error
'
);
stderr
.
writeln
(
trace
);
}
}
...
...
@@ -170,8 +166,6 @@ abstract class Domain {
}
}).
catchError
((
error
,
trace
)
{
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
stderr
.
writeln
(
"error handling '
$name
.
$command
':
$error
"
);
stderr
.
writeln
(
trace
);
});
}
...
...
@@ -286,166 +280,65 @@ class AppDomain extends Domain {
/// This domain lets callers list and monitor connected devices.
///
/// It exports a `getDevices()` call, as well as firing `device.added`
,
/// `device.removed`
, and `device.changed`
events.
/// It exports a `getDevices()` call, as well as firing `device.added`
and
/// `device.removed` events.
class
DeviceDomain
extends
Domain
{
DeviceDomain
(
Daemon
daemon
)
:
super
(
daemon
,
'device'
)
{
registerHandler
(
'getDevices'
,
getDevices
);
registerHandler
(
'enable'
,
enable
);
registerHandler
(
'disable'
,
disable
);
_androidDeviceDiscovery
=
new
AndroidDeviceDiscovery
();
_androidDeviceDiscovery
.
onAdded
.
listen
((
Device
device
)
{
sendEvent
(
'device.added'
,
_deviceToMap
(
device
));
});
_androidDeviceDiscovery
.
onRemoved
.
listen
((
Device
device
)
{
sendEvent
(
'device.removed'
,
_deviceToMap
(
device
));
});
_androidDeviceDiscovery
.
onChanged
.
listen
((
Device
device
)
{
sendEvent
(
'device.changed'
,
_deviceToMap
(
device
));
});
PollingDeviceDiscovery
deviceDiscovery
=
new
AndroidDevices
();
if
(
deviceDiscovery
.
supportsPlatform
)
_discoverers
.
add
(
deviceDiscovery
);
if
(
Platform
.
isMacOS
)
{
_iosSimulatorDeviceDiscovery
=
new
IOSSimulatorDeviceDiscovery
();
_iosSimulatorDeviceDiscovery
.
onAdded
.
listen
((
Device
device
)
{
deviceDiscovery
=
new
IOSDevices
();
if
(
deviceDiscovery
.
supportsPlatform
)
_discoverers
.
add
(
deviceDiscovery
);
deviceDiscovery
=
new
IOSSimulators
();
if
(
deviceDiscovery
.
supportsPlatform
)
_discoverers
.
add
(
deviceDiscovery
);
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
discoverer
.
onAdded
.
listen
((
Device
device
)
{
sendEvent
(
'device.added'
,
_deviceToMap
(
device
));
});
_iosSimulatorDeviceDiscovery
.
onRemoved
.
listen
((
Device
device
)
{
discoverer
.
onRemoved
.
listen
((
Device
device
)
{
sendEvent
(
'device.removed'
,
_deviceToMap
(
device
));
});
}
}
AndroidDeviceDiscovery
_androidDeviceDiscovery
;
IOSSimulatorDeviceDiscovery
_iosSimulatorDeviceDiscovery
;
List
<
PollingDeviceDiscovery
>
_discoverers
=
<
PollingDeviceDiscovery
>[];
Future
<
List
<
Device
>>
getDevices
(
dynamic
args
)
{
List
<
Device
>
devices
=
<
Device
>[];
devices
.
addAll
(
_androidDeviceDiscovery
.
getDevices
());
if
(
_iosSimulatorDeviceDiscovery
!=
null
)
devices
.
addAll
(
_iosSimulatorDeviceDiscovery
.
getDevices
());
List
<
Device
>
devices
=
_discoverers
.
expand
((
PollingDeviceDiscovery
discoverer
)
{
return
discoverer
.
devices
;
}).
toList
();
return
new
Future
.
value
(
devices
);
}
void
dispose
()
{
_androidDeviceDiscovery
.
dispose
();
_iosSimulatorDeviceDiscovery
?.
dispose
();
}
}
class
AndroidDeviceDiscovery
{
AndroidDeviceDiscovery
()
{
_initAdb
();
if
(
_adb
!=
null
)
{
_subscription
=
_adb
.
trackDevices
().
listen
(
_handleUpdatedDevices
);
}
}
Adb
_adb
;
StreamSubscription
_subscription
;
Map
<
String
,
AndroidDevice
>
_devices
=
new
Map
<
String
,
AndroidDevice
>();
StreamController
<
Device
>
addedController
=
new
StreamController
<
Device
>.
broadcast
();
StreamController
<
Device
>
removedController
=
new
StreamController
<
Device
>.
broadcast
();
StreamController
<
Device
>
changedController
=
new
StreamController
<
Device
>.
broadcast
();
List
<
Device
>
getDevices
()
=>
_devices
.
values
.
toList
();
Stream
<
Device
>
get
onAdded
=>
addedController
.
stream
;
Stream
<
Device
>
get
onRemoved
=>
removedController
.
stream
;
Stream
<
Device
>
get
onChanged
=>
changedController
.
stream
;
void
_initAdb
()
{
if
(
_adb
==
null
)
{
_adb
=
new
Adb
(
getAdbPath
(
androidSdk
));
if
(!
_adb
.
exists
())
_adb
=
null
;
/// Enable device events.
Future
enable
(
dynamic
args
)
{
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
discoverer
.
startPolling
();
}
return
new
Future
.
value
();
}
void
_handleUpdatedDevices
(
List
<
AdbDevice
>
newDevices
)
{
List
<
AndroidDevice
>
currentDevices
=
new
List
.
from
(
getDevices
());
for
(
AdbDevice
device
in
newDevices
)
{
AndroidDevice
androidDevice
=
_devices
[
device
.
id
];
if
(
androidDevice
==
null
)
{
// device added
androidDevice
=
new
AndroidDevice
(
device
.
id
,
productID:
device
.
productID
,
modelID:
device
.
modelID
,
deviceCodeName:
device
.
deviceCodeName
,
connected:
device
.
isAvailable
);
_devices
[
androidDevice
.
id
]
=
androidDevice
;
addedController
.
add
(
androidDevice
);
}
else
{
currentDevices
.
remove
(
androidDevice
);
// check state
if
(
androidDevice
.
isConnected
()
!=
device
.
isAvailable
)
{
androidDevice
.
setConnected
(
device
.
isAvailable
);
changedController
.
add
(
androidDevice
);
}
}
}
// device removed
for
(
AndroidDevice
device
in
currentDevices
)
{
_devices
.
remove
(
device
.
id
);
removedController
.
add
(
device
);
/// Disable device events.
Future
disable
(
dynamic
args
)
{
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
discoverer
.
stopPolling
();
}
return
new
Future
.
value
();
}
void
dispose
()
{
_subscription
?.
cancel
();
}
}
class
IOSSimulatorDeviceDiscovery
{
IOSSimulatorDeviceDiscovery
()
{
_subscription
=
SimControl
.
trackDevices
().
listen
(
_handleUpdatedDevices
);
}
StreamSubscription
<
List
<
SimDevice
>>
_subscription
;
Map
<
String
,
IOSSimulator
>
_devices
=
new
Map
<
String
,
IOSSimulator
>();
StreamController
<
Device
>
addedController
=
new
StreamController
<
Device
>.
broadcast
();
StreamController
<
Device
>
removedController
=
new
StreamController
<
Device
>.
broadcast
();
List
<
Device
>
getDevices
()
=>
_devices
.
values
.
toList
();
Stream
<
Device
>
get
onAdded
=>
addedController
.
stream
;
Stream
<
Device
>
get
onRemoved
=>
removedController
.
stream
;
void
_handleUpdatedDevices
(
List
<
SimDevice
>
newDevices
)
{
List
<
IOSSimulator
>
currentDevices
=
new
List
.
from
(
getDevices
());
for
(
SimDevice
device
in
newDevices
)
{
IOSSimulator
androidDevice
=
_devices
[
device
.
udid
];
if
(
androidDevice
==
null
)
{
// device added
androidDevice
=
new
IOSSimulator
(
device
.
udid
,
name:
device
.
name
);
_devices
[
androidDevice
.
id
]
=
androidDevice
;
addedController
.
add
(
androidDevice
);
}
else
{
currentDevices
.
remove
(
androidDevice
);
}
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
discoverer
.
dispose
();
}
// device removed
for
(
IOSSimulator
device
in
currentDevices
)
{
_devices
.
remove
(
device
.
id
);
removedController
.
add
(
device
);
}
}
void
dispose
()
{
_subscription
?.
cancel
();
}
}
...
...
@@ -490,7 +383,7 @@ class NotifyingLogger extends Logger {
}
void
printTrace
(
String
message
)
{
_messageController
.
add
(
new
LogMessage
(
'trace'
,
message
));
// This is a lot of traffic to send over the wire.
}
}
...
...
packages/flutter_tools/lib/src/commands/install.dart
View file @
4ec96084
...
...
@@ -7,7 +7,7 @@ import 'dart:io';
import
'../application_package.dart'
;
import
'../device.dart'
;
import
'../ios/simulator.dart'
;
import
'../ios/simulator
s
.dart'
;
import
'../runner/flutter_command.dart'
;
class
InstallCommand
extends
FlutterCommand
{
...
...
packages/flutter_tools/lib/src/commands/trace.dart
View file @
4ec96084
...
...
@@ -4,7 +4,7 @@
import
'dart:async'
;
import
'../android/
device_android
.dart'
;
import
'../android/
android_device
.dart'
;
import
'../application_package.dart'
;
import
'../globals.dart'
;
import
'../runner/flutter_command.dart'
;
...
...
packages/flutter_tools/lib/src/device.dart
View file @
4ec96084
...
...
@@ -4,12 +4,14 @@
import
'dart:async'
;
import
'android/
device_android
.dart'
;
import
'android/
android_device
.dart'
;
import
'application_package.dart'
;
import
'base/common.dart'
;
import
'base/utils.dart'
;
import
'build_configuration.dart'
;
import
'globals.dart'
;
import
'ios/device_ios.dart'
;
import
'ios/devices.dart'
;
import
'ios/simulators.dart'
;
import
'toolchain.dart'
;
/// A class to get all available devices.
...
...
@@ -18,27 +20,9 @@ class DeviceManager {
/// of their methods are invoked.
DeviceManager
()
{
// Register the known discoverers.
_deviceDiscoverers
.
add
(
new
AndroidDeviceDiscovery
());
_deviceDiscoverers
.
add
(
new
IOSDeviceDiscovery
());
_deviceDiscoverers
.
add
(
new
IOSSimulatorDiscovery
());
}
Future
_init
()
{
if
(
_initedCompleter
==
null
)
{
_initedCompleter
=
new
Completer
();
Future
.
forEach
(
_deviceDiscoverers
,
(
DeviceDiscovery
discoverer
)
{
if
(!
discoverer
.
supportsPlatform
)
return
null
;
return
discoverer
.
init
();
}).
then
((
_
)
{
_initedCompleter
.
complete
();
}).
catchError
((
error
,
stackTrace
)
{
_initedCompleter
.
completeError
(
error
,
stackTrace
);
});
}
return
_initedCompleter
.
future
;
_deviceDiscoverers
.
add
(
new
AndroidDevices
());
_deviceDiscoverers
.
add
(
new
IOSDevices
());
_deviceDiscoverers
.
add
(
new
IOSSimulators
());
}
List
<
DeviceDiscovery
>
_deviceDiscoverers
=
<
DeviceDiscovery
>[];
...
...
@@ -46,8 +30,6 @@ class DeviceManager {
/// A user-specified device ID.
String
specifiedDeviceId
;
Completer
_initedCompleter
;
bool
get
hasSpecifiedDeviceId
=>
specifiedDeviceId
!=
null
;
/// Return the device with the matching ID; else, complete the Future with
...
...
@@ -75,8 +57,6 @@ class DeviceManager {
/// Return the list of all connected devices.
Future
<
List
<
Device
>>
getAllConnectedDevices
()
async
{
await
_init
();
return
_deviceDiscoverers
.
where
((
DeviceDiscovery
discoverer
)
=>
discoverer
.
supportsPlatform
)
.
expand
((
DeviceDiscovery
discoverer
)
=>
discoverer
.
devices
)
...
...
@@ -87,10 +67,60 @@ class DeviceManager {
/// An abstract class to discover and enumerate a specific type of devices.
abstract
class
DeviceDiscovery
{
bool
get
supportsPlatform
;
Future
init
();
List
<
Device
>
get
devices
;
}
/// A [DeviceDiscovery] implementation that uses polling to discover device adds
/// and removals.
abstract
class
PollingDeviceDiscovery
extends
DeviceDiscovery
{
PollingDeviceDiscovery
(
this
.
name
);
static
const
Duration
_pollingDuration
=
const
Duration
(
seconds:
4
);
final
String
name
;
ItemListNotifier
<
Device
>
_items
;
Timer
_timer
;
List
<
Device
>
pollingGetDevices
();
void
startPolling
()
{
if
(
_timer
==
null
)
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>();
_timer
=
new
Timer
.
periodic
(
_pollingDuration
,
(
Timer
timer
)
{
_items
.
updateWithNewList
(
pollingGetDevices
());
});
}
}
void
stopPolling
()
{
_timer
?.
cancel
();
_timer
=
null
;
}
List
<
Device
>
get
devices
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>.
from
(
pollingGetDevices
());
return
_items
.
items
;
}
Stream
<
Device
>
get
onAdded
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>();
return
_items
.
onAdded
;
}
Stream
<
Device
>
get
onRemoved
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>();
return
_items
.
onRemoved
;
}
void
dispose
()
=>
stopPolling
();
String
toString
()
=>
'
$name
device discovery'
;
}
abstract
class
Device
{
Device
(
this
.
id
);
...
...
@@ -139,6 +169,16 @@ abstract class Device {
/// Stop an app package on the current device.
Future
<
bool
>
stopApp
(
ApplicationPackage
app
);
int
get
hashCode
=>
id
.
hashCode
;
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
Device
)
return
false
;
return
id
==
other
.
id
;
}
String
toString
()
=>
'
$runtimeType
$id
'
;
}
...
...
packages/flutter_tools/lib/src/ios/devices.dart
0 → 100644
View file @
4ec96084
// Copyright 2016 The Chromium 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
'dart:async'
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'../application_package.dart'
;
import
'../base/common.dart'
;
import
'../base/process.dart'
;
import
'../build_configuration.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../toolchain.dart'
;
import
'mac.dart'
;
const
String
_ideviceinstallerInstructions
=
'To work with iOS devices, please install ideviceinstaller.
\n
'
'If you use homebrew, you can install it with "
\$
brew install ideviceinstaller".'
;
class
IOSDevices
extends
PollingDeviceDiscovery
{
IOSDevices
()
:
super
(
'IOSDevices'
);
bool
get
supportsPlatform
=>
Platform
.
isMacOS
;
List
<
Device
>
pollingGetDevices
()
=>
IOSDevice
.
getAttachedDevices
();
}
class
IOSDevice
extends
Device
{
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
;
String
_listerPath
;
String
get
listerPath
=>
_listerPath
;
String
_informerPath
;
String
get
informerPath
=>
_informerPath
;
String
_debuggerPath
;
String
get
debuggerPath
=>
_debuggerPath
;
String
_loggerPath
;
String
get
loggerPath
=>
_loggerPath
;
String
_pusherPath
;
String
get
pusherPath
=>
_pusherPath
;
final
String
name
;
bool
get
supportsStartPaused
=>
false
;
static
List
<
IOSDevice
>
getAttachedDevices
([
IOSDevice
mockIOS
])
{
if
(!
doctor
.
iosWorkflow
.
hasIdeviceId
)
return
<
IOSDevice
>[];
List
<
IOSDevice
>
devices
=
[];
for
(
String
id
in
_getAttachedDeviceIDs
(
mockIOS
))
{
String
name
=
_getDeviceName
(
id
,
mockIOS
);
devices
.
add
(
new
IOSDevice
(
id
,
name:
name
));
}
return
devices
;
}
static
Iterable
<
String
>
_getAttachedDeviceIDs
([
IOSDevice
mockIOS
])
{
String
listerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
listerPath
:
_checkForCommand
(
'idevice_id'
);
try
{
String
output
=
runSync
([
listerPath
,
'-l'
]);
return
output
.
trim
().
split
(
'
\n
'
).
where
((
String
s
)
=>
s
!=
null
&&
s
.
isNotEmpty
);
}
catch
(
e
)
{
return
<
String
>[];
}
}
static
String
_getDeviceName
(
String
deviceID
,
[
IOSDevice
mockIOS
])
{
String
informerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
informerPath
:
_checkForCommand
(
'ideviceinfo'
);
return
runSync
([
informerPath
,
'-k'
,
'DeviceName'
,
'-u'
,
deviceID
]).
trim
();
}
static
final
Map
<
String
,
String
>
_commandMap
=
{};
static
String
_checkForCommand
(
String
command
,
[
String
macInstructions
=
_ideviceinstallerInstructions
])
{
return
_commandMap
.
putIfAbsent
(
command
,
()
{
try
{
command
=
runCheckedSync
([
'which'
,
command
]).
trim
();
}
catch
(
e
)
{
if
(
Platform
.
isMacOS
)
{
printError
(
'
$command
not found.
$macInstructions
'
);
}
else
{
printError
(
'Cannot control iOS devices or simulators.
$command
is not available on your platform.'
);
}
}
return
command
;
});
}
@override
bool
installApp
(
ApplicationPackage
app
)
{
try
{
runCheckedSync
([
installerPath
,
'-i'
,
app
.
localPath
]);
return
true
;
}
catch
(
e
)
{
return
false
;
}
return
false
;
}
@override
bool
isConnected
()
=>
_getAttachedDeviceIDs
().
contains
(
id
);
@override
bool
isSupported
()
=>
true
;
@override
bool
isAppInstalled
(
ApplicationPackage
app
)
{
try
{
String
apps
=
runCheckedSync
([
installerPath
,
'--list-apps'
]);
if
(
new
RegExp
(
app
.
id
,
multiLine:
true
).
hasMatch
(
apps
))
{
return
true
;
}
}
catch
(
e
)
{
return
false
;
}
return
false
;
}
@override
Future
<
bool
>
startApp
(
ApplicationPackage
app
,
Toolchain
toolchain
,
{
String
mainPath
,
String
route
,
bool
checked:
true
,
bool
clearLogs:
false
,
bool
startPaused:
false
,
int
debugPort:
observatoryDefaultPort
,
Map
<
String
,
dynamic
>
platformArgs
})
async
{
// TODO(chinmaygarde): Use checked, mainPath, route, clearLogs.
// TODO(devoncarew): Handle startPaused, debugPort.
printTrace
(
'Building
${app.name}
for
$id
'
);
// Step 1: Install the precompiled application if necessary.
bool
buildResult
=
await
buildIOSXcodeProject
(
app
,
buildForDevice:
true
);
if
(!
buildResult
)
{
printError
(
'Could not build the precompiled application for the device.'
);
return
false
;
}
// Step 2: Check that the application exists at the specified path.
Directory
bundle
=
new
Directory
(
path
.
join
(
app
.
localPath
,
'build'
,
'Release-iphoneos'
,
'Runner.app'
));
bool
bundleExists
=
bundle
.
existsSync
();
if
(!
bundleExists
)
{
printError
(
'Could not find the built application bundle at
${bundle.path}
.'
);
return
false
;
}
// Step 3: Attempt to install the application on the device.
int
installationResult
=
await
runCommandAndStreamOutput
([
'/usr/bin/env'
,
'ios-deploy'
,
'--id'
,
id
,
'--bundle'
,
bundle
.
path
,
]);
if
(
installationResult
!=
0
)
{
printError
(
'Could not install
${bundle.path}
on
$id
.'
);
return
false
;
}
printTrace
(
'Installation successful.'
);
return
true
;
}
@override
Future
<
bool
>
stopApp
(
ApplicationPackage
app
)
async
{
// Currently we don't have a way to stop an app running on iOS.
return
false
;
}
Future
<
bool
>
pushFile
(
ApplicationPackage
app
,
String
localFile
,
String
targetFile
)
async
{
if
(
Platform
.
isMacOS
)
{
runSync
(<
String
>[
pusherPath
,
'-t'
,
'1'
,
'--bundle_id'
,
app
.
id
,
'--upload'
,
localFile
,
'--to'
,
targetFile
]);
return
true
;
}
else
{
return
false
;
}
return
false
;
}
@override
TargetPlatform
get
platform
=>
TargetPlatform
.
iOS
;
DeviceLogReader
createLogReader
()
=>
new
_IOSDeviceLogReader
(
this
);
}
class
_IOSDeviceLogReader
extends
DeviceLogReader
{
_IOSDeviceLogReader
(
this
.
device
);
final
IOSDevice
device
;
String
get
name
=>
device
.
name
;
// TODO(devoncarew): Support [clear].
Future
<
int
>
logs
({
bool
clear:
false
})
async
{
if
(!
device
.
isConnected
())
return
2
;
return
await
runCommandAndStreamOutput
(
<
String
>[
device
.
loggerPath
],
prefix:
'[
$name
] '
,
filter:
new
RegExp
(
r'Runner'
)
);
}
int
get
hashCode
=>
name
.
hashCode
;
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
_IOSDeviceLogReader
)
return
false
;
return
other
.
name
==
name
;
}
}
packages/flutter_tools/lib/src/ios/mac.dart
View file @
4ec96084
...
...
@@ -2,8 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
show
JSON
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'../application_package.dart'
;
import
'../artifacts.dart'
;
import
'../base/context.dart'
;
import
'../base/process.dart'
;
import
'../globals.dart'
;
import
'../services.dart'
;
import
'setup_xcodeproj.dart'
;
String
get
homeDirectory
=>
path
.
absolute
(
Platform
.
environment
[
'HOME'
]);
// TODO(devoncarew): Refactor functionality into XCode.
const
int
kXcodeRequiredVersionMajor
=
7
;
const
int
kXcodeRequiredVersionMinor
=
2
;
...
...
@@ -51,3 +66,135 @@ class XCode {
return
false
;
}
}
Future
<
bool
>
buildIOSXcodeProject
(
ApplicationPackage
app
,
{
bool
buildForDevice
})
async
{
String
flutterProjectPath
=
Directory
.
current
.
path
;
if
(
xcodeProjectRequiresUpdate
())
{
printTrace
(
'Initializing the Xcode project.'
);
if
((
await
setupXcodeProjectHarness
(
flutterProjectPath
))
!=
0
)
{
printError
(
'Could not initialize the Xcode project.'
);
return
false
;
}
}
else
{
updateXcodeLocalProperties
(
flutterProjectPath
);
}
if
(!
_validateEngineRevision
(
app
))
return
false
;
if
(!
_checkXcodeVersion
())
return
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.
await
_addServicesToBundle
(
new
Directory
(
app
.
localPath
));
List
<
String
>
commands
=
<
String
>[
'/usr/bin/env'
,
'xcrun'
,
'xcodebuild'
,
'-target'
,
'Runner'
,
'-configuration'
,
'Release'
];
if
(
buildForDevice
)
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphoneos'
,
'-arch'
,
'arm64'
]);
}
else
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphonesimulator'
,
'-arch'
,
'x86_64'
]);
}
try
{
runCheckedSync
(
commands
,
workingDirectory:
app
.
localPath
);
return
true
;
}
catch
(
error
)
{
return
false
;
}
}
final
RegExp
_xcodeVersionRegExp
=
new
RegExp
(
r'Xcode (\d+)\..*'
);
final
String
_xcodeRequirement
=
'Xcode 7.0 or greater is required to develop for iOS.'
;
bool
_checkXcodeVersion
(
)
{
if
(!
Platform
.
isMacOS
)
return
false
;
try
{
String
version
=
runCheckedSync
(<
String
>[
'xcodebuild'
,
'-version'
]);
Match
match
=
_xcodeVersionRegExp
.
firstMatch
(
version
);
if
(
int
.
parse
(
match
[
1
])
<
7
)
{
printError
(
'Found "
${match[0]}
".
$_xcodeRequirement
'
);
return
false
;
}
}
catch
(
e
)
{
printError
(
'Cannot find "xcodebuid".
$_xcodeRequirement
'
);
return
false
;
}
return
true
;
}
bool
_validateEngineRevision
(
ApplicationPackage
app
)
{
String
skyRevision
=
ArtifactStore
.
engineRevision
;
String
iosRevision
=
_getIOSEngineRevision
(
app
);
if
(
iosRevision
!=
skyRevision
)
{
printError
(
"Error: incompatible sky_engine revision."
);
printStatus
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
false
;
}
else
{
printTrace
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
true
;
}
}
String
_getIOSEngineRevision
(
ApplicationPackage
app
)
{
File
revisionFile
=
new
File
(
path
.
join
(
app
.
localPath
,
'REVISION'
));
if
(
revisionFile
.
existsSync
())
{
return
revisionFile
.
readAsStringSync
().
trim
();
}
else
{
return
null
;
}
}
Future
_addServicesToBundle
(
Directory
bundle
)
async
{
List
<
Map
<
String
,
String
>>
services
=
[];
printTrace
(
"Trying to resolve native pub services."
);
// Step 1: Parse the service configuration yaml files present in the service
// pub packages.
await
parseServiceConfigs
(
services
);
printTrace
(
"Found
${services.length}
service definition(s)."
);
// Step 2: Copy framework dylibs to the correct spot for xcodebuild to pick up.
Directory
frameworksDirectory
=
new
Directory
(
path
.
join
(
bundle
.
path
,
"Frameworks"
));
await
_copyServiceFrameworks
(
services
,
frameworksDirectory
);
// Step 3: Copy the service definitions manifest at the correct spot for
// xcodebuild to pick up.
File
manifestFile
=
new
File
(
path
.
join
(
bundle
.
path
,
"ServiceDefinitions.json"
));
_copyServiceDefinitionsManifest
(
services
,
manifestFile
);
}
Future
_copyServiceFrameworks
(
List
<
Map
<
String
,
String
>>
services
,
Directory
frameworksDirectory
)
async
{
printTrace
(
"Copying service frameworks to '
${path.absolute(frameworksDirectory.path)}
'."
);
frameworksDirectory
.
createSync
(
recursive:
true
);
for
(
Map
<
String
,
String
>
service
in
services
)
{
String
dylibPath
=
await
getServiceFromUrl
(
service
[
'ios-framework'
],
service
[
'root'
],
service
[
'name'
]);
File
dylib
=
new
File
(
dylibPath
);
printTrace
(
"Copying
${dylib.path}
into bundle."
);
if
(!
dylib
.
existsSync
())
{
printError
(
"The service dylib '
${dylib.path}
' does not exist."
);
continue
;
}
// Shell out so permissions on the dylib are preserved.
runCheckedSync
([
'/bin/cp'
,
dylib
.
path
,
frameworksDirectory
.
path
]);
}
}
void
_copyServiceDefinitionsManifest
(
List
<
Map
<
String
,
String
>>
services
,
File
manifest
)
{
printTrace
(
"Creating service definitions manifest at '
${manifest.path}
'"
);
List
<
Map
<
String
,
String
>>
jsonServices
=
services
.
map
((
Map
<
String
,
String
>
service
)
=>
{
'name'
:
service
[
'name'
],
// Since we have already moved it to the Frameworks directory. Strip away
// the directory and basenames.
'framework'
:
path
.
basenameWithoutExtension
(
service
[
'ios-framework'
])
}).
toList
();
Map
<
String
,
dynamic
>
json
=
{
'services'
:
jsonServices
};
manifest
.
writeAsStringSync
(
JSON
.
encode
(
json
),
mode:
FileMode
.
WRITE
,
flush:
true
);
}
packages/flutter_tools/lib/src/ios/simulator.dart
deleted
100644 → 0
View file @
9e56fc4e
// Copyright 2016 The Chromium 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
'dart:async'
;
import
'dart:convert'
show
JSON
;
import
'dart:io'
;
import
'../base/process.dart'
;
import
'../globals.dart'
;
const
String
_xcrunPath
=
'/usr/bin/xcrun'
;
const
String
_simulatorPath
=
'/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator'
;
/// A wrapper around the `simctl` command line tool.
class
SimControl
{
static
Future
<
bool
>
boot
({
String
deviceId
})
async
{
if
(
_isAnyConnected
())
return
true
;
if
(
deviceId
==
null
)
{
runDetached
([
_simulatorPath
]);
Future
<
bool
>
checkConnection
([
int
attempts
=
20
])
async
{
if
(
attempts
==
0
)
{
printStatus
(
'Timed out waiting for iOS Simulator to boot.'
);
return
false
;
}
if
(!
_isAnyConnected
())
{
printStatus
(
'Waiting for iOS Simulator to boot...'
);
return
await
new
Future
.
delayed
(
new
Duration
(
milliseconds:
500
),
()
=>
checkConnection
(
attempts
-
1
)
);
}
return
true
;
}
return
await
checkConnection
();
}
else
{
try
{
runCheckedSync
([
_xcrunPath
,
'simctl'
,
'boot'
,
deviceId
]);
return
true
;
}
catch
(
e
)
{
printError
(
'Unable to boot iOS Simulator
$deviceId
: '
,
e
);
return
false
;
}
}
return
false
;
}
/// Returns a list of all available devices, both potential and connected.
static
List
<
SimDevice
>
getDevices
()
{
// {
// "devices" : {
// "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [
// {
// "state" : "Shutdown",
// "availability" : " (unavailable, runtime profile not found)",
// "name" : "iPhone 4s",
// "udid" : "1913014C-6DCB-485D-AC6B-7CD76D322F5B"
// },
// ...
List
<
String
>
args
=
<
String
>[
'simctl'
,
'list'
,
'--json'
,
'devices'
];
printTrace
(
'
$_xcrunPath
${args.join(' ')}
'
);
ProcessResult
results
=
Process
.
runSync
(
_xcrunPath
,
args
);
if
(
results
.
exitCode
!=
0
)
{
printError
(
'Error executing simctl:
${results.exitCode}
\n
${results.stderr}
'
);
return
<
SimDevice
>[];
}
List
<
SimDevice
>
devices
=
<
SimDevice
>[];
Map
<
String
,
Map
<
String
,
dynamic
>>
data
=
JSON
.
decode
(
results
.
stdout
);
Map
<
String
,
dynamic
>
devicesSection
=
data
[
'devices'
];
for
(
String
deviceCategory
in
devicesSection
.
keys
)
{
List
<
dynamic
>
devicesData
=
devicesSection
[
deviceCategory
];
for
(
Map
<
String
,
String
>
data
in
devicesData
)
{
devices
.
add
(
new
SimDevice
(
deviceCategory
,
data
));
}
}
return
devices
;
}
/// Returns all the connected simulator devices.
static
List
<
SimDevice
>
getConnectedDevices
()
{
return
getDevices
().
where
((
SimDevice
device
)
=>
device
.
isBooted
).
toList
();
}
static
StreamController
<
List
<
SimDevice
>>
_trackDevicesControler
;
/// Listens to changes in the set of connected devices. The implementation
/// currently uses polling. Callers should be careful to call cancel() on any
/// stream subscription when finished.
///
/// TODO(devoncarew): We could investigate using the usbmuxd protocol directly.
static
Stream
<
List
<
SimDevice
>>
trackDevices
()
{
if
(
_trackDevicesControler
==
null
)
{
Timer
timer
;
Set
<
String
>
deviceIds
=
new
Set
<
String
>();
_trackDevicesControler
=
new
StreamController
.
broadcast
(
onListen:
()
{
timer
=
new
Timer
.
periodic
(
new
Duration
(
seconds:
4
),
(
Timer
timer
)
{
List
<
SimDevice
>
devices
=
getConnectedDevices
();
if
(
_updateDeviceIds
(
devices
,
deviceIds
))
{
_trackDevicesControler
.
add
(
devices
);
}
});
},
onCancel:
()
{
timer
?.
cancel
();
deviceIds
.
clear
();
}
);
}
return
_trackDevicesControler
.
stream
;
}
/// Update the cached set of device IDs and return whether there were any changes.
static
bool
_updateDeviceIds
(
List
<
SimDevice
>
devices
,
Set
<
String
>
deviceIds
)
{
Set
<
String
>
newIds
=
new
Set
<
String
>.
from
(
devices
.
map
((
SimDevice
device
)
=>
device
.
udid
));
bool
changed
=
false
;
for
(
String
id
in
newIds
)
{
if
(!
deviceIds
.
contains
(
id
))
changed
=
true
;
}
for
(
String
id
in
deviceIds
)
{
if
(!
newIds
.
contains
(
id
))
changed
=
true
;
}
deviceIds
.
clear
();
deviceIds
.
addAll
(
newIds
);
return
changed
;
}
static
bool
_isAnyConnected
()
=>
getConnectedDevices
().
isNotEmpty
;
static
void
install
(
String
deviceId
,
String
appPath
)
{
runCheckedSync
([
_xcrunPath
,
'simctl'
,
'install'
,
deviceId
,
appPath
]);
}
static
void
launch
(
String
deviceId
,
String
appIdentifier
,
[
List
<
String
>
launchArgs
])
{
List
<
String
>
args
=
[
_xcrunPath
,
'simctl'
,
'launch'
,
deviceId
,
appIdentifier
];
if
(
launchArgs
!=
null
)
args
.
addAll
(
launchArgs
);
runCheckedSync
(
args
);
}
}
class
SimDevice
{
SimDevice
(
this
.
category
,
this
.
data
);
final
String
category
;
final
Map
<
String
,
String
>
data
;
String
get
state
=>
data
[
'state'
];
String
get
availability
=>
data
[
'availability'
];
String
get
name
=>
data
[
'name'
];
String
get
udid
=>
data
[
'udid'
];
bool
get
isBooted
=>
state
==
'Booted'
;
}
packages/flutter_tools/lib/src/ios/
device_io
s.dart
→
packages/flutter_tools/lib/src/ios/
simulator
s.dart
View file @
4ec96084
...
...
@@ -3,248 +3,188 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:convert'
show
JSON
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'../application_package.dart'
;
import
'../artifacts.dart'
;
import
'../base/common.dart'
;
import
'../base/process.dart'
;
import
'../build_configuration.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../ios/setup_xcodeproj.dart'
;
import
'../services.dart'
;
import
'../toolchain.dart'
;
import
'
simulator
.dart'
;
import
'
mac
.dart'
;
const
String
_ideviceinstallerInstructions
=
'To work with iOS devices, please install ideviceinstaller.
\n
'
'If you use homebrew, you can install it with "
\$
brew install ideviceinstaller".'
;
const
String
_xcrunPath
=
'/usr/bin/xcrun'
;
c
lass
IOSDeviceDiscovery
extends
DeviceDiscovery
{
List
<
Device
>
_devices
=
<
Device
>[]
;
c
onst
String
_simulatorPath
=
'/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator'
;
bool
get
supportsPlatform
=>
Platform
.
isMacOS
;
Future
init
()
{
_devices
=
IOSDevice
.
getAttachedDevices
();
return
new
Future
.
value
();
}
List
<
Device
>
get
devices
=>
_devices
;
}
class
IOSSimulatorDiscovery
extends
DeviceDiscovery
{
List
<
Device
>
_devices
=
<
Device
>[];
class
IOSSimulators
extends
PollingDeviceDiscovery
{
IOSSimulators
()
:
super
(
'IOSSimulators'
);
bool
get
supportsPlatform
=>
Platform
.
isMacOS
;
Future
init
()
{
_devices
=
IOSSimulator
.
getAttachedDevices
();
return
new
Future
.
value
();
}
List
<
Device
>
get
devices
=>
_devices
;
List
<
Device
>
pollingGetDevices
()
=>
IOSSimulator
.
getAttachedDevices
();
}
class
IOSDevice
extends
Device
{
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
;
String
_listerPath
;
String
get
listerPath
=>
_listerPath
;
String
_informerPath
;
String
get
informerPath
=>
_informerPath
;
/// A wrapper around the `simctl` command line tool.
class
SimControl
{
static
Future
<
bool
>
boot
({
String
deviceId
})
async
{
if
(
_isAnyConnected
())
return
true
;
String
_debuggerPath
;
String
get
debuggerPath
=>
_debuggerPath
;
if
(
deviceId
==
null
)
{
runDetached
([
_simulatorPath
]);
Future
<
bool
>
checkConnection
([
int
attempts
=
20
])
async
{
if
(
attempts
==
0
)
{
printStatus
(
'Timed out waiting for iOS Simulator to boot.'
);
return
false
;
}
if
(!
_isAnyConnected
())
{
printStatus
(
'Waiting for iOS Simulator to boot...'
);
return
await
new
Future
.
delayed
(
new
Duration
(
milliseconds:
500
),
()
=>
checkConnection
(
attempts
-
1
)
);
}
return
true
;
}
return
await
checkConnection
();
}
else
{
try
{
runCheckedSync
([
_xcrunPath
,
'simctl'
,
'boot'
,
deviceId
]);
return
true
;
}
catch
(
e
)
{
printError
(
'Unable to boot iOS Simulator
$deviceId
: '
,
e
);
return
false
;
}
}
String
_loggerPath
;
String
get
loggerPath
=>
_loggerPath
;
return
false
;
}
String
_pusherPath
;
String
get
pusherPath
=>
_pusherPath
;
/// Returns a list of all available devices, both potential and connected.
static
List
<
SimDevice
>
getDevices
()
{
// {
// "devices" : {
// "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [
// {
// "state" : "Shutdown",
// "availability" : " (unavailable, runtime profile not found)",
// "name" : "iPhone 4s",
// "udid" : "1913014C-6DCB-485D-AC6B-7CD76D322F5B"
// },
// ...
List
<
String
>
args
=
<
String
>[
'simctl'
,
'list'
,
'--json'
,
'devices'
];
printTrace
(
'
$_xcrunPath
${args.join(' ')}
'
);
ProcessResult
results
=
Process
.
runSync
(
_xcrunPath
,
args
);
if
(
results
.
exitCode
!=
0
)
{
printError
(
'Error executing simctl:
${results.exitCode}
\n
${results.stderr}
'
);
return
<
SimDevice
>[];
}
final
String
name
;
List
<
SimDevice
>
devices
=
<
SimDevice
>[]
;
bool
get
supportsStartPaused
=>
false
;
Map
<
String
,
Map
<
String
,
dynamic
>>
data
=
JSON
.
decode
(
results
.
stdout
);
Map
<
String
,
dynamic
>
devicesSection
=
data
[
'devices'
];
static
List
<
IOSDevice
>
getAttachedDevices
([
IOSDevice
mockIOS
])
{
if
(!
doctor
.
iosWorkflow
.
hasIdeviceId
)
return
<
IOSDevice
>[];
for
(
String
deviceCategory
in
devicesSection
.
keys
)
{
List
<
dynamic
>
devicesData
=
devicesSection
[
deviceCategory
];
List
<
IOSDevice
>
devices
=
[];
for
(
String
id
in
_getAttachedDeviceIDs
(
mockIOS
))
{
String
name
=
_getDeviceName
(
id
,
mockIOS
);
devices
.
add
(
new
IOSDevice
(
id
,
name:
name
));
for
(
Map
<
String
,
String
>
data
in
devicesData
)
{
devices
.
add
(
new
SimDevice
(
deviceCategory
,
data
));
}
}
return
devices
;
}
static
Iterable
<
String
>
_getAttachedDeviceIDs
([
IOSDevice
mockIOS
])
{
String
listerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
listerPath
:
_checkForCommand
(
'idevice_id'
);
try
{
String
output
=
runSync
([
listerPath
,
'-l'
]);
return
output
.
trim
().
split
(
'
\n
'
).
where
((
String
s
)
=>
s
!=
null
&&
s
.
isNotEmpty
);
}
catch
(
e
)
{
return
<
String
>[];
}
/// Returns all the connected simulator devices.
static
List
<
SimDevice
>
getConnectedDevices
()
{
return
getDevices
().
where
((
SimDevice
device
)
=>
device
.
isBooted
).
toList
();
}
static
String
_getDeviceName
(
String
deviceID
,
[
IOSDevice
mockIOS
])
{
String
informerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
informerPath
:
_checkForCommand
(
'ideviceinfo'
);
return
runSync
([
informerPath
,
'-k'
,
'DeviceName'
,
'-u'
,
deviceID
]).
trim
();
}
static
StreamController
<
List
<
SimDevice
>>
_trackDevicesControler
;
static
final
Map
<
String
,
String
>
_commandMap
=
{};
static
String
_checkForCommand
(
String
command
,
[
String
macInstructions
=
_ideviceinstallerInstructions
])
{
return
_commandMap
.
putIfAbsent
(
command
,
()
{
try
{
command
=
runCheckedSync
([
'which'
,
command
]).
trim
();
}
catch
(
e
)
{
if
(
Platform
.
isMacOS
)
{
printError
(
'
$command
not found.
$macInstructions
'
);
}
else
{
printError
(
'Cannot control iOS devices or simulators.
$command
is not available on your platform.'
);
}
}
return
command
;
});
}
/// Listens to changes in the set of connected devices. The implementation
/// currently uses polling. Callers should be careful to call cancel() on any
/// stream subscription when finished.
///
/// TODO(devoncarew): We could investigate using the usbmuxd protocol directly.
static
Stream
<
List
<
SimDevice
>>
trackDevices
()
{
if
(
_trackDevicesControler
==
null
)
{
Timer
timer
;
Set
<
String
>
deviceIds
=
new
Set
<
String
>();
@override
bool
installApp
(
ApplicationPackage
app
)
{
try
{
runCheckedSync
([
installerPath
,
'-i'
,
app
.
localPath
]);
return
true
;
}
catch
(
e
)
{
return
false
;
}
return
false
;
}
_trackDevicesControler
=
new
StreamController
.
broadcast
(
onListen:
()
{
timer
=
new
Timer
.
periodic
(
new
Duration
(
seconds:
4
),
(
Timer
timer
)
{
List
<
SimDevice
>
devices
=
getConnectedDevices
();
@override
bool
isConnected
()
=>
_getAttachedDeviceIDs
().
contains
(
id
);
@override
bool
isSupported
()
=>
true
;
@override
bool
isAppInstalled
(
ApplicationPackage
app
)
{
try
{
String
apps
=
runCheckedSync
([
installerPath
,
'--list-apps'
]);
if
(
new
RegExp
(
app
.
id
,
multiLine:
true
).
hasMatch
(
apps
))
{
return
true
;
}
}
catch
(
e
)
{
return
false
;
if
(
_updateDeviceIds
(
devices
,
deviceIds
))
{
_trackDevicesControler
.
add
(
devices
);
}
});
},
onCancel:
()
{
timer
?.
cancel
();
deviceIds
.
clear
();
}
);
}
return
false
;
return
_trackDevicesControler
.
stream
;
}
@override
Future
<
bool
>
startApp
(
ApplicationPackage
app
,
Toolchain
toolchain
,
{
String
mainPath
,
String
route
,
bool
checked:
true
,
bool
clearLogs:
false
,
bool
startPaused:
false
,
int
debugPort:
observatoryDefaultPort
,
Map
<
String
,
dynamic
>
platformArgs
})
async
{
// TODO(chinmaygarde): Use checked, mainPath, route, clearLogs.
// TODO(devoncarew): Handle startPaused, debugPort.
printTrace
(
'Building
${app.name}
for
$id
'
);
/// Update the cached set of device IDs and return whether there were any changes.
static
bool
_updateDeviceIds
(
List
<
SimDevice
>
devices
,
Set
<
String
>
deviceIds
)
{
Set
<
String
>
newIds
=
new
Set
<
String
>.
from
(
devices
.
map
((
SimDevice
device
)
=>
device
.
udid
));
// Step 1: Install the precompiled application if necessary.
bool
buildResult
=
await
_buildIOSXcodeProject
(
app
,
buildForDevice:
true
);
if
(!
buildResult
)
{
printError
(
'Could not build the precompiled application for the device.'
);
return
false
;
}
bool
changed
=
false
;
// Step 2: Check that the application exists at the specified path.
Directory
bundle
=
new
Directory
(
path
.
join
(
app
.
localPath
,
'build'
,
'Release-iphoneos'
,
'Runner.app'
));
bool
bundleExists
=
bundle
.
existsSync
();
if
(!
bundleExists
)
{
printError
(
'Could not find the built application bundle at
${bundle.path}
.'
);
return
false
;
for
(
String
id
in
newIds
)
{
if
(!
deviceIds
.
contains
(
id
))
changed
=
true
;
}
// Step 3: Attempt to install the application on the device.
int
installationResult
=
await
runCommandAndStreamOutput
([
'/usr/bin/env'
,
'ios-deploy'
,
'--id'
,
id
,
'--bundle'
,
bundle
.
path
,
]);
if
(
installationResult
!=
0
)
{
printError
(
'Could not install
${bundle.path}
on
$id
.'
);
return
false
;
for
(
String
id
in
deviceIds
)
{
if
(!
newIds
.
contains
(
id
))
changed
=
true
;
}
printTrace
(
'Installation successful.'
);
return
true
;
deviceIds
.
clear
();
deviceIds
.
addAll
(
newIds
);
return
changed
;
}
@override
Future
<
bool
>
stopApp
(
ApplicationPackage
app
)
async
{
// Currently we don't have a way to stop an app running on iOS.
r
eturn
false
;
static
bool
_isAnyConnected
()
=>
getConnectedDevices
().
isNotEmpty
;
static
void
install
(
String
deviceId
,
String
appPath
)
{
r
unCheckedSync
([
_xcrunPath
,
'simctl'
,
'install'
,
deviceId
,
appPath
])
;
}
Future
<
bool
>
pushFile
(
ApplicationPackage
app
,
String
localFile
,
String
targetFile
)
async
{
if
(
Platform
.
isMacOS
)
{
runSync
(<
String
>[
pusherPath
,
'-t'
,
'1'
,
'--bundle_id'
,
app
.
id
,
'--upload'
,
localFile
,
'--to'
,
targetFile
]);
return
true
;
}
else
{
return
false
;
}
return
false
;
static
void
launch
(
String
deviceId
,
String
appIdentifier
,
[
List
<
String
>
launchArgs
])
{
List
<
String
>
args
=
[
_xcrunPath
,
'simctl'
,
'launch'
,
deviceId
,
appIdentifier
];
if
(
launchArgs
!=
null
)
args
.
addAll
(
launchArgs
);
runCheckedSync
(
args
);
}
}
@override
TargetPlatform
get
platform
=>
TargetPlatform
.
iOS
;
class
SimDevice
{
SimDevice
(
this
.
category
,
this
.
data
);
final
String
category
;
final
Map
<
String
,
String
>
data
;
DeviceLogReader
createLogReader
()
=>
new
_IOSDeviceLogReader
(
this
);
String
get
state
=>
data
[
'state'
];
String
get
availability
=>
data
[
'availability'
];
String
get
name
=>
data
[
'name'
];
String
get
udid
=>
data
[
'udid'
];
bool
get
isBooted
=>
state
==
'Booted'
;
}
class
IOSSimulator
extends
Device
{
...
...
@@ -264,7 +204,7 @@ class IOSSimulator extends Device {
String
get
xcrunPath
=>
path
.
join
(
'/usr'
,
'bin'
,
'xcrun'
);
String
_getSimulatorPath
()
{
return
path
.
join
(
_
homeDirectory
,
'Library'
,
'Developer'
,
'CoreSimulator'
,
'Devices'
,
id
);
return
path
.
join
(
homeDirectory
,
'Library'
,
'Developer'
,
'CoreSimulator'
,
'Devices'
,
id
);
}
String
_getSimulatorAppHomeDirectory
(
ApplicationPackage
app
)
{
...
...
@@ -378,7 +318,7 @@ class IOSSimulator extends Device {
this
.
clearLogs
();
// Step 1: Build the Xcode project.
bool
buildResult
=
await
_
buildIOSXcodeProject
(
app
,
buildForDevice:
false
);
bool
buildResult
=
await
buildIOSXcodeProject
(
app
,
buildForDevice:
false
);
if
(!
buildResult
)
{
printError
(
'Could not build the application for the simulator.'
);
return
false
;
...
...
@@ -437,7 +377,7 @@ class IOSSimulator extends Device {
}
String
get
logFilePath
{
return
path
.
join
(
_
homeDirectory
,
'Library'
,
'Logs'
,
'CoreSimulator'
,
id
,
'system.log'
);
return
path
.
join
(
homeDirectory
,
'Library'
,
'Logs'
,
'CoreSimulator'
,
id
,
'system.log'
);
}
@override
...
...
@@ -461,36 +401,6 @@ class IOSSimulator extends Device {
}
}
class
_IOSDeviceLogReader
extends
DeviceLogReader
{
_IOSDeviceLogReader
(
this
.
device
);
final
IOSDevice
device
;
String
get
name
=>
device
.
name
;
// TODO(devoncarew): Support [clear].
Future
<
int
>
logs
({
bool
clear:
false
})
async
{
if
(!
device
.
isConnected
())
return
2
;
return
await
runCommandAndStreamOutput
(
<
String
>[
device
.
loggerPath
],
prefix:
'[
$name
] '
,
filter:
new
RegExp
(
r'Runner'
)
);
}
int
get
hashCode
=>
name
.
hashCode
;
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
_IOSDeviceLogReader
)
return
false
;
return
other
.
name
==
name
;
}
}
class
_IOSSimulatorLogReader
extends
DeviceLogReader
{
_IOSSimulatorLogReader
(
this
.
device
);
...
...
@@ -576,137 +486,3 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
return
other
.
device
.
logFilePath
==
device
.
logFilePath
;
}
}
final
RegExp
_xcodeVersionRegExp
=
new
RegExp
(
r'Xcode (\d+)\..*'
);
final
String
_xcodeRequirement
=
'Xcode 7.0 or greater is required to develop for iOS.'
;
String
get
_homeDirectory
=>
path
.
absolute
(
Platform
.
environment
[
'HOME'
]);
bool
_checkXcodeVersion
(
)
{
if
(!
Platform
.
isMacOS
)
return
false
;
try
{
String
version
=
runCheckedSync
(<
String
>[
'xcodebuild'
,
'-version'
]);
Match
match
=
_xcodeVersionRegExp
.
firstMatch
(
version
);
if
(
int
.
parse
(
match
[
1
])
<
7
)
{
printError
(
'Found "
${match[0]}
".
$_xcodeRequirement
'
);
return
false
;
}
}
catch
(
e
)
{
printError
(
'Cannot find "xcodebuid".
$_xcodeRequirement
'
);
return
false
;
}
return
true
;
}
bool
_validateEngineRevision
(
ApplicationPackage
app
)
{
String
skyRevision
=
ArtifactStore
.
engineRevision
;
String
iosRevision
=
_getIOSEngineRevision
(
app
);
if
(
iosRevision
!=
skyRevision
)
{
printError
(
"Error: incompatible sky_engine revision."
);
printStatus
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
false
;
}
else
{
printTrace
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
true
;
}
}
String
_getIOSEngineRevision
(
ApplicationPackage
app
)
{
File
revisionFile
=
new
File
(
path
.
join
(
app
.
localPath
,
'REVISION'
));
if
(
revisionFile
.
existsSync
())
{
return
revisionFile
.
readAsStringSync
().
trim
();
}
else
{
return
null
;
}
}
Future
<
bool
>
_buildIOSXcodeProject
(
ApplicationPackage
app
,
{
bool
buildForDevice
})
async
{
String
flutterProjectPath
=
Directory
.
current
.
path
;
if
(
xcodeProjectRequiresUpdate
())
{
printTrace
(
'Initializing the Xcode project.'
);
if
((
await
setupXcodeProjectHarness
(
flutterProjectPath
))
!=
0
)
{
printError
(
'Could not initialize the Xcode project.'
);
return
false
;
}
}
else
{
updateXcodeLocalProperties
(
flutterProjectPath
);
}
if
(!
_validateEngineRevision
(
app
))
return
false
;
if
(!
_checkXcodeVersion
())
return
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.
await
_addServicesToBundle
(
new
Directory
(
app
.
localPath
));
List
<
String
>
commands
=
<
String
>[
'/usr/bin/env'
,
'xcrun'
,
'xcodebuild'
,
'-target'
,
'Runner'
,
'-configuration'
,
'Release'
];
if
(
buildForDevice
)
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphoneos'
,
'-arch'
,
'arm64'
]);
}
else
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphonesimulator'
,
'-arch'
,
'x86_64'
]);
}
try
{
runCheckedSync
(
commands
,
workingDirectory:
app
.
localPath
);
return
true
;
}
catch
(
error
)
{
return
false
;
}
}
Future
_addServicesToBundle
(
Directory
bundle
)
async
{
List
<
Map
<
String
,
String
>>
services
=
[];
printTrace
(
"Trying to resolve native pub services."
);
// Step 1: Parse the service configuration yaml files present in the service
// pub packages.
await
parseServiceConfigs
(
services
);
printTrace
(
"Found
${services.length}
service definition(s)."
);
// Step 2: Copy framework dylibs to the correct spot for xcodebuild to pick up.
Directory
frameworksDirectory
=
new
Directory
(
path
.
join
(
bundle
.
path
,
"Frameworks"
));
await
_copyServiceFrameworks
(
services
,
frameworksDirectory
);
// Step 3: Copy the service definitions manifest at the correct spot for
// xcodebuild to pick up.
File
manifestFile
=
new
File
(
path
.
join
(
bundle
.
path
,
"ServiceDefinitions.json"
));
_copyServiceDefinitionsManifest
(
services
,
manifestFile
);
}
Future
_copyServiceFrameworks
(
List
<
Map
<
String
,
String
>>
services
,
Directory
frameworksDirectory
)
async
{
printTrace
(
"Copying service frameworks to '
${path.absolute(frameworksDirectory.path)}
'."
);
frameworksDirectory
.
createSync
(
recursive:
true
);
for
(
Map
<
String
,
String
>
service
in
services
)
{
String
dylibPath
=
await
getServiceFromUrl
(
service
[
'ios-framework'
],
service
[
'root'
],
service
[
'name'
]);
File
dylib
=
new
File
(
dylibPath
);
printTrace
(
"Copying
${dylib.path}
into bundle."
);
if
(!
dylib
.
existsSync
())
{
printError
(
"The service dylib '
${dylib.path}
' does not exist."
);
continue
;
}
// Shell out so permissions on the dylib are preserved.
runCheckedSync
([
'/bin/cp'
,
dylib
.
path
,
frameworksDirectory
.
path
]);
}
}
void
_copyServiceDefinitionsManifest
(
List
<
Map
<
String
,
String
>>
services
,
File
manifest
)
{
printTrace
(
"Creating service definitions manifest at '
${manifest.path}
'"
);
List
<
Map
<
String
,
String
>>
jsonServices
=
services
.
map
((
Map
<
String
,
String
>
service
)
=>
{
'name'
:
service
[
'name'
],
// Since we have already moved it to the Frameworks directory. Strip away
// the directory and basenames.
'framework'
:
path
.
basenameWithoutExtension
(
service
[
'ios-framework'
])
}).
toList
();
Map
<
String
,
dynamic
>
json
=
{
'services'
:
jsonServices
};
manifest
.
writeAsStringSync
(
JSON
.
encode
(
json
),
mode:
FileMode
.
WRITE
,
flush:
true
);
}
packages/flutter_tools/test/android_device_test.dart
View file @
4ec96084
...
...
@@ -2,7 +2,7 @@
// 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/android/
device_android
.dart'
;
import
'package:flutter_tools/src/android/
android_device
.dart'
;
import
'package:test/test.dart'
;
import
'src/context.dart'
;
...
...
packages/flutter_tools/test/base_utils_test.dart
0 → 100644
View file @
4ec96084
// Copyright 2016 The Chromium 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
'dart:async'
;
import
'package:flutter_tools/src/base/utils.dart'
;
import
'package:test/test.dart'
;
main
()
=>
defineTests
();
defineTests
()
{
group
(
'ItemListNotifier'
,
()
{
test
(
'sends notifications'
,
()
async
{
ItemListNotifier
<
String
>
list
=
new
ItemListNotifier
<
String
>();
expect
(
list
.
items
,
isEmpty
);
Future
<
List
<
String
>>
addedStreamItems
=
list
.
onAdded
.
toList
();
Future
<
List
<
String
>>
removedStreamItems
=
list
.
onRemoved
.
toList
();
list
.
updateWithNewList
(<
String
>[
'aaa'
]);
list
.
updateWithNewList
(<
String
>[
'aaa'
,
'bbb'
]);
list
.
updateWithNewList
(<
String
>[
'bbb'
]);
list
.
dispose
();
List
<
String
>
addedItems
=
await
addedStreamItems
;
List
<
String
>
removedItems
=
await
removedStreamItems
;
expect
(
addedItems
.
length
,
2
);
expect
(
addedItems
.
first
,
'aaa'
);
expect
(
addedItems
[
1
],
'bbb'
);
expect
(
removedItems
.
length
,
1
);
expect
(
removedItems
.
first
,
'aaa'
);
});
});
}
packages/flutter_tools/test/daemon_test.dart
View file @
4ec96084
...
...
@@ -3,11 +3,14 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:io'
;
import
'package:flutter_tools/src/base/context.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/commands/daemon.dart'
;
import
'package:flutter_tools/src/doctor.dart'
;
import
'package:flutter_tools/src/globals.dart'
;
import
'package:flutter_tools/src/ios/mac.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:test/test.dart'
;
...
...
@@ -31,6 +34,9 @@ defineTests() {
appContext
=
new
AppContext
();
notifyingLogger
=
new
NotifyingLogger
();
appContext
[
Logger
]
=
notifyingLogger
;
appContext
[
Doctor
]
=
new
Doctor
();
if
(
Platform
.
isMacOS
)
appContext
[
XCode
]
=
new
XCode
();
});
tearDown
(()
{
...
...
packages/flutter_tools/test/src/mocks.dart
View file @
4ec96084
...
...
@@ -2,11 +2,12 @@
// 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/android/
device_android
.dart'
;
import
'package:flutter_tools/src/android/
android_device
.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/build_configuration.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/ios/device_ios.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/ios/simulators.dart'
;
import
'package:flutter_tools/src/runner/flutter_command.dart'
;
import
'package:flutter_tools/src/toolchain.dart'
;
import
'package:mockito/mockito.dart'
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment