Skip to content

Commit

Permalink
Automatically install the probe-rs binaries on request
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatekii committed May 11, 2024
1 parent 2d8cf18 commit a8b836d
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 3 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
},
"main": "./dist/extension.js",
"activationEvents": [
"onDebug"
"onDebug",
"onStartupFinished"
],
"workspaceTrust": {
"request": "never"
Expand Down
83 changes: 82 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
ProviderResult,
WorkspaceFolder,
} from 'vscode';
import {probeRsInstalled} from './utils';

export async function activate(context: vscode.ExtensionContext) {
const descriptorFactory = new ProbeRSDebugAdapterServerDescriptorFactory();
Expand All @@ -29,9 +30,21 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.debug.onDidReceiveDebugSessionCustomEvent(
descriptorFactory.receivedCustomEvent.bind(descriptorFactory),
),
vscode.debug.onDidTerminateDebugSession(descriptorFactory.dispose.bind(descriptorFactory)),
);

(async () => {
if (!(await probeRsInstalled())) {
const resp = await vscode.window.showInformationMessage(
"probe-rs doesn't seem to be installed. Do you want to install it automatically now?",
'Install',
);

if (resp === 'Install') {
await installProbeRs();
}
}
})();

// I cannot find a way to programmatically test for when VSCode is debugging the extension, versus when a user is using the extension to debug their own code, but the following code is useful in the former situation, so I will leave it here to be commented out by extension developers when needed.
// const trackerFactory = new ProbeRsDebugAdapterTrackerFactory();
// context.subscriptions.push(
Expand Down Expand Up @@ -536,6 +549,74 @@ function startDebugServer(
});
}

/// Installs probe-rs if it is not present.
function installProbeRs() {
let windows = process.platform === 'win32';
let done = false;

vscode.window.withProgress(
{
location: vscode.ProgressLocation.Window,
cancellable: false,
title: 'Installing probe-rs ...',
},
async (progress) => {
progress.report({increment: 0});

const launchedDebugAdapter = childProcess.exec(
windows
? 'irm https://github.com/probe-rs/probe-rs/releases/download/latest/probe-rs-installer.ps1 | iex'
: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-installer.sh | sh",
(error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
done = true;
return;
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
},
);

const errorListener = (error: Error) => {
vscode.window.showInformationMessage(
'Installation failed: ${err.message}. Check the logs for more info.',
'Ok',
);
console.error(error);
done = true;
};

const exitListener = (code: number | null, signal: NodeJS.Signals | null) => {
let message;
if (code === 0) {
message = 'Installation successful.';
} else if (signal) {
message = 'Installation aborted.';
} else {
message = 'Installation failed.';
}
console.error(message);
vscode.window.showInformationMessage(message, 'Ok');
done = true;
};

launchedDebugAdapter.on('error', errorListener);
launchedDebugAdapter.on('exit', exitListener);

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
while (!done) {
await delay(100);
}

launchedDebugAdapter.removeListener('error', errorListener);
launchedDebugAdapter.removeListener('exit', exitListener);

progress.report({increment: 100});
},
);
}

// Get the name of the debugger executable
//
// This takes the value from configuration, if set, or
Expand Down
32 changes: 32 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const path = require('path');
import {promises as fs} from 'fs';

/**
* @param {string} exe executable name (without extension if on Windows)
* @return {Promise<string|null>} executable path if found
* */
async function findExecutable(executableName: string): Promise<string | null> {
const envPath = process.env.PATH || '';
const envExt = process.env.PATHEXT || '';
const pathDirs = envPath.replace(/["]+/g, '').split(path.delimiter).filter(Boolean);
const extensions = envExt.split(';');
const candidates = pathDirs.flatMap((d) =>
extensions.map((ext) => path.join(d, executableName + ext)),
);
try {
return await Promise.any(candidates.map(checkFileExists));
} catch (e) {
return null;
}
}

async function checkFileExists(filePath): Promise<string | null> {
if ((await fs.stat(filePath)).isFile()) {
return filePath;
}
return null;
}

export async function probeRsInstalled() {
return await findExecutable('probe-rs');
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": ["es6"],
"lib": ["es2021"],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */,
Expand Down

0 comments on commit a8b836d

Please sign in to comment.