Implement WAMR-IDE with vscode extension (#943)

Implement WAMR-IDE with vscode extension to enable developing
WebAssembly applications with coding, building, running and
debugging support. Support both Linux and Windows, and only
support putting all the tools in a docker image, e.g. wasi-sdk, wamrc,
iwasm and so on.

Co-authored-by: Wang Ning <justdoitwn@163.com>
This commit is contained in:
Wenyong Huang
2022-01-25 10:10:12 +08:00
committed by GitHub
parent 90a0057d33
commit d925369a1f
66 changed files with 2769 additions and 0 deletions

View File

@ -0,0 +1,24 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": [
"out",
"dist",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,7 @@
out
dist
node_modules
.vscode-test/
*.vsix
package-lock.json
src/test

View File

@ -0,0 +1,12 @@
{
"printWidth": 80,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "avoid",
"proseWrap": "always"
}

View File

@ -0,0 +1,5 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": ["dbaeumer.vscode-eslint"]
}

View File

@ -0,0 +1,15 @@
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/out/**/*.js"],
"preLaunchTask": "${defaultBuildTask}"
}
]
}

View File

@ -0,0 +1,20 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -0,0 +1,12 @@
.vscode/**
.vscode-test/**
out/test/**
src/**
.gitignore
.yarnrc
vsc-extension-quickstart.md
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts

View File

@ -0,0 +1,25 @@
# Introduction
### An integrated development environment for WASM.
# How to debug this extension
> Note that please build `lldb` firstly follow this
> [instruction](./resource/debug/README.md) if you want to enable
> `source debugging` feature of this extension
### 1. open `VSCode_Extension` directory with the `vscode`
```xml
File -> Open Folder -> select `VSCode_Extension`
```
### 2. run `npm install` in `terminal` to install necessary dependencies.
### 3. click `F5` or `ctrl+shift+D` switch to `Run and Debug` panel and click `Run Extension` to boot.
# Code Format
`prettier` is recommended and `.prettierrc.json` has been provided in workspace.
More details and usage guidance please refer
[prettier](https://prettier.io/docs/en/install.html)

View File

@ -0,0 +1,238 @@
{
"name": "wamride",
"publisher": "wamr",
"displayName": "WAMR-IDE",
"description": "An Integrated Development Environment for WASM",
"version": "0.0.1",
"engines": {
"vscode": "^1.59.0"
},
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "wamride.newProject",
"title": "Create new project",
"category": "New project"
},
{
"command": "wamride.changeWorkspace",
"title": "Change workspace",
"category": "Change Workspace"
},
{
"command": "wamride.build",
"title": "WAMRIDE:Build Wasm"
},
{
"command": "wamride.run",
"title": "WAMRIDE:Run Wasm"
},
{
"command": "wamride.debug",
"title": "WAMRIDE:Source Debug"
},
{
"command": "wamride.openFolder",
"title": "WAMRIDE:openWorkspace"
},
{
"command": "wamride.build.toggleStateIncludePath",
"title": "Toggle state of path including"
},
{
"command": "wamride.build.toggleStateExclude",
"title": "Toggle state of excluding"
},
{
"command": "wamride.targetConfig",
"title": "Target Configuration"
}
],
"viewsContainers": {
"activitybar": [
{
"id": "wamride",
"title": "WAMRIDE",
"icon": "$(star)"
}
]
},
"views": {
"wamride": [
{
"id": "wamride.views.welcome",
"name": "Quick Access"
}
]
},
"viewsWelcome": [
{
"view": "wamride.views.welcome",
"contents": "[ Project Management ]\n[$(project)New project](command:wamride.newProject)\n[$(files)Open project](command:wamride.openFolder)\n[$(book)Change workspace](command:wamride.changeWorkspace)\n[$(pencil)Configuration](command:wamride.targetConfig)"
},
{
"view": "wamride.views.welcome",
"contents": "[ Execution Management ]\n[$(gear)Build](command:wamride.build)\n[$(run)Run](command:wamride.run)\n[$(debug-alt) Debug](command:wamride.debug)",
"enablement": "ext.isWasmProject"
}
],
"menus": {
"explorer/context": [
{
"command": "wamride.build.toggleStateIncludePath",
"alt": "wamride.build.toggleStateIncludePath",
"group": "config",
"when": "explorerResourceIsFolder"
},
{
"command": "wamride.build.toggleStateExclude",
"alt": "wamride.build.toggleStateExclude",
"group": "config",
"when": "!explorerResourceIsFolder && resourceExtname in ext.supportedFileType"
}
]
},
"debuggers": [
{
"type": "wamr-debug",
"label": "WAMR lldb debugger",
"enableBreakpointsFor": {
"languageIds": [
"ada",
"arm",
"asm",
"c",
"cpp",
"crystal",
"d",
"fortan",
"fortran-modern",
"nim",
"objective-c",
"objectpascal",
"pascal",
"rust",
"swift"
]
},
"program": "./resource/debug/bin/lldb-vscode",
"windows": {
"program": "./resource/debug/bin/lldb-vscode.exe"
},
"configurationAttributes": {
"attach": {
"properties": {
"sourcePath": {
"type": "string",
"description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths."
},
"sourceMap": {
"type": "array",
"description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination pathname. Overrides sourcePath.",
"default": []
},
"debuggerRoot": {
"type": "string",
"description": "Specify a working directory to set the debug adaptor to so relative object files can be located."
},
"attachCommands": {
"type": "array",
"description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.",
"default": []
},
"initCommands": {
"type": "array",
"description": "Initialization commands executed upon debugger startup.",
"default": []
},
"preRunCommands": {
"type": "array",
"description": "Commands executed just before the program is attached to.",
"default": []
},
"stopCommands": {
"type": "array",
"description": "Commands executed each time the program stops.",
"default": []
},
"exitCommands": {
"type": "array",
"description": "Commands executed at the end of debugging session.",
"default": []
}
}
}
},
"initialConfigurations": [
{
"type": "wamr-debug",
"request": "attach",
"name": "Debug",
"stopOnEntry": true,
"attachCommands": [
"process connect -p wasm connect://127.0.0.1:1234"
]
}
],
"configurationSnippets": [
{
"label": "WAMR: Attach",
"description": "",
"body": {
"type": "wamr-debug",
"request": "attach",
"name": "${2:Attach}",
"stopOnEntry": true,
"attachCommands": [
"process connect -p wasm connect://${3:127.0.0.1}:${4:1234}"
]
}
}
]
}
],
"configuration": [
{
"title": "WAMR-IDE",
"properties": {
"WAMR-IDE.configWorkspace": {
"type": "string",
"description": "Config the workspace for WebAssembly project."
}
}
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/glob": "^7.1.3",
"@types/mocha": "^8.2.2",
"@types/node": "14.x",
"@types/vscode": "^1.54.0",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"eslint": "^7.32.0",
"glob": "^7.1.7",
"mocha": "^8.4.0",
"prettier": "2.5.1",
"typescript": "^4.3.2",
"vscode-test": "^1.5.2"
},
"dependencies": {
"@vscode/webview-ui-toolkit": "^0.8.4"
}
}

View File

@ -0,0 +1,11 @@
### If you want to enable `source debugging` for this extension, please build `lldb` firstly following this [instruction](../../../../../doc/source_debugging.md#debugging-with-interpreter).
### After building(`linux` for example), create `bin` folder and `lib` folder respectively in current directory, add following necessary target files into the folders.
```shell
/llvm/build-lldb/bin/lldb # move this file to resource/debug/bin/
/llvm/build-lldb/bin/lldb-vscode # move this file to resource/debug/bin/
/llvm/build-lldb/lib/liblldb.so.13 # move this file to resource/debug/lib/
```
### Then you can start the extension and run the execute source debugging by clicking the `debug` button in the extension panel.

View File

@ -0,0 +1,40 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
cmake_minimum_required (VERSION 2.9)
project(Main)
include(${CMAKE_CURRENT_SOURCE_DIR}/project.cmake)
set (CMAKE_SYSROOT /opt/wamr-sdk/app/libc-builtin-sysroot)
set (CMAKE_C_FLAGS " -nostdlib -g -Wno-unused-command-line-argument " CACHE INTERNAL "")
set (CMAKE_CXX_FLAGS " -nostdlib -g" CACHE INTERNAL "")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -z stack-size=${STACK_SIZE}")
set (CMAKE_EXE_LINKER_FLAGS
"-Wl,--initial-memory=${INIT_MEM_SIZE},--max-memory=${MAX_MEM_SIZE}, \
-Wl,--no-entry,")
set (CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} \
${EXPORTED_SYMBOLS},")
set (CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} \
-Wl,--allow-undefined-file=${CMAKE_SYSROOT}/share/defined-symbols.txt" CACHE INTERNAL "")
set (SRC_LIST
${PROJECT_SRC_LIST})
set (HEADER_LIST
${CMAKE_CURRENT_SOURCE_DIR}/../include
${PROJECT_INCLUDES})
include_directories(${HEADER_LIST})
add_executable (${OUTPUT_FILE_NAME} ${SRC_LIST})

View File

@ -0,0 +1,14 @@
@REM Copyright (C) 2019 Intel Corporation. All rights reserved.
@REM SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
@echo off
set target_name=%1
docker run -it --name=wasm-debug-server-ctr ^
-v "%cd%":/mnt ^
-p 1234:1234 ^
wasm-debug-server:1.0 ^
/bin/bash -c "./debug.sh %target_name%"
@REM stop and remove wasm-debug-server-container
docker stop wasm-debug-server-ctr && docker rm wasm-debug-server-ctr

View File

@ -0,0 +1,14 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#!/bin/bash
target_name=$1
docker run -it --name=wasm-debug-server-ctr \
-v $(pwd):/mnt \
-p 1234:1234 \
wasm-debug-server:1.0 \
/bin/bash -c "./debug.sh ${target_name}"
docker stop wasm-debug-server-ctr && docker rm wasm-debug-server-ctr

View File

@ -0,0 +1,14 @@
@REM Copyright (C) 2019 Intel Corporation. All rights reserved.
@REM SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
@echo off
set AoT_Binary_Name=%1
@REM start a container, mount current project path to container/mnt
docker run --name=wasm-toolchain-provider-ctr ^
-it -v %cd%:/mnt ^
wasm-toolchain-provider:1.0 ^
/bin/bash -c "./build_wasm.sh %AoT_Binary_Name%"
@REM stop and remove wasm-toolchain-ctr container
docker stop wasm-toolchain-provider-ctr && docker rm wasm-toolchain-provider-ctr

View File

@ -0,0 +1,19 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#!/bin/bash
AoT_Binary_Name=$1
# start & run docker image and build wasm
if test ! -z "$(docker ps -a | grep wasm-toolchain-ctr)"; then
sudo docker rm wasm-toolchain-ctr
fi
sudo docker run --name=wasm-toolchain-provider-ctr \
-it -v $(pwd):/mnt \
wasm-toolchain-provider:1.0 \
/bin/bash -c "./build_wasm.sh $AoT_Binary_Name"
# stop and remove wasm-toolchain-ctr container
sudo docker stop wasm-toolchain-provider-ctr && sudo docker rm wasm-toolchain-provider-ctr

View File

@ -0,0 +1,17 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
set (OUTPUT_FILE_NAME)
set (INIT_MEM_SIZE)
set (MAX_MEM_SIZE)
set (STACK_SIZE)
set (EXPORTED_SYMBOLS)
set (PROJECT_SRC_LIST)
set (PROJECT_INCLUDES)

View File

@ -0,0 +1,13 @@
@REM Copyright (C) 2019 Intel Corporation. All rights reserved.
@REM SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
@echo off
set target_name=%1
docker run -it --name=wasm-executor-ctr ^
-v "%cd%":/mnt ^
wasm-debug-server:1.0 ^
/bin/bash -c "./run.sh %target_name%"
@REM stop and remove wasm-executor-ctr
docker stop wasm-executor-ctr && docker rm wasm-executor-ctr

View File

@ -0,0 +1,12 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#!/bin/bash
target_name=$1
docker run -it --name=wasm-debug-server-ctr \
-v $(pwd):/mnt \
wasm-debug-server:1.0 \
/bin/bash -c "./run.sh ${target_name}"
docker stop wasm-debug-server-ctr && docker rm wasm-debug-server-ctr

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
.box_wrapper {
display: flex;
justify-content: center;
align-items: center;
}
.form_heading {
text-align: left;
}
.form_heading vscode-divider,
.config_form_heading vscode-divider,
.form_bottom vscode-divider,
.config_form_bottom vscode-divider {
padding-bottom: 0.5rem;
}
.form_body,
.config_form_body {
display: grid;
grid-row-gap: 1rem;
padding-bottom: 0.5rem;
}
.config_form_body div,
.config_submit_btn_wrapper {
display: grid;
grid-template-columns: 4fr 8fr;
grid-column-gap: 0.5rem;
}
.form_heading,
.form_body,
.form_bottom {
width: 400px;
}
.config_form_body,
.config_form_heading,
.config_form_bottom {
width: 550px;
}
#btn_submit {
border-radius: 5px;
}
#select_div,
#text_filed_div,
.proj_submit_btn_wrapper {
display: grid;
grid-template-columns: 3fr 9fr;
grid-column-gap: 0.5rem;
}
#ipt_projName,
#tselect_dropdown,
.config_form_body vscode-text-field,
.config_form_body vscode-text-area {
width: 100%;
}
#btn {
text-align: center;
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
const vscode = acquireVsCodeApi();
document.getElementById('btn_submit').onclick = () => {
submitFunc();
};
function submitFunc() {
let outputFileName = document.getElementById('output_file_name').value;
let initmemSize = document.getElementById('initial_mem_size').value;
let maxmemSize = document.getElementById('max_mem_size').value;
let stackSize = document.getElementById('stack_size').value;
let exportedSymbols = document.getElementById('exported_symbols').value;
vscode.postMessage({
command: 'config_build_target',
outputFileName: outputFileName,
initmemSize: initmemSize,
maxmemSize: maxmemSize,
stackSize: stackSize,
exportedSymbols: exportedSymbols,
});
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
const vscode = acquireVsCodeApi();
document.getElementById('btn_submit').onclick = () => {
submitFunc();
};
function submitFunc() {
let projectName = document.getElementById('ipt_projName').value;
let template = document.getElementById('select_dropdown').value;
vscode.postMessage({
command: 'create_new_project',
projectName: projectName,
template: template,
});
/* get msg from ext */
window.addEventListener('message', event => {
const message = event.data;
switch (message.command) {
/* send command to open the project */
case 'proj_creation_finish':
vscode.postMessage({
command: 'open_project',
projectName: message.prjName,
});
break;
default:
break;
}
});
}

View File

@ -0,0 +1,63 @@
<!--
-- Copyright (C) 2019 Intel Corporation. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="module" src="${toolkitUri}"></script>
<script type="module" src="${mainUri}"></script>
<link rel="stylesheet" href="${styleUri}">
<title>New project</title>
</head>
<body>
<div class="box_wrapper">
<div class="config_form_heading">
<h2>Config building target</h2>
<vscode-divider></vscode-divider>
</div>
</div>
<div class="box_wrapper">
<div class="config_form_body">
<div>
<label><b>Output file name: </b></label>
<vscode-text-field id="output_file_name" value=${output_file_val}></vscode-text-field>
</div>
<div>
<label><b>Initial linear memory size: </b></label>
<vscode-text-field id="initial_mem_size" value=${initial_mem_size_val}></vscode-text-field>
</div>
<div>
<label><b>Max linear memory size: </b></label>
<vscode-text-field id="max_mem_size" value=${max_mem_size_val}></vscode-text-field>
</div>
<div>
<label><b>Stack size: </b></label>
<vscode-text-field id="stack_size" value=${stack_size_val}></vscode-text-field>
</div>
<div>
<label><b>Exported symbols: </b></label>
<vscode-text-area rows="3" id="exported_symbols" placeholder="Please split each symbol with comma. Like 'app_main,on_init'" value=${exported_symbols_val}></vscode-text-area>
</div>
</div>
</div>
<div class="box_wrapper">
<div class="config_form_bottom">
<vscode-divider></vscode-divider>
<div class="config_submit_btn_wrapper">
<div></div>
<div id="btn">
<vscode-button id="btn_submit"><b>Modify</b></vscode-buton>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,55 @@
<!--
-- Copyright (C) 2019 Intel Corporation. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="module" src="${toolkitUri}"></script>
<script type="module" src="${mainUri}"></script>
<link rel="stylesheet" href="${styleUri}">
<title>Create project</title>
</head>
<body>
<div class="box_wrapper">
<div class="form_heading">
<h2>Create project</h2>
<vscode-divider class="divider_wrapper"></vscode-divider>
</div>
</div>
<div class="box_wrapper">
<div class="form_body">
<div id="text_filed_div">
<label><b>Project Name: </b></label>
<vscode-text-field id="ipt_projName"></vscode-text-field>
</div>
<div id="select_div">
<label><b>Template:</b></label>
<vscode-dropdown id="select_dropdown">
<vscode-option value="" selected:disabled style="display:none"></vscode-option>
<vscode-option value="default">default</vscode-option>
</vscode-dropdown>
</div>
</div>
</div>
<div class="box_wrapper">
<div class="form_bottom">
<vscode-divider></vscode-divider>
<div class="proj_submit_btn_wrapper">
<div></div>
<div id="btn">
<vscode-button id="btn_submit"><b>Create</b></vscode-buton>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import * as vscode from 'vscode';
import * as os from 'os';
export class WasmDebugConfigurationProvider
implements vscode.DebugConfigurationProvider {
constructor() {}
/* default port set as 1234 */
private port = 1234;
private hostPath!: string;
private providerPromise:
| Thenable<vscode.DebugConfiguration>
| undefined = undefined;
private wasmDebugConfig!: vscode.DebugConfiguration;
public resolveDebugConfiguration():
| Thenable<vscode.DebugConfiguration>
| undefined {
if (!this.providerPromise) {
this.providerPromise = Promise.resolve(this.wasmDebugConfig);
return this.providerPromise;
}
return this.providerPromise;
}
public setDebugConfig(hostPath: string, port: number) {
this.port = port;
this.hostPath = hostPath;
/* linux and windows has different debug configuration */
if (os.platform() === 'win32') {
this.wasmDebugConfig = {
type: 'wamr-debug',
name: 'Attach',
request: 'attach',
['stopOnEntry']: true,
['initCommands']: ['platform select remote-linux'],
['attachCommands']: [
'process connect -p wasm connect://127.0.0.1:' + port + '',
],
['sourceMap']: [['/mnt', hostPath]],
};
} else if (os.platform() === 'linux') {
this.wasmDebugConfig = {
type: 'wamr-debug',
name: 'Attach',
request: 'attach',
['stopOnEntry']: true,
['attachCommands']: [
'process connect -p wasm connect://127.0.0.1:' + port + '',
],
['sourceMap']: [['/mnt', hostPath]],
};
}
}
public getDebugConfig() {
return this.wasmDebugConfig;
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import * as vscode from 'vscode';
import { ReadFromFile } from '../utilities/directoryUtilities';
import * as path from 'path';
import * as os from 'os';
const DECORATION_INCLUDE_PATHS: vscode.FileDecoration =
new vscode.FileDecoration(
'✔',
'Included',
new vscode.ThemeColor('list.highlightForeground')
);
const DECORATION_EXCLUDE_FILES: vscode.FileDecoration =
new vscode.FileDecoration(
'✗',
'Excluded',
new vscode.ThemeColor('list.errorForeground')
);
export class DecorationProvider implements vscode.FileDecorationProvider {
private disposables: vscode.Disposable[] = [];
public onDidChangeFileDecorations: vscode.Event<
vscode.Uri | vscode.Uri[] | undefined
>;
private _eventEmiter: vscode.EventEmitter<vscode.Uri | vscode.Uri[]>;
constructor() {
this._eventEmiter = new vscode.EventEmitter();
this.onDidChangeFileDecorations = this._eventEmiter.event;
this.disposables.push(
vscode.window.registerFileDecorationProvider(this)
);
}
public provideFileDecoration(
uri: vscode.Uri
): vscode.ProviderResult<vscode.FileDecoration> {
let currentPrjDir,
prjConfigDir,
configFilePath,
configData,
includePathArr = new Array(),
excludeFileArr = new Array(),
pathRelative;
/* Read include_paths and exclude_fils from the config file */
currentPrjDir =
os.platform() === 'win32'
? (vscode.workspace.workspaceFolders?.[0].uri.fsPath as string)
: os.platform() === 'linux'
? (currentPrjDir = vscode.workspace.workspaceFolders?.[0].uri
.path as string)
: '';
pathRelative = (uri.fsPath ? uri.fsPath : uri.toString()).replace(
currentPrjDir,
'..'
);
prjConfigDir = path.join(currentPrjDir, '.wamr');
configFilePath = path.join(prjConfigDir, 'compilation_config.json');
if (ReadFromFile(configFilePath) !== '') {
configData = JSON.parse(ReadFromFile(configFilePath));
includePathArr = configData['include_paths'];
excludeFileArr = configData['exclude_files'];
if (includePathArr.indexOf(pathRelative) > -1) {
return DECORATION_INCLUDE_PATHS;
} else if (excludeFileArr.indexOf(pathRelative) > -1) {
return DECORATION_EXCLUDE_FILES;
}
}
}
public dispose(): void {
this.disposables.forEach(d => d.dispose());
}
public updateDecorationsForSource(uri: vscode.Uri): void {
this._eventEmiter.fire(uri);
}
}
export const decorationProvider: DecorationProvider = new DecorationProvider();

View File

@ -0,0 +1,597 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import * as fileSystem from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import { WasmTaskProvider } from './taskProvider';
import { TargetConfigPanel } from './view/TargetConfigPanel';
import { NewProjectPanel } from './view/NewProjectPanel';
import {
WriteIntoFile,
ReadFromFile,
WriteIntoFileAsync,
} from './utilities/directoryUtilities';
import { decorationProvider } from './explorer/decorationProvider';
import { WasmDebugConfigurationProvider } from './debug/debugConfigurationProvider';
let wasmTaskProvider: WasmTaskProvider;
let wasmDebugConfigProvider: WasmDebugConfigurationProvider;
var currentPrjDir = '';
var extensionPath = '';
export async function activate(context: vscode.ExtensionContext) {
var OS_PLATFORM = '',
buildScript = '',
runScript = '',
debugScript = '',
buildScriptFullPath = '',
runScriptFullPath = '',
debugScriptFullPath = '',
typeMap = new Map(),
/* include paths array used for written into config file */
includePathArr = new Array(),
/* exclude files array used for written into config file */
excludeFileArr = new Array(),
scriptMap = new Map();
/**
* Get OS platform information for differ windows and linux execution script
*/
OS_PLATFORM = os.platform();
/**
* Provide Build & Run Task with Task Provider instead of "tasks.json"
*/
/* set relative path of build.bat|sh script */
let scriptPrefix = 'resource/scripts/';
if (OS_PLATFORM === 'win32') {
buildScript = scriptPrefix.concat('build.bat');
runScript = scriptPrefix.concat('run.bat');
debugScript = scriptPrefix.concat('boot_debugger_server.bat');
} else if (OS_PLATFORM === 'linux') {
buildScript = scriptPrefix.concat('build.sh');
runScript = scriptPrefix.concat('run.sh');
debugScript = scriptPrefix.concat('boot_debugger_server.sh');
}
/* get extension's path, and set scripts' absolute path */
extensionPath = context.extensionPath;
buildScriptFullPath = path.join(extensionPath, buildScript);
runScriptFullPath = path.join(extensionPath, runScript);
debugScriptFullPath = path.join(extensionPath, debugScript);
scriptMap.set('buildScript', buildScriptFullPath);
scriptMap.set('runScript', runScriptFullPath);
scriptMap.set('debugScript', debugScriptFullPath);
typeMap.set('Build', 'Build');
typeMap.set('Run', 'Run');
typeMap.set('Debug', 'Debug');
wasmTaskProvider = new WasmTaskProvider(typeMap, scriptMap);
vscode.tasks.registerTaskProvider('wasm', wasmTaskProvider);
/* set current project directory */
if (vscode.workspace.workspaceFolders?.[0]) {
if (OS_PLATFORM === 'win32') {
currentPrjDir = vscode.workspace.workspaceFolders?.[0].uri
.fsPath as string;
} else if (OS_PLATFORM === 'linux') {
currentPrjDir = vscode.workspace.workspaceFolders?.[0].uri
.path as string;
}
}
/**
* check whether current project opened in vscode workspace is wasm project
* it not, `build`, `run` and `debug` will be disabled
*/
if (currentPrjDir !== '') {
let wamrFolder = fileSystem
.readdirSync(currentPrjDir, {
withFileTypes: true,
})
.filter(folder => folder.isDirectory() && folder.name === '.wamr');
if (wamrFolder.length !== 0) {
vscode.commands.executeCommand(
'setContext',
'ext.isWasmProject',
true
);
}
}
/* register debug configuration */
wasmDebugConfigProvider = new WasmDebugConfigurationProvider();
wasmDebugConfigProvider.setDebugConfig(currentPrjDir, 1234);
vscode.debug.registerDebugConfigurationProvider(
'wamr-debug',
wasmDebugConfigProvider
);
/* update ext.includePaths to show or hide 'Remove' button in menus */
vscode.commands.executeCommand('setContext', 'ext.supportedFileType', [
'.c',
'.cpp',
'.cxx',
]);
if (readFromConfigFile() !== '') {
let configData = JSON.parse(readFromConfigFile());
includePathArr = configData['include_paths'];
excludeFileArr = configData['exclude_files'];
if (Object.keys(configData['build_args']).length !== 0) {
TargetConfigPanel.BUILD_ARGS = configData['build_args'];
}
}
let disposableNewProj = vscode.commands.registerCommand(
'wamride.newProject',
() => {
let _ok = 'Set up now';
let _cancle = 'Maybe later';
let curWorkspace = vscode.workspace
.getConfiguration()
.get('WAMR-IDE.configWorkspace');
/* if user has not set up workspace yet, prompt to set up */
if (curWorkspace === '' || curWorkspace === undefined) {
vscode.window
.showWarningMessage(
'Please setup your workspace firstly.',
_ok,
_cancle
)
.then(item => {
if (item === _ok) {
vscode.commands.executeCommand(
'wamride.changeWorkspace'
);
} else {
return;
}
});
} else {
NewProjectPanel.render(context);
}
}
);
let disposableTargetConfig = vscode.commands.registerCommand(
'wamride.targetConfig',
() => {
if (currentPrjDir !== '') {
TargetConfigPanel.render(context);
} else {
vscode.window.showWarningMessage(
'Please create and open project firstly.',
'OK'
);
}
}
);
let disposableChangeWorkspace = vscode.commands.registerCommand(
'wamride.changeWorkspace',
async () => {
let options: vscode.OpenDialogOptions = {
canSelectFiles: false,
canSelectFolders: true,
openLabel: 'Select Workspace',
};
let Workspace = await vscode.window
.showOpenDialog(options)
.then(res => {
if (res) {
return res[0].fsPath as string;
} else {
return '';
}
});
/* update workspace value to vscode global settings */
await vscode.workspace
.getConfiguration()
.update(
'WAMR-IDE.configWorkspace',
Workspace.trim(),
vscode.ConfigurationTarget.Global
)
.then(
success => {
vscode.window.showInformationMessage(
'Workspace has been set up successfully!'
);
},
error => {
vscode.window.showErrorMessage(
'Set up Workspace failed!'
);
}
);
}
);
let disposableBuild = vscode.commands.registerCommand(
'wamride.build',
() => {
generateCMakeFile(includePathArr, excludeFileArr);
return vscode.commands.executeCommand(
'workbench.action.tasks.runTask',
'Build: Wasm'
);
}
);
let disposableDebug = vscode.commands.registerCommand(
'wamride.debug',
() => {
vscode.commands
.executeCommand('workbench.action.tasks.runTask', 'Debug: Wasm')
.then(() => {
vscode.debug.startDebugging(
undefined,
wasmDebugConfigProvider.getDebugConfig()
);
});
}
);
let disposableRun = vscode.commands.registerCommand('wamride.run', () => {
return vscode.commands.executeCommand(
'workbench.action.tasks.runTask',
'Run: Wasm'
);
});
let disposableToggleIncludePath = vscode.commands.registerCommand(
'wamride.build.toggleStateIncludePath',
fileUri => {
let pathRelative: string;
let path =
fileUri._fsPath !== null && fileUri._fsPath !== undefined
? fileUri._fsPath
: vscode.Uri.parse(fileUri.path as string).fsPath;
pathRelative = path.replace(currentPrjDir, '..');
if (includePathArr.indexOf(pathRelative) > -1) {
/* this folder has been added to include path, remove it */
includePathArr = includePathArr.filter(value => {
return value !== pathRelative;
});
} else {
includePathArr.push(pathRelative);
}
writeIntoConfigFile(
includePathArr,
excludeFileArr,
TargetConfigPanel.BUILD_ARGS
);
decorationProvider.updateDecorationsForSource(fileUri);
}
);
let disposableToggleExcludeFile = vscode.commands.registerCommand(
'wamride.build.toggleStateExclude',
fileUri => {
let pathRelative: string;
let path =
fileUri._fsPath !== null && fileUri._fsPath !== undefined
? fileUri._fsPath
: vscode.Uri.parse(fileUri.path as string).fsPath;
/* replace the current project absolute path with .. to change to relative path */
pathRelative = path.replace(currentPrjDir, '..');
if (excludeFileArr.indexOf(pathRelative) > -1) {
excludeFileArr = excludeFileArr.filter(val => {
return val !== pathRelative;
});
} else {
excludeFileArr.push(pathRelative);
}
writeIntoConfigFile(
includePathArr,
excludeFileArr,
TargetConfigPanel.BUILD_ARGS
);
/* update decoration for this source file */
decorationProvider.updateDecorationsForSource(fileUri);
}
);
let disposableOpenFolder = vscode.commands.registerCommand(
'wamride.openFolder',
() => {
let _ok = 'Set up now';
let _cancle = 'Maybe later';
let curWorkspace = vscode.workspace
.getConfiguration()
.get('WAMR-IDE.configWorkspace') as string;
/* if user has not set up workspace yet, prompt to set up */
if (curWorkspace === '' || curWorkspace === undefined) {
vscode.window
.showWarningMessage(
'Please setup your workspace firstly.',
_ok,
_cancle
)
.then(item => {
if (item === _ok) {
vscode.commands.executeCommand(
'wamride.changeWorkspace'
);
} else {
return;
}
});
} else {
/* get all directories within directory, ignore files */
let directoryArrDirent, directoryArr;
try {
directoryArrDirent = fileSystem.readdirSync(curWorkspace, {
withFileTypes: true,
});
} catch (err) {
vscode.window.showErrorMessage(
'Read from current workspace failed, please check.'
);
}
if (directoryArrDirent !== undefined) {
directoryArr = directoryArrDirent
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
vscode.window
.showQuickPick(directoryArr, {
title: 'Select project',
placeHolder: 'Please select project',
})
.then(option => {
if (!option) {
return;
}
let _path = curWorkspace.concat(
OS_PLATFORM === 'win32'
? '\\'
: OS_PLATFORM === 'linux'
? '/'
: '',
option
);
openWindoWithSituation(vscode.Uri.file(_path));
});
}
}
}
);
context.subscriptions.push(
disposableNewProj,
disposableTargetConfig,
disposableChangeWorkspace,
disposableBuild,
disposableRun,
disposableToggleIncludePath,
disposableOpenFolder,
disposableToggleExcludeFile,
disposableDebug
);
}
function openWindoWithSituation(uri: vscode.Uri) {
/**
* check if the workspace folder is empty,
* if yes, open new window, else open in current window
*/
let isWorkspaceEmpty: boolean;
isWorkspaceEmpty = !vscode.workspace.workspaceFolders?.[0] ? true : false;
isWorkspaceEmpty === false
? vscode.commands.executeCommand('vscode.openFolder', uri, {
forceNewWindow: true,
})
: vscode.commands.executeCommand('vscode.openFolder', uri);
return;
}
interface BuildArgs {
output_file_name: string;
init_memory_size: string;
max_memory_size: string;
stack_size: string;
exported_symbols: string;
}
/**
* @param: includePathArr
* @param: excludeFileArr
* Get current includePathArr and excludeFileArr from the json string that
* will be written into compilation_config.json
*/
export function writeIntoConfigFile(
includePathArr: string[],
excludeFileArr: string[],
buildArgs?: BuildArgs
) {
let jsonStr = JSON.stringify({
include_paths: includePathArr,
exclude_files: excludeFileArr,
build_args: buildArgs ? buildArgs : '{}',
});
let prjConfigDir = path.join(currentPrjDir, '.wamr');
let configFilePath = path.join(prjConfigDir, 'compilation_config.json');
WriteIntoFile(configFilePath, jsonStr);
}
export function readFromConfigFile(): string {
let prjConfigDir = path.join(currentPrjDir, '.wamr');
let configFilePath = path.join(prjConfigDir, 'compilation_config.json');
return ReadFromFile(configFilePath);
}
/**
* will be triggered when the user clicking `build` button
*/
function generateCMakeFile(
includePathArr: string[],
excludeFileArr: string[]
): void {
// -Wl,--export=${EXPORT_SYMBOLS}
let srcFilePath = path.join(currentPrjDir, 'src');
let prjConfigDir = path.join(currentPrjDir, '.wamr');
let cmakeFilePath = path.join(prjConfigDir, 'project.cmake');
let strIncludeList = 'set (PROJECT_INCLUDES';
let strSrcList = 'set (PROJECT_SRC_LIST';
let strOutputFileName = 'set (OUTPUT_FILE_NAME';
let strInitMemSize = 'set (INIT_MEM_SIZE';
let strMaxMemSize = 'set (MAX_MEM_SIZE';
let strStackSize = 'set (STACK_SIZE';
let strExportedSymbols = 'set (EXPORTED_SYMBOLS';
let fullStr = '';
let i, s, e: number;
/* change the absolute path into relative path */
let _re = currentPrjDir;
let _substr = '${CMAKE_CURRENT_SOURCE_DIR}/..';
let srcPathArr: Array<{ path: string }> | undefined;
/**
* set PROJECT_SRC_LIST
* default ADD every c OR c++ OR cpp under the src/ path
* except the files saved in the exclude_files array
*/
srcPathArr = getAllSrcFiles(srcFilePath);
if (srcPathArr === undefined) {
return;
}
for (s = 0; s < srcPathArr.length; s++) {
if (
excludeFileArr.indexOf(
srcPathArr[s].path.replace(currentPrjDir, '..')
) === -1
) {
/* replace currentPrjDir with ${CMAKE_CURRENT_SOURCE_DIR} */
let _newStr = srcPathArr[s].path
.replace(_re, _substr)
.replace(/\\/g, '/');
strSrcList = strSrcList.concat(' ', _newStr);
}
}
strSrcList = strSrcList.concat(' )');
for (i = 0; i < includePathArr.length; i++) {
let _newStr = includePathArr[i]
.replace(/../, _substr)
.replace(/\\/g, '/');
strIncludeList = strIncludeList.concat(' ', _newStr);
}
strIncludeList = strIncludeList.concat(' )');
/* set up user customized input in configBuildArgs webview */
strOutputFileName = strOutputFileName.concat(
' ',
TargetConfigPanel.BUILD_ARGS.output_file_name + ')'
);
strInitMemSize = strInitMemSize.concat(
' ',
TargetConfigPanel.BUILD_ARGS.init_memory_size + ')'
);
strMaxMemSize = strMaxMemSize.concat(
' ',
TargetConfigPanel.BUILD_ARGS.max_memory_size + ')'
);
strStackSize = strStackSize.concat(
' ',
TargetConfigPanel.BUILD_ARGS.stack_size + ')'
);
let exportedSymbolArr =
TargetConfigPanel.BUILD_ARGS.exported_symbols.split(',');
strExportedSymbols = strExportedSymbols.concat(' "');
for (e = 0; e < exportedSymbolArr.length; e++) {
strExportedSymbols = strExportedSymbols.concat(
' -Wl,',
'--export=',
exportedSymbolArr[e]
);
}
strExportedSymbols = strExportedSymbols.concat('")');
fullStr = strOutputFileName
.concat('\n', strInitMemSize)
.concat('\n', strMaxMemSize)
.concat('\n', strStackSize)
.concat('\n', strExportedSymbols)
.concat('\n', strSrcList)
.concat('\n', strIncludeList);
WriteIntoFile(cmakeFilePath, fullStr);
}
function getAllSrcFiles(_path: string) {
try {
const entries = fileSystem.readdirSync(_path, {
withFileTypes: true,
});
const files = entries
.filter(
/* filter files mismatch .c |.cpp |.cxx */
file =>
!file.isDirectory() && file.name.match('(.c|.cpp|.cxx)$')
)
.map(file => ({
path: path.join(_path, file.name),
}));
const folders = entries.filter(folder => folder.isDirectory());
for (const folder of folders) {
let fileArr = getAllSrcFiles(path.join(_path, folder.name));
fileArr ? files.push(...fileArr) : '';
}
return files;
} catch (error) {
vscode.window.showErrorMessage(error as string);
}
}

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import * as vscode from 'vscode';
import * as os from 'os';
import { TargetConfigPanel } from './view/TargetConfigPanel';
interface WasmTaskDefinition extends vscode.TaskDefinition {
/**
* The build flavor.
*/
flavor: string;
}
export interface OwnShellOption {
cmd: string;
options: vscode.ShellExecutionOptions;
}
export class WasmTaskProvider implements vscode.TaskProvider {
constructor(
public _type: Map<string, string>,
public _script: Map<string, string>
) {}
buildShellOption: OwnShellOption | undefined;
runShellOption: OwnShellOption | undefined;
debugShellOption: OwnShellOption | undefined;
private wasmPromise: Thenable<vscode.Task[]> | undefined = undefined;
public provideTasks(): Thenable<vscode.Task[]> | undefined {
let targetName =
TargetConfigPanel.BUILD_ARGS.output_file_name.split('.')[0];
if (os.platform() === 'linux') {
/* build */
this.buildShellOption = {
cmd: 'bash',
options: {
executable: this._script.get('buildScript'),
shellArgs: [targetName],
},
};
/* debug */
this.debugShellOption = {
cmd: 'bash',
options: {
executable: this._script.get('debugScript'),
shellArgs: [targetName],
},
};
/* run */
this.runShellOption = {
cmd: 'bash',
options: {
executable: this._script.get('runScript'),
shellArgs: [targetName],
},
};
} else if (os.platform() === 'win32') {
this.buildShellOption = {
cmd: this._script.get('buildScript') as string,
options: {
executable: this._script.get('buildScript'),
shellArgs: [targetName],
},
};
/* debug */
this.debugShellOption = {
cmd: this._script.get('debugScript') as string,
options: {
executable: this._script.get('debugScript'),
shellArgs: [targetName],
},
};
/* run */
this.runShellOption = {
cmd: this._script.get('runScript') as string,
options: {
executable: this._script.get('runScript'),
shellArgs: [targetName],
},
};
} else {
this.buildShellOption = {
cmd: "echo 'os platform is not supported yet'",
options: {},
};
this.debugShellOption = {
cmd: "echo 'os platform is not supported yet'",
options: {},
};
this.runShellOption = {
cmd: "echo 'os platform is not supported yet'",
options: {},
};
}
this.wasmPromise = Promise.resolve([
new vscode.Task(
{ type: 'wasm' },
vscode.TaskScope.Workspace,
'Wasm',
this._type.get('Build') as string,
new vscode.ShellExecution(
this.buildShellOption.cmd,
this.buildShellOption.options
)
),
new vscode.Task(
{ type: 'wasm' },
vscode.TaskScope.Workspace,
'Wasm',
this._type.get('Run') as string,
new vscode.ShellExecution(
this.runShellOption.cmd,
this.runShellOption.options
)
),
new vscode.Task(
{ type: 'wasm' },
vscode.TaskScope.Workspace,
'Wasm',
this._type.get('Debug') as string,
new vscode.ShellExecution(
this.debugShellOption.cmd,
this.debugShellOption.options
)
),
]);
return this.wasmPromise;
}
/**
* if the task or task in tasks.json does not set command, `
* resolveTask` will be invoked,
* otherwise, `provideTasks` will be invoked
* @param _task
* @returns
*/
public resolveTask(_task: vscode.Task): vscode.Task | undefined {
return undefined;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import fileSystem = require('fs');
import vscode = require('vscode');
import path = require('path');
/**
*
* @param path destination path
*/
export function CreateDirectory(
dest: string,
mode: string | number | null | undefined = undefined
): boolean {
try {
if (fileSystem.existsSync(dest)) {
if (fileSystem.lstatSync(dest).isDirectory()) {
return true;
} else {
return false;
}
}
if (!path) {
return false;
}
let parent = path.dirname(dest);
if (!CreateDirectory(parent, mode)) {
return false;
}
fileSystem.mkdirSync(dest, mode);
return true;
} catch (error) {
vscode.window.showErrorMessage(error as string);
return false;
}
}
export function CopyFiles(src: string, dest: string, flags?: number): boolean {
try {
fileSystem.copyFileSync(src, dest);
return true;
} catch (error) {
vscode.window.showErrorMessage(error as string);
return false;
}
}
export function WriteIntoFile(path: string, data: string): void {
try {
fileSystem.writeFileSync(path, data, null);
} catch (err) {
vscode.window.showErrorMessage(err as string);
}
}
export function ReadFromFile(path: string): string {
try {
let data = fileSystem.readFileSync(path, { encoding: 'utf-8' });
return data as string;
} catch (err) {
vscode.window.showErrorMessage(err as string);
return '';
}
}
export function WriteIntoFileAsync(
path: string,
data: string,
callback: fileSystem.NoParamCallback
): void {
try {
fileSystem.writeFile(path, data, callback);
} catch (err) {
vscode.window.showErrorMessage(err as string);
return;
}
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import { Uri, Webview } from 'vscode';
export function getUri(
webview: Webview,
extensionUri: Uri,
pathList: string[]
) {
return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList));
}

View File

@ -0,0 +1,253 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { CreateDirectory, CopyFiles } from '../utilities/directoryUtilities';
import { getUri } from '../utilities/getUri';
export class NewProjectPanel {
static USER_SET_WORKSPACE: string;
public static currentPanel: NewProjectPanel | undefined;
private readonly _panel: vscode.WebviewPanel;
private _disposables: vscode.Disposable[] = [];
static readonly USER_INTPUT_ERR: number = -2;
static readonly DIR_EXSITED_ERR: number = -1;
static readonly EXCUTION_SUCCESS: number = 0;
/**
* @param context extension context from extension.ts active func
*/
constructor(extensionUri: vscode.Uri, panel: vscode.WebviewPanel) {
this._panel = panel;
this._panel.webview.html = this._getHtmlForWebview(
this._panel.webview,
extensionUri,
'resource/webview/page/newProject.html'
);
this._setWebviewMessageListener(this._panel.webview, extensionUri);
this._panel.onDidDispose(this.dispose, null, this._disposables);
}
/**
* @param context
*/
public static render(context: vscode.ExtensionContext) {
NewProjectPanel.USER_SET_WORKSPACE = vscode.workspace
.getConfiguration()
.get('WAMR-IDE.configWorkspace') as string;
/* check if current panel is initialized */
if (NewProjectPanel.currentPanel) {
NewProjectPanel.currentPanel._panel.reveal(vscode.ViewColumn.One);
} else {
const panel = vscode.window.createWebviewPanel(
'newProject',
'Create project',
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
/* create new project panel obj */
NewProjectPanel.currentPanel = new NewProjectPanel(
context.extensionUri,
panel
);
}
}
/**
* @param projName project name input by user
* @param template
*/
private _creatNewProject(
projName: string,
template: string,
extensionUri: vscode.Uri
): number {
if (projName === '' || template === '') {
return NewProjectPanel.USER_INTPUT_ERR;
}
let ROOT_PATH = path.join(NewProjectPanel.USER_SET_WORKSPACE, projName);
let EXT_PATH = extensionUri.fsPath;
/* if the direcotry has exsited, then ignore the creation and return */
if (fs.existsSync(ROOT_PATH)) {
if (fs.lstatSync(ROOT_PATH).isDirectory()) {
return NewProjectPanel.DIR_EXSITED_ERR;
}
}
/* create necessary floders under the project directory */
CreateDirectory(path.join(ROOT_PATH, '.wamr'));
CreateDirectory(path.join(ROOT_PATH, 'include'));
CreateDirectory(path.join(ROOT_PATH, 'src'));
/* copy scripts files to project_root_path/.wamr */
CopyFiles(
path.join(EXT_PATH, 'resource/scripts/CMakeLists.txt'),
path.join(ROOT_PATH, '.wamr/CMakeLists.txt')
);
CopyFiles(
path.join(EXT_PATH, 'resource/scripts/project.cmake'),
path.join(ROOT_PATH, '.wamr/project.cmake')
);
return NewProjectPanel.EXCUTION_SUCCESS;
}
public _getHtmlForWebview(
webview: vscode.Webview,
extensionUri: vscode.Uri,
templatePath: string
) {
/* get toolkit uri */
const toolkitUri = getUri(webview, extensionUri, [
'node_modules',
'@vscode',
'webview-ui-toolkit',
'dist',
'toolkit.js',
]);
const styleUri = getUri(webview, extensionUri, [
'resource',
'webview',
'css',
'style.css',
]);
const mainUri = getUri(webview, extensionUri, [
'resource',
'webview',
'js',
'newproj.js',
]);
const resourcePath = path.join(extensionUri.fsPath, templatePath);
let html = fs.readFileSync(resourcePath, 'utf-8');
html = html
.replace(/(\${toolkitUri})/, toolkitUri.toString())
.replace(/(\${mainUri})/, mainUri.toString())
.replace(/(\${styleUri})/, styleUri.toString());
return html;
}
private _setWebviewMessageListener(
webview: vscode.Webview,
extensionUri: vscode.Uri
) {
// Handle messages from the webview
webview.onDidReceiveMessage(
message => {
switch (message.command) {
case 'create_new_project':
if (
this._creatNewProject(
message.projectName,
message.template,
extensionUri
) === NewProjectPanel.EXCUTION_SUCCESS
) {
/* post message to page to inform the project creation has finished */
webview.postMessage({
command: 'proj_creation_finish',
prjName: message.projectName,
});
} else if (
this._creatNewProject(
message.projectName,
message.template,
extensionUri
) === NewProjectPanel.DIR_EXSITED_ERR
) {
vscode.window.showErrorMessage(
'Project : ' +
message.projectName +
' exsits in your current root path, please change project name or root path!'
);
return;
} else if (
this._creatNewProject(
message.projectName,
message.template,
extensionUri
) === NewProjectPanel.USER_INTPUT_ERR
) {
vscode.window.showErrorMessage(
'Please fill chart before your submit!'
);
return;
}
return;
case 'open_project':
vscode.window.showInformationMessage(
'Project : ' +
message.projectName +
' will be opened!'
);
let isWorkspaceEmpty: boolean;
let projPath = path.join(
NewProjectPanel.USER_SET_WORKSPACE,
message.projectName
);
let uri = vscode.Uri.file(projPath);
/**
* check if the vscode workspace folder is empty,
* if yes, open new window, else open in current window
*/
isWorkspaceEmpty = !vscode.workspace
.workspaceFolders?.[0]
? true
: false;
isWorkspaceEmpty === false
? vscode.commands.executeCommand(
'vscode.openFolder',
uri,
{
forceNewWindow: true,
}
)
: vscode.commands.executeCommand(
'vscode.openFolder',
uri
);
case 'close_webview':
this._panel.dispose();
return;
default:
break;
}
},
undefined,
this._disposables
);
}
private dispose() {
NewProjectPanel.currentPanel = undefined;
this._panel.dispose();
while (this._disposables.length) {
const disposable = this._disposables.pop();
if (disposable) {
disposable.dispose();
}
}
}
}

View File

@ -0,0 +1,260 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import { readFromConfigFile, writeIntoConfigFile } from '../extension';
import { getUri } from '../utilities/getUri';
export class TargetConfigPanel {
public static currentPanel: TargetConfigPanel | undefined;
private readonly _panel: vscode.WebviewPanel;
private _disposables: vscode.Disposable[] = [];
public static BUILD_ARGS = {
output_file_name: 'main.wasm',
init_memory_size: '131072',
max_memory_size: '131072',
stack_size: '4096',
exported_symbols: 'main',
};
static readonly USER_INTPUT_ERR: number = -2;
static readonly EXCUTION_SUCCESS: number = 0;
/**
*
* @param context extension context from extension.ts active func
* @param panelName
*/
constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
this._panel = panel;
this._panel.webview.html = this._getHtmlForWebview(
this._panel.webview,
extensionUri,
'resource/webview/page/configBuildTarget.html'
);
this._panel.onDidDispose(this.dispose, null, this._disposables);
this._setWebviewMessageListener(this._panel.webview);
}
/**
*
* @param context
*/
public static render(context: vscode.ExtensionContext) {
/* check if current panel is initialized */
if (TargetConfigPanel.currentPanel) {
TargetConfigPanel.currentPanel._panel.reveal(vscode.ViewColumn.One);
} else {
const panel = vscode.window.createWebviewPanel(
'targetConfig',
'Config building target',
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
TargetConfigPanel.currentPanel = new TargetConfigPanel(
panel,
context.extensionUri
);
}
}
private _configBuildArgs(
outputFileName: string,
initmemSize: string,
maxmemSize: string,
stackSize: string,
exportedSymbols: string
): number {
if (
outputFileName === '' ||
initmemSize === '' ||
maxmemSize === '' ||
stackSize === '' ||
exportedSymbols === ''
) {
return TargetConfigPanel.USER_INTPUT_ERR;
}
let _configStr: string;
let includePathArr = new Array();
let excludeFileArr = new Array();
let configJson: any;
let _configObj = {
output_file_name: outputFileName,
init_memory_size: initmemSize,
max_memory_size: maxmemSize,
stack_size: stackSize,
exported_symbols: exportedSymbols,
};
TargetConfigPanel.BUILD_ARGS = _configObj;
_configStr = readFromConfigFile();
if (_configStr !== '' && _configStr !== undefined) {
configJson = JSON.parse(_configStr);
includePathArr =
configJson['include_paths'] === undefined
? []
: configJson['include_paths'];
excludeFileArr =
configJson['exclude_files'] === undefined
? []
: configJson['exclude_files'];
}
writeIntoConfigFile(
includePathArr,
excludeFileArr,
TargetConfigPanel.BUILD_ARGS
);
return TargetConfigPanel.EXCUTION_SUCCESS;
}
private _getHtmlForWebview(
webview: vscode.Webview,
extensionUri: vscode.Uri,
templatePath: string
) {
/* get toolkit uri */
const toolkitUri = getUri(webview, extensionUri, [
'node_modules',
'@vscode',
'webview-ui-toolkit',
'dist',
'toolkit.js',
]);
const styleUri = getUri(webview, extensionUri, [
'resource',
'webview',
'css',
'style.css',
]);
const mainUri = getUri(webview, extensionUri, [
'resource',
'webview',
'js',
'configbuildtarget.js',
]);
/* get config build target values and set into webview page */
let configData, buildArgObj;
let _output_file_name,
_init_memory_size,
_max_memory_size,
_stack_size,
_exported_symbols;
if (readFromConfigFile() !== '') {
configData = JSON.parse(readFromConfigFile());
buildArgObj = configData['build_args'];
if (buildArgObj !== undefined) {
_output_file_name = buildArgObj['output_file_name'];
_init_memory_size = buildArgObj['init_memory_size'];
_max_memory_size = buildArgObj['max_memory_size'];
_stack_size = buildArgObj['stack_size'];
_exported_symbols = buildArgObj['exported_symbols'];
}
}
const resourcePath = path.join(extensionUri.fsPath, templatePath);
let html = fs.readFileSync(resourcePath, 'utf-8');
html = html
.replace(/(\${toolkitUri})/, toolkitUri.toString())
.replace(/(\${mainUri})/, mainUri.toString())
.replace(/(\${styleUri})/, styleUri.toString())
.replace(
/(\${output_file_val})/,
_output_file_name === undefined ? '' : _output_file_name
)
.replace(
/(\${initial_mem_size_val})/,
_init_memory_size === undefined ? '' : _init_memory_size
)
.replace(
/(\${max_mem_size_val})/,
_max_memory_size === undefined ? '' : _max_memory_size
)
.replace(
/(\${stack_size_val})/,
_stack_size === undefined ? '' : _stack_size
)
.replace(
/(\${exported_symbols_val})/,
_exported_symbols === undefined ? '' : _exported_symbols
);
return html;
}
private _setWebviewMessageListener(webview: vscode.Webview) {
webview.onDidReceiveMessage(
message => {
switch (message.command) {
case 'config_build_target':
if (
message.outputFileName === '' ||
message.initmemSize === '' ||
message.maxmemSize === '' ||
message.stackSize === '' ||
message.exportedSymbols === ''
) {
vscode.window.showErrorMessage(
'Please fill chart before your submit!'
);
return;
} else if (
this._configBuildArgs(
message.outputFileName,
message.initmemSize,
message.maxmemSize,
message.stackSize,
message.exportedSymbols
) === TargetConfigPanel.EXCUTION_SUCCESS
) {
vscode.window
.showInformationMessage(
'Configurations have been saved!',
'OK'
)
.then(() => {
this._panel.dispose();
return;
});
}
default:
break;
}
},
undefined,
this._disposables
);
}
private dispose() {
TargetConfigPanel.currentPanel = undefined;
this._panel.dispose();
while (this._disposables.length) {
const disposable = this._disposables.pop();
if (disposable) {
disposable.dispose();
}
}
}
}

View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
},
"exclude": [
"node_modules",
".vscode-test"
]
}