Skip to content

Commit 6eaf779

Browse files
authored
Install patched LLDB on vscode extension activation (#1637)
Download and install the WAMR patched LLDB binary on vscode extension activation. This allows the user to download the packaged .vsix file, where the activation script should handle determining what LLDB binary they should use, and install it in the correct location.
1 parent ce3458d commit 6eaf779

7 files changed

Lines changed: 202 additions & 28 deletions

File tree

test-tools/wamr-ide/VSCode-Extension/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
"program": "./resource/debug/windows/bin/lldb-vscode.exe"
130130
},
131131
"osx": {
132-
"program": "./resource/debug/osx/bin/lldb-vscode"
132+
"program": "./resource/debug/darwin/bin/lldb-vscode"
133133
},
134134
"linux": {
135135
"program": "./resource/debug/linux/bin/lldb-vscode"
@@ -237,7 +237,9 @@
237237
"@types/glob": "^7.1.3",
238238
"@types/mocha": "^8.2.2",
239239
"@types/node": "14.x",
240+
"@types/request": "^2.48.8",
240241
"@types/vscode": "^1.54.0",
242+
"@types/yauzl": "^2.10.0",
241243
"@typescript-eslint/eslint-plugin": "^4.26.0",
242244
"@typescript-eslint/parser": "^4.26.0",
243245
"eslint": "^7.32.0",
@@ -248,6 +250,8 @@
248250
"vscode-test": "^1.5.2"
249251
},
250252
"dependencies": {
251-
"@vscode/webview-ui-toolkit": "^0.8.4"
253+
"@vscode/webview-ui-toolkit": "^0.8.4",
254+
"request": "^2.88.2",
255+
"yauzl": "^2.10.0"
252256
}
253257
}

test-tools/wamr-ide/VSCode-Extension/resource/debug/osx/.placeholder renamed to test-tools/wamr-ide/VSCode-Extension/resource/debug/darwin/.placeholder

File renamed without changes.

test-tools/wamr-ide/VSCode-Extension/src/decorationProvider.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import * as vscode from 'vscode';
7-
import { ReadFromFile } from './utilities/directoryUtilities';
7+
import { readFromFile } from './utilities/directoryUtilities';
88
import * as path from 'path';
99
import * as os from 'os';
1010

@@ -63,8 +63,8 @@ export class DecorationProvider implements vscode.FileDecorationProvider {
6363

6464
prjConfigDir = path.join(currentPrjDir, '.wamr');
6565
configFilePath = path.join(prjConfigDir, 'compilation_config.json');
66-
if (ReadFromFile(configFilePath) !== '') {
67-
configData = JSON.parse(ReadFromFile(configFilePath));
66+
if (readFromFile(configFilePath) !== '') {
67+
configData = JSON.parse(readFromFile(configFilePath));
6868
includePathArr = configData['include_paths'];
6969
excludeFileArr = configData['exclude_files'];
7070

test-tools/wamr-ide/VSCode-Extension/src/extension.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import { WasmTaskProvider } from './taskProvider';
1212
import { TargetConfigPanel } from './view/TargetConfigPanel';
1313
import { NewProjectPanel } from './view/NewProjectPanel';
1414
import {
15-
CheckIfDirectoryExist,
16-
WriteIntoFile,
17-
ReadFromFile,
15+
checkIfDirectoryExists,
16+
writeIntoFile,
17+
readFromFile,
1818
} from './utilities/directoryUtilities';
1919
import { decorationProvider } from './decorationProvider';
2020
import { WasmDebugConfigurationProvider } from './debugConfigurationProvider';
21+
import { isLLDBInstalled, promptInstallLLDB } from './utilities/lldbUtilities';
2122

2223
let wasmTaskProvider: WasmTaskProvider;
2324
let wasmDebugConfigProvider: WasmDebugConfigurationProvider;
@@ -213,7 +214,7 @@ export async function activate(context: vscode.ExtensionContext) {
213214
return;
214215
}
215216
});
216-
} else if (!CheckIfDirectoryExist(curWorkspace as string)) {
217+
} else if (!checkIfDirectoryExists(curWorkspace as string)) {
217218
vscode.window
218219
.showWarningMessage(
219220
'Invalid workspace:',
@@ -369,7 +370,7 @@ export async function activate(context: vscode.ExtensionContext) {
369370

370371
let disposableDebug = vscode.commands.registerCommand(
371372
'wamride.debug',
372-
() => {
373+
async () => {
373374
if (!isWasmProject) {
374375
vscode.window.showErrorMessage('debug failed', {
375376
modal: true,
@@ -378,6 +379,15 @@ export async function activate(context: vscode.ExtensionContext) {
378379
return;
379380
}
380381

382+
/* we should check again whether the user installed lldb, as this can be skipped during activation */
383+
try {
384+
if (!isLLDBInstalled(context)) {
385+
await promptInstallLLDB(context);
386+
}
387+
} catch (e) {
388+
vscode.window.showWarningMessage((e as Error).message);
389+
}
390+
381391
/* refuse to debug if build process failed */
382392
if (!checkIfBuildSuccess()) {
383393
vscode.window.showErrorMessage('Debug failed', {
@@ -582,7 +592,7 @@ export async function activate(context: vscode.ExtensionContext) {
582592
return;
583593
}
584594
});
585-
} else if (!CheckIfDirectoryExist(curWorkspace as string)) {
595+
} else if (!checkIfDirectoryExists(curWorkspace as string)) {
586596
vscode.window
587597
.showWarningMessage(
588598
'Invalid workspace:',
@@ -686,6 +696,14 @@ export async function activate(context: vscode.ExtensionContext) {
686696
disposableToggleExcludeFile,
687697
disposableDebug
688698
);
699+
700+
try {
701+
if (!isLLDBInstalled(context)) {
702+
await promptInstallLLDB(context);
703+
}
704+
} catch (e) {
705+
vscode.window.showWarningMessage((e as Error).message);
706+
}
689707
}
690708

691709
function openWindoWithSituation(uri: vscode.Uri) {
@@ -736,13 +754,13 @@ export function writeIntoConfigFile(
736754

737755
let prjConfigDir = path.join(currentPrjDir, '.wamr');
738756
let configFilePath = path.join(prjConfigDir, 'compilation_config.json');
739-
WriteIntoFile(configFilePath, jsonStr);
757+
writeIntoFile(configFilePath, jsonStr);
740758
}
741759

742760
export function readFromConfigFile(): string {
743761
let prjConfigDir = path.join(currentPrjDir, '.wamr');
744762
let configFilePath = path.join(prjConfigDir, 'compilation_config.json');
745-
return ReadFromFile(configFilePath);
763+
return readFromFile(configFilePath);
746764
}
747765

748766
/**
@@ -854,7 +872,7 @@ function generateCMakeFile(
854872
.concat('\n', strSrcList)
855873
.concat('\n', strIncludeList);
856874

857-
WriteIntoFile(cmakeFilePath, fullStr);
875+
writeIntoFile(cmakeFilePath, fullStr);
858876
}
859877

860878
function getAllSrcFiles(_path: string) {

test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import fileSystem = require('fs');
77
import vscode = require('vscode');
88
import path = require('path');
99
import os = require('os');
10+
import request = require('request');
11+
import yauzl = require('yauzl');
1012

1113
/**
1214
*
1315
* @param path destination path
1416
*/
15-
export function CreateDirectory(
17+
export function createDirectory(
1618
dest: string,
1719
mode: string | number | null | undefined = undefined
1820
): boolean {
@@ -30,7 +32,7 @@ export function CreateDirectory(
3032
}
3133

3234
let parent = path.dirname(dest);
33-
if (!CreateDirectory(parent, mode)) {
35+
if (!createDirectory(parent, mode)) {
3436
return false;
3537
}
3638

@@ -42,7 +44,7 @@ export function CreateDirectory(
4244
}
4345
}
4446

45-
export function CopyFiles(src: string, dest: string, flags?: number): boolean {
47+
export function copyFiles(src: string, dest: string, flags?: number): boolean {
4648
try {
4749
fileSystem.copyFileSync(src, dest);
4850
return true;
@@ -52,15 +54,15 @@ export function CopyFiles(src: string, dest: string, flags?: number): boolean {
5254
}
5355
}
5456

55-
export function WriteIntoFile(path: string, data: string): void {
57+
export function writeIntoFile(path: string, data: string): void {
5658
try {
5759
fileSystem.writeFileSync(path, data, null);
5860
} catch (err) {
5961
vscode.window.showErrorMessage(err as string);
6062
}
6163
}
6264

63-
export function ReadFromFile(path: string): string {
65+
export function readFromFile(path: string): string {
6466
try {
6567
let data = fileSystem.readFileSync(path, { encoding: 'utf-8' });
6668
return data as string;
@@ -70,7 +72,7 @@ export function ReadFromFile(path: string): string {
7072
}
7173
}
7274

73-
export function WriteIntoFileAsync(
75+
export function writeIntoFileAsync(
7476
path: string,
7577
data: string,
7678
callback: fileSystem.NoParamCallback
@@ -83,7 +85,7 @@ export function WriteIntoFileAsync(
8385
}
8486
}
8587

86-
export function CheckIfDirectoryExist(path: string): boolean {
88+
export function checkIfPathExists(path: string): boolean {
8789
try {
8890
if (fileSystem.existsSync(path)) {
8991
return true;
@@ -96,6 +98,22 @@ export function CheckIfDirectoryExist(path: string): boolean {
9698
}
9799
}
98100

101+
export function checkIfDirectoryExists(path: string): boolean {
102+
const doesPathExist = checkIfPathExists(path);
103+
if (doesPathExist) {
104+
return fileSystem.lstatSync(path).isDirectory();
105+
}
106+
return false;
107+
}
108+
109+
export function checkIfFileExists(path: string): boolean {
110+
const doesPathExist = checkIfPathExists(path);
111+
if (doesPathExist) {
112+
return fileSystem.lstatSync(path).isFile();
113+
}
114+
return false;
115+
}
116+
99117
export function checkFolderName(folderName: string) {
100118
let invalidCharacterArr: string[] = [];
101119
var valid = true;
@@ -118,3 +136,54 @@ export function checkFolderName(folderName: string) {
118136

119137
return valid;
120138
}
139+
140+
export function downloadFile(url: string, destinationPath: string): Promise<void> {
141+
return new Promise((resolve, reject) => {
142+
const file = fileSystem.createWriteStream(destinationPath);
143+
const stream = request(url, undefined, (error, response, body) => {
144+
if (response.statusCode !== 200) {
145+
reject(new Error(`Download from ${url} failed with ${response.statusMessage}`));
146+
}
147+
}).pipe(file);
148+
stream.on("close", resolve);
149+
stream.on("error", reject);
150+
});
151+
}
152+
153+
export function unzipFile(sourcePath: string, getDestinationFileName: (entryName: string) => string): Promise<string[]> {
154+
return new Promise((resolve, reject) => {
155+
const unzippedFilePaths: string[] = [];
156+
yauzl.open(sourcePath, { lazyEntries: true }, function(error, zipfile) {
157+
if (error) {
158+
reject(error);
159+
return;
160+
}
161+
zipfile.readEntry();
162+
zipfile.on("entry", function(entry) {
163+
// This entry is a directory so skip it
164+
if (/\/$/.test(entry.fileName)) {
165+
zipfile.readEntry();
166+
return;
167+
}
168+
169+
zipfile.openReadStream(entry, function(error, readStream) {
170+
if (error) {
171+
reject(error);
172+
return;
173+
}
174+
readStream.on("end", () => zipfile.readEntry());
175+
const destinationFileName = getDestinationFileName(entry.fileName);
176+
fileSystem.mkdirSync(path.dirname(destinationFileName), { recursive: true });
177+
178+
const file = fileSystem.createWriteStream(destinationFileName);
179+
readStream.pipe(file).on("error", reject);
180+
unzippedFilePaths.push(destinationFileName);
181+
});
182+
});
183+
zipfile.on("end", function() {
184+
zipfile.close();
185+
resolve(unzippedFilePaths);
186+
});
187+
});
188+
});
189+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
*/
5+
6+
import * as vscode from 'vscode';
7+
import * as os from 'os';
8+
import * as path from 'path';
9+
import * as fs from 'fs';
10+
import { checkIfFileExists, downloadFile, unzipFile } from './directoryUtilities';
11+
12+
const LLDB_RESOURCE_DIR = "resource/debug";
13+
const LLDB_OS_DOWNLOAD_URL_SUFFIX_MAP: Partial<Record<NodeJS.Platform, string>> = {
14+
"linux": "x86_64-ubuntu-22.04",
15+
"darwin": "universal-macos-latest"
16+
};
17+
18+
const WAMR_LLDB_NOT_SUPPORTED_ERROR = new Error("WAMR LLDB is not supported on this platform");
19+
20+
function getLLDBUnzipFilePath(destinationFolder: string, filename: string) {
21+
const dirs = filename.split("/");
22+
if (dirs[0] === "inst") {
23+
dirs.shift();
24+
}
25+
26+
return path.join(destinationFolder, ...dirs);
27+
}
28+
29+
function getLLDBDownloadUrl(context: vscode.ExtensionContext): string {
30+
const wamrVersion = require(path.join(context.extensionPath, "package.json")).version;
31+
const lldbOsUrlSuffix = LLDB_OS_DOWNLOAD_URL_SUFFIX_MAP[os.platform()];
32+
33+
if (!lldbOsUrlSuffix) {
34+
throw WAMR_LLDB_NOT_SUPPORTED_ERROR;
35+
}
36+
37+
return `https://github.com/bytecodealliance/wasm-micro-runtime/releases/download/WAMR-${wamrVersion}/wamr-lldb-${wamrVersion}-${lldbOsUrlSuffix}.zip`;
38+
}
39+
40+
export function isLLDBInstalled(context: vscode.ExtensionContext): boolean {
41+
const extensionPath = context.extensionPath;
42+
const lldbOSDir = os.platform();
43+
const lldbBinaryPath = path.join(extensionPath, LLDB_RESOURCE_DIR, lldbOSDir, "bin", "lldb");
44+
return checkIfFileExists(lldbBinaryPath);
45+
}
46+
47+
export async function promptInstallLLDB(context: vscode.ExtensionContext) {
48+
const extensionPath = context.extensionPath;
49+
const setupPrompt = "setup";
50+
const skipPrompt = "skip";
51+
const response = await vscode.window.showWarningMessage('No LLDB instance found. Setup now?', setupPrompt, skipPrompt);
52+
53+
if (response === skipPrompt) {
54+
return;
55+
}
56+
57+
const downloadUrl = getLLDBDownloadUrl(context);
58+
const destinationDir = os.platform();
59+
60+
if (!downloadUrl) {
61+
throw WAMR_LLDB_NOT_SUPPORTED_ERROR;
62+
}
63+
64+
const lldbDestinationFolder = path.join(extensionPath, LLDB_RESOURCE_DIR, destinationDir);
65+
const lldbZipPath = path.join(lldbDestinationFolder, "bundle.zip");
66+
67+
vscode.window.showInformationMessage(`Downloading LLDB...`);
68+
69+
await downloadFile(downloadUrl, lldbZipPath);
70+
71+
vscode.window.showInformationMessage(`LLDB downloaded to ${lldbZipPath}. Installing...`);
72+
73+
const lldbFiles = await unzipFile(lldbZipPath, filename => getLLDBUnzipFilePath(lldbDestinationFolder, filename));
74+
// Allow execution of lldb
75+
lldbFiles.forEach(file => fs.chmodSync(file, "0775"));
76+
77+
vscode.window.showInformationMessage(`LLDB installed at ${lldbDestinationFolder}`);
78+
79+
// Remove the bundle.zip
80+
fs.unlink(lldbZipPath, () => {});
81+
}
82+
83+

0 commit comments

Comments
 (0)