Skip to content

Commit 64427e9

Browse files
committed
refactor: extract debug specific logic to separate service
1 parent 22f594e commit 64427e9

File tree

7 files changed

+224
-175
lines changed

7 files changed

+224
-175
lines changed

lib/bootstrap.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ $injector.require("platformWatcherService", "./services/platform/platform-watche
4444
$injector.require("buildArtefactsService", "./services/build-artefacts-service");
4545

4646
$injector.require("runOnDevicesDataService", "./services/run-on-devices-data-service");
47+
$injector.require("runOnDevicesEmitter", "./run-on-devices-emitter");
48+
4749
$injector.require("deviceInstallAppService", "./services/device/device-install-app-service");
4850
$injector.require("deviceRefreshAppService", "./services/device/device-refresh-app-service");
51+
$injector.require("deviceDebugAppService", "./services/device/device-debug-app-service");
4952

5053
$injector.require("workflowDataService", "./services/workflow/workflow-data-service");
5154

lib/controllers/debug-on-devices-controller.ts

Whitespace-only changes.

lib/controllers/run-on-devices-controller.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
1-
import { WorkflowDataService } from "../services/workflow/workflow-data-service";
21
import { BuildPlatformService } from "../services/platform/build-platform-service";
2+
import { DeviceDebugAppService } from "../services/device/device-debug-app-service";
33
import { DeviceInstallAppService } from "../services/device/device-install-app-service";
44
import { DeviceRefreshAppService } from "../services/device/device-refresh-app-service";
5-
import { LiveSyncServiceResolver } from "../resolvers/livesync-service-resolver";
65
import { EventEmitter } from "events";
6+
import { LiveSyncServiceResolver } from "../resolvers/livesync-service-resolver";
77
import { RunOnDevicesDataService } from "../services/run-on-devices-data-service";
8-
import { RunOnDeviceEvents } from "../constants";
8+
import { RunOnDevicesEmitter } from "../run-on-devices-emitter";
9+
import { WorkflowDataService } from "../services/workflow/workflow-data-service";
910

1011
export class RunOnDevicesController extends EventEmitter {
1112
constructor(
1213
private $buildPlatformService: BuildPlatformService,
14+
private $deviceDebugAppService: DeviceDebugAppService,
1315
private $deviceInstallAppService: DeviceInstallAppService,
1416
private $deviceRefreshAppService: DeviceRefreshAppService,
1517
private $devicesService: Mobile.IDevicesService,
1618
public $hooksService: IHooksService,
1719
private $liveSyncServiceResolver: LiveSyncServiceResolver,
1820
private $logger: ILogger,
1921
private $runOnDevicesDataService: RunOnDevicesDataService,
22+
private $runOnDevicesEmitter: RunOnDevicesEmitter,
2023
private $workflowDataService: WorkflowDataService
2124
) { super(); }
2225

@@ -54,24 +57,26 @@ export class RunOnDevicesController extends EventEmitter {
5457
const { force, useHotModuleReload, skipWatcher } = liveSyncInfo;
5558
const liveSyncResultInfo = await platformLiveSyncService.fullSync({ force, useHotModuleReload, projectData, device, watch: !skipWatcher, liveSyncDeviceInfo: deviceDescriptor });
5659

57-
await this.$deviceRefreshAppService.refreshApplication(deviceDescriptor, projectData, liveSyncResultInfo, platformLiveSyncService, this);
60+
const refreshInfo = await this.$deviceRefreshAppService.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, deviceDescriptor);
61+
62+
this.$runOnDevicesEmitter.emitRunOnDeviceExecutedEvent(projectData, liveSyncResultInfo.deviceAppData.device, {
63+
syncedFiles: liveSyncResultInfo.modifiedFilesData.map(m => m.getLocalPath()),
64+
isFullSync: liveSyncResultInfo.isFullSync
65+
});
66+
67+
if (liveSyncResultInfo && deviceDescriptor.debugggingEnabled) {
68+
await this.$deviceDebugAppService.refreshApplicationWithDebug(projectData, deviceDescriptor, refreshInfo);
69+
}
5870

5971
this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`);
6072

61-
this.emitRunOnDeviceEvent(RunOnDeviceEvents.runOnDeviceStarted, {
62-
projectDir: projectData.projectDir,
63-
deviceIdentifier: device.deviceInfo.identifier,
64-
applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()]
65-
});
73+
this.$runOnDevicesEmitter.emitRunOnDeviceStartedEvent(projectData, device);
6674
} catch (err) {
6775
this.$logger.warn(`Unable to apply changes on device: ${device.deviceInfo.identifier}. Error is: ${err.message}.`);
6876

69-
this.emitRunOnDeviceEvent(RunOnDeviceEvents.runOnDeviceError, {
70-
error: err,
71-
deviceIdentifier: device.deviceInfo.identifier,
72-
projectDir: projectData.projectDir,
73-
applicationIdentifier: projectData.projectIdentifiers[platformData.platformNameLowerCase]
74-
});
77+
this.$runOnDevicesEmitter.emitRunOnDeviceErrorEvent(projectData, device, err);
78+
79+
// TODO: Consider to call here directly stopRunOnDevices
7580
}
7681
}
7782

@@ -97,19 +102,25 @@ export class RunOnDevicesController extends EventEmitter {
97102
connectTimeout: 1000
98103
});
99104

100-
await this.$deviceRefreshAppService.refreshApplication(deviceDescriptor, projectData, liveSyncResultInfo, platformLiveSyncService, this);
105+
const refreshInfo = await this.$deviceRefreshAppService.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, deviceDescriptor);
106+
107+
this.$runOnDevicesEmitter.emitRunOnDeviceExecutedEvent(projectData, liveSyncResultInfo.deviceAppData.device, {
108+
syncedFiles: liveSyncResultInfo.modifiedFilesData.map(m => m.getLocalPath()),
109+
isFullSync: liveSyncResultInfo.isFullSync
110+
});
111+
112+
if (liveSyncResultInfo && deviceDescriptor.debugggingEnabled) {
113+
await this.$deviceDebugAppService.refreshApplicationWithDebug(projectData, deviceDescriptor, refreshInfo);
114+
}
115+
116+
this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`);
101117
} catch (err) {
102118
const allErrors = (<Mobile.IDevicesOperationError>err).allErrors;
103119

104120
if (allErrors && _.isArray(allErrors)) {
105121
for (const deviceError of allErrors) {
106122
this.$logger.warn(`Unable to apply changes for device: ${deviceError.deviceIdentifier}. Error is: ${deviceError.message}.`);
107-
this.emitRunOnDeviceEvent(RunOnDeviceEvents.runOnDeviceError, {
108-
error: deviceError,
109-
deviceIdentifier: deviceError.deviceIdentifier,
110-
projectDir: projectData.projectDir,
111-
applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()]
112-
});
123+
this.$runOnDevicesEmitter.emitRunOnDeviceErrorEvent(projectData, device, deviceError);
113124
}
114125
}
115126
}
@@ -130,10 +141,5 @@ export class RunOnDevicesController extends EventEmitter {
130141
return result;
131142
}
132143
}
133-
134-
private emitRunOnDeviceEvent(event: string, runOnDeviceData: ILiveSyncEventData): boolean {
135-
this.$logger.trace(`Will emit event ${event} with data`, runOnDeviceData);
136-
return this.emit(event, runOnDeviceData);
137-
}
138144
}
139145
$injector.register("runOnDevicesController", RunOnDevicesController);

lib/run-on-devices-emitter.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { EventEmitter } from "events";
2+
import { RunOnDeviceEvents, DEBUGGER_DETACHED_EVENT_NAME, USER_INTERACTION_NEEDED_EVENT_NAME, DEBUGGER_ATTACHED_EVENT_NAME } from "./constants";
3+
4+
export class RunOnDevicesEmitter extends EventEmitter {
5+
constructor(
6+
private $logger: ILogger
7+
) { super(); }
8+
9+
public emitRunOnDeviceStartedEvent(projectData: IProjectData, device: Mobile.IDevice) {
10+
this.emitCore(RunOnDeviceEvents.runOnDeviceStarted, {
11+
projectDir: projectData.projectDir,
12+
deviceIdentifier: device.deviceInfo.identifier,
13+
applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()]
14+
});
15+
}
16+
17+
public emitRunOnDeviceNotificationEvent(projectData: IProjectData, device: Mobile.IDevice, notification: string) {
18+
this.emitCore(RunOnDeviceEvents.runOnDeviceNotification, {
19+
projectDir: projectData.projectDir,
20+
deviceIdentifier: device.deviceInfo.identifier,
21+
applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()],
22+
notification
23+
});
24+
}
25+
26+
public emitRunOnDeviceErrorEvent(projectData: IProjectData, device: Mobile.IDevice, error: Error) {
27+
this.emitCore(RunOnDeviceEvents.runOnDeviceError, {
28+
projectDir: projectData.projectDir,
29+
deviceIdentifier: device.deviceInfo.identifier,
30+
applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()],
31+
error,
32+
});
33+
}
34+
35+
public emitRunOnDeviceExecutedEvent(projectData: IProjectData, device: Mobile.IDevice, options: { syncedFiles: string[], isFullSync: boolean }) {
36+
this.emitCore(RunOnDeviceEvents.runOnDeviceExecuted, {
37+
projectDir: projectData.projectDir,
38+
deviceIdentifier: device.deviceInfo.identifier,
39+
applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()],
40+
syncedFiles: options.syncedFiles,
41+
isFullSync: options.isFullSync
42+
});
43+
}
44+
45+
public emitDebuggerAttachedEvent(debugInformation: IDebugInformation) {
46+
this.emit(DEBUGGER_ATTACHED_EVENT_NAME, debugInformation);
47+
}
48+
49+
public emitDebuggerDetachedEvent(device: Mobile.IDevice) {
50+
const deviceIdentifier = device.deviceInfo.identifier;
51+
this.emit(DEBUGGER_DETACHED_EVENT_NAME, { deviceIdentifier });
52+
}
53+
54+
public emitUserInteractionNeededEvent(projectData: IProjectData, device: Mobile.IDevice, deviceDescriptor: ILiveSyncDeviceInfo) {
55+
const deviceIdentifier = device.deviceInfo.identifier;
56+
const attachDebuggerOptions: IAttachDebuggerOptions = {
57+
platform: device.deviceInfo.platform,
58+
isEmulator: device.isEmulator,
59+
projectDir: projectData.projectDir,
60+
deviceIdentifier,
61+
debugOptions: deviceDescriptor.debugOptions,
62+
outputPath: deviceDescriptor.outputPath
63+
};
64+
this.emit(USER_INTERACTION_NEEDED_EVENT_NAME, attachDebuggerOptions);
65+
}
66+
67+
private emitCore(event: string, data: ILiveSyncEventData): void {
68+
this.$logger.trace(`Will emit event ${event} with data`, data);
69+
this.emit(event, data);
70+
}
71+
}
72+
$injector.register("runOnDevicesEmitter", RunOnDevicesEmitter);
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { performanceLog } from "../../common/decorators";
2+
import { RunOnDevicesEmitter } from "../../run-on-devices-emitter";
3+
import { EOL } from "os";
4+
5+
export class DeviceDebugAppService {
6+
constructor(
7+
private $debugDataService: IDebugDataService,
8+
private $debugService: IDebugService,
9+
private $devicesService: Mobile.IDevicesService,
10+
private $errors: IErrors,
11+
private $logger: ILogger,
12+
private $projectDataService: IProjectDataService,
13+
private $runOnDevicesEmitter: RunOnDevicesEmitter
14+
) { }
15+
16+
@performanceLog()
17+
public async refreshApplicationWithDebug(projectData: IProjectData, deviceDescriptor: ILiveSyncDeviceInfo, refreshInfo: IRestartApplicationInfo): Promise<IDebugInformation> {
18+
const { debugOptions } = deviceDescriptor;
19+
// we do not stop the application when debugBrk is false, so we need to attach, instead of launch
20+
// if we try to send the launch request, the debugger port will not be printed and the command will timeout
21+
debugOptions.start = !debugOptions.debugBrk;
22+
23+
debugOptions.forceDebuggerAttachedEvent = refreshInfo.didRestart;
24+
const deviceOption = {
25+
deviceIdentifier: deviceDescriptor.identifier,
26+
debugOptions: debugOptions,
27+
};
28+
29+
return this.enableDebuggingCoreWithoutWaitingCurrentAction(deviceOption, deviceDescriptor, { projectDir: projectData.projectDir });
30+
}
31+
32+
public async attachDebugger(settings: IAttachDebuggerOptions): Promise<IDebugInformation> {
33+
// Default values
34+
if (settings.debugOptions) {
35+
settings.debugOptions.chrome = settings.debugOptions.chrome === undefined ? true : settings.debugOptions.chrome;
36+
settings.debugOptions.start = settings.debugOptions.start === undefined ? true : settings.debugOptions.start;
37+
} else {
38+
settings.debugOptions = {
39+
chrome: true,
40+
start: true
41+
};
42+
}
43+
44+
const projectData = this.$projectDataService.getProjectData(settings.projectDir);
45+
const debugData = this.$debugDataService.createDebugData(projectData, { device: settings.deviceIdentifier });
46+
// const platformData = this.$platformsData.getPlatformData(settings.platform, projectData);
47+
48+
// Of the properties below only `buildForDevice` and `release` are currently used.
49+
// Leaving the others with placeholder values so that they may not be forgotten in future implementations.
50+
// debugData.pathToAppPackage = this.$buildArtefactsService.getLastBuiltPackagePath(platformData, buildConfig, settings.outputPath);
51+
const debugInfo = await this.$debugService.debug(debugData, settings.debugOptions);
52+
const result = this.printDebugInformation(debugInfo, settings.debugOptions.forceDebuggerAttachedEvent);
53+
return result;
54+
}
55+
56+
public printDebugInformation(debugInformation: IDebugInformation, fireDebuggerAttachedEvent: boolean = true): IDebugInformation {
57+
if (!!debugInformation.url) {
58+
if (fireDebuggerAttachedEvent) {
59+
this.$runOnDevicesEmitter.emitDebuggerAttachedEvent(debugInformation);
60+
}
61+
62+
this.$logger.info(`To start debugging, open the following URL in Chrome:${EOL}${debugInformation.url}${EOL}`.cyan);
63+
}
64+
65+
return debugInformation;
66+
}
67+
68+
@performanceLog()
69+
private async enableDebuggingCoreWithoutWaitingCurrentAction(deviceOption: IEnableDebuggingDeviceOptions, deviceDescriptor: ILiveSyncDeviceInfo, debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise<IDebugInformation> {
70+
if (!deviceDescriptor) {
71+
this.$errors.failWithoutHelp(`Couldn't enable debugging for ${deviceOption.deviceIdentifier}`);
72+
}
73+
74+
deviceDescriptor.debugggingEnabled = true;
75+
deviceDescriptor.debugOptions = deviceOption.debugOptions;
76+
const currentDeviceInstance = this.$devicesService.getDeviceByIdentifier(deviceOption.deviceIdentifier);
77+
const attachDebuggerOptions: IAttachDebuggerOptions = {
78+
deviceIdentifier: deviceOption.deviceIdentifier,
79+
isEmulator: currentDeviceInstance.isEmulator,
80+
outputPath: deviceDescriptor.outputPath,
81+
platform: currentDeviceInstance.deviceInfo.platform,
82+
projectDir: debuggingAdditionalOptions.projectDir,
83+
debugOptions: deviceOption.debugOptions
84+
};
85+
86+
let debugInformation: IDebugInformation;
87+
try {
88+
debugInformation = await this.attachDebugger(attachDebuggerOptions);
89+
} catch (err) {
90+
this.$logger.trace("Couldn't attach debugger, will modify options and try again.", err);
91+
attachDebuggerOptions.debugOptions.start = false;
92+
try {
93+
debugInformation = await this.attachDebugger(attachDebuggerOptions);
94+
} catch (innerErr) {
95+
this.$logger.trace("Couldn't attach debugger with modified options.", innerErr);
96+
throw err;
97+
}
98+
}
99+
100+
return debugInformation;
101+
}
102+
}
103+
$injector.register("deviceDebugAppService", DeviceDebugAppService);

0 commit comments

Comments
 (0)