Skip to content

Commit ab41a25

Browse files
author
Dimitar Kerezov
committed
Fix debugStop
1 parent 47b7c1a commit ab41a25

14 files changed

+220
-178
lines changed

PublicAPI.md

+98
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,8 @@ After calling the method once, you can add new devices to the same LiveSync oper
552552
553553
> NOTE: In case a consecutive call to `liveSync` method requires change in the pattern for watching files (i.e. `liveSyncData.syncAllFiles` option has changed), current watch operation will be stopped and a new one will be started.
554554
555+
> NOTE: In case `debugggingEnabled` is set to `true` in a deviceDescriptor, debugging will initially be enabled for that device and a debugger will be attached after a successful livesync operation.
556+
555557
* Definition
556558
```TypeScript
557559
/**
@@ -623,6 +625,92 @@ tns.liveSyncService.stopLiveSync(projectDir, deviceIdentifiers)
623625
});
624626
```
625627
628+
### enableDebugging
629+
Enables debugging during a LiveSync operation. This method will try to attach a debugger to the application. Note that `userInteractionNeeded` event may be raised. Additional details about the arguments can be seen [here](https://github.com/NativeScript/nativescript-cli/blob/master/lib/definitions/livesync.d.ts).
630+
631+
* Definition
632+
```TypeScript
633+
/**
634+
* Enables debugging for the specified devices
635+
* @param {IEnableDebuggingDeviceOptions[]} deviceOpts Settings used for enabling debugging for each device.
636+
* @param {IDebuggingAdditionalOptions} enableDebuggingOptions Settings used for enabling debugging.
637+
* @returns {Promise<void>[]} Array of promises for each device.
638+
*/
639+
enableDebugging(deviceOpts: IEnableDebuggingDeviceOptions[], enableDebuggingOptions: IDebuggingAdditionalOptions): Promise<void>[];
640+
```
641+
642+
* Usage
643+
```JavaScript
644+
const projectDir = "/tmp/myProject";
645+
const liveSyncData = { projectDir };
646+
const devices = [androidDeviceDescriptor, iOSDeviceDescriptor];
647+
tns.liveSyncService.liveSync(devices, liveSyncData)
648+
.then(() => {
649+
console.log("LiveSync operation started.");
650+
devices.forEach(device => {
651+
tns.liveSyncService.enableDebugging([{
652+
deviceIdentifier: device.identifier
653+
}], { projectDir });
654+
});
655+
});
656+
```
657+
658+
### attachDebugger
659+
Attaches a debugger to the specified device. Additional details about the argument can be seen [here](https://github.com/NativeScript/nativescript-cli/blob/master/lib/definitions/livesync.d.ts).
660+
661+
* Definition
662+
```TypeScript
663+
/**
664+
* Attaches a debugger to the specified device.
665+
* @param {IAttachDebuggerOptions} settings Settings used for controling the attaching process.
666+
* @returns {Promise<void>}
667+
*/
668+
attachDebugger(settings: IAttachDebuggerOptions): Promise<void>;
669+
```
670+
671+
* Usage
672+
```JavaScript
673+
tns.liveSyncService.on("userInteractionNeeded", data => {
674+
console.log("Please restart the app manually");
675+
return tns.liveSyncService.attachDebugger(data);
676+
});
677+
```
678+
679+
### disableDebugging
680+
Disables debugging during a LiveSync operation. This method will try to detach a debugger from the application. Additional details about the arguments can be seen [here](https://github.com/NativeScript/nativescript-cli/blob/master/lib/definitions/livesync.d.ts).
681+
682+
* Definition
683+
```TypeScript
684+
/**
685+
* Disables debugging for the specified devices
686+
* @param {IDisableDebuggingDeviceOptions[]} deviceOptions Settings used for disabling debugging for each device.
687+
* @param {IDebuggingAdditionalOptions} debuggingAdditionalOptions Settings used for disabling debugging.
688+
* @returns {Promise<void>[]} Array of promises for each device.
689+
*/
690+
disableDebugging(deviceOptions: IDisableDebuggingDeviceOptions[], debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<void>[];
691+
```
692+
693+
* Usage
694+
```JavaScript
695+
const projectDir = "/tmp/myProject";
696+
const liveSyncData = { projectDir };
697+
const devices = [androidDeviceDescriptor, iOSDeviceDescriptor];
698+
tns.liveSyncService.liveSync(devices, liveSyncData)
699+
.then(() => {
700+
console.log("LiveSync operation started.");
701+
devices.forEach(device => {
702+
tns.liveSyncService.enableDebugging([{
703+
deviceIdentifier: device.identifier
704+
}], { projectDir });
705+
setTimeout(() => {
706+
tns.liveSyncService.disableDebugging([{
707+
deviceIdentifier: device.identifier
708+
}], { projectDir });
709+
}, 1000 * 30);
710+
});
711+
});
712+
```
713+
626714
### Events
627715
`liveSyncService` raises several events in order to provide information for current state of the operation.
628716
* liveSyncStarted - raised whenever CLI starts a LiveSync operation for specific device. When `liveSync` method is called, the initial LiveSync operation will emit `liveSyncStarted` for each specified device. After that the event will be emitted only in case when liveSync method is called again with different device instances. The event is raised with the following data:
@@ -718,6 +806,16 @@ tns.liveSyncService.on("notify", data => {
718806
});
719807
```
720808
809+
* userInteractionNeeded - raised whenever CLI needs to restart an application but cannot so the user has to restart it manually. The event is raised with an object, which can later be passed to `attachDebugger` method of `liveSyncService`:
810+
811+
Example:
812+
```JavaScript
813+
tns.liveSyncService.on("userInteractionNeeded", data => {
814+
console.log("Please restart the app manually");
815+
return tns.liveSyncService.attachDebugger(data);
816+
});
817+
```
818+
721819
## How to add a new method to Public API
722820
CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification.
723821
For example the `$options` injected module contains information about all `--` options passed on the terminal. When the CLI is used as a library, the options are not populated. Before adding method to public API, make sure its implementation does not rely on `$options`.

lib/commands/debug.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { DebugCommandErrors } from "../constants";
66
export class DebugPlatformCommand implements ICommand {
77
public allowedParameters: ICommandParameter[] = [];
88

9-
constructor(private debugService: IPlatformDebugService,
10-
private platform: string,
9+
constructor(private platform: string,
10+
private $debugService: IDebugService,
1111
protected $devicesService: Mobile.IDevicesService,
1212
protected $platformService: IPlatformService,
1313
protected $projectData: IProjectData,
@@ -31,7 +31,7 @@ export class DebugPlatformCommand implements ICommand {
3131
debugData.deviceIdentifier = selectedDeviceForDebug.deviceInfo.identifier;
3232

3333
if (this.$options.start) {
34-
return this.$liveSyncService.printDebugInformation(await this.debugService.debug(debugData, debugOptions));
34+
return this.$liveSyncService.printDebugInformation(await this.$debugService.debug(debugData, debugOptions));
3535
}
3636

3737
await this.$devicesService.detectCurrentlyAttachedDevices({ shouldReturnImmediateResult: false, platform: this.platform });
@@ -125,7 +125,7 @@ export class DebugIOSCommand implements ICommand {
125125

126126
@cache()
127127
private get debugPlatformCommand(): DebugPlatformCommand {
128-
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { debugService: this.$iOSDebugService, platform: this.platform });
128+
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { platform: this.platform });
129129
}
130130

131131
public allowedParameters: ICommandParameter[] = [];
@@ -137,7 +137,6 @@ export class DebugIOSCommand implements ICommand {
137137
private $injector: IInjector,
138138
private $projectData: IProjectData,
139139
private $platformsData: IPlatformsData,
140-
private $iOSDebugService: IDebugService,
141140
$iosDeviceOperations: IIOSDeviceOperations) {
142141
this.$projectData.initializeProjectData();
143142
// Do not dispose ios-device-lib, so the process will remain alive and the debug application (NativeScript Inspector or Chrome DevTools) will be able to connect to the socket.
@@ -168,7 +167,7 @@ export class DebugAndroidCommand implements ICommand {
168167

169168
@cache()
170169
private get debugPlatformCommand(): DebugPlatformCommand {
171-
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { debugService: this.$androidDebugService, platform: this.platform });
170+
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { platform: this.platform });
172171
}
173172

174173
public allowedParameters: ICommandParameter[] = [];
@@ -179,8 +178,7 @@ export class DebugAndroidCommand implements ICommand {
179178
private $options: IOptions,
180179
private $injector: IInjector,
181180
private $projectData: IProjectData,
182-
private $platformsData: IPlatformsData,
183-
private $androidDebugService: IDebugService) {
181+
private $platformsData: IPlatformsData) {
184182
this.$projectData.initializeProjectData();
185183
}
186184

lib/common

lib/definitions/debug.d.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,13 @@ interface IDebugServiceBase extends NodeJS.EventEmitter {
110110
debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string>;
111111
}
112112

113-
interface IDebugService {
114-
getDebugService(platform: string): IPlatformDebugService;
113+
interface IDebugService extends IDebugServiceBase {
114+
/**
115+
* Stops debug operation for a specific device.
116+
* @param {string} deviceIdentifier Identifier of the device fo which debugging will be stopped.
117+
* @returns {Promise<void>}
118+
*/
119+
debugStop(deviceIdentifier: string): Promise<void>;
115120
}
116121

117122
/**

lib/definitions/livesync.d.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,27 @@ interface IDebugLiveSyncService extends ILiveSyncService {
203203
printDebugInformation(information: string): void;
204204

205205
/**
206-
* Enables debugging for a specified device
206+
* Enables debugging for the specified devices
207207
* @param {IEnableDebuggingDeviceOptions[]} deviceOpts Settings used for enabling debugging for each device.
208208
* @param {IDebuggingAdditionalOptions} enableDebuggingOptions Settings used for enabling debugging.
209-
* @returns {Promise<void>[]}
209+
* @returns {Promise<void>[]} Array of promises for each device.
210210
*/
211211
enableDebugging(deviceOpts: IEnableDebuggingDeviceOptions[], enableDebuggingOptions: IDebuggingAdditionalOptions): Promise<void>[];
212+
213+
/**
214+
* Disables debugging for the specified devices
215+
* @param {IDisableDebuggingDeviceOptions[]} deviceOptions Settings used for disabling debugging for each device.
216+
* @param {IDebuggingAdditionalOptions} debuggingAdditionalOptions Settings used for disabling debugging.
217+
* @returns {Promise<void>[]} Array of promises for each device.
218+
*/
219+
disableDebugging(deviceOptions: IDisableDebuggingDeviceOptions[], debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<void>[];
220+
221+
/**
222+
* Attaches a debugger to the specified device.
223+
* @param {IAttachDebuggerOptions} settings Settings used for controling the attaching process.
224+
* @returns {Promise<void>}
225+
*/
226+
attachDebugger(settings: IAttachDebuggerOptions): Promise<void>;
212227
}
213228

214229
/**

lib/services/android-debug-service.ts

+15-29
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,37 @@ import { sleep } from "../common/helpers";
22
import { DebugServiceBase } from "./debug-service-base";
33

44
export class AndroidDebugService extends DebugServiceBase implements IPlatformDebugService {
5-
private _device: Mobile.IAndroidDevice = null;
6-
5+
private _packageName: string;
76
public get platform() {
87
return "android";
98
}
109

11-
private get device(): Mobile.IAndroidDevice {
12-
return this._device;
13-
}
14-
15-
private set device(newDevice) {
16-
this._device = newDevice;
17-
}
18-
19-
constructor(protected $devicesService: Mobile.IDevicesService,
10+
constructor(protected device: Mobile.IAndroidDevice,
11+
protected $devicesService: Mobile.IDevicesService,
2012
private $errors: IErrors,
2113
private $logger: ILogger,
2214
private $androidDeviceDiscovery: Mobile.IDeviceDiscovery,
2315
private $androidProcessService: Mobile.IAndroidProcessService,
2416
private $net: INet) {
25-
super($devicesService);
17+
super(device, $devicesService);
2618
}
2719

2820
public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string> {
21+
this._packageName = debugData.applicationIdentifier;
2922
return debugOptions.emulator
3023
? this.debugOnEmulator(debugData, debugOptions)
3124
: this.debugOnDevice(debugData, debugOptions);
3225
}
3326

3427
public async debugStart(debugData: IDebugData, debugOptions: IDebugOptions): Promise<void> {
3528
await this.$devicesService.initialize({ platform: this.platform, deviceId: debugData.deviceIdentifier });
36-
let action = (device: Mobile.IAndroidDevice): Promise<void> => {
37-
this.device = device;
38-
return this.debugStartCore(debugData.applicationIdentifier, debugOptions);
39-
};
29+
const action = (device: Mobile.IAndroidDevice): Promise<void> => this.debugStartCore(debugData.applicationIdentifier, debugOptions);
4030

4131
await this.$devicesService.execute(action, this.getCanExecuteAction(debugData.deviceIdentifier));
4232
}
4333

44-
public async debugStop(): Promise<void> {
45-
return;
34+
public debugStop(): Promise<void> {
35+
return this.removePortForwarding();
4636
}
4737

4838
protected getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string {
@@ -61,6 +51,11 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe
6151
return this.debugOnDevice(debugData, debugOptions);
6252
}
6353

54+
private async removePortForwarding(packageName?: string): Promise<void> {
55+
const port = await this.getForwardedLocalDebugPortForPackageName(this.device.deviceInfo.identifier, packageName || this._packageName);
56+
return this.device.adb.executeCommand(["forward", "--remove", `tcp:${port}`]);
57+
}
58+
6459
private async getForwardedLocalDebugPortForPackageName(deviceId: string, packageName: string): Promise<number> {
6560
let port = -1;
6661
let forwardsResult = await this.device.adb.executeCommand(["forward", "--list"]);
@@ -103,14 +98,12 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe
10398
}
10499

105100
private async debugCore(device: Mobile.IAndroidDevice, packageFile: string, packageName: string, debugOptions: IDebugOptions): Promise<string> {
106-
this.device = device;
107-
108101
await this.printDebugPort(device.deviceInfo.identifier, packageName);
109102

110103
if (debugOptions.start) {
111104
return await this.attachDebugger(device.deviceInfo.identifier, packageName, debugOptions);
112105
} else if (debugOptions.stop) {
113-
await this.detachDebugger(packageName);
106+
await this.removePortForwarding();
114107
return null;
115108
} else {
116109
await this.debugStartCore(packageName, debugOptions);
@@ -128,18 +121,11 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe
128121
this.$errors.failWithoutHelp(`The application ${packageName} does not appear to be running on ${deviceId} or is not built with debugging enabled.`);
129122
}
130123

131-
const startDebuggerCommand = ["am", "broadcast", "-a", `\"${packageName}-debug\"`, "--ez", "enable", "true"];
132-
await this.device.adb.executeShellCommand(startDebuggerCommand);
133-
134124
const port = await this.getForwardedLocalDebugPortForPackageName(deviceId, packageName);
135125

136126
return this.getChromeDebugUrl(debugOptions, port);
137127
}
138128

139-
private detachDebugger(packageName: string): Promise<void> {
140-
return this.device.adb.executeShellCommand(["am", "broadcast", "-a", `${packageName}-debug`, "--ez", "enable", "false"]);
141-
}
142-
143129
private async debugStartCore(packageName: string, debugOptions: IDebugOptions): Promise<void> {
144130
// Arguments passed to executeShellCommand must be in array ([]), but it turned out adb shell "arg with intervals" still works correctly.
145131
// As we need to redirect output of a command on the device, keep using only one argument.
@@ -188,4 +174,4 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe
188174
}
189175
}
190176

191-
$injector.register("androidDebugService", AndroidDebugService);
177+
$injector.register("androidDebugService", AndroidDebugService, false);

lib/services/debug-service-base.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { EventEmitter } from "events";
22

33
export abstract class DebugServiceBase extends EventEmitter implements IPlatformDebugService {
4-
constructor(protected $devicesService: Mobile.IDevicesService) { super(); }
4+
constructor(
5+
protected device: Mobile.IDevice,
6+
protected $devicesService: Mobile.IDevicesService
7+
) {
8+
super();
9+
}
510

611
public abstract get platform(): string;
712

0 commit comments

Comments
 (0)