diff --git a/src/goCheck.ts b/src/goCheck.ts index d340aab65..6c912efd5 100644 --- a/src/goCheck.ts +++ b/src/goCheck.ts @@ -23,7 +23,18 @@ export interface ICheckResult { severity: string; } -function runTool(cmd: string, args: string[], cwd: string, severity: string, useStdErr: boolean, toolName: string, notFoundError?: string) { +/** + * Runs given Go tool and returns errors/warnings that can be fed to the Problems Matcher + * @param args Arguments to be passed while running given tool + * @param cwd cwd that will passed in the env object while running given tool + * @param serverity error or warning + * @param useStdErr If true, the stderr of the output of the given tool will be used, else stdout will be used + * @param toolName The name of the Go tool to run. If none is provided, the go runtime itself is used + * @param printUnexpectedOutput If true, then output that doesnt match expected format is printed to the output channel + */ +function runTool(args: string[], cwd: string, severity: string, useStdErr: boolean, toolName: string, printUnexpectedOutput?: boolean): Promise { + let goRuntimePath = getGoRuntimePath(); + let cmd = toolName ? getBinPath(toolName) : goRuntimePath; return new Promise((resolve, reject) => { cp.execFile(cmd, args, { cwd: cwd }, (err, stdout, stderr) => { try { @@ -31,10 +42,15 @@ function runTool(cmd: string, args: string[], cwd: string, severity: string, use if (toolName) { promptForMissingTool(toolName); } else { - vscode.window.showInformationMessage(notFoundError); + vscode.window.showInformationMessage(`Cannot find ${goRuntimePath}`); } return resolve([]); } + if (err && stderr && !useStdErr) { + outputChannel.appendLine(['Error while running tool:', cmd, ...args].join(' ')); + outputChannel.appendLine(stderr); + return resolve([]); + } let lines = (useStdErr ? stderr : stdout).toString().split('\n'); outputChannel.appendLine(['Finished running tool:', cmd, ...args].join(' ')); @@ -45,7 +61,10 @@ function runTool(cmd: string, args: string[], cwd: string, severity: string, use continue; } let match = /^([^:]*: )?((.:)?[^:]*):(\d+)(:(\d+)?)?:(?:\w+:)? (.*)$/.exec(lines[i]); - if (!match) continue; + if (!match) { + if (printUnexpectedOutput) outputChannel.appendLine(lines[i]); + continue; + } let [_, __, file, ___, lineStr, ____, charStr, msg] = match; let line = +lineStr; file = path.resolve(cwd, file); @@ -98,48 +117,39 @@ export function check(filename: string, goConfig: vscode.WorkspaceConfiguration) args = ['test', '-copybinary', '-o', tmppath, '-c', '-tags', buildTags, ...buildFlags, '.']; } runTool( - goRuntimePath, args, cwd, 'error', true, null, - `Cannot find ${goRuntimePath}` + true ).then(result => resolve(result), err => reject(err)); }); }); runningToolsPromises.push(buildPromise); } if (!!goConfig['lintOnSave']) { - let lintTool = getBinPath(goConfig['lintTool'] || 'golint'); + let lintTool = goConfig['lintTool'] || 'golint'; let lintFlags = goConfig['lintFlags'] || []; let args = [...lintFlags]; - if (lintTool === 'golint') { - args.push(filename); - } - runningToolsPromises.push(runTool( - lintTool, args, cwd, 'warning', - lintTool === 'golint', - lintTool === 'golint' ? 'golint' : null, - lintTool === 'golint' ? undefined : 'No "gometalinter" could be found. Install gometalinter to use this option.' + false, + lintTool )); } if (!!goConfig['vetOnSave']) { let vetFlags = goConfig['vetFlags'] || []; runningToolsPromises.push(runTool( - goRuntimePath, ['tool', 'vet', ...vetFlags, filename], cwd, 'warning', true, - null, - `Cannot find ${goRuntimePath}` + null )); } diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts index 1ff821553..312d37233 100644 --- a/src/goInstallTools.ts +++ b/src/goInstallTools.ts @@ -47,6 +47,11 @@ function getTools(goVersion: SemVersion): { [key: string]: string } { tools['golint'] = 'github.com/golang/lint/golint'; tools['gotests'] = 'github.com/cweill/gotests/...'; } + + if (goConfig['lintTool'] === 'gometalinter') { + tools['gometalinter'] = 'github.com/alecthomas/gometalinter'; + } + return tools; } @@ -121,9 +126,9 @@ function installTools(goVersion: SemVersion, missing?: string[]) { // http.proxy setting takes precedence over environment variables let httpProxy = vscode.workspace.getConfiguration('http').get('proxy'); - let env = Object.assign({}, process.env); + let envForTools = Object.assign({}, process.env); if (httpProxy) { - env = Object.assign({}, process.env, { + envForTools = Object.assign({}, process.env, { http_proxy: httpProxy, HTTP_PROXY: httpProxy, https_proxy: httpProxy, @@ -131,15 +136,19 @@ function installTools(goVersion: SemVersion, missing?: string[]) { }); } - // If the go.toolsGopath is set, use + // If the go.toolsGopath is set, use // its value as the GOPATH for the "go get" child process. let goConfig = vscode.workspace.getConfiguration('go'); + let envWithSeparateGoPathForTools = null; if (goConfig['toolsGopath']) { - env['GOPATH'] = goConfig['toolsGopath']; + envWithSeparateGoPathForTools = Object.assign({}, envForTools, {GOPATH: goConfig['toolsGopath']}); } missing.reduce((res: Promise, tool: string) => { return res.then(sofar => new Promise((resolve, reject) => { + // gometalinter expects its linters to be in the user's GOPATH + // Therefore, cannot use an isolated GOPATH for installing gometalinter + let env = (envWithSeparateGoPathForTools && tool !== 'gometalinter') ? envWithSeparateGoPathForTools : envForTools; cp.execFile(goRuntimePath, ['get', '-u', '-v', tools[tool]], { env }, (err, stdout, stderr) => { if (err) { outputChannel.appendLine('Installing ' + tool + ' FAILED'); @@ -147,7 +156,22 @@ function installTools(goVersion: SemVersion, missing?: string[]) { resolve([...sofar, failureReason]); } else { outputChannel.appendLine('Installing ' + tool + ' SUCCEEDED'); - resolve([...sofar, null]); + if (tool === 'gometalinter') { + // Gometalinter needs to install all the linters it uses. + outputChannel.appendLine('Installing all linters used by gometalinter....'); + let gometalinterBinPath = getBinPath('gometalinter'); + cp.execFile(gometalinterBinPath, ['--install'], (err, stdout, stderr) => { + if (!err) { + outputChannel.appendLine('Installing all linters used by gometalinter SUCCEEDED.'); + resolve([...sofar, null]); + } else { + let failureReason = `Error while running gometalinter --install;; ${stderr}`; + resolve([...sofar, failureReason]); + } + }); + } else { + resolve([...sofar, null]); + } } }); }));