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
67124dc3
Commit
67124dc3
authored
Feb 05, 2016
by
Devon Carew
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor to parse xcrun simctl list as json
add device notifications for the simulator
parent
1d3ce8e8
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
269 additions
and
157 deletions
+269
-157
daemon.dart
packages/flutter_tools/lib/src/commands/daemon.dart
+66
-2
install.dart
packages/flutter_tools/lib/src/commands/install.dart
+7
-5
device_ios.dart
packages/flutter_tools/lib/src/ios/device_ios.dart
+23
-150
simulator.dart
packages/flutter_tools/lib/src/ios/simulator.dart
+173
-0
No files found.
packages/flutter_tools/lib/src/commands/daemon.dart
View file @
67124dc3
...
...
@@ -10,6 +10,8 @@ import '../android/adb.dart';
import
'../android/device_android.dart'
;
import
'../base/context.dart'
;
import
'../device.dart'
;
import
'../ios/device_ios.dart'
;
import
'../ios/simulator.dart'
;
import
'../runner/flutter_command.dart'
;
import
'start.dart'
;
import
'stop.dart'
as
stop
;
...
...
@@ -291,18 +293,32 @@ class DeviceDomain extends Domain {
_androidDeviceDiscovery
.
onChanged
.
listen
((
Device
device
)
{
sendEvent
(
'device.changed'
,
_deviceToMap
(
device
));
});
if
(
Platform
.
isMacOS
)
{
_iosSimulatorDeviceDiscovery
=
new
IOSSimulatorDeviceDiscovery
();
_iosSimulatorDeviceDiscovery
.
onAdded
.
listen
((
Device
device
)
{
sendEvent
(
'device.added'
,
_deviceToMap
(
device
));
});
_iosSimulatorDeviceDiscovery
.
onRemoved
.
listen
((
Device
device
)
{
sendEvent
(
'device.removed'
,
_deviceToMap
(
device
));
});
}
}
AndroidDeviceDiscovery
_androidDeviceDiscovery
;
IOSSimulatorDeviceDiscovery
_iosSimulatorDeviceDiscovery
;
Future
<
List
<
Device
>>
getDevices
(
dynamic
args
)
{
List
<
Device
>
devices
=
<
Device
>[];
devices
.
addAll
(
_androidDeviceDiscovery
.
getDevices
());
if
(
_iosSimulatorDeviceDiscovery
!=
null
)
devices
.
addAll
(
_iosSimulatorDeviceDiscovery
.
getDevices
());
return
new
Future
.
value
(
devices
);
}
void
dispose
()
{
_androidDeviceDiscovery
.
dispose
();
_iosSimulatorDeviceDiscovery
?.
dispose
();
}
}
...
...
@@ -311,7 +327,7 @@ class AndroidDeviceDiscovery {
_initAdb
();
if
(
_adb
!=
null
)
{
_subscription
=
_adb
.
trackDevices
().
listen
(
_handle
New
Devices
);
_subscription
=
_adb
.
trackDevices
().
listen
(
_handle
Updated
Devices
);
}
}
...
...
@@ -337,7 +353,7 @@ class AndroidDeviceDiscovery {
}
}
void
_handle
New
Devices
(
List
<
AdbDevice
>
newDevices
)
{
void
_handle
Updated
Devices
(
List
<
AdbDevice
>
newDevices
)
{
List
<
AndroidDevice
>
currentDevices
=
new
List
.
from
(
getDevices
());
for
(
AdbDevice
device
in
newDevices
)
{
...
...
@@ -383,6 +399,54 @@ class AndroidDeviceDiscovery {
}
}
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
(
id:
device
.
udid
,
name:
device
.
name
);
_devices
[
androidDevice
.
id
]
=
androidDevice
;
addedController
.
add
(
androidDevice
);
}
else
{
currentDevices
.
remove
(
androidDevice
);
}
}
// device removed
for
(
IOSSimulator
device
in
currentDevices
)
{
_devices
.
remove
(
device
.
id
);
Device
.
removeFromCache
(
device
.
id
);
removedController
.
add
(
device
);
}
}
void
dispose
()
{
_subscription
?.
cancel
();
}
}
Map
<
String
,
dynamic
>
_deviceToMap
(
Device
device
)
{
return
<
String
,
dynamic
>{
'id'
:
device
.
id
,
...
...
packages/flutter_tools/lib/src/commands/install.dart
View file @
67124dc3
...
...
@@ -3,9 +3,11 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:io'
;
import
'../application_package.dart'
;
import
'../device.dart'
;
import
'../ios/simulator.dart'
;
import
'../runner/flutter_command.dart'
;
class
InstallCommand
extends
FlutterCommand
{
...
...
@@ -19,7 +21,7 @@ class InstallCommand extends FlutterCommand {
@override
Future
<
int
>
runInProject
()
async
{
await
downloadApplicationPackagesAndConnectToDevices
();
bool
installedAny
=
installApp
(
bool
installedAny
=
await
installApp
(
devices
,
applicationPackages
,
boot:
argResults
[
'boot'
]
...
...
@@ -28,13 +30,13 @@ class InstallCommand extends FlutterCommand {
}
}
bool
installApp
(
Future
<
bool
>
installApp
(
DeviceStore
devices
,
ApplicationPackageStore
applicationPackages
,
{
bool
boot:
false
})
{
if
(
boot
)
devices
.
iOSSimulator
?
.
boot
();
})
async
{
if
(
boot
&&
Platform
.
isMacOS
)
await
SimControl
.
boot
();
bool
installedSomewhere
=
false
;
...
...
packages/flutter_tools/lib/src/ios/device_ios.dart
View file @
67124dc3
...
...
@@ -14,6 +14,11 @@ import '../base/process.dart';
import
'../build_configuration.dart'
;
import
'../device.dart'
;
import
'../toolchain.dart'
;
import
'simulator.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
IOSDeviceDiscovery
extends
DeviceDiscovery
{
List
<
Device
>
_devices
=
<
Device
>[];
...
...
@@ -44,11 +49,6 @@ class IOSSimulatorDiscovery extends DeviceDiscovery {
class
IOSDevice
extends
Device
{
static
final
String
defaultDeviceID
=
'default_ios_id'
;
static
const
String
_macInstructions
=
'To work with iOS devices, please install ideviceinstaller. '
'If you use homebrew, you can install it with '
'"
\$
brew install ideviceinstaller".'
;
String
_installerPath
;
String
get
installerPath
=>
_installerPath
;
...
...
@@ -123,7 +123,7 @@ class IOSDevice extends Device {
static
final
Map
<
String
,
String
>
_commandMap
=
{};
static
String
_checkForCommand
(
String
command
,
[
String
macInstructions
=
_
mac
Instructions
String
macInstructions
=
_
ideviceinstaller
Instructions
])
{
return
_commandMap
.
putIfAbsent
(
command
,
()
{
try
{
...
...
@@ -263,78 +263,27 @@ class IOSDevice extends Device {
}
class
IOSSimulator
extends
Device
{
static
final
String
defaultDeviceID
=
'default_ios_sim_id'
;
static
const
String
_macInstructions
=
'To work with iOS devices, please install ideviceinstaller. '
'If you use homebrew, you can install it with '
'"
\$
brew install ideviceinstaller".'
;
static
String
_xcrunPath
=
path
.
join
(
'/usr'
,
'bin'
,
'xcrun'
);
String
_iOSSimPath
;
String
get
iOSSimPath
=>
_iOSSimPath
;
String
get
xcrunPath
=>
_xcrunPath
;
String
_name
;
String
get
name
=>
_name
;
factory
IOSSimulator
({
String
id
,
String
name
,
String
iOSSimulatorPath
})
{
IOSSimulator
device
=
Device
.
unique
(
id
??
defaultDeviceID
,
(
String
id
)
=>
new
IOSSimulator
.
fromId
(
id
));
factory
IOSSimulator
({
String
id
,
String
name
})
{
IOSSimulator
device
=
Device
.
unique
(
id
,
(
String
id
)
=>
new
IOSSimulator
.
fromId
(
id
));
device
.
_name
=
name
;
if
(
iOSSimulatorPath
==
null
)
{
iOSSimulatorPath
=
path
.
join
(
'/Applications'
,
'iOS Simulator.app'
,
'Contents'
,
'MacOS'
,
'iOS Simulator'
);
}
device
.
_iOSSimPath
=
iOSSimulatorPath
;
return
device
;
}
static
List
<
IOSSimulator
>
getAttachedDevices
()
{
return
SimControl
.
getConnectedDevices
().
map
((
SimDevice
device
)
{
return
new
IOSSimulator
(
id:
device
.
udid
,
name:
device
.
name
);
}).
toList
();
}
IOSSimulator
.
fromId
(
String
id
)
:
super
.
fromId
(
id
);
static
_IOSSimulatorInfo
_getRunningSimulatorInfo
([
IOSSimulator
mockIOS
])
{
String
xcrunPath
=
mockIOS
!=
null
?
mockIOS
.
xcrunPath
:
_xcrunPath
;
String
output
=
runCheckedSync
([
xcrunPath
,
'simctl'
,
'list'
,
'devices'
]);
Match
match
;
// iPhone 6s Plus (8AC808E1-6BAE-4153-BBC5-77F83814D414) (Booted)
Iterable
<
Match
>
matches
=
new
RegExp
(
r'[\W]*(.*) \(([^\)]+)\) \(Booted\)'
,
multiLine:
true
).
allMatches
(
output
);
if
(
matches
.
length
>
1
)
{
// More than one simulator is listed as booted, which is not allowed but
// sometimes happens erroneously. Kill them all because we don't know
// which one is actually running.
printError
(
'Multiple running simulators were detected, '
'which is not supposed to happen.'
);
for
(
Match
match
in
matches
)
{
if
(
match
.
groupCount
>
0
)
{
// TODO(devoncarew): We're killing simulator devices inside an accessor
// method; we probably shouldn't be changing state here.
printError
(
'Killing simulator
${match.group(1)}
'
);
runSync
([
xcrunPath
,
'simctl'
,
'shutdown'
,
match
.
group
(
2
)]);
}
}
}
else
if
(
matches
.
length
==
1
)
{
match
=
matches
.
first
;
}
String
_name
;
String
get
name
=>
_name
;
if
(
match
!=
null
&&
match
.
groupCount
>
0
)
{
return
new
_IOSSimulatorInfo
(
match
.
group
(
2
),
match
.
group
(
1
));
}
else
{
printTrace
(
'No running simulators found'
);
return
null
;
}
}
String
get
xcrunPath
=>
path
.
join
(
'/usr'
,
'bin'
,
'xcrun'
);
String
_getSimulatorPath
()
{
String
deviceID
=
id
==
defaultDeviceID
?
_getRunningSimulatorInfo
()?.
id
:
id
;
if
(
deviceID
==
null
)
return
null
;
return
path
.
join
(
_homeDirectory
,
'Library'
,
'Developer'
,
'CoreSimulator'
,
'Devices'
,
deviceID
);
return
path
.
join
(
_homeDirectory
,
'Library'
,
'Developer'
,
'CoreSimulator'
,
'Devices'
,
id
);
}
String
_getSimulatorAppHomeDirectory
(
ApplicationPackage
app
)
{
...
...
@@ -344,59 +293,13 @@ class IOSSimulator extends Device {
return
path
.
join
(
simulatorPath
,
'data'
);
}
static
List
<
IOSSimulator
>
getAttachedDevices
([
IOSSimulator
mockIOS
])
{
List
<
IOSSimulator
>
devices
=
[];
try
{
_IOSSimulatorInfo
deviceInfo
=
_getRunningSimulatorInfo
(
mockIOS
);
if
(
deviceInfo
!=
null
)
devices
.
add
(
new
IOSSimulator
(
id:
deviceInfo
.
id
,
name:
deviceInfo
.
name
));
}
catch
(
e
)
{
}
return
devices
;
}
Future
<
bool
>
boot
()
async
{
if
(!
Platform
.
isMacOS
)
return
false
;
if
(
isConnected
())
return
true
;
if
(
id
==
defaultDeviceID
)
{
runDetached
([
iOSSimPath
]);
Future
<
bool
>
checkConnection
([
int
attempts
=
20
])
async
{
if
(
attempts
==
0
)
{
printStatus
(
'Timed out waiting for iOS Simulator
$id
to boot.'
);
return
false
;
}
if
(!
isConnected
())
{
printStatus
(
'Waiting for iOS Simulator
$id
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'
,
id
]);
}
catch
(
e
)
{
printError
(
'Unable to boot iOS Simulator
$id
: '
,
e
);
return
false
;
}
}
return
false
;
}
@override
bool
installApp
(
ApplicationPackage
app
)
{
if
(!
isConnected
())
return
false
;
try
{
if
(
id
==
defaultDeviceID
)
{
runCheckedSync
([
xcrunPath
,
'simctl'
,
'install'
,
'booted'
,
app
.
localPath
]);
}
else
{
runCheckedSync
([
xcrunPath
,
'simctl'
,
'install'
,
id
,
app
.
localPath
]);
}
SimControl
.
install
(
id
,
app
.
localPath
);
return
true
;
}
catch
(
e
)
{
return
false
;
...
...
@@ -407,14 +310,7 @@ class IOSSimulator extends Device {
bool
isConnected
()
{
if
(!
Platform
.
isMacOS
)
return
false
;
_IOSSimulatorInfo
deviceInfo
=
_getRunningSimulatorInfo
();
if
(
deviceInfo
==
null
)
{
return
false
;
}
else
if
(
deviceInfo
.
id
==
defaultDeviceID
)
{
return
true
;
}
else
{
return
_getRunningSimulatorInfo
()?.
id
==
id
;
}
return
SimControl
.
getConnectedDevices
().
any
((
SimDevice
device
)
=>
device
.
udid
==
id
);
}
@override
...
...
@@ -462,29 +358,13 @@ class IOSSimulator extends Device {
}
// Step 3: Install the updated bundle to the simulator
int
installResult
=
await
runCommandAndStreamOutput
([
xcrunPath
,
'simctl'
,
'install'
,
id
==
defaultDeviceID
?
'booted'
:
id
,
path
.
absolute
(
bundle
.
path
)
]);
if
(
installResult
!=
0
)
{
printError
(
'Could not install the application bundle on the simulator'
);
return
false
;
}
SimControl
.
install
(
id
,
path
.
absolute
(
bundle
.
path
));
// Step 4: Launch the updated application in the simulator
runCheckedSync
([
xcrunPath
,
'simctl'
,
'launch'
,
id
==
defaultDeviceID
?
'booted'
:
id
,
app
.
id
]);
SimControl
.
launch
(
id
,
app
.
id
);
printTrace
(
'Successfully started
${app.name}
on
$id
'
);
return
true
;
}
...
...
@@ -623,13 +503,6 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
}
}
class
_IOSSimulatorInfo
{
final
String
id
;
final
String
name
;
_IOSSimulatorInfo
(
this
.
id
,
this
.
name
);
}
final
RegExp
_xcodeVersionRegExp
=
new
RegExp
(
r'Xcode (\d+)\..*'
);
final
String
_xcodeRequirement
=
'Xcode 7.0 or greater is required to develop for iOS.'
;
...
...
packages/flutter_tools/lib/src/ios/simulator.dart
0 → 100644
View file @
67124dc3
// 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/context.dart'
;
import
'../base/process.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'
;
}
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