diff --git a/lib/common/bootstrap.ts b/lib/common/bootstrap.ts index 2aa92eee6b..2aa7a2ddb7 100644 --- a/lib/common/bootstrap.ts +++ b/lib/common/bootstrap.ts @@ -122,4 +122,4 @@ $injector.require("qr", "./services/qr"); $injector.require("printPluginsService", "./services/plugins/print-plugins-service"); $injector.require("npmPluginsService", "./services/plugins/npm-plugins-service"); -$injector.require("lockfile", "./services/lockfile"); +$injector.require(["lockfile", "lockService"], "./services/lock-service"); diff --git a/lib/common/declarations.d.ts b/lib/common/declarations.d.ts index 86cc592bd7..1c337f573e 100644 --- a/lib/common/declarations.d.ts +++ b/lib/common/declarations.d.ts @@ -1965,67 +1965,6 @@ interface IiOSNotificationService { postNotification(deviceIdentifier: string, notification: string, commandType?: string): Promise; } -/** - * Copied from @types/lockfile - * Describes the options that can be passed to lockfile methods. - */ -interface ILockFileOptions { - /** - * A number of milliseconds to wait for locks to expire before giving up. - * Only used by lockFile.lock. Poll for opts.wait ms. - * If the lock is not cleared by the time the wait expires, then it returns with the original error. - */ - wait?: number; - - /** - * When using opts.wait, this is the period in ms in which it polls to check if the lock has expired. Defaults to 100. - */ - pollPeriod?: number; - - /** - * A number of milliseconds before locks are considered to have expired. - */ - stale?: number; - - /** - * Used by lock and lockSync. Retry n number of times before giving up. - */ - retries?: number; - - /** - * Used by lock. Wait n milliseconds before retrying. - */ - retryWait?: number; -} - -/** - * Describes methods that can be used to use file locking. - */ -interface ILockFile { - /** - * Acquire a file lock on the specified path. - * @param {string} lockFilePath Path to lockfile that has to be created. Defaults to `/lockfile.lock` - * @param {ILockFileOptions} lockFileOpts Options used for creating the lockfile. - * @returns {Promise} - */ - lock(lockFilePath?: string, lockFileOpts?: ILockFileOptions): Promise; - - /** - * Close and unlink the lockfile. - * @param {string} lockFilePath Path to lockfile that has to be created. Defaults to `/lockfile.lock` - * @returns {void} - */ - unlock(lockFilePath?: string): void; - - /** - * Check if the lockfile is locked and not stale. - * @param {string} lockFilePath Path to lockfile that has to be created. Defaults to `/lockfile.lock` - * @param {ILockFileOptions} lockFileOpts Options used for creating the lockfile. - * @returns {boolean} true in case file is locked, false otherwise - */ - check(lockFilePath?: string, lockFileOpts?: ILockFileOptions): boolean; -} - declare module "stringify-package" { function stringifyPackage(data: any, indent: any, newline: string): string export = stringifyPackage diff --git a/lib/common/helpers.ts b/lib/common/helpers.ts index c355e3a6dd..886754d424 100644 --- a/lib/common/helpers.ts +++ b/lib/common/helpers.ts @@ -495,6 +495,7 @@ export async function connectEventuallyUntilTimeout(factory: () => Promise { const deviceResponse = _.first((await this.$iosDeviceOperations.connectToPort([{ deviceId: deviceId, port: port }]))[deviceId]); diff --git a/lib/common/mobile/ios/ios-device-base.ts b/lib/common/mobile/ios/ios-device-base.ts index c2ef1c4602..fd384c8de8 100644 --- a/lib/common/mobile/ios/ios-device-base.ts +++ b/lib/common/mobile/ios/ios-device-base.ts @@ -5,6 +5,7 @@ export abstract class IOSDeviceBase implements Mobile.IiOSDevice { protected abstract $errors: IErrors; protected abstract $iOSDebuggerPortService: IIOSDebuggerPortService; protected abstract $processService: IProcessService; + protected abstract $lockService: ILockService; abstract deviceInfo: Mobile.IDeviceInfo; abstract applicationManager: Mobile.IDeviceApplicationManager; abstract fileSystem: Mobile.IDeviceFileSystem; @@ -24,21 +25,23 @@ export abstract class IOSDeviceBase implements Mobile.IiOSDevice { } public async getSocket(appId: string): Promise { - if (this.cachedSockets[appId]) { - return this.cachedSockets[appId]; - } + return this.$lockService.executeActionWithLock(async () => { + if (this.cachedSockets[appId]) { + return this.cachedSockets[appId]; + } - this.cachedSockets[appId] = await this.getSocketCore(appId); + this.cachedSockets[appId] = await this.getSocketCore(appId); - if (this.cachedSockets[appId]) { - this.cachedSockets[appId].on("close", () => { - this.destroySocket(appId); - }); + if (this.cachedSockets[appId]) { + this.cachedSockets[appId].on("close", () => { + this.destroySocket(appId); + }); - this.$processService.attachToProcessExitSignals(this, () => this.destroySocket(appId)); - } + this.$processService.attachToProcessExitSignals(this, () => this.destroySocket(appId)); + } - return this.cachedSockets[appId]; + return this.cachedSockets[appId]; + }, "ios-debug-socket.lock"); } public destroyLiveSyncSocket(appId: string) { diff --git a/lib/common/mobile/ios/simulator/ios-simulator-device.ts b/lib/common/mobile/ios/simulator/ios-simulator-device.ts index 1a50da4617..5a34ba89c0 100644 --- a/lib/common/mobile/ios/simulator/ios-simulator-device.ts +++ b/lib/common/mobile/ios/simulator/ios-simulator-device.ts @@ -14,6 +14,7 @@ export class IOSSimulator extends IOSDeviceBase implements Mobile.IiOSDevice { constructor(private simulator: Mobile.IiSimDevice, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, protected $errors: IErrors, + protected $lockService: ILockService, private $injector: IInjector, protected $iOSDebuggerPortService: IIOSDebuggerPortService, private $iOSSimResolver: Mobile.IiOSSimResolver, diff --git a/lib/common/services/lock-service.ts b/lib/common/services/lock-service.ts new file mode 100644 index 0000000000..807a421006 --- /dev/null +++ b/lib/common/services/lock-service.ts @@ -0,0 +1,88 @@ +import * as lockfile from "lockfile"; +import * as path from "path"; +import { cache } from "../decorators"; + +export class LockService implements ILockService { + private currentlyLockedFiles: string[] = []; + + @cache() + private get defaultLockFilePath(): string { + return this.getAbsoluteLockFilePath("lockfile.lock"); + } + + private getAbsoluteLockFilePath(relativeLockFilePath: string) { + return path.join(this.$settingsService.getProfileDir(), relativeLockFilePath); + } + + private get defaultLockParams(): ILockOptions { + // We'll retry 100 times and time between retries is 100ms, i.e. full wait in case we are unable to get lock will be 10 seconds. + // In case lock is older than the `stale` value, consider it stale and try to get a new lock. + const lockParams: ILockOptions = { + retryWait: 100, + retries: 100, + stale: 30 * 1000, + }; + + return lockParams; + } + + constructor(private $fs: IFileSystem, + private $settingsService: ISettingsService, + private $processService: IProcessService) { + this.$processService.attachToProcessExitSignals(this, () => { + const locksToRemove = _.clone(this.currentlyLockedFiles); + _.each(locksToRemove, lock => { + this.unlock(lock); + }); + }); + } + + public async executeActionWithLock(action: () => Promise, lockFilePath?: string, lockOpts?: ILockOptions): Promise { + const resolvedLockFilePath = await this.lock(lockFilePath, lockOpts); + + try { + const result = await action(); + return result; + } finally { + this.unlock(resolvedLockFilePath); + } + } + + private lock(lockFilePath?: string, lockOpts?: ILockOptions): Promise { + const { filePath, fileOpts } = this.getLockFileSettings(lockFilePath, lockOpts); + this.currentlyLockedFiles.push(filePath); + + // Prevent ENOENT error when the dir, where lock should be created, does not exist. + this.$fs.ensureDirectoryExists(path.dirname(filePath)); + + return new Promise((resolve, reject) => { + lockfile.lock(filePath, fileOpts, (err: Error) => { + err ? reject(new Error(`Timeout while waiting for lock "${filePath}"`)) : resolve(filePath); + }); + }); + } + + private unlock(lockFilePath?: string): void { + const { filePath } = this.getLockFileSettings(lockFilePath); + _.remove(this.currentlyLockedFiles, e => e === lockFilePath); + lockfile.unlockSync(filePath); + } + + private getLockFileSettings(filePath?: string, fileOpts?: ILockOptions): { filePath: string, fileOpts: ILockOptions } { + if (filePath && !path.isAbsolute(filePath)) { + filePath = this.getAbsoluteLockFilePath(filePath); + } + + filePath = filePath || this.defaultLockFilePath; + fileOpts = fileOpts || this.defaultLockParams; + + return { + filePath, + fileOpts + }; + } +} + +$injector.register("lockService", LockService); +// backwards compatibility +$injector.register("lockfile", LockService); diff --git a/lib/common/services/lockfile.ts b/lib/common/services/lockfile.ts deleted file mode 100644 index 8612e684e8..0000000000 --- a/lib/common/services/lockfile.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as lockfile from "lockfile"; -import * as path from "path"; -import { cache } from "../decorators"; - -export class LockFile implements ILockFile { - - @cache() - private get defaultLockFilePath(): string { - return path.join(this.$settingsService.getProfileDir(), "lockfile.lock"); - } - - private get defaultLockParams(): lockfile.Options { - // We'll retry 100 times and time between retries is 100ms, i.e. full wait in case we are unable to get lock will be 10 seconds. - // In case lock is older than 3 minutes, consider it stale and try to get a new lock. - const lockParams: lockfile.Options = { - retryWait: 100, - retries: 100, - stale: 180 * 1000, - }; - - return lockParams; - } - - constructor(private $fs: IFileSystem, - private $settingsService: ISettingsService) { - } - - public lock(lockFilePath?: string, lockFileOpts?: lockfile.Options): Promise { - const { filePath, fileOpts } = this.getLockFileSettings(lockFilePath, lockFileOpts); - - // Prevent ENOENT error when the dir, where lock should be created, does not exist. - this.$fs.ensureDirectoryExists(path.dirname(filePath)); - - return new Promise((resolve, reject) => { - lockfile.lock(filePath, fileOpts, (err: Error) => { - err ? reject(err) : resolve(); - }); - }); - } - - public unlock(lockFilePath?: string): void { - const { filePath } = this.getLockFileSettings(lockFilePath); - lockfile.unlockSync(filePath); - } - - public check(lockFilePath?: string, lockFileOpts?: lockfile.Options): boolean { - const { filePath, fileOpts } = this.getLockFileSettings(lockFilePath, lockFileOpts); - - return lockfile.checkSync(filePath, fileOpts); - } - - private getLockFileSettings(filePath?: string, fileOpts?: lockfile.Options): { filePath: string, fileOpts: lockfile.Options } { - filePath = filePath || this.defaultLockFilePath; - fileOpts = fileOpts || this.defaultLockParams; - - return { - filePath, - fileOpts - }; - } -} - -$injector.register("lockfile", LockFile); diff --git a/lib/common/services/user-settings-service.ts b/lib/common/services/user-settings-service.ts index f37337d4e3..d10954b616 100644 --- a/lib/common/services/user-settings-service.ts +++ b/lib/common/services/user-settings-service.ts @@ -5,12 +5,12 @@ export class UserSettingsServiceBase implements IUserSettingsService { private userSettingsFilePath: string = null; protected userSettingsData: any = null; private get lockFilePath(): string { - return `${this.userSettingsFilePath}.lock`; + return `user-settings.lock`; } constructor(userSettingsFilePath: string, protected $fs: IFileSystem, - protected $lockfile: ILockFile, + protected $lockService: ILockService, private $logger: ILogger) { this.userSettingsFilePath = userSettingsFilePath; } @@ -21,7 +21,7 @@ export class UserSettingsServiceBase implements IUserSettingsService { return this.userSettingsData ? this.userSettingsData[settingName] : null; }; - return this.executeActionWithLock(action); + return this.$lockService.executeActionWithLock(action, this.lockFilePath); } public async saveSetting(key: string, value: T): Promise { @@ -39,17 +39,7 @@ export class UserSettingsServiceBase implements IUserSettingsService { await this.saveSettings(); }; - return this.executeActionWithLock(action); - } - - private async executeActionWithLock(action: () => Promise): Promise { - try { - await this.$lockfile.lock(this.lockFilePath); - const result = await action(); - return result; - } finally { - this.$lockfile.unlock(this.lockFilePath); - } + return this.$lockService.executeActionWithLock(action, this.lockFilePath); } public saveSettings(data?: any): Promise { @@ -66,7 +56,7 @@ export class UserSettingsServiceBase implements IUserSettingsService { this.$fs.writeJson(this.userSettingsFilePath, this.userSettingsData); }; - return this.executeActionWithLock(action); + return this.$lockService.executeActionWithLock(action, this.lockFilePath); } // TODO: Remove Promise, reason: writeFile - blocked as other implementation of the interface has async operation. diff --git a/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts b/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts index 406a501fe6..f6c4cb0d31 100644 --- a/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts +++ b/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts @@ -4,7 +4,7 @@ import { Yok } from "../../../yok"; import { assert } from "chai"; import { DeviceDiscoveryEventNames, CONNECTED_STATUS } from "../../../constants"; import { DevicePlatformsConstants } from "../../../mobile/device-platforms-constants"; -import { ErrorsStub, CommonLoggerStub, HooksServiceStub } from "../stubs"; +import { ErrorsStub, CommonLoggerStub, HooksServiceStub, LockServiceStub } from "../stubs"; import { FileSystemStub } from "../../../../../test/stubs"; let currentlyRunningSimulators: Mobile.IiSimDevice[]; @@ -16,6 +16,7 @@ function createTestInjector(): IInjector { injector.register("injector", injector); injector.register("errors", ErrorsStub); injector.register("iOSDebuggerPortService", {}); + injector.register("lockService", LockServiceStub); injector.register("iOSSimResolver", { iOSSim: { getRunningSimulators: async () => currentlyRunningSimulators diff --git a/lib/common/test/unit-tests/stubs.ts b/lib/common/test/unit-tests/stubs.ts index 52f17bc965..b7dfee7f17 100644 --- a/lib/common/test/unit-tests/stubs.ts +++ b/lib/common/test/unit-tests/stubs.ts @@ -3,6 +3,13 @@ import * as util from "util"; import { EventEmitter } from "events"; +export class LockServiceStub implements ILockService { + public async executeActionWithLock(action: () => Promise, lockFilePath?: string, lockOpts?: ILockOptions): Promise { + const result = await action(); + return result; + } +} + export class CommonLoggerStub implements ILogger { setLevel(level: string): void { } getLevel(): string { return undefined; } diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index e3e89a4531..9bf8b575f1 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -746,17 +746,11 @@ interface IAppDebugSocketProxyFactory extends NodeJS.EventEmitter { } interface IiOSNotification extends NodeJS.EventEmitter { - getWaitForDebug(projectId: string): string; getAttachRequest(projectId: string, deviceId: string): string; - getAppLaunching(projectId: string): string; getReadyForAttach(projectId: string): string; - getAttachAvailabilityQuery(projectId: string): string; - getAlreadyConnected(projectId: string): string; - getAttachAvailable(projectId: string): string; } interface IiOSSocketRequestExecutor { - executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, debugOptions: IDebugOptions): Promise; executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise; } diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 48bbba18f8..c2586789b4 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -96,10 +96,6 @@ interface IDebugOptions { * The sdk version of the emulator. */ sdk?: string; - /** - * Defines if the handshake(AppLaunching notification) between CLI and runtime should be executed. The handshake is not needed when CLI retries to attach to the debugger. - */ - skipHandshake?: boolean; /** * Forces the debugger attach event to be emitted. */ diff --git a/lib/definitions/lock-service.d.ts b/lib/definitions/lock-service.d.ts new file mode 100644 index 0000000000..66c21d1a6c --- /dev/null +++ b/lib/definitions/lock-service.d.ts @@ -0,0 +1,19 @@ +import * as lockfile from "lockfile"; + +declare global { + interface ILockOptions extends lockfile.Options { } + + /** + * Describes methods that can be used to use file locking. + */ + interface ILockService { + /** + * @param action The code to be locked. + * @param {string} lockFilePath Path to lock file that has to be created. Defaults to `/lockfile.lock` + * @param {ILockOptions} lockOpts Options used for creating the lock file. + * @returns {Promise} + */ + executeActionWithLock(action: () => Promise, lockFilePath?: string, lockOpts?: ILockOptions): Promise + // TODO: expose as decorator + } +} \ No newline at end of file diff --git a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts index 18448d876a..10cebd65ea 100644 --- a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts +++ b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts @@ -123,7 +123,7 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu this.$logger.trace(err); this.emit(CONNECTION_ERROR_EVENT_NAME, err); acceptHandshake = false; - this.$logger.warn(`Cannot connect to device socket. The error message is '${err.message}'. Try starting the application manually.`); + this.$logger.warn(`Cannot connect to device socket. The error message is '${err.message}'.`); } callback(acceptHandshake); diff --git a/lib/device-sockets/ios/notification.ts b/lib/device-sockets/ios/notification.ts index d72e46057e..a9e72eb23e 100644 --- a/lib/device-sockets/ios/notification.ts +++ b/lib/device-sockets/ios/notification.ts @@ -2,17 +2,8 @@ import { EventEmitter } from "events"; import { ATTACH_REQUEST_EVENT_NAME } from "../../common/constants"; export class IOSNotification extends EventEmitter implements IiOSNotification { - private static WAIT_FOR_DEBUG_NOTIFICATION_NAME = "WaitForDebugger"; private static ATTACH_REQUEST_NOTIFICATION_NAME = "AttachRequest"; - private static APP_LAUNCHING_NOTIFICATION_NAME = "AppLaunching"; private static READY_FOR_ATTACH_NOTIFICATION_NAME = "ReadyForAttach"; - private static ATTACH_AVAILABILITY_QUERY_NOTIFICATION_NAME = "AttachAvailabilityQuery"; - private static ALREADY_CONNECTED_NOTIFICATION_NAME = "AlreadyConnected"; - private static ATTACH_AVAILABLE_NOTIFICATION_NAME = "AttachAvailable"; - - public getWaitForDebug(projectId: string) { - return this.formatNotification(IOSNotification.WAIT_FOR_DEBUG_NOTIFICATION_NAME, projectId); - } public getAttachRequest(appId: string, deviceId: string): string { // It could be too early to emit this event, but we rely that if you construct attach request, @@ -21,26 +12,10 @@ export class IOSNotification extends EventEmitter implements IiOSNotification { return this.formatNotification(IOSNotification.ATTACH_REQUEST_NOTIFICATION_NAME, appId); } - public getAppLaunching(projectId: string): string { - return this.formatNotification(IOSNotification.APP_LAUNCHING_NOTIFICATION_NAME, projectId); - } - public getReadyForAttach(projectId: string): string { return this.formatNotification(IOSNotification.READY_FOR_ATTACH_NOTIFICATION_NAME, projectId); } - public getAttachAvailabilityQuery(projectId: string) { - return this.formatNotification(IOSNotification.ATTACH_AVAILABILITY_QUERY_NOTIFICATION_NAME, projectId); - } - - public getAlreadyConnected(projectId: string) { - return this.formatNotification(IOSNotification.ALREADY_CONNECTED_NOTIFICATION_NAME, projectId); - } - - public getAttachAvailable(projectId: string) { - return this.formatNotification(IOSNotification.ATTACH_AVAILABLE_NOTIFICATION_NAME, projectId); - } - private formatNotification(notification: string, projectId: string) { return `${projectId}:NativeScript.Debug.${notification}`; } diff --git a/lib/device-sockets/ios/socket-request-executor.ts b/lib/device-sockets/ios/socket-request-executor.ts index c3eb4ccf5d..b593815c83 100644 --- a/lib/device-sockets/ios/socket-request-executor.ts +++ b/lib/device-sockets/ios/socket-request-executor.ts @@ -3,69 +3,10 @@ import * as constants from "../../common/constants"; export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor { constructor(private $errors: IErrors, private $iOSNotification: IiOSNotification, - private $iOSNotificationService: IiOSNotificationService, - private $logger: ILogger) { } + private $iOSNotificationService: IiOSNotificationService) { } public async executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise { const deviceIdentifier = device.deviceInfo.identifier; - - const observeNotificationSockets = [ - await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAlreadyConnected(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE), - await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getReadyForAttach(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE), - await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachAvailable(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE) - ]; - - const observeNotificationPromises = _(observeNotificationSockets) - .uniq() - .map(s => { - return this.$iOSNotificationService.awaitNotification(deviceIdentifier, s, timeout); - }) - .value(); - - // Trigger the notifications update. - await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachAvailabilityQuery(projectId)); - - let receivedNotification: string; - try { - receivedNotification = await Promise.race(observeNotificationPromises); - } catch (e) { - this.$errors.failWithoutHelp(`The application ${projectId} does not appear to be running on ${device.deviceInfo.displayName} or is not built with debugging enabled.`); - } - - switch (receivedNotification) { - case this.$iOSNotification.getAlreadyConnected(projectId): - this.$errors.failWithoutHelp("A client is already connected."); - break; - case this.$iOSNotification.getAttachAvailable(projectId): - await this.executeAttachAvailable(deviceIdentifier, projectId, timeout); - break; - case this.$iOSNotification.getReadyForAttach(projectId): - break; - default: - this.$logger.trace("Response from attach availability query:"); - this.$logger.trace(receivedNotification); - this.$errors.failWithoutHelp("No notification received while executing attach request."); - } - } - - public async executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, debugOptions: IDebugOptions): Promise { - try { - if (!debugOptions.skipHandshake) { - await this.executeHandshake(deviceIdentifier, projectId, timeout); - } - - if (debugOptions.debugBrk) { - await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getWaitForDebug(projectId)); - } - - await this.executeAttachAvailable(deviceIdentifier, projectId, readyForAttachTimeout); - } catch (e) { - this.$logger.trace("Launch request error: ", e); - this.$errors.failWithoutHelp("Error while waiting for response from NativeScript runtime."); - } - } - - private async executeAttachAvailable(deviceIdentifier: string, projectId: string, timeout: number): Promise { try { // We should create this promise here because we need to send the ObserveNotification on the device // before we send the PostNotification. @@ -74,18 +15,9 @@ export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor { await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachRequest(projectId, deviceIdentifier)); await readyForAttachPromise; } catch (e) { - this.$logger.trace("Attach available error: ", e); - this.$errors.failWithoutHelp(`The application ${projectId} timed out when performing the socket handshake.`); + this.$errors.failWithoutHelp(`The application ${projectId} does not appear to be running on ${deviceIdentifier} or is not built with debugging enabled. Try starting the application manually.`); } } - - private async executeHandshake(deviceIdentifier: string, projectId: string, timeout: number): Promise { - // This notification will be send only once by the runtime during application start. - // In case app is already running, we'll fail here as we'll not receive it. - const appLaunchingNotification = this.$iOSNotification.getAppLaunching(projectId); - const appLaunchingSocket = await this.$iOSNotificationService.postNotification(deviceIdentifier, appLaunchingNotification, constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE); - await this.$iOSNotificationService.awaitNotification(deviceIdentifier, +appLaunchingSocket, timeout); - } } $injector.register("iOSSocketRequestExecutor", IOSSocketRequestExecutor); diff --git a/lib/services/android-device-debug-service.ts b/lib/services/android-device-debug-service.ts index 110946dbef..8b6cf0106a 100644 --- a/lib/services/android-device-debug-service.ts +++ b/lib/services/android-device-debug-service.ts @@ -147,7 +147,7 @@ export class AndroidDeviceDebugService extends DebugServiceBase implements IDevi private async validateRunningApp(deviceId: string, packageName: string): Promise { if (!(await this.isAppRunning(packageName, deviceId))) { - this.$errors.failWithoutHelp(`The application ${packageName} does not appear to be running on ${deviceId} or is not built with debugging enabled.`); + this.$errors.failWithoutHelp(`The application ${packageName} does not appear to be running on ${deviceId} or is not built with debugging enabled. Try starting the application manually.`); } } diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 38e3b8603d..f39e165ef2 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -329,7 +329,6 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } catch (err) { this.$logger.trace("Couldn't attach debugger, will modify options and try again.", err); attachDebuggerOptions.debugOptions.start = false; - attachDebuggerOptions.debugOptions.skipHandshake = true; try { debugInformation = await this.attachDebugger(attachDebuggerOptions); } catch (innerErr) { diff --git a/lib/services/user-settings-service.ts b/lib/services/user-settings-service.ts index 8f382aadf7..6e20579c3d 100644 --- a/lib/services/user-settings-service.ts +++ b/lib/services/user-settings-service.ts @@ -4,10 +4,10 @@ import * as userSettingsServiceBaseLib from "../common/services/user-settings-se export class UserSettingsService extends userSettingsServiceBaseLib.UserSettingsServiceBase { constructor($fs: IFileSystem, $settingsService: ISettingsService, - $lockfile: ILockFile, + $lockService: ILockService, $logger: ILogger) { const userSettingsFilePath = path.join($settingsService.getProfileDir(), "user-settings.json"); - super(userSettingsFilePath, $fs, $lockfile, $logger); + super(userSettingsFilePath, $fs, $lockService, $logger); } public async loadUserSettingsFile(): Promise { diff --git a/test/stubs.ts b/test/stubs.ts index e04c772df5..c573f2304f 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -570,19 +570,6 @@ export class HooksServiceStub implements IHooksService { hookArgsName = "hookArgs"; } -export class LockFile { - - async check(): Promise { - return false; - } - - async lock(): Promise { - } - - async unlock(): Promise { - } -} - export class PrompterStub implements IPrompter { private strings: IDictionary = {}; private passwords: IDictionary = {};