From 1bc09d85e3b60e1db022e3743f369609027e07ab Mon Sep 17 00:00:00 2001 From: Hana Date: Tue, 8 Jun 2021 20:30:20 -0400 Subject: [PATCH] src/goDebugConfiguration: resolve relative paths used in cwd, output, program If relative paths are used, translate them to be relative to the workspace folder when using dlv-dap. The description in the package.json says cwd is a workspace relative or absolute path, but this seems to be broken in the old adapter (I.e., when cwd=., the old adapter simply used it as --wd value and launched the headless server in the program directory. As a result, '.' is translated as the program or package source directory). This CL doesn't attempt to fix or change the behavior of the old adapter though, but applies the translation only when dlv-dap is used. This changes the default cwd value (when users attempt to add cwd to their launch config) to be '' which is treated as if 'cwd' attribute was undefined. Users who want to use the workspace folder can use `${workspaceFolder}` or `.`. This change doesn't change 'cwd' in attach mode because this is currently used for different purpose in the legacy adapter, and it will become irrelevant in dlv-dap. Updates golang/vscode-go#1348 Change-Id: Ieb15f6bbb470a17d2e7350ccf1d8a003cbb92eeb Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/317210 Trust: Hyang-Ah Hana Kim Run-TryBot: Hyang-Ah Hana Kim TryBot-Result: kokoro Reviewed-by: Suzy Mueller --- package.json | 4 +- src/goDebugConfiguration.ts | 18 ++- src/goDebugFactory.ts | 8 +- test/integration/goDebugConfiguration.test.ts | 106 ++++++++++++++++++ 4 files changed, 129 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index aca14f9529..b6c0bd937e 100644 --- a/package.json +++ b/package.json @@ -576,8 +576,8 @@ }, "cwd": { "type": "string", - "description": "Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace.", - "default": "." + "description": "Workspace relative or absolute path to the working directory of the program being debugged if a non-empty value is specified. The 'program' folder is used as the working directory if it is omitted or empty.", + "default": "" }, "env": { "type": "object", diff --git a/src/goDebugConfiguration.ts b/src/goDebugConfiguration.ts index 76759dc298..e86819b635 100644 --- a/src/goDebugConfiguration.ts +++ b/src/goDebugConfiguration.ts @@ -23,7 +23,7 @@ import { packagePathToGoModPathMap } from './goModules'; import { getTool, getToolAtVersion } from './goTools'; import { pickProcess, pickProcessByName } from './pickProcess'; import { getFromGlobalState, updateGlobalState } from './stateUtils'; -import { getBinPath, getGoVersion } from './util'; +import { getBinPath, getGoVersion, getWorkspaceFolderPath, resolvePath } from './util'; import { parseEnvFiles } from './utils/envUtils'; import { resolveHomeDir } from './utils/pathUtils'; @@ -389,6 +389,22 @@ export class GoDebugConfigurationProvider implements vscode.DebugConfigurationPr debugConfiguration['env'] = Object.assign(goToolsEnvVars, fileEnvs, env); debugConfiguration['envFile'] = undefined; // unset, since we already processed. + const entriesWithRelativePaths = ['cwd', 'output', 'program'].filter( + (attr) => debugConfiguration[attr] && !path.isAbsolute(debugConfiguration[attr]) + ); + if (debugConfiguration['debugAdapter'] === 'dlv-dap' && entriesWithRelativePaths.length > 0) { + const workspaceRoot = folder?.uri.fsPath; + if (!workspaceRoot) { + this.showWarning( + 'relativePathsWithoutWorkspaceFolder', + 'Relative paths without a workspace folder for `cwd`, `program`, or `output` are not allowed.' + ); + return null; + } + entriesWithRelativePaths.forEach((attr) => { + debugConfiguration[attr] = path.join(workspaceRoot, debugConfiguration[attr]); + }); + } return debugConfiguration; } diff --git a/src/goDebugFactory.ts b/src/goDebugFactory.ts index e019f2696e..a247e30286 100644 --- a/src/goDebugFactory.ts +++ b/src/goDebugFactory.ts @@ -412,10 +412,10 @@ function spawnDlvDapServerProcess( logConsole(`Starting: ${dlvPath} ${dlvArgs.join(' ')}\n`); - // TODO(hyangah): determine the directories: - // run `dlv` => where dlv will create the default __debug_bin. (This won't work if the directory is not writable. Fix it) - // build program => 'program' directory. (This won't work for multimodule workspace. Fix it) - // run program => cwd (If test, make sure to run in the package directory.) + // TODO(hyangah): In module-module workspace mode, the program should be build in the super module directory + // where go.work (gopls.mod) file is present. Where dlv runs determines the build directory currently. Two options: + // 1) launch dlv in the super-module module directory and adjust launchArgs.cwd (--wd). + // 2) introduce a new buildDir launch attribute. return new Promise((resolve, reject) => { const p = spawn(dlvPath, dlvArgs, { cwd: dir, diff --git a/test/integration/goDebugConfiguration.test.ts b/test/integration/goDebugConfiguration.test.ts index 515559a6a7..8199bbda5d 100644 --- a/test/integration/goDebugConfiguration.test.ts +++ b/test/integration/goDebugConfiguration.test.ts @@ -439,6 +439,112 @@ suite('Debug Configuration Resolve Paths', () => { }); }); +suite('Debug Configuration Converts Relative Paths', () => { + const debugConfigProvider = new GoDebugConfigurationProvider(); + + function debugConfig(adapter: string) { + return { + name: 'Launch', + type: 'go', + request: 'launch', + mode: 'auto', + debugAdapter: adapter, + program: path.join('foo', 'bar.go'), + cwd: '.', + output: 'debug' + }; + } + + test('resolve relative paths with workspace root in dlv-dap mode', () => { + const config = debugConfig('dlv-dap'); + const workspaceFolder = { + uri: vscode.Uri.file(os.tmpdir()), + name: 'test', + index: 0 + }; + const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables( + workspaceFolder, + config + ); + assert.deepStrictEqual( + { program, cwd, output }, + { + program: path.join(os.tmpdir(), 'foo/bar.go'), + cwd: os.tmpdir(), + output: path.join(os.tmpdir(), 'debug') + } + ); + }); + + test('empty, undefined paths are not affected', () => { + const config = debugConfig('dlv-dap'); + config.program = undefined; + config.cwd = ''; + delete config.output; + + const workspaceFolder = { + uri: vscode.Uri.file(os.tmpdir()), + name: 'test', + index: 0 + }; + const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables( + workspaceFolder, + config + ); + assert.deepStrictEqual( + { program, cwd, output }, + { + program: undefined, + cwd: '', + output: undefined + } + ); + }); + + test('disallow relative paths with no workspace root', () => { + const config = debugConfig('dlv-dap'); + const got = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables(undefined, config); + assert.strictEqual(got, null); + }); + + test('do not affect relative paths (workspace) in legacy mode', () => { + const config = debugConfig('legacy'); + const workspaceFolder = { + uri: vscode.Uri.file(os.tmpdir()), + name: 'test', + index: 0 + }; + const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables( + workspaceFolder, + config + ); + assert.deepStrictEqual( + { program, cwd, output }, + { + program: path.join('foo', 'bar.go'), + cwd: '.', + output: 'debug' + } + ); + }); + + test('do not affect relative paths (no workspace) in legacy mode', () => { + const config = debugConfig('legacy'); + const { program, cwd, output } = debugConfigProvider.resolveDebugConfigurationWithSubstitutedVariables( + undefined, + config + ); + assert.deepStrictEqual( + { program, cwd, output }, + { + program: path.join('foo', 'bar.go'), + cwd: '.', + output: 'debug' + } + ); + }); +}); + suite('Debug Configuration Auto Mode', () => { const debugConfigProvider = new GoDebugConfigurationProvider(); test('resolve auto to debug with non-test file', () => {