diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index 30e446366c..1620567f9f 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -6,7 +6,6 @@ export abstract class DebugPlatformCommand implements ICommand { constructor(private debugService: IPlatformDebugService, private $devicesService: Mobile.IDevicesService, private $injector: IInjector, - private $logger: ILogger, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $config: IConfiguration, private $usbLiveSyncService: ILiveSyncService, @@ -14,7 +13,8 @@ export abstract class DebugPlatformCommand implements ICommand { protected $platformService: IPlatformService, protected $projectData: IProjectData, protected $options: IOptions, - protected $platformsData: IPlatformsData) { + protected $platformsData: IPlatformsData, + protected $logger: ILogger) { this.$projectData.initializeProjectData(); } @@ -31,9 +31,7 @@ export abstract class DebugPlatformCommand implements ICommand { teamId: this.$options.teamId }; - const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); - - const debugData = this.$debugDataService.createDebugData(this.debugService, this.$options, buildConfig); + let debugData = this.$debugDataService.createDebugData(this.$projectData, this.$options); await this.$platformService.trackProjectType(this.$projectData); @@ -57,6 +55,9 @@ export abstract class DebugPlatformCommand implements ICommand { await deviceAppData.device.applicationManager.stopApplication(applicationId); + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); + debugData.pathToAppPackage = this.$platformService.lastOutputPath(this.debugService.platform, buildConfig, projectData); + this.printDebugInformation(await this.debugService.debug(debugData, debugOptions)); }; @@ -80,7 +81,7 @@ export abstract class DebugPlatformCommand implements ICommand { return true; } - private printDebugInformation(information: string[]): void { + protected printDebugInformation(information: string[]): void { _.each(information, i => { this.$logger.info(`To start debugging, open the following URL in Chrome:${EOL}${i}${EOL}`.cyan); }); @@ -88,10 +89,10 @@ export abstract class DebugPlatformCommand implements ICommand { } export class DebugIOSCommand extends DebugPlatformCommand { - constructor($iOSDebugService: IPlatformDebugService, + constructor(protected $logger: ILogger, + $iOSDebugService: IPlatformDebugService, $devicesService: Mobile.IDevicesService, $injector: IInjector, - $logger: ILogger, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $config: IConfiguration, $usbLiveSyncService: ILiveSyncService, @@ -101,22 +102,28 @@ export class DebugIOSCommand extends DebugPlatformCommand { $projectData: IProjectData, $platformsData: IPlatformsData, $iosDeviceOperations: IIOSDeviceOperations) { - super($iOSDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $debugDataService, $platformService, $projectData, $options, $platformsData); + super($iOSDebugService, $devicesService, $injector, $devicePlatformsConstants, $config, $usbLiveSyncService, $debugDataService, $platformService, $projectData, $options, $platformsData, $logger); $iosDeviceOperations.setShouldDispose(this.$options.justlaunch); } public async canExecute(args: string[]): Promise { return await super.canExecute(args) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.iOS); } + + protected printDebugInformation(information: string[]): void { + if (this.$options.chrome) { + super.printDebugInformation(information); + } + } } $injector.registerCommand("debug|ios", DebugIOSCommand); export class DebugAndroidCommand extends DebugPlatformCommand { - constructor($androidDebugService: IPlatformDebugService, + constructor($logger: ILogger, + $androidDebugService: IPlatformDebugService, $devicesService: Mobile.IDevicesService, $injector: IInjector, - $logger: ILogger, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $config: IConfiguration, $usbLiveSyncService: ILiveSyncService, @@ -125,7 +132,7 @@ export class DebugAndroidCommand extends DebugPlatformCommand { $options: IOptions, $projectData: IProjectData, $platformsData: IPlatformsData) { - super($androidDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $debugDataService, $platformService, $projectData, $options, $platformsData); + super($androidDebugService, $devicesService, $injector, $devicePlatformsConstants, $config, $usbLiveSyncService, $debugDataService, $platformService, $projectData, $options, $platformsData, $logger); } public async canExecute(args: string[]): Promise { diff --git a/lib/common b/lib/common index f30ac23b0f..3aefd62e53 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit f30ac23b0f8f0e6309157f2ac0c421b19719b43a +Subproject commit 3aefd62e53bccf12c00814d3e954929dabe4c8ba diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index e048ed66ae..9746e88b14 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -288,7 +288,7 @@ interface IAndroidToolsInfoData { } interface ISocketProxyFactory extends NodeJS.EventEmitter { - createTCPSocketProxy(factory: () => Promise): any; + createTCPSocketProxy(factory: () => Promise): Promise; createWebSocketProxy(factory: () => Promise): Promise; } diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 9da884109f..752b2ad762 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -1,7 +1,7 @@ interface IDebugData { deviceIdentifier: string; applicationIdentifier: string; - pathToAppPackage: string; + pathToAppPackage?: string; projectName?: string; projectDir?: string; } @@ -17,7 +17,7 @@ interface IDebugOptions { } interface IDebugDataService { - createDebugData(debugService: IPlatformDebugService, options: IOptions, buildConfig: IBuildConfig): IDebugData; + createDebugData(projectData: IProjectData, options: IOptions): IDebugData; } interface IDebugService extends NodeJS.EventEmitter { diff --git a/lib/device-sockets/ios/socket-proxy-factory.ts b/lib/device-sockets/ios/socket-proxy-factory.ts index fea0e5a41c..45f32abf8b 100644 --- a/lib/device-sockets/ios/socket-proxy-factory.ts +++ b/lib/device-sockets/ios/socket-proxy-factory.ts @@ -3,6 +3,7 @@ import { CONNECTION_ERROR_EVENT_NAME } from "../../constants"; import { PacketStream } from "./packet-stream"; import * as net from "net"; import * as ws from "ws"; +import * as helpers from "../../common/helpers"; import temp = require("temp"); export class SocketProxyFactory extends EventEmitter implements ISocketProxyFactory { @@ -14,7 +15,9 @@ export class SocketProxyFactory extends EventEmitter implements ISocketProxyFact super(); } - public createTCPSocketProxy(factory: () => Promise): net.Server { + public async createTCPSocketProxy(factory: () => Promise): Promise { + const socketFactory = async (callback: (_socket: net.Socket) => void) => helpers.connectEventually(factory, callback); + this.$logger.info("\nSetting up proxy...\nPress Ctrl + C to terminate, or disconnect.\n"); let server = net.createServer({ @@ -31,32 +34,34 @@ export class SocketProxyFactory extends EventEmitter implements ISocketProxyFact } }); - const backendSocket = await factory(); - this.$logger.info("Backend socket created."); - - backendSocket.on("end", () => { - this.$logger.info("Backend socket closed!"); - if (!(this.$config.debugLivesync && this.$options.watch)) { - process.exit(0); - } - }); + await socketFactory((backendSocket: net.Socket) => { + this.$logger.info("Backend socket created."); - frontendSocket.on("close", () => { - console.log("frontend socket closed"); - if (!(backendSocket).destroyed) { - backendSocket.destroy(); - } + backendSocket.on("end", () => { + this.$logger.info("Backend socket closed!"); + if (!(this.$config.debugLivesync && this.$options.watch)) { + process.exit(0); + } + }); + + frontendSocket.on("close", () => { + this.$logger.info("Frontend socket closed"); + if (!(backendSocket).destroyed) { + backendSocket.destroy(); + } + }); + + backendSocket.on("close", () => { + this.$logger.info("Backend socket closed"); + if (!(frontendSocket).destroyed) { + frontendSocket.destroy(); + } + }); + + backendSocket.pipe(frontendSocket); + frontendSocket.pipe(backendSocket); + frontendSocket.resume(); }); - backendSocket.on("close", () => { - console.log("backend socket closed"); - if (!(frontendSocket).destroyed) { - frontendSocket.destroy(); - } - }); - - backendSocket.pipe(frontendSocket); - frontendSocket.pipe(backendSocket); - frontendSocket.resume(); }); let socketFileLocation = temp.path({ suffix: ".sock" }); diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index cb6cb442e0..01cb77b9b1 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -128,10 +128,8 @@ class AndroidDebugService extends DebugServiceBase implements IPlatformDebugServ let startDebuggerCommand = ["am", "broadcast", "-a", `\"${packageName}-debug\"`, "--ez", "enable", "true"]; await this.device.adb.executeShellCommand(startDebuggerCommand); - if (debugOptions.chrome) { - let port = await this.getForwardedLocalDebugPortForPackageName(deviceId, packageName); - return `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${port}`; - } + let port = await this.getForwardedLocalDebugPortForPackageName(deviceId, packageName); + return `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${port}`; } private detachDebugger(packageName: string): Promise { diff --git a/lib/services/debug-data-service.ts b/lib/services/debug-data-service.ts index ebd9c0a0f3..038e2b6091 100644 --- a/lib/services/debug-data-service.ts +++ b/lib/services/debug-data-service.ts @@ -1,41 +1,12 @@ export class DebugDataService implements IDebugDataService { - constructor(private $projectData: IProjectData, - private $platformService: IPlatformService, - private $platformsData: IPlatformsData, - private $mobileHelper: Mobile.IMobileHelper) { } - - public createDebugData(debugService: IPlatformDebugService, options: IOptions, buildConfig: IBuildConfig): IDebugData { - this.$projectData.initializeProjectData(options.path); + public createDebugData(projectData: IProjectData, options: IOptions): IDebugData { return { - applicationIdentifier: this.$projectData.projectId, - projectDir: this.$projectData.projectDir, + applicationIdentifier: projectData.projectId, + projectDir: projectData.projectDir, deviceIdentifier: options.device, - pathToAppPackage: this.getPathToAppPackage(debugService, options, buildConfig), - projectName: this.$projectData.projectName + projectName: projectData.projectName }; } - - private getPathToAppPackage(debugService: IPlatformDebugService, options: IOptions, buildConfig: IBuildConfig): string { - if (this.$mobileHelper.isAndroidPlatform(debugService.platform)) { - if (!options.start && !options.emulator) { - const platformData = this.getPlatformData(debugService); - - return this.$platformService.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; - } - } else if (this.$mobileHelper.isiOSPlatform(debugService.platform)) { - if (options.emulator) { - const platformData = this.getPlatformData(debugService); - - return this.$platformService.getLatestApplicationPackageForEmulator(platformData, buildConfig).packageName; - } - } - - return null; - } - - private getPlatformData(debugService: IPlatformDebugService): IPlatformData { - return this.$platformsData.getPlatformData(debugService.platform, this.$projectData); - } } $injector.register("debugDataService", DebugDataService); diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 0dc16e1518..22b01dffaf 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -5,6 +5,7 @@ import * as log4js from "log4js"; import { ChildProcess } from "child_process"; import { DebugServiceBase } from "./debug-service-base"; import { CONNECTION_ERROR_EVENT_NAME } from "../constants"; +import { getPidFromiOSSimulatorLogs } from "../common/helpers"; import byline = require("byline"); @@ -107,7 +108,12 @@ class IOSDebugService extends DebugServiceBase implements IPlatformDebugService lineStream.on('data', (line: NodeBuffer) => { let lineText = line.toString(); if (lineText && _.startsWith(lineText, debugData.applicationIdentifier)) { - let pid = _.trimStart(lineText, debugData.applicationIdentifier + ": "); + const pid = getPidFromiOSSimulatorLogs(debugData.applicationIdentifier, lineText); + if (!pid) { + this.$logger.trace(`Line ${lineText} does not contain PID of the application ${debugData.applicationIdentifier}.`); + return; + } + this._lldbProcess = this.$childProcess.spawn("lldb", ["-p", pid]); if (log4js.levels.TRACE.isGreaterThanOrEqualTo(this.$logger.getLevel())) { this._lldbProcess.stdout.pipe(process.stdout); @@ -182,7 +188,7 @@ class IOSDebugService extends DebugServiceBase implements IPlatformDebugService const commitSHA = "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; // corresponds to 55.0.2883 Chrome version return `chrome-devtools://devtools/remote/serve_file/@${commitSHA}/inspector.html?experiments=true&ws=localhost:${this._socketProxy.options.port}`; } else { - this._socketProxy = this.$socketProxyFactory.createTCPSocketProxy(this.getSocketFactory(device)); + this._socketProxy = await this.$socketProxyFactory.createTCPSocketProxy(this.getSocketFactory(device)); await this.openAppInspector(this._socketProxy.address(), debugData, debugOptions); return null; } diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index 446215f5b9..a547c224e4 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -74,10 +74,9 @@ class TestExecutionService implements ITestExecutionService { await this.$usbLiveSyncService.liveSync(platform, projectData); if (this.$options.debugBrk) { - const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); this.$logger.info('Starting debugger...'); let debugService: IPlatformDebugService = this.$injector.resolve(`${platform}DebugService`); - const debugData: IDebugData = this.$debugDataService.createDebugData(debugService, this.$options, buildConfig); + const debugData = this.getDebugData(platform, projectData, deployOptions); await debugService.debugStart(debugData, this.$options); } resolve(); @@ -143,8 +142,7 @@ class TestExecutionService implements ITestExecutionService { if (this.$options.debugBrk) { const debugService = this.getDebugService(platform); - const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); - const debugData = this.$debugDataService.createDebugData(debugService, this.$options, buildConfig); + const debugData = this.getDebugData(platform, projectData, deployOptions); await debugService.debug(debugData, this.$options); } else { await this.$platformService.deployPlatform(platform, appFilesUpdaterOptions, deployOptions, projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); @@ -247,5 +245,13 @@ class TestExecutionService implements ITestExecutionService { return karmaConfig; } + + private getDebugData(platform: string, projectData: IProjectData, deployOptions: IDeployPlatformOptions): IDebugData { + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); + let debugData = this.$debugDataService.createDebugData(projectData, this.$options); + debugData.pathToAppPackage = this.$platformService.lastOutputPath(platform, buildConfig, projectData); + + return debugData; + } } $injector.register('testExecutionService', TestExecutionService);