Skip to content

Commit ec9a5f8

Browse files
committed
src/goDebugConfiguration: remove user set '--gcflags' from config
When building the executable for debugging, delve sets '--gcflags'. When the user also provides this flag, there is an error in the Go build system, as the flag can only be specified once. If a user tries to set this flag, remove it and display a warning. Fixes #117 Change-Id: I84a1e718674b4bafaf31da91711d4b2f06f8b280 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/265580 Trust: Suzy Mueller <[email protected]> Trust: Hyang-Ah Hana Kim <[email protected]> Run-TryBot: Suzy Mueller <[email protected]> TryBot-Result: kokoro <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]>
1 parent 3c7e4da commit ec9a5f8

File tree

6 files changed

+215
-0
lines changed

6 files changed

+215
-0
lines changed

docs/debugging.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This document explains how to debug your Go programs in VS Code. The Go debugger
99
* [Configuration](#configuration)
1010
* [Launch Configurations](#launch-configurations)
1111
* [Specifying build tags](#specifying-build-tags)
12+
* [Specifying other build flags](#specifying-other-build-flags)
1213
* [Using VS Code Variables](#using-vs-code-variables)
1314
* [Snippets](#snippets)
1415
* [Debugging on Windows Subsystem for Linux (WSL)](#debugging-on-windows-subsystem-for-linux-wsl)
@@ -135,6 +136,22 @@ in your launch configuration. This property supports multiple tags, which you ca
135136

136137
<!--TODO(rstambler): Confirm that the extension works with a comma (not space) separated list.-->
137138

139+
### Specifying other build flags
140+
141+
The flags specified in `buildFlags` and `env.GOFLAGS` are passed to the Go compiler when building your program for debugging. Delve adds `--gcflags='all=-N -l'` to the list of build flags to disable optimizations. User specified buildFlags conflict with this setting, so the extension removes them ([Issue #117](https://github.com/golang/vscode-go/issues/117)). If you wish to debug a program using custom `--gcflags`, build the program using `go build` and launch using `exec` mode:
142+
143+
```json
144+
{
145+
"name": "Launch executable",
146+
"type": "go",
147+
"request": "launch",
148+
"mode": "exec",
149+
"program": "/absolute/path/to/executable"
150+
}
151+
```
152+
153+
Note that it is not recommended to debug optimized executables as Delve may not have the information necessary to properly debug your program.
154+
138155
### Using [VS Code variables]
139156

140157
Any property in the launch configuration that requires a file path can be specified in terms of [VS Code variables]. Here are some useful ones to know:

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
},
4747
"extensionDependencies": [],
4848
"dependencies": {
49+
"@types/yargs-parser": "^15.0.0",
4950
"deep-equal": "^2.0.2",
5051
"diff": "^4.0.2",
5152
"glob": "^7.1.6",

src/goDebugConfiguration.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import path = require('path');
99
import vscode = require('vscode');
10+
import parse = require('yargs-parser');
11+
import unparse = require('yargs-unparser');
1012
import { toolExecutionEnvironment } from './goEnv';
1113
import { promptForMissingTool } from './goInstallTools';
1214
import { packagePathToGoModPathMap } from './goModules';
@@ -92,6 +94,28 @@ export class GoDebugConfigurationProvider implements vscode.DebugConfigurationPr
9294
debugConfiguration['cwd'] = resolvePath(debugConfiguration['cwd']);
9395
}
9496

97+
// Remove any '--gcflags' entries and show a warning
98+
if (debugConfiguration['buildFlags']) {
99+
const resp = this.removeFlag(debugConfiguration['buildFlags'], 'gcflags');
100+
if (resp.removed) {
101+
debugConfiguration['buildFlags'] = resp.args;
102+
this.showWarning(
103+
'ignoreDebugGCFlagsWarning',
104+
`User specified build flag '--gcflags' in 'buildFlags' is being ignored (see [debugging with build flags](https://github.com/golang/vscode-go/blob/master/docs/debugging.md#specifying-other-build-flags) documentation)`
105+
);
106+
}
107+
}
108+
if (debugConfiguration['env'] && debugConfiguration['env']['GOFLAGS']) {
109+
const resp = this.removeFlag(debugConfiguration['env']['GOFLAGS'], 'gcflags');
110+
if (resp.removed) {
111+
debugConfiguration['env']['GOFLAGS'] = resp.args;
112+
this.showWarning(
113+
'ignoreDebugGCFlagsWarning',
114+
`User specified build flag '--gcflags' in 'GOFLAGS' is being ignored (see [debugging with build flags](https://github.com/golang/vscode-go/blob/master/docs/debugging.md#specifying-other-build-flags) documentation)`
115+
);
116+
}
117+
}
118+
95119
debugConfiguration['dlvToolPath'] = getBinPath('dlv');
96120
if (!path.isAbsolute(debugConfiguration['dlvToolPath'])) {
97121
promptForMissingTool('dlv');
@@ -156,4 +180,13 @@ export class GoDebugConfigurationProvider implements vscode.DebugConfigurationPr
156180
}
157181
});
158182
}
183+
184+
private removeFlag(args: string, flag: string): {args: string, removed: boolean} {
185+
const argv = parse(args, {configuration: {'short-option-groups': false}});
186+
if (argv[flag]) {
187+
delete argv[flag];
188+
return { args: unparse(argv).join(' '), removed: true };
189+
}
190+
return {args, removed: false};
191+
}
159192
}

test/integration/goDebugConfiguration.test.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import os = require('os');
44
import path = require('path');
55
import sinon = require('sinon');
66
import vscode = require('vscode');
7+
import parse = require('yargs-parser');
78
import { GoDebugConfigurationProvider } from '../../src/goDebugConfiguration';
89
import goEnv = require('../../src/goEnv');
910
import { updateGoVarsFromConfig } from '../../src/goInstallTools';
@@ -279,3 +280,136 @@ suite('Debug Configuration Merge User Settings', () => {
279280
});
280281
});
281282
});
283+
284+
suite('Debug Configuration Modify User Config', () => {
285+
const debugConfigProvider = new GoDebugConfigurationProvider();
286+
287+
function checkBuildFlags(input: string, expected: { [key: string]: any }) {
288+
// Parse the string result.
289+
const actual = parse(input, {configuration: {'short-option-groups': false}} );
290+
291+
// Delete the empty entry that is created by parse.
292+
delete actual['_'];
293+
294+
// Compare the two maps.
295+
assert.strictEqual(actual.size, expected.size);
296+
297+
const expectedKeys = [];
298+
for (const key in expected) {
299+
if (expected.hasOwnProperty(key)) {
300+
expectedKeys.push(key);
301+
}
302+
}
303+
expectedKeys.sort();
304+
305+
const actualKeys = [];
306+
for (const key in actual) {
307+
if (actual.hasOwnProperty(key)) {
308+
actualKeys.push(key);
309+
}
310+
}
311+
actualKeys.sort();
312+
313+
for (let i = 0; i < expectedKeys.length; i ++) {
314+
assert.strictEqual(actualKeys[i], expectedKeys[i]);
315+
assert.strictEqual(actual[actualKeys[i]], expected[expectedKeys[i]]);
316+
}
317+
}
318+
319+
suite('remove gcflags', () => {
320+
test('remove user set --gcflags in buildFlags', () => {
321+
const config = {
322+
name: 'Launch',
323+
type: 'go',
324+
request: 'launch',
325+
mode: 'auto',
326+
program: '${fileDirname}',
327+
env: {},
328+
buildFlags: '--gcflags=all=-l'
329+
};
330+
331+
debugConfigProvider.resolveDebugConfiguration(undefined, config);
332+
333+
checkBuildFlags(config.buildFlags, {});
334+
});
335+
336+
test('remove user set -gcflags in buildFlags', () => {
337+
const config = {
338+
name: 'Launch',
339+
type: 'go',
340+
request: 'launch',
341+
mode: 'auto',
342+
program: '${fileDirname}',
343+
env: {},
344+
buildFlags: `-gcflags all=-l`
345+
};
346+
347+
debugConfigProvider.resolveDebugConfiguration(undefined, config);
348+
349+
checkBuildFlags(config.buildFlags, {});
350+
});
351+
352+
test('remove user set --gcflags while preserving other build flags in buildFlags', () => {
353+
const config = {
354+
name: 'Launch',
355+
type: 'go',
356+
request: 'launch',
357+
mode: 'auto',
358+
program: '${fileDirname}',
359+
env: {},
360+
buildFlags: '-race --gcflags=all=-l --mod=mod'
361+
};
362+
363+
debugConfigProvider.resolveDebugConfiguration(undefined, config);
364+
365+
checkBuildFlags(config.buildFlags, {race: true, mod: 'mod'});
366+
});
367+
368+
test('preserve empty buildFlags', () => {
369+
const config = {
370+
name: 'Launch',
371+
type: 'go',
372+
request: 'launch',
373+
mode: 'auto',
374+
program: '${fileDirname}',
375+
env: {},
376+
buildFlags: ''
377+
};
378+
379+
debugConfigProvider.resolveDebugConfiguration(undefined, config);
380+
381+
checkBuildFlags(config.buildFlags, {});
382+
});
383+
384+
test('preserve buildFlags', () => {
385+
const config = {
386+
name: 'Launch',
387+
type: 'go',
388+
request: 'launch',
389+
mode: 'auto',
390+
program: '${fileDirname}',
391+
env: {},
392+
buildFlags: '-race --mod=mod'
393+
};
394+
395+
debugConfigProvider.resolveDebugConfiguration(undefined, config);
396+
397+
checkBuildFlags(config.buildFlags, {race: true, mod: 'mod'});
398+
});
399+
400+
test('remove user set --gcflags in GOFLAGS', () => {
401+
const config = {
402+
name: 'Launch',
403+
type: 'go',
404+
request: 'launch',
405+
mode: 'auto',
406+
program: '${fileDirname}',
407+
env: {GOFLAGS: '-race --gcflags=-l --mod=mod'},
408+
};
409+
410+
debugConfigProvider.resolveDebugConfiguration(undefined, config);
411+
412+
checkBuildFlags(config.env.GOFLAGS, {race: true, mod: 'mod'});
413+
});
414+
});
415+
});

typings/yargs-unparser.d.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
declare module 'yargs-unparser' {
2+
// Modified from './node_modules/@types/yargs-parser/index.d.ts'.
3+
namespace yargsUnparser {
4+
interface Arguments {
5+
/** Non-option arguments */
6+
_: string[];
7+
/** The script name or node command */
8+
$0: string;
9+
/** All remaining options */
10+
[argName: string]: any;
11+
}
12+
13+
interface Options {
14+
alias?: { [key: string]: string | string[] };
15+
default?: { [key: string]: any };
16+
command?: string;
17+
}
18+
19+
interface Unparser {
20+
(argv: Arguments, opts?: Options): string[];
21+
}
22+
}
23+
var yargsUnparser: yargsUnparser.Unparser;
24+
export = yargsUnparser;
25+
}

0 commit comments

Comments
 (0)