1
1
///<reference path="../../../.d.ts"/>
2
2
"use strict" ;
3
3
4
- import util = require( "util" ) ;
5
4
import Future = require( "fibers/future" ) ;
5
+ import osenv = require( "osenv" ) ;
6
+ import path = require( "path" ) ;
7
+ import shell = require( "shelljs" ) ;
8
+ import util = require( "util" ) ;
6
9
7
10
class IosEmulatorServices implements Mobile . IiOSSimulatorService {
11
+ private _cachedSimulatorId : string ;
12
+
8
13
constructor ( private $logger : ILogger ,
9
14
private $emulatorSettingsService : Mobile . IEmulatorSettingsService ,
10
15
private $errors : IErrors ,
11
16
private $childProcess : IChildProcess ,
12
17
private $mobileHelper : Mobile . IMobileHelper ,
13
18
private $devicePlatformsConstants : Mobile . IDevicePlatformsConstants ,
14
19
private $hostInfo : IHostInfo ,
15
- private $options : IOptions ) { }
20
+ private $options : IOptions ,
21
+ private $fs : IFileSystem ,
22
+ private $bplistParser : IBinaryPlistParser ) { }
16
23
17
24
public checkDependencies ( ) : IFuture < void > {
18
25
return ( ( ) => {
19
26
} ) . future < void > ( ) ( ) ;
20
27
}
21
28
22
- checkAvailability ( dependsOnProject : boolean = true ) : IFuture < void > {
29
+ public checkAvailability ( dependsOnProject : boolean = true ) : IFuture < void > {
23
30
return ( ( ) => {
24
31
if ( ! this . $hostInfo . isDarwin ) {
25
32
this . $errors . fail ( "iOS Simulator is available only on Mac OS X." ) ;
@@ -32,7 +39,7 @@ class IosEmulatorServices implements Mobile.IiOSSimulatorService {
32
39
} ) . future < void > ( ) ( ) ;
33
40
}
34
41
35
- startEmulator ( app : string , emulatorOptions ?: Mobile . IEmulatorOptions ) : IFuture < void > {
42
+ public startEmulator ( app : string , emulatorOptions ?: Mobile . IEmulatorOptions ) : IFuture < void > {
36
43
return ( ( ) => {
37
44
this . killLaunchdSim ( ) . wait ( ) ;
38
45
this . startEmulatorCore ( app , emulatorOptions ) ;
@@ -51,6 +58,30 @@ class IosEmulatorServices implements Mobile.IiOSSimulatorService {
51
58
52
59
return this . $childProcess . exec ( `${ nodeCommandName } ${ iosSimPath } ${ opts . join ( ' ' ) } ` ) ;
53
60
}
61
+
62
+ public sync ( appIdentifier : string , projectFilesPath : string , notRunningSimulatorAction : ( ) => IFuture < void > ) : IFuture < void > {
63
+ let syncAction = ( applicationPath : string ) => shell . cp ( "-Rf" , projectFilesPath , applicationPath ) ;
64
+ return this . syncCore ( appIdentifier , notRunningSimulatorAction , syncAction ) ;
65
+ }
66
+
67
+ public syncFiles ( appIdentifier : string , projectFilesPath : string , projectFiles : string [ ] , notRunningSimulatorAction : ( ) => IFuture < void > ) : IFuture < void > {
68
+ let syncAction = ( applicationPath : string ) => _ . each ( projectFiles , projectFile => {
69
+ this . $logger . trace ( `Transfering ${ projectFile } to ${ path . join ( applicationPath , "app" ) } ` ) ;
70
+ shell . cp ( "-Rf" , projectFile , path . join ( applicationPath , "app" ) ) ;
71
+ } ) ;
72
+ return this . syncCore ( appIdentifier , notRunningSimulatorAction , syncAction ) ;
73
+ }
74
+
75
+ public isSimulatorRunning ( ) : IFuture < boolean > {
76
+ return ( ( ) => {
77
+ try {
78
+ let output = this . $childProcess . exec ( "ps cax | grep launchd_sim" ) . wait ( ) ;
79
+ return output . indexOf ( 'launchd_sim' ) !== - 1 ;
80
+ } catch ( e ) {
81
+ return false ;
82
+ }
83
+ } ) . future < boolean > ( ) ( ) ;
84
+ }
54
85
55
86
private killLaunchdSim ( ) : IFuture < void > {
56
87
this . $logger . info ( "Cleaning up before starting the iOS Simulator" ) ;
@@ -104,5 +135,80 @@ class IosEmulatorServices implements Mobile.IiOSSimulatorService {
104
135
105
136
this . $childProcess . spawn ( nodeCommandName , opts , { stdio : "inherit" } ) ;
106
137
}
138
+
139
+ private getRunningSimulatorId ( appIdentifier : string ) : IFuture < string > {
140
+ return ( ( ) : string => {
141
+ if ( this . $options . device ) {
142
+ this . _cachedSimulatorId = this . $options . device ;
143
+ }
144
+
145
+ if ( ! this . _cachedSimulatorId ) {
146
+ let output = this . $childProcess . exec ( "xcrun simctl list" ) . wait ( ) ;
147
+ let lines = output . split ( "\n" ) ;
148
+ let regex = / [ \s \S ] + ?\( ( [ 0 - 9 A - F \- ] + ?) \) \s + ?\( B o o t e d \) / ;
149
+ _ . each ( lines , ( line : string ) => {
150
+ let match : any = regex . exec ( line ) ;
151
+ if ( match ) {
152
+ this . _cachedSimulatorId = match [ 1 ] ;
153
+ return false ;
154
+ }
155
+ } ) ;
156
+
157
+ if ( ! this . _cachedSimulatorId ) {
158
+ regex = / [ \s \S ] + ?\( ( [ 0 - 9 A - F \- ] + ?) \) \s + ?\( S h u t d o w n \) / ;
159
+ _ . each ( lines , ( line : string ) => {
160
+ let match : any = regex . exec ( line ) ;
161
+ if ( match ) {
162
+ this . _cachedSimulatorId = match [ 1 ] ;
163
+ return false ;
164
+ }
165
+ } ) ;
166
+ }
167
+ }
168
+
169
+ return this . _cachedSimulatorId ;
170
+ } ) . future < string > ( ) ( ) ;
171
+ }
172
+
173
+ private getApplicationPath ( appIdentifier : string , runningSimulatorId : string ) : IFuture < string > {
174
+ return ( ( ) => {
175
+ let rootApplicationsPath = path . join ( osenv . home ( ) , `/Library/Developer/CoreSimulator/Devices/${ runningSimulatorId } /data/Containers/Bundle/Application` ) ;
176
+ let applicationGuids = this . $fs . readDirectory ( rootApplicationsPath ) . wait ( ) ;
177
+ let result : string = null ;
178
+ _ . each ( applicationGuids , applicationGuid => {
179
+ let fullApplicationPath = path . join ( rootApplicationsPath , applicationGuid ) ;
180
+ let applicationDirContents = this . $fs . readDirectory ( fullApplicationPath ) . wait ( ) ;
181
+ let applicationName = _ . find ( applicationDirContents , fileName => path . extname ( fileName ) === ".app" ) ;
182
+ let plistFilePath = path . join ( fullApplicationPath , applicationName , "Info.plist" ) ;
183
+ let applicationData = this . $bplistParser . parseFile ( plistFilePath ) . wait ( ) ;
184
+ if ( applicationData [ 0 ] . CFBundleIdentifier === appIdentifier ) {
185
+ result = path . join ( fullApplicationPath , applicationName ) ;
186
+ return false ;
187
+ }
188
+ } ) ;
189
+
190
+ return result ;
191
+ } ) . future < string > ( ) ( ) ;
192
+ }
193
+
194
+ private syncCore ( appIdentifier : string , notRunningSimulatorAction : ( ) => IFuture < void > , syncAction : ( applicationPath : string ) => void ) : IFuture < void > {
195
+ return ( ( ) => {
196
+ if ( ! this . isSimulatorRunning ( ) . wait ( ) ) {
197
+ notRunningSimulatorAction ( ) . wait ( ) ;
198
+ }
199
+
200
+ let runningSimulatorId = this . getRunningSimulatorId ( appIdentifier ) . wait ( ) ;
201
+ let applicationPath = this . getApplicationPath ( appIdentifier , runningSimulatorId ) . wait ( ) ;
202
+ syncAction ( applicationPath ) ;
203
+
204
+ try {
205
+ this . $childProcess . exec ( "killall -KILL launchd_sim" ) . wait ( ) ;
206
+ this . $childProcess . exec ( `xcrun simctl launch ${ runningSimulatorId } ${ appIdentifier } ` ) . wait ( ) ;
207
+ } catch ( e ) {
208
+ this . $logger . trace ( "Unable to kill simulator: " + e ) ;
209
+ }
210
+
211
+ } ) . future < void > ( ) ( ) ;
212
+ }
107
213
}
108
214
$injector . register ( "iOSEmulatorServices" , IosEmulatorServices ) ;
0 commit comments