Skip to content

Commit ec65dd0

Browse files
committed
✨ remote path mapping
1 parent 2fd7b69 commit ec65dd0

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,31 @@
10171017
},
10181018
"default": []
10191019
},
1020+
"pathMappings": {
1021+
"type": "array",
1022+
"label": "Additional path mappings.",
1023+
"items": {
1024+
"type": "object",
1025+
"label": "Path mapping",
1026+
"required": [
1027+
"localRoot",
1028+
"remoteRoot"
1029+
],
1030+
"properties": {
1031+
"localRoot": {
1032+
"type": "string",
1033+
"label": "Local source root.",
1034+
"default": ""
1035+
},
1036+
"remoteRoot": {
1037+
"type": "string",
1038+
"label": "Remote source root.",
1039+
"default": ""
1040+
}
1041+
}
1042+
},
1043+
"default": []
1044+
},
10201045
"logToFile": {
10211046
"type": "boolean",
10221047
"description": "Enable logging of debugger events to a log file.",

src/client/debugger/Common/Contracts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ export interface AttachRequestArguments extends DebugProtocol.AttachRequestArgum
8080
host?: string;
8181
secret?: string;
8282
logToFile?: boolean;
83+
pathMappings?: { localRoot: string; remoteRoot: string }[];
84+
debugOptions?: DebugOptions[];
8385
}
8486

8587
export interface IDebugServer {

src/client/debugger/configProviders/pythonV2Provider.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,13 @@ export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvide
3939
if (this.serviceContainer.get<IPlatformService>(IPlatformService).isWindows) {
4040
debugConfiguration.debugOptions.push(DebugOptions.FixFilePathCase);
4141
}
42+
43+
if (!debugConfiguration.pathMappings) {
44+
debugConfiguration.pathMappings = [];
45+
}
46+
debugConfiguration.pathMappings!.push({
47+
localRoot: debugConfiguration.localRoot,
48+
remoteRoot: debugConfiguration.remoteRoot
49+
});
4250
}
4351
}

src/test/debugger/attach.ptvsd.test.ts

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import { ChildProcess, spawn } from 'child_process';
99
import * as getFreePort from 'get-port';
1010
import * as path from 'path';
11+
import * as TypeMoq from 'typemoq';
12+
import { DebugConfiguration, Uri } from 'vscode';
1113
import { DebugClient } from 'vscode-debugadapter-testsupport';
1214
import { EXTENSION_ROOT_DIR } from '../../client/common/constants';
1315
import '../../client/common/extensions';
@@ -43,13 +45,7 @@ suite('Attach Debugger - Experimental', () => {
4345
} catch { }
4446
}
4547
});
46-
test('Confirm we are able to attach to a running program', async function () {
47-
this.timeout(20000);
48-
// Lets skip this test on AppVeyor (very flaky on AppVeyor).
49-
if (IS_APPVEYOR) {
50-
return;
51-
}
52-
48+
async function testAttachingToRemoteProcess(localRoot: string, remoteRoot: string, pathSeparator: string) {
5349
const port = await getFreePort({ host: 'localhost', port: 3000 });
5450
const customEnv = { ...process.env };
5551

@@ -58,6 +54,8 @@ suite('Attach Debugger - Experimental', () => {
5854
customEnv['PYTHONPATH'] = PTVSD_PATH;
5955
const pythonArgs = ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug.fileToCommandArgument()];
6056
procToKill = spawn('python', pythonArgs, { env: customEnv, cwd: path.dirname(fileToDebug) });
57+
// wait for remote socket to start
58+
await sleep(1000);
6159

6260
// Send initialize, attach
6361
const initializePromise = debugClient.initializeRequest({
@@ -69,15 +67,23 @@ suite('Attach Debugger - Experimental', () => {
6967
supportsVariableType: true,
7068
supportsVariablePaging: true
7169
});
72-
const attachPromise = debugClient.attachRequest({
73-
localRoot: path.dirname(fileToDebug),
74-
remoteRoot: path.dirname(fileToDebug),
70+
const options: AttachRequestArguments & DebugConfiguration = {
71+
name: 'attach',
72+
request: 'attach',
73+
localRoot,
74+
remoteRoot,
7575
type: 'pythonExperimental',
7676
port: port,
7777
host: 'localhost',
78-
logToFile: false,
78+
logToFile: true,
7979
debugOptions: [DebugOptions.RedirectOutput]
80-
});
80+
};
81+
const serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
82+
serviceContainer.setup(c => c.get(IPlatformService, TypeMoq.It.isAny())).returns(() => new PlatformService());
83+
const configProvider = new PythonV2DebugConfigurationProvider(serviceContainer.object);
84+
85+
const launchArgs = await configProvider.resolveDebugConfiguration({ index: 0, name: 'root', uri: Uri.file(localRoot) }, options);
86+
const attachPromise = debugClient.attachRequest(launchArgs);
8187

8288
await Promise.all([
8389
initializePromise,
@@ -90,7 +96,9 @@ suite('Attach Debugger - Experimental', () => {
9096
const stdOutPromise = debugClient.assertOutput('stdout', 'this is stdout');
9197
const stdErrPromise = debugClient.assertOutput('stderr', 'this is stderr');
9298

93-
const breakpointLocation = { path: fileToDebug, column: 1, line: 12 };
99+
// Don't use path utils, as we're building the paths manually (mimic windows paths on unix test servers and vice versa).
100+
const localFileName = `${localRoot}${pathSeparator}${path.basename(fileToDebug)}`;
101+
const breakpointLocation = { path: localFileName, column: 1, line: 12 };
94102
const breakpointPromise = debugClient.setBreakpointsRequest({
95103
lines: [breakpointLocation.line],
96104
breakpoints: [{ line: breakpointLocation.line, column: breakpointLocation.column }],
@@ -111,5 +119,25 @@ suite('Attach Debugger - Experimental', () => {
111119
debugClient.waitForEvent('exited'),
112120
debugClient.waitForEvent('terminated')
113121
]);
122+
}
123+
test('Confirm we are able to attach to a running program', async function () {
124+
this.timeout(20000);
125+
// Lets skip this test on AppVeyor (very flaky on AppVeyor).
126+
if (IS_APPVEYOR) {
127+
return;
128+
}
129+
130+
await testAttachingToRemoteProcess(path.dirname(fileToDebug), path.dirname(fileToDebug), path.sep);
131+
});
132+
test('Confirm localpath translations are done correctly', async function () {
133+
this.timeout(20000);
134+
// Lets skip this test on AppVeyor (very flaky on AppVeyor).
135+
if (IS_APPVEYOR) {
136+
return;
137+
}
138+
139+
const localWorkspace = IS_WINDOWS ? '/home/user/Desktop/project/src' : 'C:\\Project\\src';
140+
const pathSeparator = IS_WINDOWS ? '\\' : '/';
141+
await testAttachingToRemoteProcess(localWorkspace, path.dirname(fileToDebug), pathSeparator);
114142
});
115143
});

0 commit comments

Comments
 (0)