Skip to content

Commit 794f3e4

Browse files
authored
Merge pull request #96 from NativeScript/buhov/debug-with-livesync
Enable debugging with livesync
2 parents 6c8607b + bbc2186 commit 794f3e4

File tree

5 files changed

+59
-55
lines changed

5 files changed

+59
-55
lines changed

Diff for: src/debug-adapter/webKitDebugAdapter.ts

+21-25
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export class WebKitDebugAdapter implements DebugProtocol.IDebugAdapter {
6363
this._scriptsById = new Map<WebKitProtocol.Debugger.ScriptId, WebKitProtocol.Debugger.Script>();
6464
this._committedBreakpointsByUrl = new Map<string, WebKitProtocol.Debugger.BreakpointId[]>();
6565
this._setBreakpointsRequestQ = Promise.resolve<void>();
66+
this._lastOutputEvent = null;
6667
this.fireEvent({ seq: 0, type: 'event', event: 'clearTargetContext'});
6768
}
6869

@@ -121,7 +122,7 @@ export class WebKitDebugAdapter implements DebugProtocol.IDebugAdapter {
121122
Services.logger.log(`${args.request}(${JSON.stringify(args)})`);
122123
}
123124

124-
private processRequest(args: DebugProtocol.IRequestArgs) {
125+
private processRequest(args: DebugProtocol.IRequestArgs): Promise<void> {
125126
this.configureLoggingForRequest(args);
126127
// Initialize the request
127128
Services.appRoot = args.appRoot;
@@ -147,30 +148,23 @@ export class WebKitDebugAdapter implements DebugProtocol.IDebugAdapter {
147148
cliCommand.tnsProcess.on('close', (code, signal) => { Services.logger.error(`The tns command finished its execution with code ${code}.`, Tags.FrontendMessage); });
148149
}
149150

151+
let promiseResolve = null;
152+
let promise: Promise<void> = new Promise<void>((res, rej) => { promiseResolve = res; });
150153
// Attach to the running application
151-
let connectionEstablished = cliCommand.backendIsReadyForConnection.then((connectionToken: string | number) => {
152-
if (this._request.isAndroid) {
153-
Services.logger.log(`Attaching to application on port ${connectionToken}`);
154-
let androidConnection = new AndroidConnection();
155-
this.setConnection(androidConnection);
156-
let port: number = <number>connectionToken;
157-
return androidConnection.attach(port, 'localhost');
158-
}
159-
if (this._request.isIos) {
160-
Services.logger.log(`Attaching to application on socket path ${connectionToken}`);
161-
let iosConnection = new IosConnection();
162-
this.setConnection(iosConnection);
163-
let socketFilePath: string = <string>connectionToken;
164-
return iosConnection.attach(socketFilePath);
165-
}
154+
cliCommand.tnsOutputEventEmitter.on('readyForConnection', (connectionToken: string | number) => {
155+
connectionToken = this._request.isAndroid ? this._request.androidProject.getDebugPortSync() : connectionToken;
156+
Services.logger.log(`Attaching to application on ${connectionToken}`);
157+
let connection: INSDebugConnection = this._request.isAndroid ? new AndroidConnection() : new IosConnection();
158+
this.setConnection(connection);
159+
let attachPromise = this._request.isAndroid ? (<AndroidConnection>connection).attach(<number>connectionToken, 'localhost') : (<IosConnection>connection).attach(<string>connectionToken);
160+
attachPromise.then(() => {
161+
// Send InitializedEvent
162+
this.fireEvent(new InitializedEvent());
163+
promiseResolve();
164+
});
166165
});
167166

168-
// Send InitializedEvent
169-
return connectionEstablished.then(() => this.fireEvent(new InitializedEvent()), e => {
170-
Services.logger.error(`Error: ${e}`, Tags.FrontendMessage);
171-
this.clearEverything();
172-
return utils.errP(e);
173-
});
167+
return promise;
174168
}
175169

176170
private setConnection(connection: INSDebugConnection) : INSDebugConnection {
@@ -201,10 +195,12 @@ export class WebKitDebugAdapter implements DebugProtocol.IDebugAdapter {
201195
}
202196

203197
private terminateSession(): void {
204-
//this.fireEvent(new TerminatedEvent());
205-
206-
Services.logger.log("Terminating debug session");
207198
this.clearEverything();
199+
// In case of a sync request the session is not terminated when the backend is detached
200+
if (!this._request.isSync) {
201+
Services.logger.log("Terminating debug session");
202+
this.fireEvent(new TerminatedEvent());
203+
}
208204
}
209205

210206
private clearEverything(): void {

Diff for: src/project/androidProject.ts

+16-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {ChildProcess} from 'child_process';
22
import * as stream from 'stream';
3+
import {EventEmitter} from 'events';
34
import {Project, DebugResult} from './project';
45
import * as scanner from './streamScanner';
56
import {Version} from '../common/version';
@@ -18,11 +19,13 @@ export class AndroidProject extends Project {
1819
}
1920

2021
public attach(tnsArgs?: string[]): DebugResult {
21-
return { tnsProcess: null, backendIsReadyForConnection: this.getDebugPort().debugPort };
22+
let tnsOutputEventEmitter = new EventEmitter();
23+
setTimeout(() => tnsOutputEventEmitter.emit('readyForConnection')); // emit readyForConnection on the next tick
24+
return { tnsProcess: null, tnsOutputEventEmitter: tnsOutputEventEmitter };
2225
}
2326

2427
public debugWithSync(options: { stopOnEntry: boolean, syncAllFiles: boolean }, tnsArgs?: string[]): DebugResult {
25-
let args: string[] = ["--no-rebuild"];
28+
let args: string[] = ["--no-rebuild", "--watch"];
2629
if (options.syncAllFiles) { args.push("--syncAllFiles"); }
2730
args = args.concat(tnsArgs);
2831

@@ -35,26 +38,21 @@ export class AndroidProject extends Project {
3538
args = args.concat(tnsArgs);
3639

3740
let debugProcess : ChildProcess = super.executeDebugCommand(args);
38-
let backendIsReady = new scanner.StringMatchingScanner(debugProcess.stdout).nextMatch('# NativeScript Debugger started #').then(_ => {
39-
// wait a little before trying to connect, this gives a chance for adb to be able to connect to the debug socket
40-
return new Promise((resolve, reject) => setTimeout(() => { resolve(); }, 500));
41-
}).then(() => this.getDebugPort().debugPort);
42-
43-
return { tnsProcess: debugProcess, backendIsReadyForConnection: backendIsReady };
41+
let tnsOutputEventEmitter: EventEmitter = new EventEmitter();
42+
this.configureReadyEvent(debugProcess.stdout, tnsOutputEventEmitter);
43+
return { tnsProcess: debugProcess, tnsOutputEventEmitter: tnsOutputEventEmitter };
4444
}
4545

46-
private getDebugPort(): GetDebugPortResult {
47-
let debugProcess : ChildProcess = super.executeDebugCommand(["--get-port"]);
48-
let portNumberPromise: Promise<number> = this.waitForPortNumber(debugProcess.stdout);
49-
return { tnsProcess: debugProcess, debugPort: portNumberPromise };
46+
public getDebugPortSync(): number {
47+
let output = this.cli.executeSync(["debug", "android", "--get--port"], this.appRoot);
48+
let port = parseInt(output.match("(?:debug port: )([\\d]{5})")[1]);
49+
return port;
5050
}
5151

52-
private waitForPortNumber(readableStream: stream.Readable): Promise<number> {
53-
let streamScanner = new scanner.StringMatchingScanner(readableStream);
54-
return streamScanner.nextMatch(new RegExp("(?:debug port: )([\\d]{5})")).then((match: scanner.MatchFound) => {
55-
let portNumber = parseInt(<string>match.matches[1]);
56-
streamScanner.stop();
57-
return portNumber;
52+
private configureReadyEvent(readableStream: stream.Readable, eventEmitter: EventEmitter): void {
53+
new scanner.StringMatchingScanner(readableStream).onEveryMatch('# NativeScript Debugger started #', (match: scanner.MatchFound) => {
54+
// wait a little before trying to connect, this gives a chance for adb to be able to connect to the debug socket
55+
setTimeout(() => { eventEmitter.emit('readyForConnection'); }, 500);
5856
});
5957
}
6058
}

Diff for: src/project/iosProject.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {ChildProcess} from 'child_process';
22
import * as stream from 'stream';
3+
import {EventEmitter} from 'events';
34
import {Project, DebugResult} from './project';
45
import * as scanner from './streamScanner';
56
import {Version} from '../common/version';
@@ -24,12 +25,13 @@ export class IosProject extends Project {
2425
args = args.concat(tnsArgs);
2526

2627
let debugProcess : ChildProcess = super.executeDebugCommand(args);
27-
let socketPathPromise = this.waitForSocketPath(debugProcess.stdout);
28-
return { tnsProcess: debugProcess, backendIsReadyForConnection: socketPathPromise };
28+
let tnsOutputEventEmitter: EventEmitter = new EventEmitter();
29+
this.configureReadyEvent(debugProcess.stdout, tnsOutputEventEmitter);
30+
return { tnsProcess: debugProcess, tnsOutputEventEmitter: tnsOutputEventEmitter };
2931
}
3032

3133
public debugWithSync(options: { stopOnEntry: boolean, syncAllFiles: boolean }, tnsArgs?: string[]): DebugResult {
32-
let args: string[] = ["--no-rebuild"];
34+
let args: string[] = ["--no-rebuild", "--watch"];
3335
if (options.syncAllFiles) { args.push("--syncAllFiles"); }
3436
args = args.concat(tnsArgs);
3537

@@ -42,18 +44,17 @@ export class IosProject extends Project {
4244
args = args.concat(tnsArgs);
4345

4446
let debugProcess : ChildProcess = super.executeDebugCommand(args);
45-
let socketPathPromise = this.waitForSocketPath(debugProcess.stdout);
46-
return { tnsProcess: debugProcess, backendIsReadyForConnection: socketPathPromise };
47+
let tnsOutputEventEmitter: EventEmitter = new EventEmitter();
48+
this.configureReadyEvent(debugProcess.stdout, tnsOutputEventEmitter);
49+
return { tnsProcess: debugProcess, tnsOutputEventEmitter: tnsOutputEventEmitter };
4750
}
4851

49-
private waitForSocketPath(readableStream: stream.Readable): Promise<string> {
52+
private configureReadyEvent(readableStream: stream.Readable, eventEmitter: EventEmitter): void {
5053
let socketPathPrefix = 'socket-file-location: ';
5154
let streamScanner = new scanner.StringMatchingScanner(readableStream);
52-
53-
return streamScanner.nextMatch(new RegExp(socketPathPrefix + '.*\.sock')).then((match: scanner.MatchFound) => {
55+
streamScanner.onEveryMatch(new RegExp(socketPathPrefix + '.*\.sock'), (match: scanner.MatchFound) => {
5456
let socketPath = (<string>match.matches[0]).substr(socketPathPrefix.length);
55-
streamScanner.stop();
56-
return socketPath;
57+
eventEmitter.emit('readyForConnection', socketPath);
5758
});
5859
}
5960

Diff for: src/project/project.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import {ChildProcess} from 'child_process';
2+
import {EventEmitter} from 'events';
23
import {Version} from '../common/version';
34
import {NativeScriptCli} from './nativeScriptCli';
45

5-
export type DebugResult = { tnsProcess: ChildProcess, backendIsReadyForConnection: Promise<any> };
6+
export type DebugResult = { tnsProcess: ChildProcess, tnsOutputEventEmitter: EventEmitter };
67

78
export abstract class Project {
89
private _appRoot: string;
@@ -13,7 +14,7 @@ export abstract class Project {
1314
this._cli = cli;
1415
}
1516

16-
public get rootPath(): string { return this._appRoot; }
17+
public get appRoot(): string { return this._appRoot; }
1718

1819
public get cli(): NativeScriptCli { return this._cli; }
1920

Diff for: src/project/streamScanner.ts

+8
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ export class StringMatchingScanner extends StreamScanner {
6161
this._metas = [];
6262
}
6363

64+
public onEveryMatch(test: string | RegExp, handler: (result: MatchFound) => void) {
65+
let handlerWrapper = (result: MatchFound) => {
66+
handler(result);
67+
this.nextMatch(test).then(handlerWrapper);
68+
};
69+
this.nextMatch(test).then(handlerWrapper);
70+
}
71+
6472
public nextMatch(test: string | RegExp): Promise<MatchFound> {
6573
let meta: MatchMeta = {
6674
test: test,

0 commit comments

Comments
 (0)