diff --git a/.gitignore b/.gitignore index fbd9b8e31..fa9e05133 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ product-mini/platforms/linux-sgx/enclave-sample/iwasm build_out tests/wamr-test-suites/workspace + +!/test-tools/wamr-ide/VSCode-Extension/.vscode \ No newline at end of file diff --git a/test-tools/wamr-ide/Media/Config_building_target.png b/test-tools/wamr-ide/Media/Config_building_target.png new file mode 100644 index 000000000..a1270007f Binary files /dev/null and b/test-tools/wamr-ide/Media/Config_building_target.png differ diff --git a/test-tools/wamr-ide/Media/build_folder.png b/test-tools/wamr-ide/Media/build_folder.png new file mode 100644 index 000000000..f69c5b3d6 Binary files /dev/null and b/test-tools/wamr-ide/Media/build_folder.png differ diff --git a/test-tools/wamr-ide/Media/build_terminal.png b/test-tools/wamr-ide/Media/build_terminal.png new file mode 100644 index 000000000..a65ae9b2d Binary files /dev/null and b/test-tools/wamr-ide/Media/build_terminal.png differ diff --git a/test-tools/wamr-ide/Media/change_workspace_dialog.png b/test-tools/wamr-ide/Media/change_workspace_dialog.png new file mode 100644 index 000000000..9f6a61428 Binary files /dev/null and b/test-tools/wamr-ide/Media/change_workspace_dialog.png differ diff --git a/test-tools/wamr-ide/Media/compilation_config.png b/test-tools/wamr-ide/Media/compilation_config.png new file mode 100644 index 000000000..43c60a21f Binary files /dev/null and b/test-tools/wamr-ide/Media/compilation_config.png differ diff --git a/test-tools/wamr-ide/Media/compilation_config_2.png b/test-tools/wamr-ide/Media/compilation_config_2.png new file mode 100644 index 000000000..c16bb09ec Binary files /dev/null and b/test-tools/wamr-ide/Media/compilation_config_2.png differ diff --git a/test-tools/wamr-ide/Media/debug.png b/test-tools/wamr-ide/Media/debug.png new file mode 100644 index 000000000..15c8e8de6 Binary files /dev/null and b/test-tools/wamr-ide/Media/debug.png differ diff --git a/test-tools/wamr-ide/Media/decoration_for_files.png b/test-tools/wamr-ide/Media/decoration_for_files.png new file mode 100644 index 000000000..dd5c0bd74 Binary files /dev/null and b/test-tools/wamr-ide/Media/decoration_for_files.png differ diff --git a/test-tools/wamr-ide/Media/install_from_vsix.png b/test-tools/wamr-ide/Media/install_from_vsix.png new file mode 100644 index 000000000..f313ec590 Binary files /dev/null and b/test-tools/wamr-ide/Media/install_from_vsix.png differ diff --git a/test-tools/wamr-ide/Media/new_project_page.png b/test-tools/wamr-ide/Media/new_project_page.png new file mode 100644 index 000000000..b498aca4b Binary files /dev/null and b/test-tools/wamr-ide/Media/new_project_page.png differ diff --git a/test-tools/wamr-ide/Media/open_project_page.png b/test-tools/wamr-ide/Media/open_project_page.png new file mode 100644 index 000000000..d4e534307 Binary files /dev/null and b/test-tools/wamr-ide/Media/open_project_page.png differ diff --git a/test-tools/wamr-ide/Media/project_template.png b/test-tools/wamr-ide/Media/project_template.png new file mode 100644 index 000000000..e10b102ec Binary files /dev/null and b/test-tools/wamr-ide/Media/project_template.png differ diff --git a/test-tools/wamr-ide/Media/right_click_menus_1.png b/test-tools/wamr-ide/Media/right_click_menus_1.png new file mode 100644 index 000000000..0649f2f49 Binary files /dev/null and b/test-tools/wamr-ide/Media/right_click_menus_1.png differ diff --git a/test-tools/wamr-ide/Media/right_click_menus_2.png b/test-tools/wamr-ide/Media/right_click_menus_2.png new file mode 100644 index 000000000..9d8e32216 Binary files /dev/null and b/test-tools/wamr-ide/Media/right_click_menus_2.png differ diff --git a/test-tools/wamr-ide/Media/run.png b/test-tools/wamr-ide/Media/run.png new file mode 100644 index 000000000..7a98e0fbb Binary files /dev/null and b/test-tools/wamr-ide/Media/run.png differ diff --git a/test-tools/wamr-ide/Media/save_configuration.png b/test-tools/wamr-ide/Media/save_configuration.png new file mode 100644 index 000000000..39a5c1c4a Binary files /dev/null and b/test-tools/wamr-ide/Media/save_configuration.png differ diff --git a/test-tools/wamr-ide/Media/set_up_workspace_message.png b/test-tools/wamr-ide/Media/set_up_workspace_message.png new file mode 100644 index 000000000..c812a59a4 Binary files /dev/null and b/test-tools/wamr-ide/Media/set_up_workspace_message.png differ diff --git a/test-tools/wamr-ide/Media/wamr_ide_main_menu.png b/test-tools/wamr-ide/Media/wamr_ide_main_menu.png new file mode 100644 index 000000000..d9ac2fddd Binary files /dev/null and b/test-tools/wamr-ide/Media/wamr_ide_main_menu.png differ diff --git a/test-tools/wamr-ide/README.md b/test-tools/wamr-ide/README.md new file mode 100644 index 000000000..7e11695bd --- /dev/null +++ b/test-tools/wamr-ide/README.md @@ -0,0 +1,178 @@ +# WAMR-IDE + +## Introduction + +The WAMR-IDE is an Integrated Development Environment to develop WebAssembly application with coding, compiling and source debugging support. It contains 3 components: `VSCode extension`, `WASM-toolchain-provider docker image` and `WASM source debug server docker image`. + +- `VSCode extension` is an extension which can be installed in `vscode`, with which user can build and manage projects, develop `wasm application`, including `building`, `running` and `debugging`. + +- `WASM-toolchain-provider` is a docker image which provides building environment for wasm. + +- `WASM source debug server` is a docker image which provides running and source debugging environment for wasm application. + +--- + +## How to setup WAMR IDE + +1. Install `VSCode` on host. + + - make sure the version of [vscode](https://code.visualstudio.com/Download) you installed is at least _1.59.0_ + +2. Install `Docker` on host. + + 1. [Windows: Docker Desktop](https://docs.docker.com/desktop/windows/install/) + 2. [Ubuntu: Docker Engine](https://docs.docker.com/engine/install/ubuntu) + ```xml + OS requirements: + To install Docker Engine, you need the 64-bit version of one of these Ubuntu versions: + - Ubuntu Impish 21.10 + - Ubuntu Hirsute 21.04 + - Ubuntu Focal 20.04(LTS) + - Ubuntu Bionic 18.04(LTS) + ``` + +3. Build docker images + + We have 2 docker images which should be built or loaded on your host, `wasm-toolchain-provider` and `wamr-debug-server`. To build these 2 images, please enter the `WASM_Source_Debug_Server/Docker` & `WASM_Toolchain/Docker`, then execute the `build_docker_image` script respectively. + + ```shell + $ cd WASM_Toolchain/Docker + $ ./build_docker_image.bat # or ./build_docker_image.sh on linux + $ cd WASM_Source_Debug_Server/Docker + $ ./build_docker_image.bat # or ./build_docker_image.sh on linux + ``` + +4. Generate wamride extension package file + + `wamride-0.0.1.vsix` can be packaged by [`npm vsce`](https://code.visualstudio.com/api/working-with-extensions/publishing-extension). + + > Note that patched `lldb` should be built and put into the `VSCode_Extension/resource/debug` folder before your package or extension debug process if you want to enable `source debugging` feature. Please follow this [instruction](../../doc/source_debugging.md#debugging-with-interpreter) to build `lldb`. + > + > **You can also debug the extension directly follow this [instruction](./VSCode_Extension/README.md) without packing the extension.** + +5. Install extension from vsix + + ![install_from_vsix](./Media/install_from_vsix.png "install wamr-ide from vsix") + + and select `wamride-0.0.1.vsix` which you have saved on your host. + +--- + +## How to use `wamr-ide` + +#### `WAMR-IDE` extension contains 2 components as following picture showing. `Project management` and `Execution management` + +![wamr_ide_main_menu](../Media/wamr_ide_main_menu.png "wamr-ide main menu") + +### Project Management + +1. New project + + When you click `New project` button, extension will pop up a message box at the bottom right of the screen as picture showing. + + ![set_up_workspace_message](./Media/set_up_workspace_message.png "set up workspace message box") + + You can click `Set up now` and select the target folder to create project workspace, click `Maybe later` to close the message box. + + > Note that your selected workspace folder should be **empty**. + + After setting up workspace, extension will prompt successful message: + + ```xml + workspace set up successfully! + ``` + + Then click `New project` button again, a new page will show as following. + + ![new_project_page](./Media/new_project_page.png "new project page") + + Enter the `project name` and select the `template`, then click `Create` button. And a new project will be generated and opened in your current `vscode window` or in a new `vscode window`. + + > Opening in current windows or a new one depends on whether your `vscode's explorer` is empty or not. If empty, open in current window, or open in the new vscode window. + + A new initialized project is as following picture shows. + + ![project_template](./Media/project_template.png "default project template") + + `.wamr` is the project configuration folder which contains 3 files, `CMakeLists.txt`, `project.cmake` and `compilation_config.json`. `CMakeLists.txt` is used to build `wasm target` and the `project.cmake` is included in `CMakeLists.txt`. `project.cmake` includes the user's customized configuration like those folders which should be added include path. + +2. Open project + + Click `Open project` button, `quick-pick-box` will show as following. All projects under your current workspace will be shown and can be selected. + + ![configuration file](./Media/open_project_page.png "configuration file") + +3. Change workspace + + Click `Change workspace` button, a dialog will show as following. You can select 1 folder in file system as workspace, and the new workspace path will override previous workspace, and all new created projects will be generated in the new workspace. + + ![change workspace ](./Media/change_workspace_dialog.png "change workspace dialog") + +4. Configuration + Click `Configuration` button, a new page will be shown as following. You can config building target with `Include paths`, `Initial & Max linear memory`, `stack size`, `exported_symbols` and `include paths`, `exclude files`. + + ![config building target](./Media/Config_building_target.png "config building target") + + Then click `Modify` button to confirm, if configurations are modified successfully and following message will pop. Click `OK`, the page will be auto closed. + + ![save configuration](./Media/save_configuration.png "save configuration") + + And all configuration will be saved in `.wamr/compilation_config.json`. + + ![configuration file](./Media/compilation_config.png "configuration file") + +5. Customize `include paths` and `exclude source files` + + Extension supports adding header file folder to `include path` and excluding source file from build. + +- `Add to include path` + + - Move the cursor to the `folder` and right click, then `menus` will be shown as following. Click `Toggle state of path including`. + + ![right click menus](./Media/right_click_menus_1.png "right click menus") + +- `Exclude source file from build` + + - Move the cursor to the `source file` and right click, then `menus` will be shown as following. Click `Toggle state of excluding`. + + ![right click menus](./Media/right_click_menus_2.png "right click menus") + + #### After setting up `include path` and `exclude files`, the corresponding folder and files will be decorated with color and icon as following picture shows. + + ![decoration for files](./Media/decoration_for_files.png "decoration for files") + + At the same time, all added `include path` and `exclude files` will be saved in `.wamr/compilation_config.json` as json array. + + ![compilation config](./Media/compilation_config_2.png "compilation config") + +> `Toggle state of path including` just shows when selecting `folder` and hides with other resources. +> +> `Toggle state of excluding` just shows when selecting `[.c | .cpp | .cxx] source files` and hides with other resources. + +### Execution Management + +1. `Build` + + When you have completed coding and ready to build target, click `build` button and the `wasm-toolchain` will auto start a container and execute the building process. + + ![build terminal output](./Media/build_terminal.png "build terminal output") + + After successful building execution, `build` folder will be generated in `explorer`, in which `${output_file_name}.wasm` is exist. + + ![build folder](./Media/build_folder.png "build folder") + + > Note that to start `docker service` firstly. + +2. `Run` + + Click `Run` button and `wasm-debug-server` docker image will auto start a container and execute the running process. + + ![run](./Media/run.png "run wasm") + +3. `Debug` + + Click `Debug` button will trigger start ip `wamr-debug-server` docker image, and boot up `lldb debug server` inside of iwasm. Then start a debugging session with configuration to connect. Tap `F11` or click `step into` to start debugging. + + ![debug](./Media/debug.png "source debugging") + + > Docker containers will be auto stopped and removed after the execution. diff --git a/test-tools/wamr-ide/VSCode-Extension/.eslintrc.json b/test-tools/wamr-ide/VSCode-Extension/.eslintrc.json new file mode 100644 index 000000000..f9b22b793 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/.eslintrc.json @@ -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" + ] +} diff --git a/test-tools/wamr-ide/VSCode-Extension/.gitignore b/test-tools/wamr-ide/VSCode-Extension/.gitignore new file mode 100644 index 000000000..a5697da9f --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/.gitignore @@ -0,0 +1,7 @@ +out +dist +node_modules +.vscode-test/ +*.vsix +package-lock.json +src/test \ No newline at end of file diff --git a/test-tools/wamr-ide/VSCode-Extension/.prettierrc.json b/test-tools/wamr-ide/VSCode-Extension/.prettierrc.json new file mode 100644 index 000000000..3c85a97fe --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/.prettierrc.json @@ -0,0 +1,12 @@ +{ + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "bracketSpacing": true, + "jsxBracketSameLine": false, + "arrowParens": "avoid", + "proseWrap": "always" +} diff --git a/test-tools/wamr-ide/VSCode-Extension/.vscode/extensions.json b/test-tools/wamr-ide/VSCode-Extension/.vscode/extensions.json new file mode 100644 index 000000000..0d8ed8b15 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint"] +} diff --git a/test-tools/wamr-ide/VSCode-Extension/.vscode/launch.json b/test-tools/wamr-ide/VSCode-Extension/.vscode/launch.json new file mode 100644 index 000000000..b75b8ccaa --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/.vscode/launch.json @@ -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}" + } + ] +} diff --git a/test-tools/wamr-ide/VSCode-Extension/.vscode/tasks.json b/test-tools/wamr-ide/VSCode-Extension/.vscode/tasks.json new file mode 100644 index 000000000..65a9dcf8e --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/.vscode/tasks.json @@ -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 + } + } + ] +} diff --git a/test-tools/wamr-ide/VSCode-Extension/.vscodeignore b/test-tools/wamr-ide/VSCode-Extension/.vscodeignore new file mode 100644 index 000000000..fbd882434 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/.vscodeignore @@ -0,0 +1,12 @@ +.vscode/** +.vscode-test/** +out/test/** + +src/** +.gitignore +.yarnrc +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts diff --git a/test-tools/wamr-ide/VSCode-Extension/README.md b/test-tools/wamr-ide/VSCode-Extension/README.md new file mode 100644 index 000000000..65d5ecaea --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/README.md @@ -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) diff --git a/test-tools/wamr-ide/VSCode-Extension/package.json b/test-tools/wamr-ide/VSCode-Extension/package.json new file mode 100644 index 000000000..1d8b1c8a7 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/package.json @@ -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" + } +} diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/debug/README.md b/test-tools/wamr-ide/VSCode-Extension/resource/debug/README.md new file mode 100644 index 000000000..e672d8b22 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/debug/README.md @@ -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. diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/CMakeLists.txt b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/CMakeLists.txt new file mode 100644 index 000000000..4752623d5 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/CMakeLists.txt @@ -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}) + diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.bat b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.bat new file mode 100644 index 000000000..9b8459b6b --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.bat @@ -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 diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.sh b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.sh new file mode 100644 index 000000000..b963685cf --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.sh @@ -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 \ No newline at end of file diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.bat b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.bat new file mode 100644 index 000000000..aaf89a3d9 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.bat @@ -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 diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.sh b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.sh new file mode 100644 index 000000000..d6b794059 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.sh @@ -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 diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/project.cmake b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/project.cmake new file mode 100644 index 000000000..7248979cf --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/project.cmake @@ -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) + diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.bat b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.bat new file mode 100644 index 000000000..5e7ac83f4 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.bat @@ -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 diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.sh b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.sh new file mode 100644 index 000000000..b8087a823 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.sh @@ -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 \ No newline at end of file diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/webview/css/style.css b/test-tools/wamr-ide/VSCode-Extension/resource/webview/css/style.css new file mode 100644 index 000000000..8975ecf7e --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/webview/css/style.css @@ -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; +} diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/webview/js/configbuildtarget.js b/test-tools/wamr-ide/VSCode-Extension/resource/webview/js/configbuildtarget.js new file mode 100644 index 000000000..58b5afe23 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/webview/js/configbuildtarget.js @@ -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, + }); +} diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/webview/js/newproj.js b/test-tools/wamr-ide/VSCode-Extension/resource/webview/js/newproj.js new file mode 100644 index 000000000..30e169788 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/webview/js/newproj.js @@ -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; + } + }); +} diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/webview/page/configBuildTarget.html b/test-tools/wamr-ide/VSCode-Extension/resource/webview/page/configBuildTarget.html new file mode 100644 index 000000000..b4c431511 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/webview/page/configBuildTarget.html @@ -0,0 +1,63 @@ + + + + + + + + + + + New project + + + +
+
+

Config building target

+ +
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+ +
+
+
+ Modify +
+
+
+
+ + + diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/webview/page/newProject.html b/test-tools/wamr-ide/VSCode-Extension/resource/webview/page/newProject.html new file mode 100644 index 000000000..71e67bd89 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/resource/webview/page/newProject.html @@ -0,0 +1,55 @@ + + + + + + + + + + + Create project + + + +
+
+

Create project

+ +
+
+ +
+
+
+ + +
+ +
+ + + + default + +
+
+
+ +
+
+ +
+
+
+ Create +
+
+
+
+ + + diff --git a/test-tools/wamr-ide/VSCode-Extension/src/debug/debugConfigurationProvider.ts b/test-tools/wamr-ide/VSCode-Extension/src/debug/debugConfigurationProvider.ts new file mode 100644 index 000000000..09793d791 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/debug/debugConfigurationProvider.ts @@ -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 + | undefined = undefined; + + private wasmDebugConfig!: vscode.DebugConfiguration; + + public resolveDebugConfiguration(): + | Thenable + | 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; + } +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/explorer/decorationProvider.ts b/test-tools/wamr-ide/VSCode-Extension/src/explorer/decorationProvider.ts new file mode 100644 index 000000000..39b269571 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/explorer/decorationProvider.ts @@ -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; + + 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 { + 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(); diff --git a/test-tools/wamr-ide/VSCode-Extension/src/extension.ts b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts new file mode 100644 index 000000000..845162129 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts @@ -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); + } +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/taskProvider.ts b/test-tools/wamr-ide/VSCode-Extension/src/taskProvider.ts new file mode 100644 index 000000000..323e97f4c --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/taskProvider.ts @@ -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, + public _script: Map + ) {} + + buildShellOption: OwnShellOption | undefined; + runShellOption: OwnShellOption | undefined; + debugShellOption: OwnShellOption | undefined; + + private wasmPromise: Thenable | undefined = undefined; + + public provideTasks(): Thenable | 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; + } +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts b/test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts new file mode 100644 index 000000000..eab82090d --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts @@ -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; + } +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/utilities/getUri.ts b/test-tools/wamr-ide/VSCode-Extension/src/utilities/getUri.ts new file mode 100644 index 000000000..d897489d7 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/utilities/getUri.ts @@ -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)); +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/view/NewProjectPanel.ts b/test-tools/wamr-ide/VSCode-Extension/src/view/NewProjectPanel.ts new file mode 100644 index 000000000..17732ec00 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/view/NewProjectPanel.ts @@ -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(); + } + } + } +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/view/TargetConfigPanel.ts b/test-tools/wamr-ide/VSCode-Extension/src/view/TargetConfigPanel.ts new file mode 100644 index 000000000..1f4a544aa --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/view/TargetConfigPanel.ts @@ -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(); + } + } + } +} diff --git a/test-tools/wamr-ide/VSCode-Extension/tsconfig.json b/test-tools/wamr-ide/VSCode-Extension/tsconfig.json new file mode 100644 index 000000000..b65c74510 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/tsconfig.json @@ -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" + ] +} diff --git a/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/Dockerfile b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/Dockerfile new file mode 100644 index 000000000..fd24b0cde --- /dev/null +++ b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/Dockerfile @@ -0,0 +1,32 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +FROM gcc:9.3.0 AS BASE + +## set work directory +WORKDIR /root/ +COPY resource /root/ + +## - download cmake with wget and set up +RUN wget https://github.com/Kitware/CMake/releases/download/v3.21.1/cmake-3.21.1-linux-x86_64.tar.gz \ + && tar -zxvf cmake-3.21.1-linux-x86_64.tar.gz \ + && rm -f cmake-3.21.1-linux-x86_64.tar.gz \ + && mv cmake-3.21.1-linux-x86_64 /opt/cmake \ + && ln -s /opt/cmake/bin/cmake /bin/cmake \ + && apt-get install make + +## -clone wamr-repo and build iwasm +RUN git clone -b main --depth=1 https://github.com/bytecodealliance/wasm-micro-runtime.git \ + && cd /root/wasm-micro-runtime/product-mini/platforms/linux \ + && mkdir build && cd build \ + && cmake .. -DWAMR_BUILD_DEBUG_INTERP=1 && make \ + && cp /root/wasm-micro-runtime/product-mini/platforms/linux/build/iwasm /root/iwasm \ + && rm -fr /root/wasm-micro-runtime + +FROM ubuntu:20.04 +# COPY files from BASE image +COPY --from=BASE /root/iwasm /root +COPY --from=BASE /root/debug.sh /root +COPY --from=BASE /root/run.sh /root + +WORKDIR /root/ \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/README.md b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/README.md new file mode 100644 index 000000000..a21165657 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/README.md @@ -0,0 +1,15 @@ +### Build Docker Image + +- execute `build_docker_image.bat` on `Windows` +- execute `build_docker_image.sh` on `Linux` + + ```shell + chmod +x resource/* + ./build_docker_image.sh + ``` + +### Resource Details + +- `Dockerflie` is the source file to build `wasm-debug-server` docker image +- `resource/debug.sh` is the script to execute the `/mnt/build/${target}.wasm` in debug mode, will start up the debugger server inside of the `iwasm` and hold to wait for connecting. +- `resource/run.sh` is the script to execute the `/mnt/build/${target}.wasm` directly. diff --git a/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/build_docker_image.bat b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/build_docker_image.bat new file mode 100644 index 000000000..54a224ef2 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/build_docker_image.bat @@ -0,0 +1,8 @@ +@REM Copyright (C) 2019 Intel Corporation. All rights reserved. +@REM SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +@echo off +docker build -t wasm-debug-server:1.0 . + +@REM delete intermediate docker image +sudo docker image prune -f \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/build_docker_image.sh b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/build_docker_image.sh new file mode 100644 index 000000000..6cf95dbef --- /dev/null +++ b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/build_docker_image.sh @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#!/bin/bash +docker build -t wasm-debug-server:1.0 . + +# delete intermediate docker image +sudo docker image prune -f \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/resource/debug.sh b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/resource/debug.sh new file mode 100644 index 000000000..48458870f --- /dev/null +++ b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/resource/debug.sh @@ -0,0 +1,6 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#!/bin/bash +TARGET=$1 +./iwasm -g=0.0.0.0:1234 /mnt/build/${TARGET}.wasm \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/resource/run.sh b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/resource/run.sh new file mode 100644 index 000000000..4e3acecba --- /dev/null +++ b/test-tools/wamr-ide/WASM-Source-Debug-Server/Docker/resource/run.sh @@ -0,0 +1,6 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#!/bin/bash +TARGET=$1 +./iwasm /mnt/build/${TARGET}.wasm \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/.dockerignore b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/.dockerignore new file mode 100644 index 000000000..a49cbd220 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/.dockerignore @@ -0,0 +1,4 @@ +# move unnecessary files here to save build time cost and image size +*.md +*.sh +*.bat \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/Dockerfile b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/Dockerfile new file mode 100644 index 000000000..8f9da4d48 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/Dockerfile @@ -0,0 +1,63 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +## Build docker image that consists of gcc, cmake, wasi-sdk +FROM gcc:9.3.0 AS BASE + +## set work directory +WORKDIR /root/ + +COPY resource /root/ + +## set compilation environment for wamrc +# - cmake +# - wasi-sdk +# - wamr-sdk + +## - download cmake with wget and set up +RUN wget https://github.com/Kitware/CMake/releases/download/v3.21.1/cmake-3.21.1-linux-x86_64.tar.gz \ + && tar -zxvf cmake-3.21.1-linux-x86_64.tar.gz \ + && rm -f cmake-3.21.1-linux-x86_64.tar.gz \ + && mv cmake-3.21.1-linux-x86_64 /opt/cmake \ + && ln -s /opt/cmake/bin/cmake /bin/cmake \ + && apt-get install make + +## - download wasi-sdk with wget and set up to /opt/wasi-sdk +RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz \ + && tar -zxvf wasi-sdk-12.0-linux.tar.gz \ + && rm -f wasi-sdk-12.0-linux.tar.gz + +RUN git clone -b main --depth=1 https://github.com/bytecodealliance/wasm-micro-runtime.git \ + && cd /root/wasm-micro-runtime/wamr-compiler \ + && ./build_llvm.sh \ + # - build wamrc + && cd /root/wasm-micro-runtime/wamr-compiler \ + && mkdir build \ + && cd build \ + && cmake .. \ + && make \ + # - copy the wamrc to /root + && cp /root/wasm-micro-runtime/wamr-compiler/build/wamrc /root/wamrc \ + && mkdir -p /opt/wamr-sdk/app \ + && cp -r /root/wasm-micro-runtime/wamr-sdk/app/libc-builtin-sysroot /opt/wamr-sdk/app/ \ + # - remove the wamr repo to save the size + && rm -fr /root/wasm-micro-runtime + +## STAGE 2 +FROM ubuntu:20.04 +RUN mkdir -p /opt/wasi-sdk \ + && mkdir -p /opt/cmake \ + && mkdir -p /opt/wamr-sdk/app + +# COPY files from BASE image +COPY --from=BASE /opt/cmake/ /opt/cmake/ +COPY --from=BASE /opt/wamr-sdk/app/ /opt/wamr-sdk/app/ +COPY --from=BASE /root/wasi-sdk-12.0/ /opt/wasi-sdk/ +COPY --from=BASE /root/wamrc /root +COPY --from=BASE /root/build_wasm.sh /root + +RUN ln -s /opt/cmake/bin/cmake /usr/bin/cmake \ + && ln -s /root/wamrc /usr/bin/wamrc +RUN apt-get update && apt-get install make + +WORKDIR /root \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/README.md b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/README.md new file mode 100644 index 000000000..ceacc77f3 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/README.md @@ -0,0 +1,71 @@ +# WASM Toolchain Provider Introduction + +## Files on HOST + +#### Dockerfile + +- ubuntu : 20.04 +- set up the necessary toolchains + - WASI-SDK (version: 12.0) + - WAMR-SDK + - repo: bytecode-alliance/wasm-micro-runtime + - branch: main + - LLVM (latest repo build) + - CMake (version: 3.21.1) + +#### build_docker_image.sh + +- the script to build docker image for Linux platform +- tag: 1.0 + +#### build_docker_image.bat + +- the script to build docker image for windows platform +- tag: 1.0 + +#### run_container.sh + +- the script to start and run the docker container for Linux platform +- mount `host directory` and `container directory` + - temporally using `$(pwd)/host_mnt_test` in **host** and `/mnt` in **container** +- set docker container name with `--name` + - temporally set to _wasm-toolchain-ctr_ + +#### run_container.bat + +- the script to start and run the docker container for windows platform + +## Files inside docker + +### `wamrc` + +### `wasi-sdk` + +# Build Docker Image + +- Linux + +```shell +chmod +x resource/* +./build_docker_image.sh +``` + +- Windows + +```shell +./build_docker_image.bat +``` + +# Run Docker Container + +- Linux + +```shell +./run_container.sh +``` + +- Windows + +```shell +./run_container.bat +``` diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/build_docker_image.bat b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/build_docker_image.bat new file mode 100644 index 000000000..4438ce562 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/build_docker_image.bat @@ -0,0 +1,12 @@ +@REM Copyright (C) 2019 Intel Corporation. All rights reserved. +@REM SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +@echo off + +@REM pull gcc and ubuntu image firstly no matter whether exist or not. +docker pull gcc:9.3.0 +docker pull ubuntu:20.04 +docker build -t wasm-toolchain-provider:1.0 . + +@REM delete intermediate docker image +sudo docker image prune -f diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/build_docker_image.sh b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/build_docker_image.sh new file mode 100644 index 000000000..0ad021440 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/build_docker_image.sh @@ -0,0 +1,10 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#!/bin/bash +sudo docker pull gcc:9.3.0 +sudo docker pull ubuntu:20.04 +sudo docker build -t wasm-toolchain-provider:1.0 . + +# delete intermediate docker image +sudo docker image prune -f diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/resource/build_wasm.sh b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/resource/build_wasm.sh new file mode 100644 index 000000000..8b98a10cc --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/resource/build_wasm.sh @@ -0,0 +1,24 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#!/bin/bash +export CC=/opt/wasi-sdk/bin/clang +export CXX=/opt/wasi-sdk/bin/clang++ + +if [ -d /mnt/build ];then + rm -fr /mnt/build +fi + +mkdir -p /mnt/build +cd /mnt/build/ +echo "========> compile wasm with wasi-sdk" +cmake ../.wamr && make + +echo && echo +echo "========> compile wasm to AoT with wamrc" +# target name will be provided: +# - user input the target name in IDE +# - generated wasm binary name will be set as user's input target name +# - aot binary name should be the same as wasm binary name +# - target name will be provided through 1st parameter +wamrc -o $1.aot $1.wasm \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/run_container.bat b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/run_container.bat new file mode 100644 index 000000000..efab908d2 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/run_container.bat @@ -0,0 +1,16 @@ +@REM Copyright (C) 2019 Intel Corporation. All rights reserved. +@REM SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +@echo off + +@REM # create mount directory on host + +if not exist host_mnt ( + md host_mnt +) + +docker run -it ^ + -v %cd%\host_mnt:/mnt ^ + --name wasm-toolchain-provider-ctr ^ + wasm-toolchain-provider:1.0 ^ + /bin/bash \ No newline at end of file diff --git a/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/run_container.sh b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/run_container.sh new file mode 100644 index 000000000..b9bc91ef9 --- /dev/null +++ b/test-tools/wamr-ide/WASM-Toolchain-Provider/Docker/run_container.sh @@ -0,0 +1,15 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#!/bin/bash +set -x + +# create mount directory on host +if [ ! -d host_mnt ];then + mkdir host_mnt +fi + +sudo docker run --name=wasm-toolchain-provider-ctr \ + -it -v $(pwd)/host_mnt:/mnt \ + wasm-toolchain-provider:1.0 \ + /bin/bash