Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically install the probe-rs binaries on request #93

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
87 changes: 86 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 @@ -543,6 +556,78 @@ 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
? `powershell.exe -encodedCommand ${Buffer.from(
'irm https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-installer.ps1 | iex',
'utf16le',
).toString('base64')}`
: "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. Go to https://probe.rs to check out the setup and troubleshooting instructions.';
}
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
Loading