Skip to content

Commit 0b89d3e

Browse files
Expose enable/disable debugging (#3065)
Expose enable/disable debugging when CLI is required as a library. Includes: * Unify `debug-livesync-service` and `livesync-service` into one class. Enable said class to attach a debugger at runtime during LiveSync * Remove redundant `debugLivesync` property from `$config` and its every usage * Remove redundant `debugStop` code in `android-debug-service` * Raise events in the event of inability to start/stop the app on iOS
1 parent 9fb0efc commit 0b89d3e

30 files changed

+532
-400
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
### getLiveSyncDeviceDescriptors
627715
Gives information for currently running LiveSync operation and parameters used to start it on each device.
628716
@@ -741,6 +829,16 @@ tns.liveSyncService.on("notify", data => {
741829
});
742830
```
743831
832+
* 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`:
833+
834+
Example:
835+
```JavaScript
836+
tns.liveSyncService.on("userInteractionNeeded", data => {
837+
console.log("Please restart the app manually");
838+
return tns.liveSyncService.attachDebugger(data);
839+
});
840+
```
841+
744842
## How to add a new method to Public API
745843
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.
746844
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/bootstrap.ts

-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ $injector.requireCommand("platform|clean", "./commands/platform-clean");
106106

107107
$injector.requirePublicClass("liveSyncService", "./services/livesync/livesync-service");
108108
$injector.require("liveSyncCommandHelper", "./services/livesync/livesync-command-helper");
109-
$injector.require("debugLiveSyncService", "./services/livesync/debug-livesync-service");
110109
$injector.require("androidLiveSyncService", "./services/livesync/android-livesync-service");
111110
$injector.require("iOSLiveSyncService", "./services/livesync/ios-livesync-service");
112111
$injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); // The name is used in https://github.com/NativeScript/nativescript-dev-typescript

lib/commands/debug.ts

+15-15
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,
@@ -16,31 +16,29 @@ export class DebugPlatformCommand implements ICommand {
1616
protected $logger: ILogger,
1717
protected $errors: IErrors,
1818
private $debugDataService: IDebugDataService,
19-
private $debugLiveSyncService: IDebugLiveSyncService,
20-
private $config: IConfiguration,
19+
private $liveSyncService: IDebugLiveSyncService,
2120
private $prompter: IPrompter,
2221
private $liveSyncCommandHelper: ILiveSyncCommandHelper) {
2322
}
2423

2524
public async execute(args: string[]): Promise<void> {
26-
const debugOptions = this.$options;
25+
const debugOptions = <IDebugOptions>_.cloneDeep(this.$options.argv);
2726

2827
let debugData = this.$debugDataService.createDebugData(this.$projectData, this.$options);
2928

3029
await this.$platformService.trackProjectType(this.$projectData);
31-
3230
const selectedDeviceForDebug = await this.getDeviceForDebug();
3331
debugData.deviceIdentifier = selectedDeviceForDebug.deviceInfo.identifier;
3432

3533
if (this.$options.start) {
36-
return this.$debugLiveSyncService.printDebugInformation(await this.debugService.debug(debugData, debugOptions));
34+
return this.$liveSyncService.printDebugInformation(await this.$debugService.debug(debugData, debugOptions));
3735
}
3836

39-
this.$config.debugLivesync = true;
40-
4137
await this.$devicesService.detectCurrentlyAttachedDevices({ shouldReturnImmediateResult: false, platform: this.platform });
4238

43-
await this.$liveSyncCommandHelper.executeLiveSyncOperation([selectedDeviceForDebug], this.$debugLiveSyncService, this.platform);
39+
await this.$liveSyncCommandHelper.executeLiveSyncOperation([selectedDeviceForDebug], this.platform, {
40+
[selectedDeviceForDebug.deviceInfo.identifier]: true
41+
});
4442
}
4543

4644
public async getDeviceForDebug(): Promise<Mobile.IDevice> {
@@ -104,6 +102,10 @@ export class DebugPlatformCommand implements ICommand {
104102
this.$errors.fail(`Applications for platform ${this.platform} can not be built on this OS`);
105103
}
106104

105+
if (this.$options.release) {
106+
this.$errors.fail("--release flag is not applicable to this command");
107+
}
108+
107109
const platformData = this.$platformsData.getPlatformData(this.platform, this.$projectData);
108110
const platformProjectService = platformData.platformProjectService;
109111
await platformProjectService.validate(this.$projectData);
@@ -123,7 +125,7 @@ export class DebugIOSCommand implements ICommand {
123125

124126
@cache()
125127
private get debugPlatformCommand(): DebugPlatformCommand {
126-
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { debugService: this.$iOSDebugService, platform: this.platform });
128+
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { platform: this.platform });
127129
}
128130

129131
public allowedParameters: ICommandParameter[] = [];
@@ -135,7 +137,6 @@ export class DebugIOSCommand implements ICommand {
135137
private $injector: IInjector,
136138
private $projectData: IProjectData,
137139
private $platformsData: IPlatformsData,
138-
private $iOSDebugService: IDebugService,
139140
$iosDeviceOperations: IIOSDeviceOperations) {
140141
this.$projectData.initializeProjectData();
141142
// 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.
@@ -166,7 +167,7 @@ export class DebugAndroidCommand implements ICommand {
166167

167168
@cache()
168169
private get debugPlatformCommand(): DebugPlatformCommand {
169-
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { debugService: this.$androidDebugService, platform: this.platform });
170+
return this.$injector.resolve<DebugPlatformCommand>(DebugPlatformCommand, { platform: this.platform });
170171
}
171172

172173
public allowedParameters: ICommandParameter[] = [];
@@ -177,8 +178,7 @@ export class DebugAndroidCommand implements ICommand {
177178
private $options: IOptions,
178179
private $injector: IInjector,
179180
private $projectData: IProjectData,
180-
private $platformsData: IPlatformsData,
181-
private $androidDebugService: IDebugService) {
181+
private $platformsData: IPlatformsData) {
182182
this.$projectData.initializeProjectData();
183183
}
184184

lib/commands/run.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class RunCommandBase implements ICommand {
5959
await this.$devicesService.detectCurrentlyAttachedDevices({ shouldReturnImmediateResult: false, platform: this.platform });
6060
let devices = this.$devicesService.getDeviceInstances();
6161
devices = devices.filter(d => !this.platform || d.deviceInfo.platform.toLowerCase() === this.platform.toLowerCase());
62-
await this.$liveSyncCommandHelper.executeLiveSyncOperation(devices, this.$liveSyncService, this.platform);
62+
await this.$liveSyncCommandHelper.executeLiveSyncOperation(devices, this.platform);
6363
}
6464
}
6565

lib/config.ts

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export class Configuration extends ConfigBase implements IConfiguration { // Use
88
TYPESCRIPT_COMPILER_OPTIONS = {};
99
ANDROID_DEBUG_UI: string = null;
1010
USE_POD_SANDBOX: boolean = false;
11-
debugLivesync: boolean = false;
1211

1312
/*don't require logger and everything that has logger as dependency in config.js due to cyclic dependency*/
1413
constructor(protected $fs: IFileSystem) {

lib/constants.ts

+3
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,16 @@ export const ANGULAR_NAME = "angular";
8585
export const TYPESCRIPT_NAME = "typescript";
8686
export const BUILD_OUTPUT_EVENT_NAME = "buildOutput";
8787
export const CONNECTION_ERROR_EVENT_NAME = "connectionError";
88+
export const USER_INTERACTION_NEEDED_EVENT_NAME = "userInteractionNeeded";
89+
export const DEBUGGER_ATTACHED_EVENT_NAME = "debuggerAttached";
8890
export const VERSION_STRING = "version";
8991
export const INSPECTOR_CACHE_DIRNAME = "ios-inspector";
9092
export const POST_INSTALL_COMMAND_NAME = "post-install-cli";
9193

9294
export class DebugCommandErrors {
9395
public static UNABLE_TO_USE_FOR_DEVICE_AND_EMULATOR = "The options --for-device and --emulator cannot be used simultaneously. Please use only one of them.";
9496
public static NO_DEVICES_EMULATORS_FOUND_FOR_OPTIONS = "Unable to find device or emulator for specified options.";
97+
public static UNSUPPORTED_DEVICE_OS_FOR_DEBUGGING = "Unsupported device OS for debugging";
9598
}
9699

97100
export const enum NativePlatformStatus {

lib/declarations.d.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,6 @@ interface IStaticConfig extends Config.IStaticConfig { }
311311
interface IConfiguration extends Config.IConfig {
312312
ANDROID_DEBUG_UI: string;
313313
USE_POD_SANDBOX: boolean;
314-
debugLivesync: boolean;
315314
}
316315

317316
interface IApplicationPackage {
@@ -405,8 +404,7 @@ interface IDeviceEmulator extends IEmulator, IDeviceIdentifier { }
405404

406405
interface IRunPlatformOptions extends IJustLaunch, IDeviceEmulator { }
407406

408-
interface IDeployPlatformOptions extends IAndroidReleaseOptions, IPlatformTemplate, IRelease, IClean, IDeviceEmulator, IProvision, ITeamIdentifier {
409-
projectDir: string;
407+
interface IDeployPlatformOptions extends IAndroidReleaseOptions, IPlatformTemplate, IRelease, IClean, IDeviceEmulator, IProvision, ITeamIdentifier, IProjectDir {
410408
forceInstall?: boolean;
411409
}
412410

lib/definitions/debug.d.ts

+10-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
/**
22
* Describes information for starting debug process.
33
*/
4-
interface IDebugData {
5-
/**
6-
* Id of the device on which the debug process will be started.
7-
*/
8-
deviceIdentifier: string;
9-
4+
interface IDebugData extends Mobile.IDeviceIdentifier {
105
/**
116
* Application identifier of the app that it will be debugged.
127
*/
@@ -115,14 +110,19 @@ interface IDebugServiceBase extends NodeJS.EventEmitter {
115110
debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string>;
116111
}
117112

118-
interface IDebugService {
119-
getDebugService(device: Mobile.IDevice): 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>;
120120
}
121121

122122
/**
123123
* Describes actions required for debugging on specific platform (Android or iOS).
124124
*/
125-
interface IPlatformDebugService extends IDebugServiceBase {
125+
interface IPlatformDebugService extends IDebugServiceBase, IPlatform {
126126
/**
127127
* Starts debug operation.
128128
* @param {IDebugData} debugData Describes information for device and application that will be debugged.
@@ -135,10 +135,5 @@ interface IPlatformDebugService extends IDebugServiceBase {
135135
* Stops debug operation.
136136
* @returns {Promise<void>}
137137
*/
138-
debugStop(): Promise<void>
139-
140-
/**
141-
* Mobile platform of the device - Android or iOS.
142-
*/
143-
platform: string;
138+
debugStop(): Promise<void>;
144139
}

lib/definitions/emulator-platform-service.d.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
interface IEmulatorInfo {
1+
interface IEmulatorInfo extends IPlatform {
22
name: string;
33
version: string;
4-
platform: string;
54
id: string;
65
type: string;
76
isRunning?: boolean;

0 commit comments

Comments
 (0)