src: add handling for on-close behavior for tools
I doubt many other tools will have on-close behavior, but this feels a bit nicer. We can always delete the function if it becomes unnecessary. Unfortunately the diffs are not as nice as I had hoped because a few variables had to be moved around, but hopefully this isn't too bad.
Change-Id: I4905d19264859ea3b967dfec566402185ef00c00
GitHub-Last-Rev: ce5dd584514dd00fc5457cd66dd8c928de5223df
GitHub-Pull-Request: golang/vscode-go#40
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/233600
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index 52683b3..f86c6da 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -191,7 +191,7 @@
.reduce((res: Promise<string[]>, tool: ToolAtVersion) => {
return res.then(
(sofar) =>
- new Promise<string[]>((resolve, reject) => {
+ new Promise<string[]>(async (resolve, reject) => {
// Disable modules for tools which are installed with the "..." wildcard.
// TODO: ... will be supported in Go 1.13, so enable these tools to use modules then.
const modulesOffForTool = modulesOff || disableModulesForWildcard(tool, goVersion);
@@ -204,17 +204,18 @@
tmpGoModFile = path.join(toolsTmpDir, 'go.mod');
fs.writeFileSync(tmpGoModFile, 'module tools');
}
+ let importPath: string;
+ if (modulesOffForTool) {
+ importPath = getImportPath(tool, goVersion);
+ } else {
+ importPath = getImportPathWithVersion(tool, tool.version, goVersion);
+ }
- const opts = {
- env: envForTools,
- cwd: toolsTmpDir
- };
const callback = (err: Error, stdout: string, stderr: string) => {
// Make sure to delete the temporary go.mod file, if it exists.
if (tmpGoModFile && fs.existsSync(tmpGoModFile)) {
fs.unlinkSync(tmpGoModFile);
}
- const importPath = getImportPathWithVersion(tool, tool.version, goVersion);
if (err) {
outputChannel.appendLine('Installing ' + importPath + ' FAILED');
const failureReason = tool.name + ';;' + err + stdout.toString() + stderr.toString();
@@ -225,66 +226,51 @@
}
};
- let closeToolPromise = Promise.resolve(true);
- const toolBinPath = getBinPath(tool.name);
- if (path.isAbsolute(toolBinPath) && isGocode(tool)) {
- closeToolPromise = new Promise<boolean>((innerResolve) => {
- cp.execFile(toolBinPath, ['close'], {}, (err, stdout, stderr) => {
- if (stderr && stderr.indexOf(`rpc: can't find service Server.`) > -1) {
- outputChannel.appendLine(
- 'Installing gocode aborted as existing process cannot be closed. Please kill the running process for gocode and try again.'
- );
- return innerResolve(false);
- }
- innerResolve(true);
- });
- });
- }
-
- closeToolPromise.then((success) => {
- if (!success) {
+ // Perform any on-close actions before reinstalling the tool.
+ if (tool.close) {
+ const errMsg = await tool.close();
+ if (errMsg) {
+ outputChannel.appendLine(errMsg);
resolve([...sofar, null]);
return;
}
- const args = ['get', '-v'];
- // Only get tools at master if we are not using modules.
- if (modulesOffForTool) {
- args.push('-u');
- }
- // Tools with a "mod" suffix should not be installed,
- // instead we run "go build -o" to rename them.
- if (hasModSuffix(tool)) {
- args.push('-d');
- }
- let importPath: string;
- if (modulesOffForTool) {
- importPath = getImportPath(tool, goVersion);
+ }
+ const args = ['get', '-v'];
+ // Only get tools at master if we are not using modules.
+ if (modulesOffForTool) {
+ args.push('-u');
+ }
+ // Tools with a "mod" suffix should not be installed,
+ // instead we run "go build -o" to rename them.
+ if (hasModSuffix(tool)) {
+ args.push('-d');
+ }
+ args.push(importPath);
+ const opts = {
+ env: envForTools,
+ cwd: toolsTmpDir
+ };
+ cp.execFile(goRuntimePath, args, opts, (err, stdout, stderr) => {
+ if (stderr.indexOf('unexpected directory layout:') > -1) {
+ outputChannel.appendLine(
+ `Installing ${importPath} failed with error "unexpected directory layout". Retrying...`
+ );
+ cp.execFile(goRuntimePath, args, opts, callback);
+ } else if (!err && hasModSuffix(tool)) {
+ const outputFile = path.join(
+ toolsGopath,
+ 'bin',
+ process.platform === 'win32' ? `${tool.name}.exe` : tool.name
+ );
+ cp.execFile(
+ goRuntimePath,
+ ['build', '-o', outputFile, getImportPath(tool, goVersion)],
+ opts,
+ callback
+ );
} else {
- importPath = getImportPathWithVersion(tool, tool.version, goVersion);
+ callback(err, stdout, stderr);
}
- args.push(importPath);
- cp.execFile(goRuntimePath, args, opts, (err, stdout, stderr) => {
- if (stderr.indexOf('unexpected directory layout:') > -1) {
- outputChannel.appendLine(
- `Installing ${importPath} failed with error "unexpected directory layout". Retrying...`
- );
- cp.execFile(goRuntimePath, args, opts, callback);
- } else if (!err && hasModSuffix(tool)) {
- const outputFile = path.join(
- toolsGopath,
- 'bin',
- process.platform === 'win32' ? `${tool.name}.exe` : tool.name
- );
- cp.execFile(
- goRuntimePath,
- ['build', '-o', outputFile, getImportPath(tool, goVersion)],
- opts,
- callback
- );
- } else {
- callback(err, stdout, stderr);
- }
- });
});
})
);
diff --git a/src/goTools.ts b/src/goTools.ts
index 7b6819b..1552c49 100644
--- a/src/goTools.ts
+++ b/src/goTools.ts
@@ -5,10 +5,13 @@
'use strict';
+import cp = require('child_process');
import moment = require('moment');
+import path = require('path');
import semver = require('semver');
+import util = require('util');
import { goLiveErrorsEnabled } from './goLiveErrors';
-import { getGoConfig, GoVersion } from './util';
+import { getBinPath, getGoConfig, GoVersion } from './util';
export interface Tool {
name: string;
@@ -28,6 +31,11 @@
// Go with which this tool can be used.
minimumGoVersion?: semver.SemVer;
maximumGoVersion?: semver.SemVer;
+
+ // close performs any shutdown tasks that a tool must execute before a new
+ // version is installed. It returns a string containing an error message on
+ // failure.
+ close?: () => Promise<string>;
}
/**
@@ -169,6 +177,22 @@
importPath: 'github.com/mdempsky/gocode',
isImportant: true,
description: 'Auto-completion, does not work with modules',
+ close: async (): Promise<string> => {
+ const toolBinPath = getBinPath('gocode');
+ if (!path.isAbsolute(toolBinPath)) {
+ return '';
+ }
+ try {
+ const execFile = util.promisify(cp.execFile);
+ const { stderr } = await execFile(toolBinPath, ['close']);
+ if (stderr.indexOf(`rpc: can't find service Server.`) > -1) {
+ return `Installing gocode aborted as existing process cannot be closed. Please kill the running process for gocode and try again.`;
+ }
+ } catch (err) {
+ return `Failed to close gocode process: ${err}.`;
+ }
+ return '';
+ },
},
'gocode-gomod': {
name: 'gocode-gomod',