@@ -5,22 +5,39 @@ import ws = require("ws");
55import stream = require( "stream" ) ;
66import path = require( "path" ) ;
77import http = require( "http" ) ;
8+ import Future = require( "fibers/future" ) ;
89
910module notification {
11+ function formatNotification ( bundleId : string , notification : string ) {
12+ return `${ bundleId } :NativeScript.Debug.${ notification } ` ;
13+ }
14+
1015 export function waitForDebug ( bundleId : string ) : string {
11- return bundleId + ":NativeScript.Debug. WaitForDebugger";
16+ return formatNotification ( bundleId , " WaitForDebugger") ;
1217 }
1318
1419 export function attachRequest ( bundleId : string ) : string {
15- return bundleId + ":NativeScript.Debug. AttachRequest";
20+ return formatNotification ( bundleId , " AttachRequest") ;
1621 }
1722
1823 export function appLaunching ( bundleId : string ) : string {
19- return bundleId + ":NativeScript.Debug. AppLaunching";
24+ return formatNotification ( bundleId , " AppLaunching") ;
2025 }
2126
2227 export function readyForAttach ( bundleId : string ) : string {
23- return bundleId + ":NativeScript.Debug.ReadyForAttach" ;
28+ return formatNotification ( bundleId , "ReadyForAttach" ) ;
29+ }
30+
31+ export function attachAvailabilityQuery ( bundleId : string ) {
32+ return formatNotification ( bundleId , "AttachAvailabilityQuery" ) ;
33+ }
34+
35+ export function alreadyConnected ( bundleId : string ) {
36+ return formatNotification ( bundleId , "AlreadyConnected" ) ;
37+ }
38+
39+ export function attachAvailable ( bundleId : string ) {
40+ return formatNotification ( bundleId , "AttachAvailable" ) ;
2441 }
2542}
2643
@@ -111,12 +128,27 @@ class IOSDebugService implements IDebugService {
111128 return ( ( ) => {
112129 this . $devicesServices . initialize ( { platform : this . platform , deviceId : this . $options . device } ) . wait ( ) ;
113130 this . $devicesServices . execute ( device => ( ( ) => {
114- this . $platformService . deployOnDevice ( this . platform ) . wait ( ) ;
115-
116- var iosDevice = < iOSDevice . IOSDevice > device ;
117- iosDevice . runApplication ( this . $projectData . projectId /* , ["--nativescript-debug-brk"] */ ) . wait ( ) ;
131+ // we intentionally do not wait on this here, because if we did, we'd miss the AppLaunching notification
132+ let deploy = this . $platformService . deployOnDevice ( this . platform ) ;
133+
134+ let iosDevice = < iOSDevice . IOSDevice > device ;
135+ let projectId = this . $projectData . projectId ;
136+ let npc = new iOSProxyServices . NotificationProxyClient ( iosDevice , this . $injector ) ;
137+
138+ try {
139+ awaitNotification ( npc , notification . appLaunching ( projectId ) , 60000 ) . wait ( ) ;
140+ process . nextTick ( ( ) => {
141+ npc . postNotificationAndAttachForData ( notification . waitForDebug ( projectId ) ) ;
142+ npc . postNotificationAndAttachForData ( notification . attachRequest ( projectId ) ) ;
143+ } ) ;
144+ awaitNotification ( npc , notification . readyForAttach ( projectId ) , 5000 ) . wait ( ) ;
145+ } catch ( e ) {
146+ this . $errors . failWithoutHelp ( "Timeout waiting for NativeScript debugger." ) ;
147+ }
148+
118149 createWebSocketProxy ( this . $logger , ( callback ) => connectEventually ( ( ) => iosDevice . connectToPort ( InspectorBackendPort ) , callback ) ) ;
119150 this . executeOpenDebuggerClient ( ) . wait ( ) ;
151+ deploy . wait ( ) ;
120152 } ) . future < void > ( ) ( ) ) . wait ( ) ;
121153 } ) . future < void > ( ) ( ) ;
122154 }
@@ -125,9 +157,38 @@ class IOSDebugService implements IDebugService {
125157 return ( ( ) => {
126158 this . $devicesServices . initialize ( { platform : this . platform , deviceId : this . $options . device } ) . wait ( ) ;
127159 this . $devicesServices . execute ( device => ( ( ) => {
128- var iosDevice = < iOSDevice . IOSDevice > device ;
129- createWebSocketProxy ( this . $logger , ( callback ) => connectEventually ( ( ) => iosDevice . connectToPort ( InspectorBackendPort ) , callback ) ) ;
130- this . executeOpenDebuggerClient ( ) . wait ( ) ;
160+ let iosDevice = < iOSDevice . IOSDevice > device ;
161+ let projectId = this . $projectData . projectId ;
162+ let npc = new iOSProxyServices . NotificationProxyClient ( iosDevice , this . $injector ) ;
163+
164+ let [ alreadyConnected , readyForAttach , attachAvailable ] = [
165+ notification . alreadyConnected ( projectId ) ,
166+ notification . readyForAttach ( projectId ) ,
167+ notification . attachAvailable ( projectId )
168+ ] . map ( ( notification ) => awaitNotification ( npc , notification , 2000 ) ) ;
169+
170+ npc . postNotificationAndAttachForData ( notification . attachAvailabilityQuery ( projectId ) ) ;
171+
172+ let receivedNotification : IFuture < string > ;
173+ try {
174+ receivedNotification = whenAny ( alreadyConnected , readyForAttach , attachAvailable ) . wait ( ) ;
175+ } catch ( e ) {
176+ this . $errors . failWithoutHelp ( `The application ${ projectId } does not appear to be running on ${ device . getDisplayName ( ) } or is not built with debugging enabled.` ) ;
177+ }
178+
179+ switch ( receivedNotification ) {
180+ case alreadyConnected :
181+ this . $errors . failWithoutHelp ( "A debugger is already connected." ) ;
182+ case attachAvailable :
183+ process . nextTick ( ( ) => npc . postNotificationAndAttachForData ( notification . attachRequest ( projectId ) ) ) ;
184+ try { awaitNotification ( npc , notification . readyForAttach ( projectId ) , 2000 ) . wait ( ) ; }
185+ catch ( e ) {
186+ this . $errors . failWithoutHelp ( `The application ${ projectId } timed out when performing the NativeScript debugger handshake.` ) ;
187+ }
188+ case readyForAttach :
189+ createWebSocketProxy ( this . $logger , ( callback ) => connectEventually ( ( ) => iosDevice . connectToPort ( InspectorBackendPort ) , callback ) ) ;
190+ this . executeOpenDebuggerClient ( ) . wait ( ) ;
191+ }
131192 } ) . future < void > ( ) ( ) ) . wait ( ) ;
132193 } ) . future < void > ( ) ( ) ;
133194 }
@@ -183,18 +244,16 @@ function createWebSocketProxy($logger: ILogger, socketFactory: (handler: (socket
183244 var server = ws . createServer ( < any > {
184245 port : localPort ,
185246 verifyClient : ( info : any , callback : any ) => {
247+ $logger . info ( "Frontend client connected." ) ;
186248 socketFactory ( ( socket ) => {
249+ $logger . info ( "Backend socket created." ) ;
187250 info . req [ "__deviceSocket" ] = socket ;
188251 callback ( true ) ;
189252 } ) ;
190253 }
191254 } ) ;
192255 server . on ( "connection" , ( webSocket ) => {
193- $logger . info ( "Frontend client connected." ) ;
194-
195256 var deviceSocket : net . Socket = ( < any > webSocket . upgradeReq ) [ "__deviceSocket" ] ;
196-
197- $logger . info ( "Backend socket created." ) ;
198257 var packets = new PacketStream ( ) ;
199258 deviceSocket . pipe ( packets ) ;
200259
@@ -225,48 +284,50 @@ function createWebSocketProxy($logger: ILogger, socketFactory: (handler: (socket
225284 return server ;
226285}
227286
228- class IOSDeviceDebugging {
229- private $notificationProxyClient : iOSProxyServices . NotificationProxyClient ;
230-
231- constructor ( private bundleId : string ,
232- private $iOSDevice : iOSDevice . IOSDevice ,
233- private $logger : ILogger ,
234- private $injector : IInjector ) {
235-
236- this . $notificationProxyClient = this . $injector . resolve ( iOSProxyServices . NotificationProxyClient , { device : this . $iOSDevice } )
237- }
238-
239- public debugApplicationOnStart ( ) {
240- var appLaunchMessage = notification . appLaunching ( this . bundleId ) ;
241- this . $notificationProxyClient . addObserver ( appLaunchMessage , ( ) => {
242- this . $logger . info ( "Got AppLaunching" ) ;
243- this . proxyDebuggingTraffic ( ) ;
244- var waitForDebuggerMessage = notification . waitForDebug ( this . bundleId ) ;
245- this . $notificationProxyClient . postNotificationAndAttachForData ( waitForDebuggerMessage ) ;
246- } ) ;
287+ function awaitNotification ( npc : iOSProxyServices . NotificationProxyClient , notification : string , timeout : number ) : IFuture < string > {
288+ let future = new Future < string > ( ) ;
289+
290+ let timeoutObject = setTimeout ( ( ) => {
291+ detachObserver ( ) ;
292+ future . throw ( new Error ( "Timeout receiving notification." ) ) ;
293+ } , timeout ) ;
294+
295+ function notificationObserver ( notification : string ) {
296+ clearTimeout ( timeoutObject ) ;
297+ detachObserver ( ) ;
298+ future . return ( notification ) ;
247299 }
248-
249- public debugRunningApplication ( ) {
250- this . proxyDebuggingTraffic ( ) ;
251- var attachRequestMessage = notification . attachRequest ( this . bundleId ) ;
252- this . $notificationProxyClient . postNotificationAndAttachForData ( attachRequestMessage ) ;
300+
301+ function detachObserver ( ) {
302+ process . nextTick ( ( ) => npc . removeObserver ( notification , notificationObserver ) ) ;
253303 }
304+
305+ npc . addObserver ( notification , notificationObserver ) ;
306+
307+ return future ;
308+ }
254309
255- private proxyDebuggingTraffic ( ) : void {
256- var identifier = this . $iOSDevice . getIdentifier ( ) ;
257- this . $logger . info ( "Device Identifier: " + identifier ) ;
310+ function whenAny < T > ( ... futures : IFuture < T > [ ] ) : IFuture < IFuture < T > > {
311+ let resultFuture = new Future < IFuture < T > > ( ) ;
312+ let futuresLeft = futures . length ;
258313
259- var readyForAttachMessage = notification . readyForAttach ( this . bundleId ) ;
260- this . $notificationProxyClient . addObserver ( readyForAttachMessage , ( ) => {
261- createWebSocketProxy ( this . $logger , ( callback ) => callback ( this . $iOSDevice . connectToPort ( InspectorBackendPort ) ) ) ;
314+ for ( let future of futures ) {
315+ var futureLocal = future ;
316+ future . resolve ( ( error , result ?) => {
317+ futuresLeft -- ;
318+
319+ if ( ! resultFuture . isResolved ( ) ) {
320+ if ( typeof error === "undefined" ) {
321+ resultFuture . return ( futureLocal ) ;
322+ } else if ( futuresLeft == 0 ) {
323+ resultFuture . throw ( new Error ( "None of the futures succeeded." ) ) ;
324+ }
325+ }
262326 } ) ;
263327 }
264-
265- private printHowToTerminate ( ) {
266- this . $logger . info ( "\nSetting up debugger proxy...\n\nPress Ctrl + C to terminate, or disconnect.\n" ) ;
267- }
328+
329+ return resultFuture ;
268330}
269- $injector . register ( "iosDeviceDebugging" , IOSDeviceDebugging ) ;
270331
271332class PacketStream extends stream . Transform {
272333 private buffer : Buffer ;
0 commit comments