Skip to content

Commit 7d97c85

Browse files
author
Dimitar Tachev
authored
Merge pull request #4296 from NativeScript/tachev/fix-ios-debug-brk
fix: enable debug-brk on real iOS devices
2 parents bc47f1a + cc440dd commit 7d97c85

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1108
-1848
lines changed

lib/commands/debug.ts

+5-61
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
import { CONNECTED_STATUS } from "../common/constants";
2-
import { isInteractive } from "../common/helpers";
31
import { cache } from "../common/decorators";
4-
import { DebugCommandErrors } from "../constants";
52
import { ValidatePlatformCommandBase } from "./command-base";
63
import { LiveSyncCommandHelper } from "../helpers/livesync-command-helper";
7-
import { performanceLog } from "../common/decorators";
84

95
export class DebugPlatformCommand extends ValidatePlatformCommandBase implements ICommand {
106
public allowedParameters: ICommandParameter[] = [];
@@ -21,7 +17,6 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
2117
protected $errors: IErrors,
2218
private $debugDataService: IDebugDataService,
2319
private $liveSyncService: IDebugLiveSyncService,
24-
private $prompter: IPrompter,
2520
private $liveSyncCommandHelper: ILiveSyncCommandHelper,
2621
private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper) {
2722
super($options, $platformsData, $platformService, $projectData);
@@ -37,7 +32,11 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
3732

3833
const debugOptions = <IDebugOptions>_.cloneDeep(this.$options.argv);
3934

40-
const selectedDeviceForDebug = await this.getDeviceForDebug();
35+
const selectedDeviceForDebug = await this.$devicesService.pickSingleDevice({
36+
onlyEmulators: this.$options.emulator,
37+
onlyDevices: this.$options.forDevice,
38+
deviceId: this.$options.device
39+
});
4140

4241
const debugData = this.$debugDataService.createDebugData(this.$projectData, { device: selectedDeviceForDebug.deviceInfo.identifier });
4342

@@ -56,61 +55,6 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
5655
});
5756
}
5857

59-
@performanceLog()
60-
public async getDeviceForDebug(): Promise<Mobile.IDevice> {
61-
if (this.$options.forDevice && this.$options.emulator) {
62-
this.$errors.fail(DebugCommandErrors.UNABLE_TO_USE_FOR_DEVICE_AND_EMULATOR);
63-
}
64-
65-
if (this.$options.device) {
66-
const device = await this.$devicesService.getDevice(this.$options.device);
67-
return device;
68-
}
69-
70-
// Now let's take data for each device:
71-
const availableDevicesAndEmulators = this.$devicesService.getDeviceInstances()
72-
.filter(d => d.deviceInfo.status === CONNECTED_STATUS && (!this.platform || d.deviceInfo.platform.toLowerCase() === this.platform.toLowerCase()));
73-
74-
const selectedDevices = availableDevicesAndEmulators.filter(d => this.$options.emulator ? d.isEmulator : (this.$options.forDevice ? !d.isEmulator : true));
75-
76-
if (selectedDevices.length > 1) {
77-
if (isInteractive()) {
78-
const choices = selectedDevices.map(e => `${e.deviceInfo.identifier} - ${e.deviceInfo.displayName}`);
79-
80-
const selectedDeviceString = await this.$prompter.promptForChoice("Select device for debugging", choices);
81-
82-
const selectedDevice = _.find(selectedDevices, d => `${d.deviceInfo.identifier} - ${d.deviceInfo.displayName}` === selectedDeviceString);
83-
return selectedDevice;
84-
} else {
85-
const sortedInstances = _.sortBy(selectedDevices, e => e.deviceInfo.version);
86-
const emulators = sortedInstances.filter(e => e.isEmulator);
87-
const devices = sortedInstances.filter(d => !d.isEmulator);
88-
let selectedInstance: Mobile.IDevice;
89-
90-
if (this.$options.emulator || this.$options.forDevice) {
91-
// When --emulator or --forDevice is passed, the instances are already filtered
92-
// So we are sure we have exactly the type we need and we can safely return the last one (highest OS version).
93-
selectedInstance = _.last(sortedInstances);
94-
} else {
95-
if (emulators.length) {
96-
selectedInstance = _.last(emulators);
97-
} else {
98-
selectedInstance = _.last(devices);
99-
}
100-
}
101-
102-
this.$logger.warn(`Multiple devices/emulators found. Starting debugger on ${selectedInstance.deviceInfo.identifier}. ` +
103-
"If you want to debug on specific device/emulator, you can specify it with --device option.");
104-
105-
return selectedInstance;
106-
}
107-
} else if (selectedDevices.length === 1) {
108-
return _.head(selectedDevices);
109-
}
110-
111-
this.$errors.failWithoutHelp(DebugCommandErrors.NO_DEVICES_EMULATORS_FOUND_FOR_OPTIONS);
112-
}
113-
11458
public async canExecute(args: string[]): Promise<ICanExecuteCommandOutput> {
11559
this.$androidBundleValidatorHelper.validateNoAab();
11660

lib/commands/test.ts

-19
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,5 @@
11
import * as helpers from "../common/helpers";
22

3-
function RunTestCommandFactory(platform: string) {
4-
return function RunTestCommand(
5-
$options: IOptions,
6-
$testExecutionService: ITestExecutionService,
7-
$projectData: IProjectData,
8-
$analyticsService: IAnalyticsService,
9-
$platformEnvironmentRequirements: IPlatformEnvironmentRequirements) {
10-
$projectData.initializeProjectData();
11-
$analyticsService.setShouldDispose($options.justlaunch || !$options.watch);
12-
const projectFilesConfig = helpers.getProjectFilesConfig({ isReleaseBuild: $options.release });
13-
this.execute = (args: string[]): Promise<void> => $testExecutionService.startTestRunner(platform, $projectData, projectFilesConfig);
14-
this.canExecute = (args: string[]): Promise<boolean> => canExecute({ $platformEnvironmentRequirements, $projectData, $options, platform });
15-
this.allowedParameters = [];
16-
};
17-
}
18-
19-
$injector.registerCommand("dev-test|android", RunTestCommandFactory('android'));
20-
$injector.registerCommand("dev-test|ios", RunTestCommandFactory('iOS'));
21-
223
function RunKarmaTestCommandFactory(platform: string) {
234
return function RunKarmaTestCommand($options: IOptions, $testExecutionService: ITestExecutionService, $projectData: IProjectData, $analyticsService: IAnalyticsService, $platformEnvironmentRequirements: IPlatformEnvironmentRequirements) {
245
$projectData.initializeProjectData();

lib/common/definitions/mobile.d.ts

+34-27
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,8 @@ declare module Mobile {
111111
}
112112

113113
interface IiOSDevice extends IDevice {
114-
getLiveSyncSocket(appId: string): Promise<any>;
115-
destroyLiveSyncSocket(appId: string): void;
116-
117-
getDebugSocket(appId: string): Promise<any>;
114+
getDebugSocket(appId: string, projectName: string): Promise<any>;
118115
destroyDebugSocket(appId: string): void;
119-
120116
openDeviceLogStream(options?: IiOSLogStreamOptions): Promise<void>;
121117
destroyAllSockets(): void;
122118
}
@@ -221,12 +217,6 @@ declare module Mobile {
221217
* @param {string} projectName The project name of the currently running application for which we need the logs.
222218
*/
223219
setProjectNameForDevice(deviceIdentifier: string, projectName: string): void;
224-
225-
/**
226-
* Disables logs on the specified device and does not print any logs on the console.
227-
* @param {string} deviceIdentifier The unique identifier of the device.
228-
*/
229-
muteLogsForDevice(deviceIdentifier: string): void;
230220
}
231221

232222
/**
@@ -248,10 +238,6 @@ declare module Mobile {
248238
*/
249239
projectName?: string;
250240

251-
/**
252-
* Specifies if the logs will be printed on the console.
253-
*/
254-
muteLogs?: boolean;
255241
}
256242

257243
/**
@@ -311,19 +297,27 @@ declare module Mobile {
311297
justLaunch?: boolean;
312298
}
313299

300+
interface IStartApplicationData extends IApplicationData {
301+
waitForDebugger?: boolean;
302+
}
303+
314304
interface IInstallAppData extends IApplicationData {
315305
packagePath: string;
316306
}
317307

308+
interface IRunningAppInfo {
309+
pid: string;
310+
}
311+
318312
interface IDeviceApplicationManager extends NodeJS.EventEmitter {
319313
getInstalledApplications(): Promise<string[]>;
320314
isApplicationInstalled(appIdentifier: string): Promise<boolean>;
321315
installApplication(packageFilePath: string, appIdentifier?: string): Promise<void>;
322316
uninstallApplication(appIdentifier: string): Promise<void>;
323317
reinstallApplication(appIdentifier: string, packageFilePath: string): Promise<void>;
324-
startApplication(appData: IApplicationData): Promise<void>;
318+
startApplication(appData: IStartApplicationData): Promise<void>;
325319
stopApplication(appData: IApplicationData): Promise<void>;
326-
restartApplication(appData: IApplicationData): Promise<void>;
320+
restartApplication(appData: IStartApplicationData): Promise<void>;
327321
checkForApplicationUpdates(): Promise<void>;
328322
isLiveSyncSupported(appIdentifier: string): Promise<boolean>;
329323
getApplicationInfo(applicationIdentifier: string): Promise<Mobile.IApplicationInfo>;
@@ -550,6 +544,27 @@ declare module Mobile {
550544
* @returns {Promise<string[]>} - Returns array of errors.
551545
*/
552546
startEmulator(options?: IStartEmulatorOptions): Promise<string[]>;
547+
548+
/**
549+
* Returns a single device based on the specified options. If more than one devices are matching,
550+
* prompts the user for a manual choice or returns the first one for non interactive terminals.
551+
*/
552+
pickSingleDevice(options: IPickSingleDeviceOptions): Promise<Mobile.IDevice>
553+
}
554+
555+
interface IPickSingleDeviceOptions {
556+
/**
557+
* Pick from the connected emulators only
558+
*/
559+
onlyEmulators: boolean;
560+
/**
561+
* Pick from the connected real devices only
562+
*/
563+
onlyDevices: boolean;
564+
/**
565+
* Pick a specific device
566+
*/
567+
deviceId: string;
553568
}
554569

555570
interface IListEmulatorsOptions {
@@ -941,14 +956,6 @@ declare module Mobile {
941956
* @returns {net.Socket} Returns instance of net.Socket when connection is successful, otherwise undefined is returned.
942957
*/
943958
connectToPort(connectToPortData: IConnectToPortData): Promise<any>;
944-
945-
/**
946-
* Runs an application on emulator
947-
* @param app The path to executable .app
948-
* @param emulatorOptions Emulator options that can be passed
949-
* @returns {Promise<any>} Returns the appId with the process of the running application on the simulator. For example: org.nativescript.myapp 55434
950-
*/
951-
runApplicationOnEmulator(app: string, emulatorOptions?: IRunApplicationOnEmulatorOptions): Promise<any>;
952959
}
953960

954961
interface IEmulatorSettingsService {
@@ -1251,9 +1258,9 @@ interface IIOSDeviceOperations extends IDisposable, NodeJS.EventEmitter {
12511258

12521259
deleteFiles(deleteArray: IOSDeviceLib.IDeleteFileData[], errorHandler?: DeviceOperationErrorHandler): Promise<IOSDeviceResponse>;
12531260

1254-
start(startArray: IOSDeviceLib.IDdiApplicationData[], errorHandler?: DeviceOperationErrorHandler): Promise<IOSDeviceResponse>;
1261+
start(startArray: IOSDeviceLib.IIOSApplicationData[], errorHandler?: DeviceOperationErrorHandler): Promise<IOSDeviceResponse>;
12551262

1256-
stop(stopArray: IOSDeviceLib.IDdiApplicationData[], errorHandler?: DeviceOperationErrorHandler): Promise<IOSDeviceResponse>;
1263+
stop(stopArray: IOSDeviceLib.IIOSApplicationData[], errorHandler?: DeviceOperationErrorHandler): Promise<IOSDeviceResponse>;
12571264

12581265
postNotification(postNotificationArray: IOSDeviceLib.IPostNotificationData[], errorHandler?: DeviceOperationErrorHandler): Promise<IOSDeviceResponse>;
12591266

lib/common/mobile/android/android-application-manager.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,15 @@ export class AndroidApplicationManager extends ApplicationManagerBase {
4747
return this.adb.executeShellCommand(["pm", "uninstall", `${appIdentifier}`], { treatErrorsAsWarnings: true });
4848
}
4949

50-
public async startApplication(appData: Mobile.IApplicationData): Promise<void> {
50+
public async startApplication(appData: Mobile.IStartApplicationData): Promise<void> {
51+
if (appData.waitForDebugger) {
52+
await this.adb.executeShellCommand([`cat /dev/null > ${LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appData.appId}-debugbreak`]);
53+
}
54+
55+
// If the app is debuggable, the Runtime will update the file when its ready for debugging
56+
// and we will be able to take decisions and synchronize the debug experience based on the content
57+
await this.adb.executeShellCommand([`cat /dev/null > ${LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appData.appId}-debugger-started`]);
58+
5159
/*
5260
Example "pm dump <app_identifier> | grep -A 1 MAIN" output"
5361
android.intent.action.MAIN:

lib/common/mobile/device-log-provider-base.ts

-4
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ export abstract class DeviceLogProviderBase extends EventEmitter implements Mobi
2626
this.setLogLevel(logLevel, deviceIdentifier);
2727
}
2828

29-
public muteLogsForDevice(deviceIdentifier: string): void {
30-
this.setDeviceLogOptionsProperty(deviceIdentifier, (deviceLogOptions: Mobile.IDeviceLogOptions) => deviceLogOptions.muteLogs, true);
31-
}
32-
3329
protected getApplicationPidForDevice(deviceIdentifier: string): string {
3430
return this.devicesLogOptions[deviceIdentifier] && this.devicesLogOptions[deviceIdentifier].applicationPid;
3531
}

lib/common/mobile/device-log-provider.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
1111
const loggingOptions = this.getDeviceLogOptionsForDevice(deviceIdentifier);
1212
const data = this.$logFilter.filterData(platform, lineText, loggingOptions);
1313
if (data) {
14-
this.logDataCore(data, loggingOptions);
14+
this.logDataCore(data);
1515
this.emit(DEVICE_LOG_EVENT_NAME, lineText, deviceIdentifier, platform);
1616
}
1717
}
@@ -20,10 +20,8 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
2020
this.$logFilter.loggingLevel = logLevel.toUpperCase();
2121
}
2222

23-
private logDataCore(data: string, loggingOptions: Mobile.IDeviceLogOptions): void {
24-
if (!loggingOptions || (loggingOptions && !loggingOptions.muteLogs)) {
25-
this.$logger.write(data);
26-
}
23+
private logDataCore(data: string): void {
24+
this.$logger.write(data);
2725
}
2826
}
2927
$injector.register("deviceLogProvider", DeviceLogProvider);

lib/common/mobile/ios/device/ios-application-manager.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class IOSApplicationManager extends ApplicationManagerBase {
7474
this.$logger.trace("Application %s has been uninstalled successfully.", appIdentifier);
7575
}
7676

77-
public async startApplication(appData: Mobile.IApplicationData): Promise<void> {
77+
public async startApplication(appData: Mobile.IStartApplicationData): Promise<void> {
7878
if (!await this.isApplicationInstalled(appData.appId)) {
7979
this.$errors.failWithoutHelp("Invalid application id: %s. All available application ids are: %s%s ", appData.appId, EOL, this.applicationsLiveSyncInfos.join(EOL));
8080
}
@@ -89,7 +89,6 @@ export class IOSApplicationManager extends ApplicationManagerBase {
8989
const { appId } = appData;
9090

9191
this.device.destroyDebugSocket(appId);
92-
this.device.destroyLiveSyncSocket(appId);
9392

9493
const action = () => this.$iosDeviceOperations.stop([{ deviceId: this.device.deviceInfo.identifier, ddi: this.$options.ddi, appId }]);
9594

@@ -101,7 +100,7 @@ export class IOSApplicationManager extends ApplicationManagerBase {
101100
}
102101
}
103102

104-
public async restartApplication(appData: Mobile.IApplicationData): Promise<void> {
103+
public async restartApplication(appData: Mobile.IStartApplicationData): Promise<void> {
105104
try {
106105
await this.setDeviceLogData(appData);
107106
await this.stopApplication(appData);
@@ -119,8 +118,9 @@ export class IOSApplicationManager extends ApplicationManagerBase {
119118
}
120119
}
121120

122-
private async runApplicationCore(appData: Mobile.IApplicationData): Promise<void> {
123-
await this.$iosDeviceOperations.start([{ deviceId: this.device.deviceInfo.identifier, appId: appData.appId, ddi: this.$options.ddi }]);
121+
private async runApplicationCore(appData: Mobile.IStartApplicationData): Promise<void> {
122+
const waitForDebugger = appData.waitForDebugger && appData.waitForDebugger.toString();
123+
await this.$iosDeviceOperations.start([{ deviceId: this.device.deviceInfo.identifier, appId: appData.appId, ddi: this.$options.ddi, waitForDebugger }]);
124124
}
125125

126126
@cache()

lib/common/mobile/ios/device/ios-device.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ export class IOSDevice extends IOSDeviceBase {
1717
protected $errors: IErrors,
1818
private $injector: IInjector,
1919
protected $iOSDebuggerPortService: IIOSDebuggerPortService,
20+
protected $deviceLogProvider: Mobile.IDeviceLogProvider,
2021
protected $lockService: ILockService,
2122
private $iOSSocketRequestExecutor: IiOSSocketRequestExecutor,
2223
protected $processService: IProcessService,
23-
private $deviceLogProvider: Mobile.IDeviceLogProvider,
2424
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
2525
private $iOSDeviceProductNameMapper: Mobile.IiOSDeviceProductNameMapper,
2626
private $iosDeviceOperations: IIOSDeviceOperations,
@@ -60,9 +60,10 @@ export class IOSDevice extends IOSDeviceBase {
6060
}
6161
}
6262

63-
protected async getSocketCore(appId: string): Promise<net.Socket> {
63+
protected async getDebugSocketCore(appId: string, projectName: string): Promise<net.Socket> {
64+
await super.attachToDebuggerFoundEvent(projectName);
6465
await this.$iOSSocketRequestExecutor.executeAttachRequest(this, constants.AWAIT_NOTIFICATION_TIMEOUT_SECONDS, appId);
65-
const port = await this.getDebuggerPort(appId);
66+
const port = await super.getDebuggerPort(appId);
6667
const deviceId = this.deviceInfo.identifier;
6768

6869
const socket = await helpers.connectEventuallyUntilTimeout(

0 commit comments

Comments
 (0)